]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojo/data/ItemFileReadStore.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dojo / data / ItemFileReadStore.js
1 if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
3 dojo.provide("dojo.data.ItemFileReadStore");
4
5 dojo.require("dojo.data.util.filter");
6 dojo.require("dojo.data.util.simpleFetch");
7 dojo.require("dojo.date.stamp");
8
9 dojo.declare("dojo.data.ItemFileReadStore", null,{
10         //      summary:
11         //              The ItemFileReadStore implements the dojo.data.api.Read API and reads
12         //              data from JSON files that have contents in this format --
13         //              { items: [
14         //                      { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
15         //                      { name:'Fozzie Bear', wears:['hat', 'tie']},
16         //                      { name:'Miss Piggy', pets:'Foo-Foo'}
17         //              ]}
18         //              Note that it can also contain an 'identifer' property that specified which attribute on the items 
19         //              in the array of items that acts as the unique identifier for that item.
20         //
21         constructor: function(/* Object */ keywordParameters){
22                 //      summary: constructor
23                 //      keywordParameters: {url: String}
24                 //      keywordParameters: {data: jsonObject}
25                 //      keywordParameters: {typeMap: object)
26                 //              The structure of the typeMap object is as follows:
27                 //              {
28                 //                      type0: function || object,
29                 //                      type1: function || object,
30                 //                      ...
31                 //                      typeN: function || object
32                 //              }
33                 //              Where if it is a function, it is assumed to be an object constructor that takes the 
34                 //              value of _value as the initialization parameters.  If it is an object, then it is assumed
35                 //              to be an object of general form:
36                 //              {
37                 //                      type: function, //constructor.
38                 //                      deserialize:    function(value) //The function that parses the value and constructs the object defined by type appropriately.
39                 //              }
40         
41                 this._arrayOfAllItems = [];
42                 this._arrayOfTopLevelItems = [];
43                 this._loadFinished = false;
44                 this._jsonFileUrl = keywordParameters.url;
45                 this._jsonData = keywordParameters.data;
46                 this._datatypeMap = keywordParameters.typeMap || {};
47                 if(!this._datatypeMap['Date']){
48                         //If no default mapping for dates, then set this as default.
49                         //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
50                         //of generically representing dates.
51                         this._datatypeMap['Date'] = {
52                                                                                         type: Date,
53                                                                                         deserialize: function(value){
54                                                                                                 return dojo.date.stamp.fromISOString(value);
55                                                                                         }
56                                                                                 };
57                 }
58                 this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
59                 this._itemsByIdentity = null;
60                 this._storeRefPropName = "_S";  // Default name for the store reference to attach to every item.
61                 this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
62                 this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
63                 this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
64                 this._loadInProgress = false;   //Got to track the initial load to prevent duelling loads of the dataset.
65                 this._queuedFetches = [];
66         },
67         
68         url: "",        // use "" rather than undefined for the benefit of the parser (#3539)
69
70         _assertIsItem: function(/* item */ item){
71                 //      summary:
72                 //              This function tests whether the item passed in is indeed an item in the store.
73                 //      item: 
74                 //              The item to test for being contained by the store.
75                 if(!this.isItem(item)){ 
76                         throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
77                 }
78         },
79
80         _assertIsAttribute: function(/* attribute-name-string */ attribute){
81                 //      summary:
82                 //              This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
83                 //      attribute: 
84                 //              The attribute to test for being contained by the store.
85                 if(typeof attribute !== "string"){ 
86                         throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
87                 }
88         },
89
90         getValue: function(     /* item */ item, 
91                                                 /* attribute-name-string */ attribute, 
92                                                 /* value? */ defaultValue){
93                 //      summary: 
94                 //              See dojo.data.api.Read.getValue()
95                 var values = this.getValues(item, attribute);
96                 return (values.length > 0)?values[0]:defaultValue; // mixed
97         },
98
99         getValues: function(/* item */ item, 
100                                                 /* attribute-name-string */ attribute){
101                 //      summary: 
102                 //              See dojo.data.api.Read.getValues()
103
104                 this._assertIsItem(item);
105                 this._assertIsAttribute(attribute);
106                 return item[attribute] || []; // Array
107         },
108
109         getAttributes: function(/* item */ item){
110                 //      summary: 
111                 //              See dojo.data.api.Read.getAttributes()
112                 this._assertIsItem(item);
113                 var attributes = [];
114                 for(var key in item){
115                         // Save off only the real item attributes, not the special id marks for O(1) isItem.
116                         if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
117                                 attributes.push(key);
118                         }
119                 }
120                 return attributes; // Array
121         },
122
123         hasAttribute: function( /* item */ item,
124                                                         /* attribute-name-string */ attribute) {
125                 //      summary: 
126                 //              See dojo.data.api.Read.hasAttribute()
127                 return this.getValues(item, attribute).length > 0;
128         },
129
130         containsValue: function(/* item */ item, 
131                                                         /* attribute-name-string */ attribute, 
132                                                         /* anything */ value){
133                 //      summary: 
134                 //              See dojo.data.api.Read.containsValue()
135                 var regexp = undefined;
136                 if(typeof value === "string"){
137                         regexp = dojo.data.util.filter.patternToRegExp(value, false);
138                 }
139                 return this._containsValue(item, attribute, value, regexp); //boolean.
140         },
141
142         _containsValue: function(       /* item */ item, 
143                                                                 /* attribute-name-string */ attribute, 
144                                                                 /* anything */ value,
145                                                                 /* RegExp?*/ regexp){
146                 //      summary: 
147                 //              Internal function for looking at the values contained by the item.
148                 //      description: 
149                 //              Internal function for looking at the values contained by the item.  This 
150                 //              function allows for denoting if the comparison should be case sensitive for
151                 //              strings or not (for handling filtering cases where string case should not matter)
152                 //      
153                 //      item:
154                 //              The data item to examine for attribute values.
155                 //      attribute:
156                 //              The attribute to inspect.
157                 //      value:  
158                 //              The value to match.
159                 //      regexp:
160                 //              Optional regular expression generated off value if value was of string type to handle wildcarding.
161                 //              If present and attribute values are string, then it can be used for comparison instead of 'value'
162                 return dojo.some(this.getValues(item, attribute), function(possibleValue){
163                         if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
164                                 if(possibleValue.toString().match(regexp)){
165                                         return true; // Boolean
166                                 }
167                         }else if(value === possibleValue){
168                                 return true; // Boolean
169                         }
170                 });
171         },
172
173         isItem: function(/* anything */ something){
174                 //      summary: 
175                 //              See dojo.data.api.Read.isItem()
176                 if(something && something[this._storeRefPropName] === this){
177                         if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
178                                 return true;
179                         }
180                 }
181                 return false; // Boolean
182         },
183
184         isItemLoaded: function(/* anything */ something){
185                 //      summary: 
186                 //              See dojo.data.api.Read.isItemLoaded()
187                 return this.isItem(something); //boolean
188         },
189
190         loadItem: function(/* object */ keywordArgs){
191                 //      summary: 
192                 //              See dojo.data.api.Read.loadItem()
193                 this._assertIsItem(keywordArgs.item);
194         },
195
196         getFeatures: function(){
197                 //      summary: 
198                 //              See dojo.data.api.Read.getFeatures()
199                 return this._features; //Object
200         },
201
202         getLabel: function(/* item */ item){
203                 //      summary: 
204                 //              See dojo.data.api.Read.getLabel()
205                 if(this._labelAttr && this.isItem(item)){
206                         return this.getValue(item,this._labelAttr); //String
207                 }
208                 return undefined; //undefined
209         },
210
211         getLabelAttributes: function(/* item */ item){
212                 //      summary: 
213                 //              See dojo.data.api.Read.getLabelAttributes()
214                 if(this._labelAttr){
215                         return [this._labelAttr]; //array
216                 }
217                 return null; //null
218         },
219
220         _fetchItems: function(  /* Object */ keywordArgs, 
221                                                         /* Function */ findCallback, 
222                                                         /* Function */ errorCallback){
223                 //      summary: 
224                 //              See dojo.data.util.simpleFetch.fetch()
225                 var self = this;
226                 var filter = function(requestArgs, arrayOfItems){
227                         var items = [];
228                         if(requestArgs.query){
229                                 var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false; 
230
231                                 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
232                                 //same value for each item examined.  Much more efficient.
233                                 var regexpList = {};
234                                 for(var key in requestArgs.query){
235                                         var value = requestArgs.query[key];
236                                         if(typeof value === "string"){
237                                                 regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
238                                         }
239                                 }
240
241                                 for(var i = 0; i < arrayOfItems.length; ++i){
242                                         var match = true;
243                                         var candidateItem = arrayOfItems[i];
244                                         if(candidateItem === null){
245                                                 match = false;
246                                         }else{
247                                                 for(var key in requestArgs.query) {
248                                                         var value = requestArgs.query[key];
249                                                         if (!self._containsValue(candidateItem, key, value, regexpList[key])){
250                                                                 match = false;
251                                                         }
252                                                 }
253                                         }
254                                         if(match){
255                                                 items.push(candidateItem);
256                                         }
257                                 }
258                                 findCallback(items, requestArgs);
259                         }else{
260                                 // We want a copy to pass back in case the parent wishes to sort the array. 
261                                 // We shouldn't allow resort of the internal list, so that multiple callers 
262                                 // can get lists and sort without affecting each other.  We also need to
263                                 // filter out any null values that have been left as a result of deleteItem()
264                                 // calls in ItemFileWriteStore.
265                                 for(var i = 0; i < arrayOfItems.length; ++i){
266                                         var item = arrayOfItems[i];
267                                         if(item !== null){
268                                                 items.push(item);
269                                         }
270                                 }
271                                 findCallback(items, requestArgs);
272                         }
273                 };
274
275                 if(this._loadFinished){
276                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
277                 }else{
278
279                         if(this._jsonFileUrl){
280                                 //If fetches come in before the loading has finished, but while
281                                 //a load is in progress, we have to defer the fetching to be 
282                                 //invoked in the callback.
283                                 if(this._loadInProgress){
284                                         this._queuedFetches.push({args: keywordArgs, filter: filter});
285                                 }else{
286                                         this._loadInProgress = true;
287                                         var getArgs = {
288                                                         url: self._jsonFileUrl, 
289                                                         handleAs: "json-comment-optional"
290                                                 };
291                                         var getHandler = dojo.xhrGet(getArgs);
292                                         getHandler.addCallback(function(data){
293                                                 try{
294                                                         self._getItemsFromLoadedData(data);
295                                                         self._loadFinished = true;
296                                                         self._loadInProgress = false;
297                                                         
298                                                         filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
299                                                         self._handleQueuedFetches();
300                                                 }catch(e){
301                                                         self._loadFinished = true;
302                                                         self._loadInProgress = false;
303                                                         errorCallback(e, keywordArgs);
304                                                 }
305                                         });
306                                         getHandler.addErrback(function(error){
307                                                 self._loadInProgress = false;
308                                                 errorCallback(error, keywordArgs);
309                                         });
310                                 }
311                         }else if(this._jsonData){
312                                 try{
313                                         this._loadFinished = true;
314                                         this._getItemsFromLoadedData(this._jsonData);
315                                         this._jsonData = null;
316                                         filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
317                                 }catch(e){
318                                         errorCallback(e, keywordArgs);
319                                 }
320                         }else{
321                                 errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
322                         }
323                 }
324         },
325
326         _handleQueuedFetches: function(){
327                 //      summary: 
328                 //              Internal function to execute delayed request in the store.
329                 //Execute any deferred fetches now.
330                 if (this._queuedFetches.length > 0) {
331                         for(var i = 0; i < this._queuedFetches.length; i++){
332                                 var fData = this._queuedFetches[i];
333                                 var delayedQuery = fData.args;
334                                 var delayedFilter = fData.filter;
335                                 if(delayedFilter){
336                                         delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions)); 
337                                 }else{
338                                         this.fetchItemByIdentity(delayedQuery);
339                                 }
340                         }
341                         this._queuedFetches = [];
342                 }
343         },
344
345         _getItemsArray: function(/*object?*/queryOptions){
346                 //      summary: 
347                 //              Internal function to determine which list of items to search over.
348                 //      queryOptions: The query options parameter, if any.
349                 if(queryOptions && queryOptions.deep) {
350                         return this._arrayOfAllItems; 
351                 }
352                 return this._arrayOfTopLevelItems;
353         },
354
355         close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
356                  //     summary: 
357                  //             See dojo.data.api.Read.close()
358         },
359
360         _getItemsFromLoadedData: function(/* Object */ dataObject){
361                 //      summary:
362                 //              Function to parse the loaded data into item format and build the internal items array.
363                 //      description:
364                 //              Function to parse the loaded data into item format and build the internal items array.
365                 //
366                 //      dataObject:
367                 //              The JS data object containing the raw data to convery into item format.
368                 //
369                 //      returns: array
370                 //              Array of items in store item format.
371                 
372                 // First, we define a couple little utility functions...
373                 
374                 function valueIsAnItem(/* anything */ aValue){
375                         // summary:
376                         //              Given any sort of value that could be in the raw json data,
377                         //              return true if we should interpret the value as being an
378                         //              item itself, rather than a literal value or a reference.
379                         // example:
380                         //      |       false == valueIsAnItem("Kermit");
381                         //      |       false == valueIsAnItem(42);
382                         //      |       false == valueIsAnItem(new Date());
383                         //      |       false == valueIsAnItem({_type:'Date', _value:'May 14, 1802'});
384                         //      |       false == valueIsAnItem({_reference:'Kermit'});
385                         //      |       true == valueIsAnItem({name:'Kermit', color:'green'});
386                         //      |       true == valueIsAnItem({iggy:'pop'});
387                         //      |       true == valueIsAnItem({foo:42});
388                         var isItem = (
389                                 (aValue != null) &&
390                                 (typeof aValue == "object") &&
391                                 (!dojo.isArray(aValue)) &&
392                                 (!dojo.isFunction(aValue)) &&
393                                 (aValue.constructor == Object) &&
394                                 (typeof aValue._reference == "undefined") && 
395                                 (typeof aValue._type == "undefined") && 
396                                 (typeof aValue._value == "undefined")
397                         );
398                         return isItem;
399                 }
400                 
401                 var self = this;
402                 function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
403                         self._arrayOfAllItems.push(anItem);
404                         for(var attribute in anItem){
405                                 var valueForAttribute = anItem[attribute];
406                                 if(valueForAttribute){
407                                         if(dojo.isArray(valueForAttribute)){
408                                                 var valueArray = valueForAttribute;
409                                                 for(var k = 0; k < valueArray.length; ++k){
410                                                         var singleValue = valueArray[k];
411                                                         if(valueIsAnItem(singleValue)){
412                                                                 addItemAndSubItemsToArrayOfAllItems(singleValue);
413                                                         }
414                                                 }
415                                         }else{
416                                                 if(valueIsAnItem(valueForAttribute)){
417                                                         addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
418                                                 }
419                                         }
420                                 }
421                         }
422                 }
423
424                 this._labelAttr = dataObject.label;
425
426                 // We need to do some transformations to convert the data structure
427                 // that we read from the file into a format that will be convenient
428                 // to work with in memory.
429
430                 // Step 1: Walk through the object hierarchy and build a list of all items
431                 var i;
432                 var item;
433                 this._arrayOfAllItems = [];
434                 this._arrayOfTopLevelItems = dataObject.items;
435
436                 for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
437                         item = this._arrayOfTopLevelItems[i];
438                         addItemAndSubItemsToArrayOfAllItems(item);
439                         item[this._rootItemPropName]=true;
440                 }
441
442                 // Step 2: Walk through all the attribute values of all the items, 
443                 // and replace single values with arrays.  For example, we change this:
444                 //              { name:'Miss Piggy', pets:'Foo-Foo'}
445                 // into this:
446                 //              { name:['Miss Piggy'], pets:['Foo-Foo']}
447                 // 
448                 // We also store the attribute names so we can validate our store  
449                 // reference and item id special properties for the O(1) isItem
450                 var allAttributeNames = {};
451                 var key;
452
453                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
454                         item = this._arrayOfAllItems[i];
455                         for(key in item){
456                                 if (key !== this._rootItemPropName)
457                                 {
458                                         var value = item[key];
459                                         if(value !== null){
460                                                 if(!dojo.isArray(value)){
461                                                         item[key] = [value];
462                                                 }
463                                         }else{
464                                                 item[key] = [null];
465                                         }
466                                 }
467                                 allAttributeNames[key]=key;
468                         }
469                 }
470
471                 // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
472                 // This should go really fast, it will generally never even run the loop.
473                 while(allAttributeNames[this._storeRefPropName]){
474                         this._storeRefPropName += "_";
475                 }
476                 while(allAttributeNames[this._itemNumPropName]){
477                         this._itemNumPropName += "_";
478                 }
479                 while(allAttributeNames[this._reverseRefMap]){
480                         this._reverseRefMap += "_";
481                 }
482
483                 // Step 4: Some data files specify an optional 'identifier', which is 
484                 // the name of an attribute that holds the identity of each item. 
485                 // If this data file specified an identifier attribute, then build a 
486                 // hash table of items keyed by the identity of the items.
487                 var arrayOfValues;
488
489                 var identifier = dataObject.identifier;
490                 if(identifier){
491                         this._itemsByIdentity = {};
492                         this._features['dojo.data.api.Identity'] = identifier;
493                         for(i = 0; i < this._arrayOfAllItems.length; ++i){
494                                 item = this._arrayOfAllItems[i];
495                                 arrayOfValues = item[identifier];
496                                 var identity = arrayOfValues[0];
497                                 if(!this._itemsByIdentity[identity]){
498                                         this._itemsByIdentity[identity] = item;
499                                 }else{
500                                         if(this._jsonFileUrl){
501                                                 throw new Error("dojo.data.ItemFileReadStore:  The json data as specified by: [" + this._jsonFileUrl + "] is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
502                                         }else if(this._jsonData){
503                                                 throw new Error("dojo.data.ItemFileReadStore:  The json data provided by the creation arguments is malformed.  Items within the list have identifier: [" + identifier + "].  Value collided: [" + identity + "]");
504                                         }
505                                 }
506                         }
507                 }else{
508                         this._features['dojo.data.api.Identity'] = Number;
509                 }
510
511                 // Step 5: Walk through all the items, and set each item's properties 
512                 // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
513                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
514                         item = this._arrayOfAllItems[i];
515                         item[this._storeRefPropName] = this;
516                         item[this._itemNumPropName] = i;
517                 }
518
519                 // Step 6: We walk through all the attribute values of all the items,
520                 // looking for type/value literals and item-references.
521                 //
522                 // We replace item-references with pointers to items.  For example, we change:
523                 //              { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
524                 // into this:
525                 //              { name:['Kermit'], friends:[miss_piggy] } 
526                 // (where miss_piggy is the object representing the 'Miss Piggy' item).
527                 //
528                 // We replace type/value pairs with typed-literals.  For example, we change:
529                 //              { name:['Nelson Mandela'], born:[{_type:'Date', _value:'July 18, 1918'}] }
530                 // into this:
531                 //              { name:['Kermit'], born:(new Date('July 18, 1918')) } 
532                 //
533                 // We also generate the associate map for all items for the O(1) isItem function.
534                 for(i = 0; i < this._arrayOfAllItems.length; ++i){
535                         item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
536                         for(key in item){
537                                 arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
538                                 for(var j = 0; j < arrayOfValues.length; ++j) {
539                                         value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
540                                         if(value !== null && typeof value == "object"){
541                                                 if(value._type && value._value){
542                                                         var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
543                                                         var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
544                                                         if(!mappingObj){ 
545                                                                 throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
546                                                         }else if(dojo.isFunction(mappingObj)){
547                                                                 arrayOfValues[j] = new mappingObj(value._value);
548                                                         }else if(dojo.isFunction(mappingObj.deserialize)){
549                                                                 arrayOfValues[j] = mappingObj.deserialize(value._value);
550                                                         }else{
551                                                                 throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
552                                                         }
553                                                 }
554                                                 if(value._reference){
555                                                         var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
556                                                         if(!dojo.isObject(referenceDescription)){
557                                                                 // example: 'Miss Piggy'
558                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
559                                                                 arrayOfValues[j] = this._itemsByIdentity[referenceDescription];
560                                                         }else{
561                                                                 // example: {name:'Miss Piggy'}
562                                                                 // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
563                                                                 for(var k = 0; k < this._arrayOfAllItems.length; ++k){
564                                                                         var candidateItem = this._arrayOfAllItems[k];
565                                                                         var found = true;
566                                                                         for(var refKey in referenceDescription){
567                                                                                 if(candidateItem[refKey] != referenceDescription[refKey]){ 
568                                                                                         found = false; 
569                                                                                 }
570                                                                         }
571                                                                         if(found){ 
572                                                                                 arrayOfValues[j] = candidateItem; 
573                                                                         }
574                                                                 }
575                                                         }
576                                                         if(this.referenceIntegrity){
577                                                                 var refItem = arrayOfValues[j];
578                                                                 if(this.isItem(refItem)){
579                                                                         this._addReferenceToMap(refItem, item, key);
580                                                                 }
581                                                         }
582                                                 }else if(this.isItem(value)){
583                                                         //It's a child item (not one referenced through _reference).  
584                                                         //We need to treat this as a referenced item, so it can be cleaned up
585                                                         //in a write store easily.
586                                                         if(this.referenceIntegrity){
587                                                                 this._addReferenceToMap(value, item, key);
588                                                         }
589                                                 }
590                                         }
591                                 }
592                         }
593                 }
594         },
595
596         _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
597                  //     summary:
598                  //             Method to add an reference map entry for an item and attribute.
599                  //     description:
600                  //             Method to add an reference map entry for an item and attribute.                  //
601                  //     refItem:
602                  //             The item that is referenced.
603                  //     parentItem:
604                  //             The item that holds the new reference to refItem.
605                  //     attribute:
606                  //             The attribute on parentItem that contains the new reference.
607                  
608                  //Stub function, does nothing.  Real processing is in ItemFileWriteStore.
609         },
610
611         getIdentity: function(/* item */ item){
612                 //      summary: 
613                 //              See dojo.data.api.Identity.getIdentity()
614                 var identifier = this._features['dojo.data.api.Identity'];
615                 if(identifier === Number){
616                         return item[this._itemNumPropName]; // Number
617                 }else{
618                         var arrayOfValues = item[identifier];
619                         if(arrayOfValues){
620                                 return arrayOfValues[0]; // Object || String
621                         }
622                 }
623                 return null; // null
624         },
625
626         fetchItemByIdentity: function(/* Object */ keywordArgs){
627                 //      summary: 
628                 //              See dojo.data.api.Identity.fetchItemByIdentity()
629
630                 // Hasn't loaded yet, we have to trigger the load.
631                 if(!this._loadFinished){
632                         var self = this;
633                         if(this._jsonFileUrl){
634
635                                 if(this._loadInProgress){
636                                         this._queuedFetches.push({args: keywordArgs});
637                                 }else{
638                                         this._loadInProgress = true;
639                                         var getArgs = {
640                                                         url: self._jsonFileUrl, 
641                                                         handleAs: "json-comment-optional"
642                                         };
643                                         var getHandler = dojo.xhrGet(getArgs);
644                                         getHandler.addCallback(function(data){
645                                                 var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
646                                                 try{
647                                                         self._getItemsFromLoadedData(data);
648                                                         self._loadFinished = true;
649                                                         self._loadInProgress = false;
650                                                         var item = self._getItemByIdentity(keywordArgs.identity);
651                                                         if(keywordArgs.onItem){
652                                                                 keywordArgs.onItem.call(scope, item);
653                                                         }
654                                                         self._handleQueuedFetches();
655                                                 }catch(error){
656                                                         self._loadInProgress = false;
657                                                         if(keywordArgs.onError){
658                                                                 keywordArgs.onError.call(scope, error);
659                                                         }
660                                                 }
661                                         });
662                                         getHandler.addErrback(function(error){
663                                                 self._loadInProgress = false;
664                                                 if(keywordArgs.onError){
665                                                         var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
666                                                         keywordArgs.onError.call(scope, error);
667                                                 }
668                                         });
669                                 }
670
671                         }else if(this._jsonData){
672                                 // Passed in data, no need to xhr.
673                                 self._getItemsFromLoadedData(self._jsonData);
674                                 self._jsonData = null;
675                                 self._loadFinished = true;
676                                 var item = self._getItemByIdentity(keywordArgs.identity);
677                                 if(keywordArgs.onItem){
678                                         var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
679                                         keywordArgs.onItem.call(scope, item);
680                                 }
681                         } 
682                 }else{
683                         // Already loaded.  We can just look it up and call back.
684                         var item = this._getItemByIdentity(keywordArgs.identity);
685                         if(keywordArgs.onItem){
686                                 var scope =  keywordArgs.scope?keywordArgs.scope:dojo.global;
687                                 keywordArgs.onItem.call(scope, item);
688                         }
689                 }
690         },
691
692         _getItemByIdentity: function(/* Object */ identity){
693                 //      summary:
694                 //              Internal function to look an item up by its identity map.
695                 var item = null;
696                 if(this._itemsByIdentity){
697                         item = this._itemsByIdentity[identity];
698                 }else{
699                         item = this._arrayOfAllItems[identity];
700                 }
701                 if(item === undefined){
702                         item = null;
703                 }
704                 return item; // Object
705         },
706
707         getIdentityAttributes: function(/* item */ item){
708                 //      summary: 
709                 //              See dojo.data.api.Identity.getIdentifierAttributes()
710                  
711                 var identifier = this._features['dojo.data.api.Identity'];
712                 if(identifier === Number){
713                         // If (identifier === Number) it means getIdentity() just returns
714                         // an integer item-number for each item.  The dojo.data.api.Identity
715                         // spec says we need to return null if the identity is not composed 
716                         // of attributes 
717                         return null; // null
718                 }else{
719                         return [identifier]; // Array
720                 }
721         },
722         
723         _forceLoad: function(){
724                 //      summary: 
725                 //              Internal function to force a load of the store if it hasn't occurred yet.  This is required
726                 //              for specific functions to work properly.  
727                 var self = this;
728                 if(this._jsonFileUrl){
729                                 var getArgs = {
730                                         url: self._jsonFileUrl, 
731                                         handleAs: "json-comment-optional",
732                                         sync: true
733                                 };
734                         var getHandler = dojo.xhrGet(getArgs);
735                         getHandler.addCallback(function(data){
736                                 try{
737                                         //Check to be sure there wasn't another load going on concurrently 
738                                         //So we don't clobber data that comes in on it.  If there is a load going on
739                                         //then do not save this data.  It will potentially clobber current data.
740                                         //We mainly wanted to sync/wait here.
741                                         //TODO:  Revisit the loading scheme of this store to improve multi-initial
742                                         //request handling.
743                                         if (self._loadInProgress !== true && !self._loadFinished) {
744                                                 self._getItemsFromLoadedData(data);
745                                                 self._loadFinished = true;
746                                         }
747                                 }catch(e){
748                                         console.log(e);
749                                         throw e;
750                                 }
751                         });
752                         getHandler.addErrback(function(error){
753                                 throw error;
754                         });
755                 }else if(this._jsonData){
756                         self._getItemsFromLoadedData(self._jsonData);
757                         self._jsonData = null;
758                         self._loadFinished = true;
759                 } 
760         }
761 });
762 //Mix in the simple fetch implementation to this class.
763 dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
764
765 }