Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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