summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/lang/observable.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/lang/observable.js')
-rw-r--r--js/dojo/dojox/lang/observable.js269
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.
+});
+
+});