blob: ef35563970c4328e7d01dfc763f948838c04c72a [file] [log] [blame]
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();