diff options
Diffstat (limited to '')
| -rw-r--r-- | js/dojo-1.6/dojox/rpc/JsonRest.xd.js | 469 |
1 files changed, 469 insertions, 0 deletions
diff --git a/js/dojo-1.6/dojox/rpc/JsonRest.xd.js b/js/dojo-1.6/dojox/rpc/JsonRest.xd.js new file mode 100644 index 0000000..1044928 --- /dev/null +++ b/js/dojo-1.6/dojox/rpc/JsonRest.xd.js @@ -0,0 +1,469 @@ +/*
+ Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
+ Available via Academic Free License >= 2.1 OR the modified BSD license.
+ see: http://dojotoolkit.org/license for details
+*/
+
+
+dojo._xdResourceLoaded(function(dojo, dijit, dojox){
+return {depends: [["provide", "dojox.rpc.JsonRest"],
+["require", "dojox.json.ref"],
+["require", "dojox.rpc.Rest"]],
+defineResource: function(dojo, dijit, dojox){if(!dojo._hasResource["dojox.rpc.JsonRest"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojox.rpc.JsonRest"] = true;
+dojo.provide("dojox.rpc.JsonRest");
+dojo.require("dojox.json.ref");
+dojo.require("dojox.rpc.Rest");
+
+
+ var dirtyObjects = [];
+ var Rest = dojox.rpc.Rest;
+ var jr;
+ function resolveJson(service, deferred, value, defaultId){
+ var timeStamp = deferred.ioArgs && deferred.ioArgs.xhr && deferred.ioArgs.xhr.getResponseHeader("Last-Modified");
+ if(timeStamp && Rest._timeStamps){
+ Rest._timeStamps[defaultId] = timeStamp;
+ }
+ var hrefProperty = service._schema && service._schema.hrefProperty;
+ if(hrefProperty){
+ dojox.json.ref.refAttribute = hrefProperty;
+ }
+ value = value && dojox.json.ref.resolveJson(value, {
+ defaultId: defaultId,
+ index: Rest._index,
+ timeStamps: timeStamp && Rest._timeStamps,
+ time: timeStamp,
+ idPrefix: service.servicePath.replace(/[^\/]*$/,''),
+ idAttribute: jr.getIdAttribute(service),
+ schemas: jr.schemas,
+ loader: jr._loader,
+ idAsRef: service.idAsRef,
+ assignAbsoluteIds: true
+ });
+ dojox.json.ref.refAttribute = "$ref";
+ return value;
+ }
+ jr = dojox.rpc.JsonRest={
+ serviceClass: dojox.rpc.Rest,
+ conflictDateHeader: "If-Unmodified-Since",
+ commit: function(kwArgs){
+ // summary:
+ // Saves the dirty data using REST Ajax methods
+
+ kwArgs = kwArgs || {};
+ var actions = [];
+ var alreadyRecorded = {};
+ var savingObjects = [];
+ for(var i = 0; i < dirtyObjects.length; i++){
+ var dirty = dirtyObjects[i];
+ var object = dirty.object;
+ var old = dirty.old;
+ var append = false;
+ if(!(kwArgs.service && (object || old) &&
+ (object || old).__id.indexOf(kwArgs.service.servicePath)) && dirty.save){
+ delete object.__isDirty;
+ if(object){
+ if(old){
+ // changed object
+ var pathParts;
+ if((pathParts = object.__id.match(/(.*)#.*/))){ // it is a path reference
+ // this means it is a sub object, we must go to the parent object and save it
+ object = Rest._index[pathParts[1]];
+ }
+ if(!(object.__id in alreadyRecorded)){// if it has already been saved, we don't want to repeat it
+ // record that we are saving
+ alreadyRecorded[object.__id] = object;
+ if(kwArgs.incrementalUpdates
+ && !pathParts){ // I haven't figured out how we would do incremental updates on sub-objects yet
+ // make an incremental update using a POST
+ var incremental = (typeof kwArgs.incrementalUpdates == 'function' ?
+ kwArgs.incrementalUpdates : function(){
+ incremental = {};
+ for(var j in object){
+ if(object.hasOwnProperty(j)){
+ if(object[j] !== old[j]){
+ incremental[j] = object[j];
+ }
+ }else if(old.hasOwnProperty(j)){
+ // we can't use incremental updates to remove properties
+ return null;
+ }
+ }
+ return incremental;
+ })(object, old);
+ }
+
+ if(incremental){
+ actions.push({method:"post",target:object, content: incremental});
+ }
+ else{
+ actions.push({method:"put",target:object,content:object});
+ }
+ }
+ }else{
+ // new object
+ var service = jr.getServiceAndId(object.__id).service;
+ var idAttribute = jr.getIdAttribute(service);
+ if((idAttribute in object) && !kwArgs.alwaysPostNewItems){
+ // if the id attribute is specified, then we should know the location
+ actions.push({method:"put",target:object, content:object});
+ }else{
+ actions.push({method:"post",target:{__id:service.servicePath},
+ content:object});
+ }
+ }
+ }else if(old){
+ // deleted object
+ actions.push({method:"delete",target:old});
+ }//else{ this would happen if an object is created and then deleted, don't do anything
+ savingObjects.push(dirty);
+ dirtyObjects.splice(i--,1);
+ }
+ }
+ dojo.connect(kwArgs,"onError",function(){
+ if(kwArgs.revertOnError !== false){
+ var postCommitDirtyObjects = dirtyObjects;
+ dirtyObjects = savingObjects;
+ var numDirty = 0; // make sure this does't do anything if it is called again
+ jr.revert(); // revert if there was an error
+ dirtyObjects = postCommitDirtyObjects;
+ }
+ else{
+ dirtyObjects = dirtyObject.concat(savingObjects);
+ }
+ });
+ jr.sendToServer(actions, kwArgs);
+ return actions;
+ },
+ sendToServer: function(actions, kwArgs){
+ var xhrSendId;
+ var plainXhr = dojo.xhr;
+ var left = actions.length;// this is how many changes are remaining to be received from the server
+ var i, contentLocation;
+ var timeStamp;
+ var conflictDateHeader = this.conflictDateHeader;
+ // add headers for extra information
+ dojo.xhr = function(method,args){
+ // keep the transaction open as we send requests
+ args.headers = args.headers || {};
+ // the last one should commit the transaction
+ args.headers['Transaction'] = actions.length - 1 == i ? "commit" : "open";
+ if(conflictDateHeader && timeStamp){
+ args.headers[conflictDateHeader] = timeStamp;
+ }
+ if(contentLocation){
+ args.headers['Content-ID'] = '<' + contentLocation + '>';
+ }
+ return plainXhr.apply(dojo,arguments);
+ };
+ for(i =0; i < actions.length;i++){ // iterate through the actions to execute
+ var action = actions[i];
+ dojox.rpc.JsonRest._contentId = action.content && action.content.__id; // this is used by OfflineRest
+ var isPost = action.method == 'post';
+ timeStamp = action.method == 'put' && Rest._timeStamps[action.content.__id];
+ if(timeStamp){
+ // update it now
+ Rest._timeStamps[action.content.__id] = (new Date()) + '';
+ }
+ // send the content location to the server
+ contentLocation = isPost && dojox.rpc.JsonRest._contentId;
+ var serviceAndId = jr.getServiceAndId(action.target.__id);
+ var service = serviceAndId.service;
+ var dfd = action.deferred = service[action.method](
+ serviceAndId.id.replace(/#/,''), // if we are using references, we need eliminate #
+ dojox.json.ref.toJson(action.content, false, service.servicePath, true)
+ );
+ (function(object, dfd, service){
+ dfd.addCallback(function(value){
+ try{
+ // Implements id assignment per the HTTP specification
+ var newId = dfd.ioArgs.xhr && dfd.ioArgs.xhr.getResponseHeader("Location");
+ //TODO: match URLs if the servicePath is relative...
+ if(newId){
+ // if the path starts in the middle of an absolute URL for Location, we will use the just the path part
+ var startIndex = newId.match(/(^\w+:\/\/)/) && newId.indexOf(service.servicePath);
+ newId = startIndex > 0 ? newId.substring(startIndex) : (service.servicePath + newId).
+ // now do simple relative URL resolution in case of a relative URL.
+ replace(/^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/,'$2$3');
+ object.__id = newId;
+ Rest._index[newId] = object;
+ }
+ value = resolveJson(service, dfd, value, object && object.__id);
+ }catch(e){}
+ if(!(--left)){
+ if(kwArgs.onComplete){
+ kwArgs.onComplete.call(kwArgs.scope, actions);
+ }
+ }
+ return value;
+ });
+ })(action.content, dfd, service);
+
+ dfd.addErrback(function(value){
+
+ // on an error we want to revert, first we want to separate any changes that were made since the commit
+ left = -1; // first make sure that success isn't called
+ kwArgs.onError.call(kwArgs.scope, value);
+ });
+ }
+ // revert back to the normal XHR handler
+ dojo.xhr = plainXhr;
+
+ },
+ getDirtyObjects: function(){
+ return dirtyObjects;
+ },
+ revert: function(service){
+ // summary:
+ // Reverts all the changes made to JSON/REST data
+ for(var i = dirtyObjects.length; i > 0;){
+ i--;
+ var dirty = dirtyObjects[i];
+ var object = dirty.object;
+ var old = dirty.old;
+ var store = dojox.data._getStoreForItem(object || old);
+
+ if(!(service && (object || old) &&
+ (object || old).__id.indexOf(service.servicePath))){
+ // if we are in the specified store or if this is a global revert
+ if(object && old){
+ // changed
+ for(var j in old){
+ if(old.hasOwnProperty(j) && object[j] !== old[j]){
+ if(store){
+ store.onSet(object, j, object[j], old[j]);
+ }
+ object[j] = old[j];
+ }
+ }
+ for(j in object){
+ if(!old.hasOwnProperty(j)){
+ if(store){
+ store.onSet(object, j, object[j]);
+ }
+ delete object[j];
+ }
+ }
+ }else if(!old){
+ // was an addition, remove it
+ if(store){
+ store.onDelete(object);
+ }
+ }else{
+ // was a deletion, we will add it back
+ if(store){
+ store.onNew(old);
+ }
+ }
+ delete (object || old).__isDirty;
+ dirtyObjects.splice(i, 1);
+ }
+ }
+ },
+ changing: function(object,_deleting){
+ // summary:
+ // adds an object to the list of dirty objects. This object
+ // contains a reference to the object itself as well as a
+ // cloned and trimmed version of old object for use with
+ // revert.
+ if(!object.__id){
+ return;
+ }
+ object.__isDirty = true;
+ //if an object is already in the list of dirty objects, don't add it again
+ //or it will overwrite the premodification data set.
+ for(var i=0; i<dirtyObjects.length; i++){
+ var dirty = dirtyObjects[i];
+ if(object==dirty.object){
+ if(_deleting){
+ // we are deleting, no object is an indicator of deletiong
+ dirty.object = false;
+ if(!this._saveNotNeeded){
+ dirty.save = true;
+ }
+ }
+ return;
+ }
+ }
+ var old = object instanceof Array ? [] : {};
+ for(i in object){
+ if(object.hasOwnProperty(i)){
+ old[i] = object[i];
+ }
+ }
+ dirtyObjects.push({object: !_deleting && object, old: old, save: !this._saveNotNeeded});
+ },
+ deleteObject: function(object){
+ // summary:
+ // deletes an object
+ // object:
+ // object to delete
+ this.changing(object,true);
+ },
+ getConstructor: function(/*Function|String*/service, schema){
+ // summary:
+ // Creates or gets a constructor for objects from this service
+ if(typeof service == 'string'){
+ var servicePath = service;
+ service = new dojox.rpc.Rest(service,true);
+ this.registerService(service, servicePath, schema);
+ }
+ if(service._constructor){
+ return service._constructor;
+ }
+ service._constructor = function(data){
+ // summary:
+ // creates a new object for this table
+ //
+ // data:
+ // object to mixed in
+ var self = this;
+ var args = arguments;
+ var properties;
+ var initializeCalled;
+ function addDefaults(schema){
+ if(schema){
+ addDefaults(schema['extends']);
+ properties = schema.properties;
+ for(var i in properties){
+ var propDef = properties[i];
+ if(propDef && (typeof propDef == 'object') && ("default" in propDef)){
+ self[i] = propDef["default"];
+ }
+ }
+ }
+ if(schema && schema.prototype && schema.prototype.initialize){
+ initializeCalled = true;
+ schema.prototype.initialize.apply(self, args);
+ }
+ }
+ addDefaults(service._schema);
+ if(!initializeCalled && data && typeof data == 'object'){
+ dojo.mixin(self,data);
+ }
+ var idAttribute = jr.getIdAttribute(service);
+ Rest._index[this.__id = this.__clientId =
+ service.servicePath + (this[idAttribute] ||
+ Math.random().toString(16).substring(2,14) + '@' + ((dojox.rpc.Client && dojox.rpc.Client.clientId) || "client"))] = this;
+ if(dojox.json.schema && properties){
+ dojox.json.schema.mustBeValid(dojox.json.schema.validate(this, service._schema));
+ }
+ dirtyObjects.push({object:this, save: true});
+ };
+ return dojo.mixin(service._constructor, service._schema, {load:service});
+ },
+ fetch: function(absoluteId){
+ // summary:
+ // Fetches a resource by an absolute path/id and returns a dojo.Deferred.
+ var serviceAndId = jr.getServiceAndId(absoluteId);
+ return this.byId(serviceAndId.service,serviceAndId.id);
+ },
+ getIdAttribute: function(service){
+ // summary:
+ // Return the ids attribute used by this service (based on it's schema).
+ // Defaults to "id", if not other id is defined
+ var schema = service._schema;
+ var idAttr;
+ if(schema){
+ if(!(idAttr = schema._idAttr)){
+ for(var i in schema.properties){
+ if(schema.properties[i].identity || (schema.properties[i].link == "self")){
+ schema._idAttr = idAttr = i;
+ }
+ }
+ }
+ }
+ return idAttr || 'id';
+ },
+ getServiceAndId: function(/*String*/absoluteId){
+ // summary:
+ // Returns the REST service and the local id for the given absolute id. The result
+ // is returned as an object with a service property and an id property
+ // absoluteId:
+ // This is the absolute id of the object
+ var serviceName = '';
+
+ for(var service in jr.services){
+ if((absoluteId.substring(0, service.length) == service) && (service.length >= serviceName.length)){
+ serviceName = service;
+ }
+ }
+ if (serviceName){
+ return {service: jr.services[serviceName], id:absoluteId.substring(serviceName.length)};
+ }
+ var parts = absoluteId.match(/^(.*\/)([^\/]*)$/);
+ return {service: new jr.serviceClass(parts[1], true), id:parts[2]};
+ },
+ services:{},
+ schemas:{},
+ registerService: function(/*Function*/ service, /*String*/ servicePath, /*Object?*/ schema){
+ // summary:
+ // Registers a service for as a JsonRest service, mapping it to a path and schema
+ // service:
+ // This is the service to register
+ // servicePath:
+ // This is the path that is used for all the ids for the objects returned by service
+ // schema:
+ // This is a JSON Schema object to associate with objects returned by this service
+ servicePath = service.servicePath = servicePath || service.servicePath;
+ service._schema = jr.schemas[servicePath] = schema || service._schema || {};
+ jr.services[servicePath] = service;
+ },
+ byId: function(service, id){
+ // if caching is allowed, we look in the cache for the result
+ var deferred, result = Rest._index[(service.servicePath || '') + id];
+ if(result && !result._loadObject){// cache hit
+ deferred = new dojo.Deferred();
+ deferred.callback(result);
+ return deferred;
+ }
+ return this.query(service, id);
+ },
+ query: function(service, id, args){
+ var deferred = service(id, args);
+
+ deferred.addCallback(function(result){
+ if(result.nodeType && result.cloneNode){
+ // return immediately if it is an XML document
+ return result;
+ }
+ return resolveJson(service, deferred, result, typeof id != 'string' || (args && (args.start || args.count)) ? undefined: id);
+ });
+ return deferred;
+ },
+ _loader: function(callback){
+ // load a lazy object
+ var serviceAndId = jr.getServiceAndId(this.__id);
+ var self = this;
+ jr.query(serviceAndId.service, serviceAndId.id).addBoth(function(result){
+ // if they are the same this means an object was loaded, otherwise it
+ // might be a primitive that was loaded or maybe an error
+ if(result == self){
+ // we can clear the flag, so it is a loaded object
+ delete result.$ref;
+ delete result._loadObject;
+ }else{
+ // it is probably a primitive value, we can't change the identity of an object to
+ // the loaded value, so we will keep it lazy, but define the lazy loader to always
+ // return the loaded value
+ self._loadObject = function(callback){
+ callback(result);
+ };
+ }
+ callback(result);
+ });
+ },
+ isDirty: function(item){
+ // summary
+ // returns true if the item is marked as dirty or true if there are any dirty items
+ if(!item){
+ return !!dirtyObjects.length;
+ }
+ return item.__isDirty;
+ }
+
+ };
+
+}
+
+}};});
|
