var __screenCapturePageContext__ = { | |
clone: function(object) { | |
function StubObj() { } | |
StubObj.prototype = object; | |
var newObj = new StubObj(); | |
newObj.getInternalObject = function() { | |
return this.__proto__; | |
} | |
newObj.toString = function() { | |
try { | |
return this.__proto__.toString(); | |
} catch (e) { | |
return 'object Object'; | |
} | |
} | |
return newObj; | |
}, | |
bind: function(newThis, func) { | |
var args = []; | |
for(var i = 2;i < arguments.length; i++) { | |
args.push(arguments[i]); | |
} | |
return function() { | |
return func.apply(newThis, args); | |
} | |
}, | |
bodyWrapperDelegate_: null, | |
currentHookStatus_: false, | |
scrollValueHooker: function(oldValue, newValue, reason) { | |
// When we hook the value of scrollLeft/Top of body, it always returns 0. | |
return 0; | |
}, | |
toggleBodyScrollValueHookStatus: function() { | |
this.currentHookStatus_ = !this.currentHookStatus_; | |
if (this.currentHookStatus_) { | |
var This = this; | |
try { | |
document.__defineGetter__('body', function() { | |
return This.bodyWrapperDelegate_.getWrapper(); | |
}); | |
} catch (e) { | |
window.console.log('error' + e); | |
} | |
this.bodyWrapperDelegate_.watch('scrollLeft', this.scrollValueHooker); | |
this.bodyWrapperDelegate_.watch('scrollTop', this.scrollValueHooker); | |
} else { | |
this.bodyWrapperDelegate_.unwatch('scrollLeft', this.scrollValueHooker); | |
this.bodyWrapperDelegate_.unwatch('scrollTop', this.scrollValueHooker); | |
var This = this; | |
try { | |
document.__defineGetter__('body', function() { | |
return This.bodyWrapperDelegate_.getWrapper().getInternalObject(); | |
}); | |
} catch (e) { | |
window.console.log('error' + e); | |
} | |
} | |
}, | |
checkHookStatus: function() { | |
var needHookScrollValue = document.documentElement.getAttributeNode( | |
'__screen_capture_need_hook_scroll_value__'); | |
needHookScrollValue = | |
!!(needHookScrollValue && needHookScrollValue.nodeValue == 'true'); | |
if (this.currentHookStatus_ != needHookScrollValue) | |
this.toggleBodyScrollValueHookStatus(); | |
}, | |
init: function() { | |
if (!this.bodyWrapperDelegate_) { | |
this.bodyWrapperDelegate_ = | |
new __screenCapturePageContext__.ObjectWrapDelegate( | |
document.body, '^(DOCUMENT_[A-Z_]+|[A-Z_]+_NODE)$'); | |
document.documentElement.addEventListener( | |
'__screen_capture_check_hook_status_event__', | |
__screenCapturePageContext__.bind(this, this.checkHookStatus)); | |
} | |
} | |
}; | |
// ObjectWrapDelegate class will create a empty object(wrapper), map its | |
// prototype to the 'originalObject', then search all non-function properties | |
// (except those properties which match the propertyNameFilter) of the | |
// 'orginalObject' and set corresponding getter/setter to the wrapper. | |
// Then you can manipulate the wrapper as 'originalObject' because the wrapper | |
// use the corresponding getter/setter to access the corresponding properties in | |
// 'originalObject' and the all function calls can be call through the prototype | |
// inherit. | |
// After createing the wrapper object, you can use watch method to monitor any | |
// property which you want to know when it has been read(get) or change(set). | |
// Please see the detail comment on method watch. | |
// Remember the ObjectWrapDelegate returns the wrapDelegateObject instead of | |
// really wrapper object. You have to use ObjectWrapDelegate.getWrapper to get | |
// real wrapper object. | |
// parameter @originalObject, object which you want to wrap | |
// parameter @propertyNameFilter, string, regular expression pattern string for | |
// those properties you don't put in the wrap object. | |
__screenCapturePageContext__.ObjectWrapDelegate = function( | |
originalObject, propertyNameFilter) { | |
this.window_ = window; | |
// The wrapper is the object we use to wrap the 'originalObject'. | |
this.wrapper_ = __screenCapturePageContext__.clone(originalObject); | |
// This array saves all properties we set our getter/setter for them. | |
this.properties_ = []; | |
// This object to save all watch handlers. Each watch handler is bind to one | |
// certain property which is in properties_. | |
this.watcherTable_ = {}; | |
// Check the propertyNameFilter parameter. | |
if (typeof propertyNameFilter == 'undefined') { | |
propertyNameFilter = ''; | |
} else if (typeof propertyNameFilter != 'string') { | |
try { | |
propertyNameFilter = propertyNameFilter.toString(); | |
} catch (e) { | |
propertyNameFilter = ''; | |
} | |
} | |
if (propertyNameFilter.length) { | |
this.propertyNameFilter_ = new RegExp(''); | |
this.propertyNameFilter_.compile(propertyNameFilter); | |
} else { | |
this.propertyNameFilter_ = null; | |
} | |
// For closure to access the private data of class. | |
var This = this; | |
// Set the getter object. | |
function setGetterAndSetter(wrapper, propertyName) { | |
wrapper.__defineGetter__(propertyName, function() { | |
var internalObj = this.getInternalObject(); | |
var originalReturnValue = internalObj[propertyName]; | |
var returnValue = originalReturnValue; | |
// See whether this property has been watched. | |
var watchers = This.watcherTable_[propertyName]; | |
if (watchers) { | |
// copy the watcher to a cache in case someone call unwatch inside the | |
// watchHandler. | |
var watchersCache = watchers.concat(); | |
for (var i = 0, l = watchersCache.length; i < l; ++i) { | |
var watcher = watchersCache[i]; | |
if (!watcher) { | |
window.console.log('wrapper\'s watch for ' + propertyName + | |
' is unavailable!'); | |
continue; // should never happend | |
} | |
originalReturnValue = returnValue; | |
try { | |
returnValue = watcher(returnValue, returnValue, 'get'); | |
} catch (e) { | |
returnValue = originalReturnValue; | |
} | |
} | |
} | |
return returnValue; | |
}); | |
// Set the setter object. | |
wrapper.__defineSetter__(propertyName, function(value) { | |
var internalObj = this.getInternalObject(); | |
var originalValue = value; | |
var userValue = originalValue; | |
var oldValue; | |
try { | |
oldValue = internalObj[propertyName]; | |
} catch (e) { | |
oldValue = null; | |
} | |
// See whether this property has been watched. | |
var watchers = This.watcherTable_[propertyName]; | |
if (watchers) { | |
// copy the watcher to a cache in case someone call unwatch inside the | |
// watchHandler. | |
var watchersCache = watchers.concat(); | |
for (var i = 0, l = watchersCache.length; i < l; ++i) { | |
var watcher = watchersCache[i]; | |
if (!watcher) { | |
window.console.log('wrapper\'s watch for ' + propertyName + | |
' is unavailable!'); | |
continue; // should never happend | |
} | |
originalValue = userValue; | |
try { | |
userValue = watcher(oldValue, userValue, 'set'); | |
} catch (e) { | |
userValue = originalValue; | |
} | |
} | |
} | |
internalObj[propertyName] = userValue; | |
}); | |
}; | |
this.cleanUp_ = function() { | |
This.window_.removeEventListener('unload', This.cleanUp_, false); | |
// Delete all properties | |
for (var i = 0, l = This.properties_.length; i < l; ++i) { | |
delete This.wrapper_[This.properties_[i]]; | |
} | |
This.window_ = null; | |
This.wrapper_ = null; | |
This.properties_ = null; | |
This.watcherTable_ = null; | |
This.propertyNameFilter_ = null; | |
This = null; | |
} | |
// We only bridge the non-function properties. | |
for (var prop in originalObject) { | |
if (this.propertyNameFilter_ && this.propertyNameFilter_.test(prop)) { | |
this.propertyNameFilter_.test(''); | |
continue; | |
} | |
if (typeof originalObject[prop] != 'function') { | |
this.properties_.push(prop); | |
setGetterAndSetter(this.wrapper_, prop); | |
} | |
} | |
// Listen the unload event. | |
this.window_.addEventListener('unload', this.cleanUp_, false); | |
}; | |
__screenCapturePageContext__.ObjectWrapDelegate.prototype.getWrapper = | |
function() { | |
return this.wrapper_; | |
} | |
// Check whether a property is in the wrapper or not. If yes, return true. | |
// Otherwise return false. | |
__screenCapturePageContext__.ObjectWrapDelegate.prototype.hasProperty = | |
function(propertyName) { | |
for (var i = 0, l = this.properties_.length; i < l; ++i) { | |
if (propertyName == this.properties_[i]) | |
return true; | |
} | |
return false; | |
} | |
// Watches for a property to be accessed or be assigned a value and runs a | |
// function when that occurs. | |
// Watches for accessing a property or assignment to a property named prop in | |
// this object, calling handler(oldval, newval, reason) whenever prop is | |
// get/set and storing the return value in that property. | |
// A watchpoint can filter (or nullify) the value assignment, by returning a | |
// modified newval (or by returning oldval). | |
// When watchpoint is trigering by get opeartor, the oldval is equal with | |
// newval. The reason will be 'get'. | |
// When watchpoint is trigering by set opeartor, The reason will be 'set'. | |
// If you delete a property for which a watchpoint has been set, | |
// that watchpoint does not disappear. If you later recreate the property, | |
// the watchpoint is still in effect. | |
// To remove a watchpoint, use the unwatch method. | |
// If register the watchpoint successfully, return true. Otherwise return false. | |
__screenCapturePageContext__.ObjectWrapDelegate.prototype.watch = function( | |
propertyName, watchHandler) { | |
if (!this.hasProperty(propertyName)) | |
return false; | |
var watchers = this.watcherTable_[propertyName]; | |
if (watchers) { | |
for (var i = 0, l = watchers.length; i < l; ++i) { | |
if (watchHandler == watchers[i]) | |
return true; | |
} | |
} else { | |
watchers = new Array(); | |
this.watcherTable_[propertyName] = watchers; | |
} | |
watchers.push(watchHandler); | |
return true; | |
} | |
// Removes a watchpoint set with the watch method. | |
__screenCapturePageContext__.ObjectWrapDelegate.prototype.unwatch = function( | |
propertyName, watchHandler) { | |
if (!this.hasProperty(propertyName)) | |
return false; | |
var watchers = this.watcherTable_[propertyName]; | |
if (watchers) { | |
for (var i = 0, l = watchers.length; i < l; ++i) { | |
if (watchHandler == watchers[i]) { | |
watchers.splice(i, 1); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
__screenCapturePageContext__.init(); |