diff options
Diffstat (limited to 'includes/js/dojox/rpc/JsonRestStore.js')
-rw-r--r-- | includes/js/dojox/rpc/JsonRestStore.js | 661 |
1 files changed, 0 insertions, 661 deletions
diff --git a/includes/js/dojox/rpc/JsonRestStore.js b/includes/js/dojox/rpc/JsonRestStore.js deleted file mode 100644 index dd14874..0000000 --- a/includes/js/dojox/rpc/JsonRestStore.js +++ /dev/null @@ -1,661 +0,0 @@ -if(!dojo._hasResource["dojox.data.JsonRestStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. -dojo._hasResource["dojox.data.JsonRestStore"] = true; -dojo.provide("dojox.data.JsonRestStore"); -dojo.require("dojox.rpc.Rest"); -dojo.require("dojox.rpc.JsonReferencing"); // TODO: Make it work without this dependency - -// A JsonRestStore takes a REST service and uses it the remote communication for a -// read/write dojo.data implementation. To use a JsonRestStore you should create a -// service with a REST transport. This can be configured with an SMD: -//{ -// services: { -// jsonRestStore: { -// transport: "REST", -// envelope: "URL", -// target: "store.php", -// contentType:"application/json", -// parameters: [ -// {name: "location", type: "string", optional: true} -// ] -// } -// } -//} -// The SMD can then be used to create service, and the service can be passed to a JsonRestStore. For example: -// var myServices = new dojox.rpc.Service(dojo.moduleUrl("dojox.rpc.tests.resources", "test.smd")); -// var jsonStore = new dojox.data.JsonRestStore({service:myServices.jsonRestStore}); -// -// The JsonRestStore will then cause all saved modifications to be server using Rest commands (PUT, POST, or DELETE). -// The JsonRestStore also supports lazy loading. References can be made to objects that have not been loaded. -// For example if a service returned: -// {"name":"Example","lazyLoadedObject":{"$ref":"obj2"}} -// -// And this object has accessed using the dojo.data API: -// var obj = jsonStore.getValue(myObject,"lazyLoadedObject"); -// The object would automatically be requested from the server (with an object id of "obj2"). -// -// When using a Rest store on a public network, it is important to implement proper security measures to -// control access to resources - -dojox.data.ASYNC_MODE = 0; -dojox.data.SYNC_MODE = 1; -dojo.declare("dojox.data.JsonRestStore", - null, - { - mode: dojox.data.ASYNC_MODE, - constructor: function(options){ - //summary: - // JsonRestStore constructor, instantiate a new JsonRestStore - // A JsonRestStore can be configured from a JSON Schema. Queries are just - // passed through as URLs for XHR requests, - // so there is nothing to configure, just plug n play. - // Of course there are some options to fiddle with if you want: - // - // jsonSchema: /* object */ - // - // service: /* function */ - // This is the service object that is used to retrieve lazy data and save results - // The function should be directly callable with a single parameter of an object id to be loaded - // The function should also have the following methods: - // put(id,value) - puts the value at the given id - // post(id,value) - posts (appends) the value at the given id - // delete(id) - deletes the value corresponding to the given id - // - // idAttribute: /* string */ - // Defaults to 'id'. The name of the attribute that holds an objects id. - // This can be a preexisting id provided by the server. - // If an ID isn't already provided when an object - // is fetched or added to the store, the autoIdentity system - // will generate an id for it and add it to the index. - - // mode: dojox.data.ASYNC_MODE || dojox.data.SYNC_MODE - // Defaults to ASYNC_MODE. This option sets the default mode for this store. - // Sync calls return their data immediately from the calling function - // instead of calling the callback functions. Functions such as - // fetchItemByIdentity() and fetch() both accept a string parameter in addtion - // to the normal keywordArgs parameter. When passed this option, SYNC_MODE will - // automatically be used even when the default mode of the system is ASYNC_MODE. - // A normal request to fetch or fetchItemByIdentity (with kwArgs object) can also - // include a mode property to override this setting for that one request. - - //setup a byId alias to the api call - this.byId=this.fetchItemByIdentity; - // if the advanced json parser is enabled, we can pass through object updates as onSet events - dojo.connect(dojox.rpc,"onUpdate",this,function(obj,attrName,oldValue,newValue){ - var prefix = this.service.serviceName + '/'; - if (!obj._id){ - console.log("no id on updated object ", obj); - } - else if (obj._id.substring(0,prefix.length) == prefix) - this.onSet(obj,attrName,oldValue,newValue); - }); - if (options){ - dojo.mixin(this,options); - } - if (!this.service) - throw Error("A service is required for JsonRestStore"); - if (!(this.service.contentType + '').match(/application\/json/)) - throw Error("A service must use a contentType of 'application/json' in order to be used in a JsonRestStore"); - this.idAttribute = (this.service._schema && this.service._schema._idAttr) || 'id'; - var arrayModifyingMethodNames = ["splice","push","pop","unshift","shift","reverse","sort"]; - this._arrayModifyingMethods = {}; - var array = []; - var _this = this; - // setup array augmentation, for catching mods and setting arrays as dirty - for (var i = 0; i < arrayModifyingMethodNames.length; i++){ - (function(key){ // closure for the method to be bound correctly - var method = array[key]; - _this._arrayModifyingMethods[key] = function(){ - _this._setDirty(this); // set the array as dirty before the native modifying operation - return method.apply(this,arguments); - } - _this._arrayModifyingMethods[key]._augmented = 1; - })(arrayModifyingMethodNames[i]); - } - this._deletedItems=[]; - this._dirtyItems=[]; - //given a url, load json data from as the store - }, - - _loadById: function(id,callback){ - var slashIndex = id.indexOf('/'); - var serviceName = id.substring(0,slashIndex); - var id = id.substring(slashIndex + 1); - (this.service.serviceName == serviceName ? - this.service : // use the current service if it is the right one - dojox.rpc.services[serviceName])(id) // otherwise call by looking up the service - .addCallback(callback); - }, - getValue: function(item, property,lazyCallback){ - // summary: - // Gets the value of an item's 'property' - // - // item: /* object */ - // property: /* string */ - // property to look up value for - // lazyCallback: /* function*/ - // not part of the API, but if you are using lazy loading properties, you may provide a callback to resume, in order to have asynchronous loading - var value = item[property]; - if (value && value.$ref){ - dojox.rpc._sync = !lazyCallback; // tell the service to operate synchronously (I have some concerns about the "thread" safety with FF3, as I think it does event stacking on sync calls) - this._loadById((value && value._id) || (item._id + '.' + property),lazyCallback); - delete dojox.rpc._sync; // revert to normal async behavior - } else if (lazyCallback){lazyCallback(value);} - return value; - }, - - getValues: function(item, property){ - // summary: - // Gets the value of an item's 'property' and returns - // it. If this value is an array it is just returned, - // if not, the value is added to an array and that is returned. - // - // item: /* object */ - // property: /* string */ - // property to look up value for - - var val = this.getValue(item,property); - return dojo.isArray(val) ? val : [val]; - }, - - getAttributes: function(item){ - // summary: - // Gets the available attributes of an item's 'property' and returns - // it as an array. - // - // item: /* object */ - - var res = []; - for (var i in item){ - res.push(i); - } - return res; - }, - - hasAttribute: function(item,attribute){ - // summary: - // Checks to see if item has attribute - // - // item: /* object */ - // attribute: /* string */ - return attribute in item; - }, - - containsValue: function(item, attribute, value){ - // summary: - // Checks to see if 'item' has 'value' at 'attribute' - // - // item: /* object */ - // attribute: /* string */ - // value: /* anything */ - return getValue(item,attribute)==value; - }, - - - isItem: function(item){ - // summary: - // Checks to see if a passed 'item' - // is really a JsonRestStore item. - // - // item: /* object */ - // attribute: /* string */ - - return !!(dojo.isObject(item) && item._id); - }, - - isItemLoaded: function(item){ - // summary: - // returns isItem() :) - // - // item: /* object */ - - return !item.$ref; - }, - - loadItem: function(item){ - // summary: - // Loads an item that has not been loaded yet. Lazy loading should happen through getValue, and if used properly, this should never need to be called - // returns true. Note this does not work with lazy loaded primitives! - if (item.$ref){ - dojox.rpc._sync = true; // tell the service to operate synchronously - this._loadById(item._id) - delete dojox.rpc._sync; // revert to normal async behavior - } - - return true; - }, - - _walk : function(value,forEach){ - // walk the graph, avoiding duplication - var walked=[]; - function walk(value){ - if (value && typeof value == 'object' && !value.__walked){ - value.__walked = true; - walked.push(value); - for (var i in value){ - if (walk(value[i])){ - forEach(value,i,value[i]); - } - } - return true; - } - } - walk(value); - forEach({},null,value); - for (var i = 0; i < walked.length;i++) - delete walked[i].__walked; - }, - fetch: function(args){ - //console.log("fetch() ", args); - // summary - // - // fetch takes either a string argument or a keywordArgs - // object containing the parameters for the search. - // If passed a string, fetch will interpret this string - // as the query to be performed and will do so in - // SYNC_MODE returning the results immediately. - // If an object is supplied as 'args', its options will be - // parsed and then contained query executed. - // - // query: /* string or object */ - // Defaults to "". This is basically passed to the XHR request as the URL to get the data - // - // start: /* int */ - // Starting item in result set - // - // count: /* int */ - // Maximum number of items to return - // - // cache: /* boolean */ - // - // sort: /* function */ - // Not Implemented yet - // - // The following only apply to ASYNC requests (the default) - // - // onBegin: /* function */ - // called before any results are returned. Parameters - // will be the count and the original fetch request - // - // onItem: /*function*/ - // called for each returned item. Parameters will be - // the item and the fetch request - // - // onComplete: /* function */ - // called on completion of the request. Parameters will - // be the complete result set and the request - // - // onError: /* function */ - // colled in the event of an error - - if(dojo.isString(args)){ - query = args; - args={query: query, mode: dojox.data.SYNC_MODE}; - - } - - var query; - if (!args || !args.query){ - if (!args){ - var args={}; - } - - if (!args.query){ - args.query=""; - query=args.query; - } - - } - - if (dojo.isObject(args.query)){ - if (args.query.query){ - query = args.query.query; - }else{ - query = args.query = ""; - } - if (args.query.queryOptions){ - args.queryOptions=args.query.queryOptions - } - }else{ - query=args.query; - } - if (args.start || args.count){ - query += '[' + (args.start ? args.start : '') + ':' + (args.count ? ((args.start || 0) + args.count) : '') + ']'; - } - var results = dojox.rpc._index[this.service.serviceName + '/' + query]; - if (!args.mode){args.mode = this.mode;} - var _this = this; - var defResult; - dojox.rpc._sync = this.mode; - dojox._newId = query; - if (results && !("cache" in args && !args.cache)){ // TODO: Add TTL maybe? - defResult = new dojo.Deferred; - defResult.callback(results); - } - else { - defResult = this.service(query); - } - defResult.addCallback(function(results){ - delete dojox._newId; // cleanup - if (args.onBegin){ - args["onBegin"].call(_this, results.length, args); - } - _this._walk(results,function(obj,i,value){ - if (value instanceof Array){ - for (var i in _this._arrayModifyingMethods){ - if (!value[i]._augmented){ - value[i] = _this._arrayModifyingMethods[i]; - } - } - - } - }); - if (args.onItem){ - for (var i=0; i<results.length;i++){ - args["onItem"].call(_this, results[i], args); - } - } - if (args.onComplete){ - args["onComplete"].call(_this, results, args); - } - return results; - }); - defResult.addErrback(args.onError); - return args; - }, - - - getFeatures: function(){ - // summary: - // return the store feature set - - return { - "dojo.data.api.Read": true, - "dojo.data.api.Identity": true, - "dojo.data.api.Write": true, - "dojo.data.api.Notification": true - } - }, - - getLabel: function(item){ - // summary - // returns the label for an item. Just gets the "label" attribute. - // - return this.getValue(item,"label"); - }, - - getLabelAttributes: function(item){ - // summary: - // returns an array of attributes that are used to create the label of an item - return ["label"]; - }, - - sort: function(a,b){ - console.log("TODO::implement default sort algo"); - }, - - //Identity API Support - - getIdentity: function(item){ - // summary - // returns the identity of an item or throws - // a not found error. - var prefix = this.service.serviceName + '/'; - if (!item._id){// generate a good random id - item._id = prefix + Math.random().toString(16).substring(2,14)+Math.random().toString(16).substring(2,14); - } - if (item._id.substring(0,prefix.length) != prefix){ - throw Error("Identity attribute not found"); - } - return item._id.substring(prefix.length); - }, - - getIdentityAttributes: function(item){ - // summary: - // returns the attributes which are used to make up the - // identity of an item. Basically returns this.idAttribute - - return [this.idAttribute]; - }, - - fetchItemByIdentity: function(args){ - // summary: - // fetch an item by its identity. fetch and fetchItemByIdentity work exactly the same - return this.fetch(args); - }, - - //Write API Support - newItem: function(data, parentInfo){ - // summary: - // adds a new item to the store at the specified point. - // Takes two parameters, data, and options. - // - // data: /* object */ - // The data to be added in as an item. - // parentInfo: - // An optional javascript object defining what item is the parent of this item (in a hierarchical store. Not all stores do hierarchical items), - // and what attribute of that parent to assign the new item to. If this is present, and the attribute specified - // is a multi-valued attribute, it will append this item into the array of values for that attribute. The structure - // of the object is as follows: - // { - // parent: someItem, - // } - // or - // { - // parentId: someItemId, - // } - - if (this.service._schema && this.service._schema.clazz && data.constructor != this.service._schema.clazz) - data = dojo.mixin(new this.service._schema.clazz,data); - this.getIdentity(data); - this._getParent(parentInfo).push(data); // essentially what newItem really means - this.onNew(data); - return data; - }, - _getParent : function(parentInfo){ - - var parentId = (parentInfo && parentInfo.parentId) || this.parentId || ''; - var parent = (parentInfo && parentInfo.parent) || dojox.rpc._index[this.service.serviceName + '/' + parentId] || []; - if (!parent._id){ - parent._id = this.service.serviceName + '/' + parentId; - this._setDirty(parent); // set it dirty so it will be post - } - return parent; - }, - deleteItem: function(item,/*array*/parentInfo){ - // summary - // deletes item any references to that item from the store. - // - // item: - // item to delete - // - // removeFrom: This an item or items from which to remove references to this object. This store does not record references, - // so if this parameter the entire object graph from load items will be searched for references. Providing this parameter - // is vastly faster. An empty object or truthy primitive can be passed if no references need to be removed - - // If the desire is to delete only one reference, unsetAttribute or - // setValue is the way to go. - if (this.isItem(item)) - this._deletedItems.push(item); - var _this = this; - this._walk(((parentInfo || this.parentId) && this._getParent(parentInfo)) || dojox.rpc._index,function(obj,i,val){ - if (obj[i] === item){ // find a reference to this object - if (_this.isItem(obj)){ - if (isNaN(i) || !obj.splice){ // remove a property - _this.unsetAttribute(obj,i); - delete obj[i]; - } - else {// remove an array entry - obj.splice(i,1); - } - } - } - }); - this.onDelete(item); - }, - - _setDirty: function(item){ - // summary: - // adds an item to the list of dirty items. This item - // contains a reference to the item itself as well as a - // cloned and trimmed version of old item for use with - // revert. - var i; - if (!item._id) - return; - //if an item is already in the list of dirty items, don't add it again - //or it will overwrite the premodification data set. - for (i=0; i<this._dirtyItems.length; i++){ - if (item==this._dirtyItems[i].item){ - return; - } - } - var old = item instanceof Array ? [] : {}; - for (i in item) - if (item.hasOwnProperty(i)) - old[i] = item[i]; - this._dirtyItems.push({item: item, old: old}); - }, - - setValue: function(item, attribute, value){ - // summary: - // sets 'attribute' on 'item' to 'value' - - var old = item[attribute]; - if (old != value){ - this._setDirty(item); - item[attribute]=value; - this.onSet(item,attribute,old,value); - } - }, - - setValues: function(item, attribute, values){ - // summary: - // sets 'attribute' on 'item' to 'value' value - // must be an array. - - - if (!dojo.isArray(values)){throw new Error("setValues expects to be passed an Array object as its value");} - this._setDirty(item); - var old = item[attribute]; - item[attribute]=values; - this.onSet(item,attribute,old,values); - }, - - unsetAttribute: function(item, attribute){ - // summary: - // unsets 'attribute' on 'item' - - this._setDirty(item); - var old = item[attribute]; - delete item[attribute]; - this.onSet(item,attribute,old,undefined); - }, - _commitAppend: function(listId,item){ - return this.service.post(listId,item); - }, - save: function(kwArgs){ - // summary: - // Saves the dirty data using REST Ajax methods - - var data = []; - - var left = 0; // this is how many changes are remaining to be received from the server - var _this = this; - function finishOne(){ - if (!(--left)) - _this.onSave(data); - } - while (this._dirtyItems.length > 0){ - var dirty = this._dirtyItems.pop(); - var item = dirty.item; - var append = false; - left++; - var deferred; - if (item instanceof Array && dirty.old instanceof Array){ - // see if we can just append the item with a post - append = true; - for (var i = 0, l = dirty.old.length; i < l; i++){ - if (item[i] != dirty.old[i]){ - append = false; - } - } - if (append){ // if we can, we will do posts to add from here - for (;i<item.length;i++){ - deferred = this._commitAppend(this.getIdentity(item),item[i]); - deferred.addCallback(finishOne); - } - } - } - if (!append){ - deferred = this.service.put(this.getIdentity(item),item); - deferred.addCallback(finishOne); - } - - data.push(item); - } - while (this._deletedItems.length > 0){ - left++; - this.service['delete'](this.getIdentity(this._deletedItems.pop())).addCallback(finishOne); - } - }, - - - revert: function(){ - // summary - // returns any modified data to its original state prior to a save(); - - while (this._dirtyItems.length>0){ - var i; - var d = this._dirtyItems.pop(); - for (i in d.old){ - d.item[i] = d.old[i]; - } - for (i in d.item){ - if (!d.old.hasOwnProperty(i)) - delete d.item[i] - } - } - this.onRevert(); - }, - - - isDirty: function(item){ - // summary - // returns true if the item is marked as dirty. - for (var i=0, l=this._dirtyItems.length; i<l; i++){ - if (this._dirtyItems[i]==item){return true}; - } - }, - - - //Notifcation Support - - onSet: function(){ - }, - - onNew: function(){ - - }, - - onDelete: function(){ - - }, - - onSave: function(items){ - // summary: - // notification of the save event..not part of the notification api, - // but probably should be. - //console.log("onSave() ", items); - }, - - onRevert: function(){ - // summary: - // notification of the revert event..not part of the notification api, - // but probably should be. - - } - } -); - - -} |