diff options
Diffstat (limited to 'includes/js/dojox/rpc/JsonReferencing.js')
-rw-r--r-- | includes/js/dojox/rpc/JsonReferencing.js | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/includes/js/dojox/rpc/JsonReferencing.js b/includes/js/dojox/rpc/JsonReferencing.js new file mode 100644 index 0000000..618ea06 --- /dev/null +++ b/includes/js/dojox/rpc/JsonReferencing.js @@ -0,0 +1,265 @@ +if(!dojo._hasResource["dojox.rpc.JsonReferencing"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojox.rpc.JsonReferencing"] = true; +dojo.provide("dojox.rpc.JsonReferencing"); +dojo.require("dojo.date.stamp"); +dojo.require("dojo._base.Deferred"); + +// summary: +// Adds advanced JSON {de}serialization capabilities to the base json library. +// This enhances the capabilities of dojo.toJson and dojo.fromJson, +// adding referencing support, date handling, and other extra format handling. +// On parsing, references are resolved. When references are made to +// ids/objects that have been loaded yet, a Deferred object will be used as the +// value and as soon as a callback is added to the Deferred object, the target +// object will be loaded. + + + +dojox.rpc._index={}; // the global map of id->object +dojox.rpc.onUpdate = function(/*Object*/ object, /* attribute-name-string */ attribute, /* any */ oldValue, /* any */ newValue){ + // summary: + // This function is called when an existing object in the system is updated. Existing objects are found by id. +}; + +dojox.rpc.resolveJson = function(/*Object*/ root,/*Object?*/ schema){ + // summary: + // Indexes and resolves references in the JSON object. + // A JSON Schema object that can be used to advise the handling of the JSON (defining ids, date properties, urls, etc) + // + // root: + // The root object of the object graph to be processed + // + // schema: A JSON Schema object that can be used to advise the parsing of the JSON (defining ids, date properties, urls, etc) // + // Currently this provides a means for context based id handling + // + // return: + // An object, the result of the processing + var ref,reWalk=[]; + function makeIdInfo(schema){ // find out what attribute and what id prefix to use + if (schema){ + var attr; + if (!(attr = schema._idAttr)){ + for (var i in schema.properties){ + if (schema.properties[i].unique){ + schema._idAttr = attr = i; + } + } + } + if (attr || schema._idPrefix){ + return {attr:attr || 'id',prefix:schema._idPrefix}; + } + } + + return false; + } + function walk(it,stop,schema,idInfo,defaultId){ + // this walks the new graph, resolving references and making other changes + var val,i; + var id = it[idInfo.attr]; + id = (id && (idInfo.prefix + id)) || defaultId; // if there is an id, prefix it, otherwise inherit + var target = it; + + if (id){ // if there is an id available... + it._id = id; + if (dojox.rpc._index[id]){ // if the id already exists in the system, we should use the existing object, and just update it + target = dojox.rpc._index[id]; + delete target.$ref; // remove this artifact + } + dojox.rpc._index[id] = target; // add the prefix, set _id, and index it + if (schema && dojox.validate && dojox.validate.jsonSchema){ // if json schema is activated, we can load it in the registry of instance schemas map + dojox.validate.jsonSchema._schemas[id] = schema; + } + + } + for (i in it){ + if (it.hasOwnProperty(i) && (typeof (val=it[i]) =='object') && val){ + ref=val.$ref; + if (ref){ // a reference was found + var stripped = ref.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');// trim it + if(/[\w\[\]\.\$ \/\r\n\t]/.test(stripped) && !/=|((^|\W)new\W)/.test(stripped)){ // make sure it is a safe reference + var path = ref.match(/(^\.*[^\.\[]+)([\.\[].*)?/); // divide along the path + if ((ref=path[1]=='$' ? root:dojox.rpc._index[new dojo._Url(idInfo.prefix,path[1])]) && // a $ indicates to start with the root, otherwise start with an id + (ref = path[2] ? eval('ref' + path[2]) : ref)){// starting point was found, use eval to resolve remaining property references + // otherwise, no starting point was found (id not found), if stop is set, it does not exist, we have + // unloaded reference, if stop is not set, it may be in a part of the graph not walked yet, + // we will wait for the second loop + val = ref; + } + else{ + if (!stop){ + if (!rewalking) + reWalk.push(it); // we need to rewalk it to resolve references + var rewalking = true; // we only want to add it once + } + else { + ref = val.$ref; + val = new dojo.Deferred(); + val._id = idInfo.prefix + ref; + (function(val,ref){ + var connectId = dojo.connect(val,"addCallbacks",function(){ + dojo.disconnect(connectId); + dojox.rpc.services[idInfo.prefix.substring(0,idInfo.prefix.length-1)](ref) // otherwise call by looking up the service + .addCallback(dojo.hitch(val,val.callback)); + + }); + })(val,ref); + } + } + } + } + else { + if (!stop){ // if we are in stop, that means we are in the second loop, and we only need to check this current one, + // further walking may lead down circular loops + var valSchema = val.schema || // a schema can be self-defined by the object, + (schema && schema.properties && schema.properties[i]); // or it can from the schema sub-object definition + if (valSchema){ + idInfo = makeIdInfo(valSchema)||idInfo; + } + val = walk(val,reWalk==it,valSchema,idInfo,id && (id + ('[' + dojo._escapeString(i) + ']'))); + } + } + } + if (dojo.isString(val) && schema && schema.properties && schema.properties[i] && schema.properties[i].format=='date-time'){// parse the date string + val = dojo.date.stamp.fromISOString(val); // create a date object + } + it[i] = val; + var old = target[i]; + if (val !== old){ // only update if it changed + target[i] = val; // update the target + propertyChange(i,old,val); + } + } + function propertyChange(key,old,newValue){ + setTimeout(function(){ + dojox.rpc.onUpdate(target,i,old,newValue); // call the listener for each update + }); + } + if (target != it){ // this means we are updating, we need to remove deleted + for (i in target){ + if (!it.hasOwnProperty(i) && i != '_id' && !(target instanceof Array && isNaN(i))){ + propertyChange(i,target[i],undefined); + delete target[i]; + } + } + } + return target; + } + var idInfo = makeIdInfo(schema)||{attr:'id',prefix:''}; + if (!root){ return root; } + root = walk(root,false,schema,idInfo,dojox._newId && (new dojo._Url(idInfo.prefix,dojox._newId) +'')); // do the main walk through + walk(reWalk,false,schema,idInfo); // re walk any parts that were not able to resolve references on the first round + return root; +}; +dojox.rpc.fromJson = function(/*String*/ str,/*Object?*/ schema){ + // summary: + // evaluates the passed string-form of a JSON object. + // A JSON Schema object that can be used to advise the parsing of the JSON (defining ids, date properties, urls, etc) + // which may defined by setting dojox.currentSchema to the current schema you want to use for this evaluation + // + // json: + // a string literal of a JSON item, for instance: + // '{ "foo": [ "bar", 1, { "baz": "thud" } ] }' + // schema: A JSON Schema object that can be used to advise the parsing of the JSON (defining ids, date properties, urls, etc) // + // Currently this provides a means for context based id handling + // + // return: + // An object, the result of the evaluation + root = eval('(' + str + ')'); // do the eval + if (root){ + return this.resolveJson(root,schema); + } + return root; +} +dojox.rpc.toJson = function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*Object?*/ schema){ + // summary: + // Create a JSON serialization of an object. + // This has support for referencing, including circular references, duplicate references, and out-of-message references + // id and path-based referencing is supported as well and is based on http://www.json.com/2007/10/19/json-referencing-proposal-and-library/. + // + // it: + // an object to be serialized. + // + // prettyPrint: + // if true, we indent objects and arrays to make the output prettier. + // The variable dojo.toJsonIndentStr is used as the indent string + // -- to use something other than the default (tab), + // change that variable before calling dojo.toJson(). + // + // schema: A JSON Schema object that can be used to advise the parsing of the JSON (defining ids, date properties, urls, etc) // + // Currently this provides a means for context based id handling + // + // return: + // a String representing the serialized version of the passed object. + + var idPrefix = (schema&& schema._idPrefix) || ''; // the id prefix for this context + var paths={}; + function serialize(it,path,_indentStr){ + if (it && dojo.isObject(it)){ + var value; + if (it instanceof Date){ // properly serialize dates + return '"' + dojo.date.stamp.toISOString(it,{zulu:true}) + '"'; + } + var id = it._id; + if (id){ // we found an identifiable object, we will just serialize a reference to it... unless it is the root + + if (path != '$'){ + return serialize({$ref:id.charAt(0)=='$' ? id : // a pure path based reference, leave it alone + id.substring(0,idPrefix.length)==idPrefix ? // see if the reference is in the current context + id.substring(idPrefix.length): // a reference with a prefix matching the current context, the prefix should be removed + '../' + id});// a reference to a different context, assume relative url based referencing + } + path = id; + } + else { + it._id = path; // we will create path ids for other objects in case they are circular + paths[path] = it;// save it here so they can be deleted at the end + } + _indentStr = _indentStr || ""; + var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : ""; + var newLine = prettyPrint ? "\n" : ""; + var sep = prettyPrint ? " " : ""; + + if (it instanceof Array){ + var res = dojo.map(it, function(obj,i){ + var val = serialize(obj, path + '[' + i + ']', nextIndent); + if(!dojo.isString(val)){ + val = "undefined"; + } + return newLine + nextIndent + val; + }); + return "[" + res.join("," + sep) + newLine + _indentStr + "]"; + } + + var output = []; + for(var i in it){ + var keyStr; + if(typeof i == "number"){ + keyStr = '"' + i + '"'; + }else if(dojo.isString(i) && i != '_id'){ + keyStr = dojo._escapeString(i); + }else{ + // skip non-string or number keys + continue; + } + var val = serialize(it[i],path+(i.match(/^[a-zA-Z]\w*$/) ? // can we use simple .property syntax? + ('.' + i) : // yes, otherwise we have to escape it + ('[' + dojo._escapeString(i) + ']')),nextIndent); + if(!dojo.isString(val)){ + // skip non-serializable values + continue; + } + output.push(newLine + nextIndent + keyStr + ":" + sep + val); + } + return "{" + output.join("," + sep) + newLine + _indentStr + "}"; + } + + return dojo.toJson(it); // use the default serializer for primitives + } + var json = serialize(it,'$',''); + for (i in paths){ // cleanup the temporary path-generated ids + delete paths[i]._id; + } + return json; +} + +} |