aboutsummaryrefslogtreecommitdiff
path: root/includes/js/dojox/data/jsonPathStore.js
diff options
context:
space:
mode:
Diffstat (limited to 'includes/js/dojox/data/jsonPathStore.js')
-rw-r--r--includes/js/dojox/data/jsonPathStore.js1191
1 files changed, 0 insertions, 1191 deletions
diff --git a/includes/js/dojox/data/jsonPathStore.js b/includes/js/dojox/data/jsonPathStore.js
deleted file mode 100644
index 01e4e23..0000000
--- a/includes/js/dojox/data/jsonPathStore.js
+++ /dev/null
@@ -1,1191 +0,0 @@
-if(!dojo._hasResource["dojox.data.jsonPathStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojox.data.jsonPathStore"] = true;
-dojo.provide("dojox.data.jsonPathStore");
-dojo.require("dojox.jsonPath");
-dojo.require("dojo.date");
-dojo.require("dojo.date.locale");
-dojo.require("dojo.date.stamp");
-
-dojox.data.ASYNC_MODE = 0;
-dojox.data.SYNC_MODE = 1;
-
-dojo.declare("dojox.data.jsonPathStore",
- null,
- {
- mode: dojox.data.ASYNC_MODE,
- metaLabel: "_meta",
- hideMetaAttributes: false,
- autoIdPrefix: "_auto_",
- autoIdentity: true,
- idAttribute: "_id",
- indexOnLoad: true,
- labelAttribute: "",
- url: "",
- _replaceRegex: /\'\]/gi,
-
- constructor: function(options){
- //summary:
- // jsonPathStore constructor, instantiate a new jsonPathStore
- //
- // Takes a single optional parameter in the form of a Javascript object
- // containing one or more of the following properties.
- //
- // data: /*JSON String*/ || /* Javascript Object */,
- // JSON String or Javascript object this store will control
- // JSON is converted into an object, and an object passed to
- // the store will be used directly. If no data and no url
- // is provide, an empty object, {}, will be used as the initial
- // store.
- //
- // url: /* string url */
- // Load data from this url in JSON format and use the Object
- // created from the data as the data source.
- //
- // indexOnLoad: /* boolean */
- // Defaults to true, but this may change in the near future.
- // Parse the data object and set individual objects up as
- // appropriate. This will add meta data and assign
- // id's to objects that dont' have them as defined by the
- // idAttribute option. Disabling this option will keep this
- // parsing from happening until a query is performed at which
- // time only the top level of an item has meta info stored.
- // This might work in some situations, but you will almost
- // always want to indexOnLoad or use another option which
- // will create an index. In the future we will support a
- // generated index that maps by jsonPath allowing the
- // server to take some of this load for larger data sets.
- //
- // 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. There
- // are utility routines for exporting data from the store
- // that can clean any generated IDs before exporting and leave
- // preexisting id's in tact.
- //
- // metaLabel: /* string */
- // Defaults to '_meta' overrides the attribute name that is used by the store
- // for attaching meta information to an object while
- // in the store's control. Defaults to '_meta'.
- //
- // hideMetaAttributes: /* boolean */
- // Defaults to False. When enabled, calls to getAttributes() will not
- // include the meta attribute.
- //
- // autoIdPrefix: /*string*/
- // Defaults to "_auto_". This string is used as the prefix to any
- // objects which have a generated id. A numeric index is appended
- // to this string to complete the ID
- //
- // 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 (options){
- dojo.mixin(this,options);
- }
-
- this._dirtyItems=[];
- this._autoId=0;
- this._referenceId=0;
- this._references={};
- this._fetchQueue=[];
- this.index={};
-
- //regex to identify when we're travelling down metaObject (which we don't want to do)
- var expr="("+this.metaLabel+"\'\])";
- this.metaRegex = new RegExp(expr);
-
-
- //no data or url, start with an empty object for a store
- if (!this.data && !this.url){
- this.setData({});
- }
-
- //we have data, but no url, set the store as the data
- if (this.data && !this.url){
- this.setData(this.data);
-
- //remove the original refernce, we're now using _data from here on out
- delete this.data;
- }
-
- //given a url, load json data from as the store
- if (this.url){
- dojo.xhrGet({
- url: options.url,
- handleAs: "json",
- load: dojo.hitch(this, "setData"),
- sync: this.mode
- });
- }
- },
-
- _loadData: function(data){
- // summary:
- // load data into the store. Index it if appropriate.
- if (this._data){
- delete this._data;
- }
-
- if (dojo.isString(data)){
- this._data = dojo.fromJson(data);
- }else{
- this._data = data;
- }
-
- if (this.indexOnLoad){
- this.buildIndex();
- }
-
- this._updateMeta(this._data, {path: "$"});
-
- this.onLoadData(this._data);
- },
-
- onLoadData: function(data){
- // summary
- // Called after data has been loaded in the store.
- // If any requests happened while the startup is happening
- // then process them now.
-
- while (this._fetchQueue.length>0){
- var req = this._fetchQueue.shift();
- this.fetch(req);
- }
-
- },
-
- setData: function(data){
- // summary:
- // set the stores' data to the supplied object and then
- // load and/or setup that data with the required meta info
- this._loadData(data);
- },
-
- buildIndex: function(path, item){
- //summary:
- // parse the object structure, and turn any objects into
- // jsonPathStore items. Basically this just does a recursive
- // series of fetches which itself already examines any items
- // as they are retrieved and setups up the required meta information.
- //
- // path: /* string */
- // jsonPath Query for the starting point of this index construction.
-
- if (!this.idAttribute){
- throw new Error("buildIndex requires idAttribute for the store");
- }
-
- item = item || this._data;
- var origPath = path;
- path = path||"$";
- path += "[*]";
- var data = this.fetch({query: path,mode: dojox.data.SYNC_MODE});
- for(var i=0; i<data.length;i++){
- if(dojo.isObject(data[i])){
- var newPath = data[i][this.metaLabel]["path"];
- if (origPath){
- //console.log("newPath: ", newPath);
- //console.log("origPath: ", origPath);
- //console.log("path: ", path);
- //console.log("data[i]: ", data[i]);
- var parts = origPath.split("\[\'");
- var attribute = parts[parts.length-1].replace(this._replaceRegex,'');
- //console.log("attribute: ", attribute);
- //console.log("ParentItem: ", item, attribute);
- if (!dojo.isArray(data[i])){
- this._addReference(data[i], {parent: item, attribute:attribute});
- this.buildIndex(newPath, data[i]);
- }else{
- this.buildIndex(newPath,item);
- }
- }else{
- var parts = newPath.split("\[\'");
- var attribute = parts[parts.length-1].replace(this._replaceRegex,'');
- this._addReference(data[i], {parent: this._data, attribute:attribute});
- this.buildIndex(newPath, data[i]);
- }
- }
- }
- },
-
- _correctReference: function(item){
- // summary:
- // make sure we have an reference to the item in the store
- // and not a clone. Takes an item, matches it to the corresponding
- // item in the store and if it is the same, returns itself, otherwise
- // it returns the item from the store.
-
- if (this.index[item[this.idAttribute]][this.metaLabel]===item[this.metaLabel]){
- return this.index[item[this.idAttribute]];
- }
- return item;
- },
-
- getValue: function(item, property){
- // summary:
- // Gets the value of an item's 'property'
- //
- // item: /* object */
- // property: /* string */
- // property to look up value for
- item = this._correctReference(item);
- return item[property];
- },
-
- 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
-
- item = this._correctReference(item);
- return dojo.isArray(item[property]) ? item[property] : [item[property]];
- },
-
- getAttributes: function(item){
- // summary:
- // Gets the available attributes of an item's 'property' and returns
- // it as an array. If the store has 'hideMetaAttributes' set to true
- // the attributed identified by 'metaLabel' will not be included.
- //
- // item: /* object */
-
- item = this._correctReference(item);
- var res = [];
- for (var i in item){
- if (this.hideMetaAttributes && (i==this.metaLabel)){continue;}
- res.push(i);
- }
- return res;
- },
-
- hasAttribute: function(item,attribute){
- // summary:
- // Checks to see if item has attribute
- //
- // item: /* object */
- // attribute: /* string */
-
- item = this._correctReference(item);
- if (attribute in item){return true;}
- return false;
- },
-
- containsValue: function(item, attribute, value){
- // summary:
- // Checks to see if 'item' has 'value' at 'attribute'
- //
- // item: /* object */
- // attribute: /* string */
- // value: /* anything */
- item = this._correctReference(item);
-
- if (item[attribute] && item[attribute]==value){return true}
- if (dojo.isObject(item[attribute]) || dojo.isObject(value)){
- if (this._shallowCompare(item[attribute],value)){return true}
- }
- return false;
- },
-
- _shallowCompare: function(a, b){
- //summary does a simple/shallow compare of properties on an object
- //to the same named properties on the given item. Returns
- //true if all props match. It will not descend into child objects
- //but it will compare child date objects
-
- if ((dojo.isObject(a) && !dojo.isObject(b))|| (dojo.isObject(b) && !dojo.isObject(a))) {
- return false;
- }
-
- if ( a["getFullYear"] || b["getFullYear"] ){
- //confirm that both are dates
- if ( (a["getFullYear"] && !b["getFullYear"]) || (b["getFullYear"] && !a["getFullYear"]) ){
- return false;
- }else{
- if (!dojo.date.compare(a,b)){
- return true;
- }
- return false;
- }
- }
-
- for (var i in b){
- if (dojo.isObject(b[i])){
- if (!a[i] || !dojo.isObject(a[i])){return false}
-
- if (b[i]["getFullYear"]){
- if(!a[i]["getFullYear"]){return false}
- if (dojo.date.compare(a,b)){return false}
- }else{
- if (!this._shallowCompare(a[i],b[i])){return false}
- }
- }else{
- if (!b[i] || (a[i]!=b[i])){return false}
- }
- }
-
- //make sure there werent props on a that aren't on b, if there aren't, then
- //the previous section will have already evaluated things.
-
- for (var i in a){
- if (!b[i]){return false}
- }
-
- return true;
- },
-
- isItem: function(item){
- // summary:
- // Checks to see if a passed 'item'
- // is really a jsonPathStore item. Currently
- // it only verifies structure. It does not verify
- // that it belongs to this store at this time.
- //
- // item: /* object */
- // attribute: /* string */
-
- if (!dojo.isObject(item) || !item[this.metaLabel]){return false}
- if (this.requireId && this._hasId && !item[this._id]){return false}
- return true;
- },
-
- isItemLoaded: function(item){
- // summary:
- // returns isItem() :)
- //
- // item: /* object */
-
- item = this._correctReference(item);
- return this.isItem(item);
- },
-
- loadItem: function(item){
- // summary:
- // returns true. Future implementatins might alter this
- return true;
- },
-
- _updateMeta: function(item, props){
- // summary:
- // verifies that 'item' has a meta object attached
- // and if not it creates it by setting it to 'props'
- // if the meta attribute already exists, mix 'props'
- // into it.
-
- if (item && item[this.metaLabel]){
- dojo.mixin(item[this.metaLabel], props);
- return;
- }
-
- item[this.metaLabel]=props;
- },
-
- cleanMeta: function(data, options){
- // summary
- // Recurses through 'data' and removes an
- // meta information that has been attached. This
- // function will also removes any id's that were autogenerated
- // from objects. It will not touch id's that were not generated
-
- data = data || this._data;
-
- if (data[this.metaLabel]){
- if(data[this.metaLabel]["autoId"]){
- delete data[this.idAttribute];
- }
- delete data[this.metaLabel];
- }
-
- if (dojo.isArray(data)){
- for(var i=0; i<data.length;i++){
- if(dojo.isObject(data[i]) || dojo.isArray(data[i]) ){
- this.cleanMeta(data[i]);
- }
- }
- } else if (dojo.isObject(data)){
- for (var i in data){
- this.cleanMeta(data[i]);
- }
- }
- },
-
- 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 "$..*". jsonPath query to be performed
- // on data store. **note that since some widgets
- // expect this to be an object, an object in the form
- // of {query: '$[*'], queryOptions: "someOptions"} is
- // acceptable
- //
- // mode: dojox.data.SYNC_MODE || dojox.data.ASYNC_MODE
- // Override the stores default mode.
- //
- // queryOptions: /* object */
- // Options passed on to the underlying jsonPath query
- // system.
- //
- // start: /* int */
- // Starting item in result set
- //
- // count: /* int */
- // Maximum number of items to return
- //
- // 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
-
- // we're not started yet, add this request to a queue and wait till we do
- if (!this._data){
- this._fetchQueue.push(args);
- return args;
- }
- 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.mode) {args.mode = this.mode;}
- if (!args.queryOptions) {args.queryOptions={};}
-
- args.queryOptions.resultType='BOTH';
- var results = dojox.jsonPath.query(this._data, query, args.queryOptions);
- var tmp=[];
- var count=0;
- for (var i=0; i<results.length; i++){
- if(args.start && i<args.start){continue;}
- if (args.count && (count >= args.count)) { continue; }
-
- var item = results[i]["value"];
- var path = results[i]["path"];
- if (!dojo.isObject(item)){continue;}
- if(this.metaRegex.exec(path)){continue;}
-
- //this automatically records the objects path
- this._updateMeta(item,{path: results[i].path});
-
- //if autoIdentity and no id, generate one and add it to the item
- if(this.autoIdentity && !item[this.idAttribute]){
- var newId = this.autoIdPrefix + this._autoId++;
- item[this.idAttribute]=newId;
- item[this.metaLabel]["autoId"]=true;
- }
-
- //add item to the item index if appropriate
- if(item[this.idAttribute]){this.index[item[this.idAttribute]]=item}
- count++;
- tmp.push(item);
- }
- results = tmp;
- var scope = args.scope || dojo.global;
-
- if ("sort" in args){
- console.log("TODO::add support for sorting in the fetch");
- }
-
- if (args.mode==dojox.data.SYNC_MODE){
- return results;
- };
-
- if (args.onBegin){
- args["onBegin"].call(scope, results.length, args);
- }
-
- if (args.onItem){
- for (var i=0; i<results.length;i++){
- args["onItem"].call(scope, results[i], args);
- }
- }
-
- if (args.onComplete){
- args["onComplete"].call(scope, results, args);
- }
-
- return args;
- },
-
- dump: function(options){
- // summary:
- //
- // exports the store data set. Takes an options
- // object with a number of parameters
- //
- // data: /* object */
- // Defaults to the root of the store.
- // The data to be exported.
- //
- // clone: /* boolean */
- // clone the data set before returning it
- // or modifying it for export
- //
- // cleanMeta: /* boolean */
- // clean the meta data off of the data. Note
- // that this will happen to the actual
- // store data if !clone. If you want
- // to continue using the store after
- // this operation, it is probably better to export
- // it as a clone if you want it cleaned.
- //
- // suppressExportMeta: /* boolean */
- // By default, when data is exported from the store
- // some information, such as as a timestamp, is
- // added to the root of exported data. This
- // prevents that from happening. It is mainly used
- // for making tests easier.
- //
- // type: "raw" || "json"
- // Defaults to 'json'. 'json' will convert the data into
- // json before returning it. 'raw' will just return a
- // reference to the object
-
- var options = options || {};
- var d=options.data || this._data;
-
- if (!options.suppressExportMeta && options.clone){
- data = dojo.clone(d);
- if (data[this.metaLabel]){
- data[this.metaLabel]["clone"]=true;
- }
- }else{
- var data=d;
- }
-
- if (!options.suppressExportMeta && data[this.metaLabel]){
- data[this.metaLabel]["last_export"]=new Date().toString()
- }
-
- if(options.cleanMeta){
- this.cleanMeta(data);
- }
-
- //console.log("Exporting: ", options, dojo.toJson(data));
- switch(options.type){
- case "raw":
- return data;
- case "json":
- default:
- return dojo.toJson(data);
- }
- },
-
- 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. The label
- // is created by setting the store's labelAttribute
- // property with either an attribute name or an array
- // of attribute names. Developers can also
- // provide the store with a createLabel function which
- // will do the actaul work of creating the label. If not
- // the default will just concatenate any of the identified
- // attributes together.
- item = this._correctReference(item);
- var label="";
-
- if (dojo.isFunction(this.createLabel)){
- return this.createLabel(item);
- }
-
- if (this.labelAttribute){
- if (dojo.isArray(this.labelAttribute)) {
- for(var i=0; i<this.labelAttribute.length; i++){
- if (i>0) { label+=" ";}
- label += item[this.labelAttribute[i]];
- }
- return label;
- }else{
- return item[this.labelAttribute];
- }
- }
- return item.toString();
- },
-
- getLabelAttributes: function(item){
- // summary:
- // returns an array of attributes that are used to create the label of an item
- item = this._correctReference(item);
- return dojo.isArray(this.labelAttribute) ? this.labelAttribute : [this.labelAttribute];
- },
-
- 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.
-
- if (this.isItem(item)){
- return item[this.idAttribute];
- }
- throw new Error("Id not found for item");
- },
-
- 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. This store also provides
- // a much more finger friendly alias, 'byId' which does the
- // same thing as this function. If provided a string
- // this call will be treated as a SYNC request and will
- // return the identified item immediatly. Alternatively it
- // takes a object as a set of keywordArgs:
- //
- // identity: /* string */
- // the id of the item you want to retrieve
- //
- // mode: dojox.data.SYNC_MODE || dojox.data.ASYNC_MODE
- // overrides the default store fetch mode
- //
- // onItem: /* function */
- // Result call back. Passed the fetched item.
- //
- // onError: /* function */
- // error callback.
- var id;
- if (dojo.isString(args)){
- id = args;
- args = {identity: id, mode: dojox.data.SYNC_MODE}
- }else{
- if (args){
- id = args["identity"];
- }
- if (!args.mode){args.mode = this.mode}
- }
-
- if (this.index && (this.index[id] || this.index["identity"])){
-
- if (args.mode==dojox.data.SYNC_MODE){
- return this.index[id];
- }
-
- if (args.onItem){
- args["onItem"].call(args.scope || dojo.global, this.index[id], args);
- }
-
- return args;
- }else{
- if (args.mode==dojox.data.SYNC_MODE){
- return false;
- }
- }
-
-
- if(args.onError){
- args["onItem"].call(args.scope || dojo.global, new Error("Item Not Found: " + id), args);
- }
-
- return args;
- },
-
- //Write API Support
- newItem: function(data, options){
- // 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. This could be a
- // new javascript object, or it could be an item that
- // already exists in the store. If it already exists in the
- // store, then this will be added as a reference.
- //
- // options: /* object */
- //
- // item: /* item */
- // reference to an existing store item
- //
- // attribute: /* string */
- // attribute to add the item at. If this is
- // not provided, the item's id will be used as the
- // attribute name. If specified attribute is an
- // array, the new item will be push()d on to the
- // end of it.
- // oldValue: /* old value of item[attribute]
- // newValue: new value item[attribute]
-
- var meta={};
-
- //default parent to the store root;
- var pInfo ={item:this._data};
-
- if (options){
- if (options.parent){
- options.item = options.parent;
- }
-
- dojo.mixin(pInfo, options);
- }
-
- if (this.idAttribute && !data[this.idAttribute]){
- if (this.requireId){throw new Error("requireId is enabled, new items must have an id defined to be added");}
- if (this.autoIdentity){
- var newId = this.autoIdPrefix + this._autoId++;
- data[this.idAttribute]=newId;
- meta["autoId"]=true;
- }
- }
-
- if (!pInfo && !pInfo.attribute && !this.idAttribute && !data[this.idAttribute]){
- throw new Error("Adding a new item requires, at a minumum, either the pInfo information, including the pInfo.attribute, or an id on the item in the field identified by idAttribute");
- }
-
- //pInfo.parent = this._correctReference(pInfo.parent);
- //if there is no parent info supplied, default to the store root
- //and add to the pInfo.attribute or if that doestn' exist create an
- //attribute with the same name as the new items ID
- if(!pInfo.attribute){pInfo.attribute = data[this.idAttribute]}
-
- pInfo.oldValue = this._trimItem(pInfo.item[pInfo.attribute]);
- if (dojo.isArray(pInfo.item[pInfo.attribute])){
- this._setDirty(pInfo.item);
- pInfo.item[pInfo.attribute].push(data);
- }else{
- this._setDirty(pInfo.item);
- pInfo.item[pInfo.attribute]=data;
- }
-
- pInfo.newValue = pInfo.item[pInfo.attribute];
-
- //add this item to the index
- if(data[this.idAttribute]){this.index[data[this.idAttribute]]=data}
-
- this._updateMeta(data, meta)
-
- //keep track of all references in the store so we can delete them as necessary
- this._addReference(data, pInfo);
-
- //mark this new item as dirty
- this._setDirty(data);
-
- //Notification API
- this.onNew(data, pInfo);
-
- //returns the original item, now decorated with some meta info
- return data;
- },
-
- _addReference: function(item, pInfo){
- // summary
- // adds meta information to an item containing a reference id
- // so that references can be deleted as necessary, when passed
- // only a string, the string for parent info, it will only
- // it will be treated as a string reference
-
- //console.log("_addReference: ", item, pInfo);
- var rid = '_ref_' + this._referenceId++;
- if (!item[this.metaLabel]["referenceIds"]){
- item[this.metaLabel]["referenceIds"]=[];
- }
-
- item[this.metaLabel]["referenceIds"].push(rid);
- this._references[rid] = pInfo;
- },
-
- deleteItem: function(item){
- // summary
- // deletes item and any references to that item from the store.
- // If the desire is to delete only one reference, unsetAttribute or
- // setValue is the way to go.
-
- item = this._correctReference(item);
- console.log("Item: ", item);
- if (this.isItem(item)){
- while(item[this.metaLabel]["referenceIds"].length>0){
- console.log("refs map: " , this._references);
- console.log("item to delete: ", item);
- var rid = item[this.metaLabel]["referenceIds"].pop();
- var pInfo = this._references[rid];
-
- console.log("deleteItem(): ", pInfo, pInfo.parent);
- parentItem = pInfo.parent;
- var attribute = pInfo.attribute;
- if(parentItem && parentItem[attribute] && !dojo.isArray(parentItem[attribute])){
- this._setDirty(parentItem);
- this.unsetAttribute(parentItem, attribute);
- delete parentItem[attribute];
- }
-
- if (dojo.isArray(parentItem[attribute])){
- console.log("Parent is array");
- var oldValue = this._trimItem(parentItem[attribute]);
- var found=false;
- for (var i=0; i<parentItem[attribute].length && !found;i++){
- if (parentItem[attribute][i][this.metaLabel]===item[this.metaLabel]){
- found=true;
- }
- }
-
- if (found){
- this._setDirty(parentItem);
- var del = parentItem[attribute].splice(i-1,1);
- delete del;
- }
-
- var newValue = this._trimItem(parentItem[attribute]);
- this.onSet(parentItem,attribute,oldValue,newValue);
- }
- delete this._references[rid];
-
- }
- this.onDelete(item);
- delete 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.
-
- //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 (var i=0; i<this._dirtyItems.length; i++){
- if (item[this.idAttribute]==this._dirtyItems[i][this.idAttribute]){
- return;
- }
- }
-
- this._dirtyItems.push({item: item, old: this._trimItem(item)});
- this._updateMeta(item, {isDirty: true});
- },
-
- setValue: function(item, attribute, value){
- // summary:
- // sets 'attribute' on 'item' to 'value'
- item = this._correctReference(item);
-
- this._setDirty(item);
- var old = item[attribute] | undefined;
- 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.
-
-
- item = this._correctReference(item);
- 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] || null;
- item[attribute]=values
- this.onSet(item,attribute,old,values);
- },
-
- unsetAttribute: function(item, attribute){
- // summary:
- // unsets 'attribute' on 'item'
-
- item = this._correctReference(item);
- this._setDirty(item);
- var old = item[attribute];
- delete item[attribute];
- this.onSet(item,attribute,old,null);
- },
-
- save: function(kwArgs){
- // summary:
- // Takes an optional set of keyword Args with
- // some save options. Currently only format with options
- // being "raw" or "json". This function goes through
- // the dirty item lists, clones and trims the item down so that
- // the items children are not part of the data (the children are replaced
- // with reference objects). This data is compiled into a single array, the dirty objects
- // are all marked as clean, and the new data is then passed on to the onSave handler.
-
- var data = [];
-
- if (!kwArgs){kwArgs={}}
- while (this._dirtyItems.length > 0){
- var item = this._dirtyItems.pop()["item"];
- var t = this._trimItem(item);
- var d;
- switch(kwArgs.format){
- case "json":
- d = dojo.toJson(t);
- break;
- case "raw":
- default:
- d = t;
- }
- data.push(d);
- this._markClean(item);
- }
-
- this.onSave(data);
- },
-
- _markClean: function(item){
- // summary
- // remove this meta information marking an item as "dirty"
-
- if (item && item[this.metaLabel] && item[this.metaLabel]["isDirty"]){
- delete item[this.metaLabel]["isDirty"];
- }
- },
-
- revert: function(){
- // summary
- // returns any modified data to its original state prior to a save();
-
- while (this._dirtyItems.length>0){
- var d = this._dirtyItems.pop();
- this._mixin(d.item, d.old);
- }
- this.onRevert();
- },
-
- _mixin: function(target, data){
- // summary:
- // specialized mixin that hooks up objects in the store where references are identified.
-
- if (dojo.isObject(data)){
- if (dojo.isArray(data)){
- while(target.length>0){target.pop();}
- for (var i=0; i<data.length;i++){
- if (dojo.isObject(data[i])){
- if (dojo.isArray(data[i])){
- var mix=[];
- }else{
- var mix={};
- if (data[i][this.metaLabel] && data[i][this.metaLabel]["type"] && data[i][this.metaLabel]["type"]=='reference'){
- target[i]=this.index[data[i][this.idAttribute]];
- continue;
- }
- }
-
- this._mixin(mix, data[i]);
- target.push(mix);
- }else{
- target.push(data[i]);
- }
- }
- }else{
- for (var i in target){
- if (i in data){continue;}
- delete target[i];
- }
-
- for (var i in data){
- if (dojo.isObject(data[i])){
- if (dojo.isArray(data[i])){
- var mix=[];
- }else{
- if (data[i][this.metaLabel] && data[i][this.metaLabel]["type"] && data[i][this.metaLabel]["type"]=='reference'){
- target[i]=this.index[data[i][this.idAttribute]];
- continue;
- }
-
- var mix={};
- }
- this._mixin(mix, data[i]);
- target[i]=mix;
- }else{
- target[i]=data[i];
- }
- }
-
- }
- }
- },
-
- isDirty: function(item){
- // summary
- // returns true if the item is marked as dirty.
-
- item = this._correctReference(item);
- return item && item[this.metaLabel] && item[this.metaLabel]["isDirty"];
- },
-
- _createReference: function(item){
- // summary
- // Create a small reference object that can be used to replace
- // child objects during a trim
-
- var obj={};
- obj[this.metaLabel]={
- type:'reference'
- };
-
- obj[this.idAttribute]=item[this.idAttribute];
- return obj;
- },
-
- _trimItem: function(item){
- //summary:
- // copy an item recursively stoppying at other items that have id's
- // and replace them with a refrence object;
- var copy;
- if (dojo.isArray(item)){
- copy = [];
- for (var i=0; i<item.length;i++){
- if (dojo.isArray(item[i])){
- copy.push(this._trimItem(item[i]))
- }else if (dojo.isObject(item[i])){
- if (item[i]["getFullYear"]){
- copy.push(dojo.date.stamp.toISOString(item[i]));
- }else if (item[i][this.idAttribute]){
- copy.push(this._createReference(item[i]));
- }else{
- copy.push(this._trimItem(item[i]));
- }
- } else {
- copy.push(item[i]);
- }
- }
- return copy;
- }
-
- if (dojo.isObject(item)){
- copy = {};
-
- for (var attr in item){
- if (!item[attr]){ copy[attr]=undefined;continue;}
- if (dojo.isArray(item[attr])){
- copy[attr] = this._trimItem(item[attr]);
- }else if (dojo.isObject(item[attr])){
- if (item[attr]["getFullYear"]){
- copy[attr] = dojo.date.stamp.toISOString(item[attr]);
- }else if(item[attr][this.idAttribute]){
- copy[attr]=this._createReference(item[attr]);
- } else {
- copy[attr]=this._trimItem(item[attr]);
- }
- } else {
- copy[attr]=item[attr];
- }
- }
- return copy;
- }
- },
-
- //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.
-
- }
- }
-);
-
-//setup an alias to byId, is there a better way to do this?
-dojox.data.jsonPathStore.byId=dojox.data.jsonPathStore.fetchItemByIdentity;
-
-}