]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojox/data/KeyValueStore.js
add Dojo 1.1.1
[eow] / static / dojo-release-1.1.1 / dojox / data / KeyValueStore.js
1 if(!dojo._hasResource["dojox.data.KeyValueStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojox.data.KeyValueStore"] = true;
3 dojo.provide("dojox.data.KeyValueStore");
4
5 dojo.require("dojo.data.util.filter");
6 dojo.require("dojo.data.util.simpleFetch");
7
8 dojo.declare("dojox.data.KeyValueStore", null, {
9         //      summary:
10         //              This is a dojo.data store implementation.  It can take in either a Javascript
11         //              array, JSON string, or URL as the data source.  Data is expected to be in the 
12         //              following format:
13         //                      [
14         //                              { "key1": "value1" },
15         //                              { "key2": "value2" }
16         //                      ]
17         //              This is to mimic the Java Properties file format.  Each 'item' from this store 
18         //              is a JS object representing a key-value pair.  If an item in the above array has 
19         //              more than one key/value pair, only the first will be used/accessed.
20         constructor: function(/* Object */ keywordParameters){
21                 //      summary: constructor
22                 //      keywordParameters: {url: String}
23                 //      keywordParameters: {data: string}
24                 //      keywordParameters: {dataVar: jsonObject}
25                 if(keywordParameters.url){
26                         this.url = keywordParameters.url;
27                 }
28                 this._keyValueString = keywordParameters.data;
29                 this._keyValueVar = keywordParameters.dataVar;
30                 this._keyAttribute = "key";
31                 this._valueAttribute = "value";
32                 this._storeProp = "_keyValueStore";
33                 this._features = {      
34                         'dojo.data.api.Read': true,
35                         'dojo.data.api.Identity': true 
36                 };
37                 this._loadInProgress = false;   //Got to track the initial load to prevent duelling loads of the dataset.
38                 this._queuedFetches = [];
39         },
40         
41         url: "",
42         data: "",
43         
44         _assertIsItem: function(/* item */ item){
45                 //      summary:
46                 //      This function tests whether the item passed in is indeed an item in the store.
47                 //      item: 
48                 //              The item to test for being contained by the store.
49                 if(!this.isItem(item)){ 
50                         throw new Error("dojox.data.KeyValueStore: a function was passed an item argument that was not an item");
51                 }
52         },
53         
54         _assertIsAttribute: function(/* item */ item, /* String */ attribute){
55                 //      summary:
56                 //      This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
57                 //      attribute: 
58                 //              The attribute to test for being contained by the store.
59                 if(!dojo.isString(attribute)){
60                         throw new Error("dojox.data.KeyValueStore: a function was passed an attribute argument that was not an attribute object nor an attribute name string");
61                 }
62         },
63
64 /***************************************
65      dojo.data.api.Read API
66 ***************************************/
67         getValue: function(     /* item */ item, 
68                                                 /* attribute-name-string */ attribute, 
69                                                 /* value? */ defaultValue){
70                 //      summary: 
71                 //              See dojo.data.api.Read.getValue()
72                 this._assertIsItem(item);
73                 this._assertIsAttribute(item, attribute);
74                 if(attribute == this._keyAttribute){ // Looking for key
75                         return item[this._keyAttribute];
76                 }
77                 return item[this._valueAttribute]; // Otherwise, attribute == ('value' || the actual key )
78         },
79
80         getValues: function(/* item */ item,
81                                                 /* attribute-name-string */ attribute){
82                 //      summary: 
83                 //              See dojo.data.api.Read.getValues()
84                 //              Key/Value syntax does not support multi-valued attributes, so this is just a
85                 //              wrapper function for getValue().
86                 var value = this.getValue(item, attribute);
87                 return (value ? [value] : []); //Array
88         },
89
90         getAttributes: function(/* item */ item){
91                 //      summary: 
92                 //              See dojo.data.api.Read.getAttributes()
93                 return [this._keyAttribute, this._valueAttribute, item[this._keyAttribute]];
94         },
95
96         hasAttribute: function( /* item */ item,
97                                                         /* attribute-name-string */ attribute){
98                 //      summary: 
99                 //              See dojo.data.api.Read.hasAttribute()
100                 this._assertIsItem(item);
101                 this._assertIsAttribute(item, attribute);
102                 return (attribute == this._keyAttribute || attribute == this._valueAttribute || attribute == item[this._keyAttribute]);
103         },
104
105         containsValue: function(/* item */ item,
106                                                         /* attribute-name-string */ attribute, 
107                                                         /* anything */ value){
108                 //      summary: 
109                 //              See dojo.data.api.Read.containsValue()
110                 var regexp = undefined;
111                 if(typeof value === "string"){
112                    regexp = dojo.data.util.filter.patternToRegExp(value, false);
113                 }
114                 return this._containsValue(item, attribute, value, regexp); //boolean.
115         },
116
117         _containsValue: function(       /* item */ item, 
118                                                                 /* attribute || attribute-name-string */ attribute, 
119                                                                 /* anything */ value,
120                                                                 /* RegExp?*/ regexp){
121                 //      summary: 
122                 //              Internal function for looking at the values contained by the item.
123                 //      description: 
124                 //              Internal function for looking at the values contained by the item.  This 
125                 //              function allows for denoting if the comparison should be case sensitive for
126                 //              strings or not (for handling filtering cases where string case should not matter)
127                 //      
128                 //      item:
129                 //              The data item to examine for attribute values.
130                 //      attribute:
131                 //              The attribute to inspect.
132                 //      value:  
133                 //              The value to match.
134                 //      regexp:
135                 //              Optional regular expression generated off value if value was of string type to handle wildcarding.
136                 //              If present and attribute values are string, then it can be used for comparison instead of 'value'
137                 var values = this.getValues(item, attribute);
138                 for(var i = 0; i < values.length; ++i){
139                         var possibleValue = values[i];
140                         if(typeof possibleValue === "string" && regexp){
141                                 return (possibleValue.match(regexp) !== null);
142                         }else{
143                                 //Non-string matching.
144                                 if(value === possibleValue){
145                                         return true; // Boolean
146                                 }
147                         }
148                 }
149                 return false; // Boolean
150         },
151
152         isItem: function(/* anything */ something){
153                 //      summary: 
154                 //              See dojo.data.api.Read.isItem()
155                 if (something && something[this._storeProp] === this) {
156                         return true; //Boolean
157                 }
158                 return false; //Boolean
159         },
160
161         isItemLoaded: function(/* anything */ something) {
162                 //      summary: 
163                 //              See dojo.data.api.Read.isItemLoaded()
164                 //              The KeyValueStore always loads all items, so if it's an item, then it's loaded.
165                 return this.isItem(something); //Boolean
166         },
167
168         loadItem: function(/* object */ keywordArgs){
169                 //      summary: 
170                 //              See dojo.data.api.Read.loadItem()
171                 //      description:
172                 //              The KeyValueStore always loads all items, so if it's an item, then it's loaded.
173                 //              From the dojo.data.api.Read.loadItem docs:
174                 //                      If a call to isItemLoaded() returns true before loadItem() is even called,
175                 //                      then loadItem() need not do any work at all and will not even invoke
176                 //                      the callback handlers.
177         },
178
179         getFeatures: function(){
180                 //      summary: 
181                 //              See dojo.data.api.Read.getFeatures()
182                 return this._features; //Object
183         },
184
185         close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
186                 //      summary: 
187                 //              See dojo.data.api.Read.close()
188         },
189
190         getLabel: function(/* item */ item){
191                 //      summary: 
192                 //              See dojo.data.api.Read.getLabel()
193                 return item[this._keyAttribute];
194         },
195
196         getLabelAttributes: function(/* item */ item){
197                 //      summary: 
198                 //              See dojo.data.api.Read.getLabelAttributes()
199                 return [this._keyAttribute];
200         },
201         
202         // The dojo.data.api.Read.fetch() function is implemented as
203         // a mixin from dojo.data.util.simpleFetch.
204         // That mixin requires us to define _fetchItems().
205         _fetchItems: function(  /* Object */ keywordArgs,
206                                                         /* Function */ findCallback,
207                                                         /* Function */ errorCallback){
208                 //      summary: 
209                 //              See dojo.data.util.simpleFetch.fetch()
210                 
211                 var self = this;
212
213                 var filter = function(requestArgs, arrayOfAllItems){
214                         var items = null;
215                         if(requestArgs.query){
216                                 items = [];
217                                 var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false; 
218
219                                 //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
220                                 //same value for each item examined.  Much more efficient.
221                                 var regexpList = {};
222                                 for(var key in requestArgs.query){
223                                         var value = requestArgs.query[key];
224                                         if(typeof value === "string"){
225                                                 regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
226                                         }
227                                 }
228
229                                 for(var i = 0; i < arrayOfAllItems.length; ++i){
230                                         var match = true;
231                                         var candidateItem = arrayOfAllItems[i];
232                                         for(var key in requestArgs.query){
233                                                 var value = requestArgs.query[key];
234                                                 if(!self._containsValue(candidateItem, key, value, regexpList[key])){
235                                                         match = false;
236                                                 }
237                                         }
238                                         if(match){
239                                                 items.push(candidateItem);
240                                         }
241                                 }
242                         }else if(requestArgs.identity){
243                                 items = [];
244                                 var item;
245                                 for(var key in arrayOfAllItems){
246                                         item = arrayOfAllItems[key];
247                                         if(item[self._keyAttribute] == requestArgs.identity){
248                                                 items.push(item);
249                                                 break;
250                                         }
251                                 }
252                         }else{
253                                 // We want a copy to pass back in case the parent wishes to sort the array.  We shouldn't allow resort 
254                                 // of the internal list so that multiple callers can get lists and sort without affecting each other.
255                                 if(arrayOfAllItems.length> 0){
256                                         items = arrayOfAllItems.slice(0,arrayOfAllItems.length); 
257                                 }
258                         }
259                         findCallback(items, requestArgs);
260                 };
261
262                 if(this._loadFinished){
263                         filter(keywordArgs, this._arrayOfAllItems);
264                 }else{
265                         if(this.url !== ""){
266                                 //If fetches come in before the loading has finished, but while
267                                 //a load is in progress, we have to defer the fetching to be 
268                                 //invoked in the callback.
269                                 if(this._loadInProgress){
270                                         this._queuedFetches.push({args: keywordArgs, filter: filter});
271                                 }else{
272                                         this._loadInProgress = true;
273                                         var getArgs = {
274                                                         url: self.url, 
275                                                         handleAs: "json-comment-filtered"
276                                                 };
277                                         var getHandler = dojo.xhrGet(getArgs);
278                                         getHandler.addCallback(function(data){
279                                                 self._processData(data);
280                                                 filter(keywordArgs, self._arrayOfAllItems);
281                                                 self._handleQueuedFetches();
282                                         });
283                                         getHandler.addErrback(function(error){
284                                                 self._loadInProgress = false;
285                                                 throw error;
286                                         });
287                                 }
288                         }else if(this._keyValueString){
289                                 this._processData(eval(this._keyValueString));
290                                 this._keyValueString = null;
291                                 filter(keywordArgs, this._arrayOfAllItems);
292                         }else if(this._keyValueVar){
293                                 this._processData(this._keyValueVar);
294                                 this._keyValueVar = null;
295                                 filter(keywordArgs, this._arrayOfAllItems);
296                         }else{
297                                 throw new Error("dojox.data.KeyValueStore: No source data was provided as either URL, String, or Javascript variable data input.");
298                         }
299                 }
300                 
301         },
302
303         _handleQueuedFetches: function(){
304                 //      summary: 
305                 //              Internal function to execute delayed request in the store.
306                 //Execute any deferred fetches now.
307                 if(this._queuedFetches.length > 0){
308                         for(var i = 0; i < this._queuedFetches.length; i++){
309                                 var fData = this._queuedFetches[i];
310                                 var delayedFilter = fData.filter;
311                                 var delayedQuery = fData.args;
312                                 if(delayedFilter){
313                                         delayedFilter(delayedQuery, this._arrayOfAllItems); 
314                                 }else{
315                                         this.fetchItemByIdentity(fData.args);
316                                 }
317                         }
318                         this._queuedFetches = [];
319                 }
320         },
321         
322         _processData: function(/* Array */ data){
323                 this._arrayOfAllItems = [];
324                 for(var i=0; i<data.length; i++){
325                         this._arrayOfAllItems.push(this._createItem(data[i]));
326                 }
327                 this._loadFinished = true;
328                 this._loadInProgress = false;
329         },
330         
331         _createItem: function(/* Object */ something){
332                 var item = {};
333                 item[this._storeProp] = this;
334                 for(var i in something){
335                         item[this._keyAttribute] = i;
336                         item[this._valueAttribute] = something[i];
337                         break;
338                 }
339                 return item; //Object
340         },
341
342 /***************************************
343      dojo.data.api.Identity API
344 ***************************************/
345         getIdentity: function(/* item */ item){
346                 //      summary: 
347                 //              See dojo.data.api.Identity.getIdentity()
348                 if(this.isItem(item)){
349                         return item[this._keyAttribute]; //String
350                 }
351                 return null; //null
352         },
353
354         getIdentityAttributes: function(/* item */ item){
355                 //      summary: 
356                 //              See dojo.data.api.Identity.getIdentifierAttributes()
357                 return [this._keyAttribute];
358         },
359
360         fetchItemByIdentity: function(/* object */ keywordArgs){
361                 //      summary: 
362                 //              See dojo.data.api.Identity.fetchItemByIdentity()
363                 keywordArgs.oldOnItem = keywordArgs.onItem;
364                 keywordArgs.onItem = null;
365                 keywordArgs.onComplete = this._finishFetchItemByIdentity ;
366                 this.fetch(keywordArgs);
367         },
368         
369         _finishFetchItemByIdentity: function(/* Array */ items, /* object */ request) {
370                 var scope = request.scope || dojo.global;
371                 if(items.length){
372                         request.oldOnItem.call(scope, items[0]);
373                 }else{
374                         request.oldOnItem.call(scope, null);
375                 }
376         }
377 });
378 //Mix in the simple fetch implementation to this class.
379 dojo.extend(dojox.data.KeyValueStore,dojo.data.util.simpleFetch);
380
381 }