diff options
Diffstat (limited to 'js/dojo/dojox/lang/observable.js')
| -rw-r--r-- | js/dojo/dojox/lang/observable.js | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/js/dojo/dojox/lang/observable.js b/js/dojo/dojox/lang/observable.js new file mode 100644 index 0000000..e5b1176 --- /dev/null +++ b/js/dojo/dojox/lang/observable.js @@ -0,0 +1,269 @@ +//>>built +// wrapped by build app +define("dojox/lang/observable", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.lang.observable"); +// Used to create a wrapper object with monitored reads and writes +// +dojo.experimental("dojox.lang.observable"); +// IMPORTANT DISCLAIMER: +// This is experimental and based on hideous hacks. +// There are severe limitations on the ability of wrapper objects: +// Only properties that have vbscript-legal names are accessible (similar to JavaScript, but they can't start with an underscore). +// The wrapper objects are not expando in IE, because they are built +// from VBScript objects. This means you can't add new properties after an object is created. +// The wrapper objects can not be used a prototype for other objects. +// Only properties with primitive values can be wrapped. +// This has performance implications as well. +dojox.lang.observable = function(/*Object*/wrapped,/*function*/onRead,/*function*/onWrite,/*function*/onInvoke){ + // summary: + // Creates a wrapper object, which can be observed. The wrapper object + // is a proxy to the wrapped object. If you will be making multiple wrapper + // objects with the same set of listeners, it is recommended that you + // use makeObservable, as it is more memory efficient. + // + // wrapped: + // The object to be wrapped and monitored for property access and modification + // + // onRead: + // See dojox.lang.makeObservable.onRead + // onWrite: + // See dojox.lang.makeObservable.onWrite + // onInvoke: + // See dojox.lang.makeObservable.onInvoke + + return dojox.lang.makeObservable(onRead,onWrite,onInvoke)(wrapped); +} +dojox.lang.makeObservable = function(/*function*/onRead,/*function*/onWrite,/*function*/onInvoke,/*Object*/hiddenFunctions){ + + // summary: + // Creates and returns an observable creator function. All the objects that + // are created with the returned constructor will use the provided onRead and + // onWrite listeners. + // The created constructor should be called with a single argument, + // the object that will be wrapped to be observed. The constructor will + // return the wrapper object. + // + // onRead: + // This is called whenever one of the wrapper objects created + // from the constructor has a property that is accessed. onRead + // will be called with two arguments, the first being the wrapped object, + // and the second is the name of property that is being accessed. + // The value that onRead returns will be used as the value returned + // by the property access + // + // onWrite: + // This is called whenever one of the wrapper objects created + // from the constructor has a property that is modified. onWrite + // will be called with three arguments, the first being the wrapped object, + // the second is the name of property that is being modified, and the + // third is the value that is being set on the property. + // + // onInvoke: + // This is called when a method on the object is invoked. The first + // argument is the wrapper object, the second is the original wrapped object, + // the third is the method name, and the fourth is the arguments. + // + // hiddenFunctions: + // allows you to define functions that should be delegated + // but may not be enumerable on the wrapped objects, so they must be + // explicitly included + // + // example: + // The following could be used to create a wrapper that would + // prevent functions from being accessed on an object: + // | function onRead(obj,prop){ + // | return typeof obj[prop] == 'function' ? null : obj[prop]; + // | } + // | var observable = dojox.lang.makeObservable(onRead,onWrite); + // | var obj = {foo:1,bar:function(){}}; + // | obj = observable(obj); + // | obj.foo -> 1 + // | obj.bar -> null + // + hiddenFunctions = hiddenFunctions || {}; + onInvoke = onInvoke || function(scope,obj,method,args){ + // default implementation for onInvoke, just passes the call through + return obj[method].apply(scope,args); + }; + function makeInvoker(scope,wrapped,i){ + return function(){ + // this is function used for all methods in the wrapper object + return onInvoke(scope,wrapped,i,arguments); + }; + } + + if(dojox.lang.lettableWin){ // create the vb class + var factory = dojox.lang.makeObservable; + factory.inc = (factory.inc || 0) + 1; + // create globals for the getters and setters so they can be accessed from the vbscript + var getName = "gettable_"+factory.inc; + dojox.lang.lettableWin[getName] = onRead; + var setName = "settable_"+factory.inc; + dojox.lang.lettableWin[setName] = onWrite; + var cache = {}; + return function(wrapped){ + if(wrapped.__observable){ // if it already has an observable, use that + return wrapped.__observable; + } + if(wrapped.data__){ + throw new Error("Can wrap an object that is already wrapped"); + } + // create the class + var props = [], i, l; + for(i in hiddenFunctions){ + props.push(i); + } + var vbReservedWords = {type:1,event:1}; + // find the unique signature for the class so we can reuse it if possible + for(i in wrapped){ + if(i.match(/^[a-zA-Z][\w\$_]*$/) && !(i in hiddenFunctions) && !(i in vbReservedWords)){ //can only do properties with valid vb names/tokens and primitive values + props.push(i); + } + } + var signature = props.join(","); + var prop,clazz = cache[signature]; + if(!clazz){ + var tname = "dj_lettable_"+(factory.inc++); + var gtname = tname+"_dj_getter"; + var cParts = [ + "Class "+tname, + " Public data__" // this our reference to the original object + ]; + for(i=0, l=props.length; i<l; i++){ + prop = props[i]; + var type = typeof wrapped[prop]; + if(type == 'function' || hiddenFunctions[prop]){ // functions must go in regular properties for delegation:/ + cParts.push(" Public " + prop); + }else if(type != 'object'){ // the getters/setters can only be applied to primitives + cParts.push( + " Public Property Let "+prop+"(val)", + " Call "+setName+"(me.data__,\""+prop+"\",val)", + " End Property", + " Public Property Get "+prop, + " "+prop+" = "+getName+"(me.data__,\""+prop+"\")", + " End Property"); + } + } + cParts.push("End Class"); + cParts.push( + "Function "+gtname+"()", + " Dim tmp", + " Set tmp = New "+tname, + " Set "+gtname+" = tmp", + "End Function"); + dojox.lang.lettableWin.vbEval(cParts.join("\n")); + + // Put the new class in the cache + cache[signature] = clazz = function(){ + return dojox.lang.lettableWin.construct(gtname); // the class can't be accessed, only called, so we have to wrap it with a function + }; + } + console.log("starting5"); + var newObj = clazz(); + newObj.data__ = wrapped; + console.log("starting6"); + try { + wrapped.__observable = newObj; + } catch(e){ // some objects are not expando + } + for(i = 0, l = props.length; i < l; i++){ + prop = props[i]; + try { + var val = wrapped[prop]; + } + catch(e){ + console.log("error ",prop,e); + } + if(typeof val == 'function' || hiddenFunctions[prop]){ // we can make a delegate function here + newObj[prop] = makeInvoker(newObj,wrapped,prop); + } + } + return newObj; + }; + }else{ + return function(wrapped){ // do it with getters and setters + if(wrapped.__observable){ // if it already has an observable, use that + return wrapped.__observable; + } + var newObj = wrapped instanceof Array ? [] : {}; + newObj.data__ = wrapped; + for(var i in wrapped){ + if(i.charAt(0) != '_'){ + if(typeof wrapped[i] == 'function'){ + newObj[i] = makeInvoker(newObj,wrapped,i); // TODO: setup getters and setters so we can detect when this changes + }else if(typeof wrapped[i] != 'object'){ + (function(i){ + newObj.__defineGetter__(i,function(){ + return onRead(wrapped,i); + }); + newObj.__defineSetter__(i,function(value){ + return onWrite(wrapped,i,value); + }); + })(i); + } + } + } + for(i in hiddenFunctions){ + newObj[i] = makeInvoker(newObj,wrapped,i); + } + wrapped.__observable = newObj; + return newObj; + }; + } +}; +if(!{}.__defineGetter__){ + if(dojo.isIE){ + // to setup the crazy lettable hack we need to + // introduce vb script eval + // the only way that seems to work for adding a VBScript to the page is with a document.write + // document.write is not always available, so we use an iframe to do the document.write + // the iframe also provides a good hiding place for all the global variables that we must + // create in order for JScript and VBScript to interact. + var frame; + if(document.body){ // if the DOM is ready we can add it + frame = document.createElement("iframe"); + document.body.appendChild(frame); + }else{ // other we have to write it out + document.write("<iframe id='dj_vb_eval_frame'></iframe>"); + frame = document.getElementById("dj_vb_eval_frame"); + } + frame.style.display="none"; + var doc = frame.contentWindow.document; + dojox.lang.lettableWin = frame.contentWindow; + doc.write('<html><head><script language="VBScript" type="text/VBScript">' + + 'Function vb_global_eval(code)' + + 'ExecuteGlobal(code)' + + 'End Function' + + '</script>' + + '<script type="text/javascript">' + + 'function vbEval(code){ \n' + // this has to be here to call it from another frame + 'return vb_global_eval(code);' + + '}' + + 'function construct(name){ \n' + // and this too + 'return window[name]();' + + '}' + + '</script>' + + '</head><body>vb-eval</body></html>'); + doc.close(); + }else{ + throw new Error("This browser does not support getters and setters"); + } +} + +dojox.lang.ReadOnlyProxy = +// summary: +// Provides a read only proxy to another object, this can be +// very useful in object-capability systems +// example: +// | var obj = {foo:"bar"}; +// | var readonlyObj = dojox.lang.ReadOnlyProxy(obj); +// | readonlyObj.foo = "test" // throws an error +// | obj.foo = "new bar"; +// | readonlyObj.foo -> returns "new bar", always reflects the current value of the original (it is not just a copy) +dojox.lang.makeObservable(function(obj,i){ + return obj[i]; + },function(obj,i,value){ + // just ignore, exceptions don't seem to propagate through the VB stack. +}); + +}); |
