source: Dev/branches/rest-dojo-ui/client/dojox/data/CsvStore.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 22.7 KB
Line 
1define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/xhr", "dojo/_base/window","dojo/data/util/filter", "dojo/data/util/simpleFetch"],
2  function(lang, declare, xhr, winUtil, filterUtil, simpleFetch) {
3
4var CsvStore = declare("dojox.data.CsvStore", null, {
5        // summary:
6        //              The CsvStore implements the dojo.data.api.Read API and reads
7        //              data from files in CSV (Comma Separated Values) format.
8        //              All values are simple string values. References to other items
9        //              are not supported as attribute values in this datastore.
10        //
11        //              Example data file:
12        //              name, color, age, tagline
13        //              Kermit, green, 12, "Hi, I'm Kermit the Frog."
14        //              Fozzie Bear, orange, 10, "Wakka Wakka Wakka!"
15        //              Miss Piggy, pink, 11, "Kermie!"
16        //
17        //              Note that values containing a comma must be enclosed with quotes ("")
18        //              Also note that values containing quotes must be escaped with two consecutive quotes (""quoted"")
19        //
20        // examples:
21        //              var csvStore = new dojox.data.CsvStore({url:"movies.csv");
22        //              var csvStore = new dojox.data.CsvStore({url:"http://example.com/movies.csv");
23
24        constructor: function(/* Object */ keywordParameters){
25                // summary:
26                //              initializer
27                // keywordParameters: {url: String}
28                // keywordParameters: {data: String}
29                // keywordParameters: {label: String} The column label for the column to use for the label returned by getLabel.
30                // keywordParameters: {identifier: String} The column label for the column to use for the identity.  Optional.  If not set, the identity is the row number.
31               
32                this._attributes = [];                  // e.g. ["Title", "Year", "Producer"]
33                this._attributeIndexes = {};    // e.g. {Title: 0, Year: 1, Producer: 2}
34                this._dataArray = [];                   // e.g. [[<Item0>],[<Item1>],[<Item2>]]
35                this._arrayOfAllItems = [];             // e.g. [{_csvId:0,_csvStore:store},...]
36                this._loadFinished = false;
37                if(keywordParameters.url){
38                        this.url = keywordParameters.url;
39                }
40                this._csvData = keywordParameters.data;
41                if(keywordParameters.label){
42                        this.label = keywordParameters.label;
43                }else if(this.label === ""){
44                        this.label = undefined;
45                }
46                this._storeProp = "_csvStore";  // Property name for the store reference on every item.
47                this._idProp = "_csvId";                // Property name for the Item Id on every item.
48                this._features = {
49                        'dojo.data.api.Read': true,
50                        'dojo.data.api.Identity': true
51                };
52                this._loadInProgress = false;   //Got to track the initial load to prevent duelling loads of the dataset.
53                this._queuedFetches = [];
54                this.identifier = keywordParameters.identifier;
55                if(this.identifier === ""){
56                        delete this.identifier;
57                }else{
58                        this._idMap = {};
59                }
60                if("separator" in keywordParameters){
61                        this.separator = keywordParameters.separator;
62                }
63                if("urlPreventCache" in keywordParameters){
64                        this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
65                }
66        },
67
68        // url: [public] string
69        //              Declarative hook for setting Csv source url.
70        url: "",
71
72        // label: [public] string
73        //              Declarative hook for setting the label attribute.
74        label: "",
75
76        // identifier: [public] string
77        //              Declarative hook for setting the identifier.
78        identifier: "",
79
80        // separator: [public] string
81        //              Declatative and programmatic hook for defining the separator
82        //              character used in the Csv style file.
83        separator: ",",
84
85        // separator: [public] string
86        //              Parameter to allow specifying if preventCache should be passed to
87        //              the xhrGet call or not when loading data from a url.
88        //              Note this does not mean the store calls the server on each fetch,
89        //              only that the data load has preventCache set as an option.
90        urlPreventCache: false,
91
92        _assertIsItem: function(/* item */ item){
93                // summary:
94                //      This function tests whether the item passed in is indeed an item in the store.
95                // item:
96                //              The item to test for being contained by the store.
97                if(!this.isItem(item)){
98                        throw new Error(this.declaredClass + ": a function was passed an item argument that was not an item");
99                }
100        },
101       
102        _getIndex: function(item){
103                // summary:
104                //              Internal function to get the internal index to the item data from the item handle
105                // item:
106                //              The idem handle to get the index for.
107                var idx = this.getIdentity(item);
108                if(this.identifier){
109                        idx = this._idMap[idx];
110                }
111                return idx;
112        },
113
114/***************************************
115     dojo.data.api.Read API
116***************************************/
117        getValue: function(     /* item */ item,
118                                                /* attribute || attribute-name-string */ attribute,
119                                                /* value? */ defaultValue){
120                // summary:
121                //      See dojo.data.api.Read.getValue()
122                //              Note that for the CsvStore, an empty string value is the same as no value,
123                //              so the defaultValue would be returned instead of an empty string.
124                this._assertIsItem(item);
125                var itemValue = defaultValue;
126                if(typeof attribute === "string"){
127                        var ai = this._attributeIndexes[attribute];
128                        if(ai != null){
129                                var itemData = this._dataArray[this._getIndex(item)];
130                                itemValue = itemData[ai] || defaultValue;
131                        }
132                }else{
133                        throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
134                }
135                return itemValue; //String
136        },
137
138        getValues: function(/* item */ item,
139                                                /* attribute || attribute-name-string */ attribute){
140                // summary:
141                //              See dojo.data.api.Read.getValues()
142                //              CSV syntax does not support multi-valued attributes, so this is just a
143                //              wrapper function for getValue().
144                var value = this.getValue(item, attribute);
145                return (value ? [value] : []); //Array
146        },
147
148        getAttributes: function(/* item */ item){
149                // summary:
150                //              See dojo.data.api.Read.getAttributes()
151                this._assertIsItem(item);
152                var attributes = [];
153                var itemData = this._dataArray[this._getIndex(item)];
154                for(var i=0; i<itemData.length; i++){
155                        // Check for empty string values. CsvStore treats empty strings as no value.
156                        if(itemData[i] !== ""){
157                                attributes.push(this._attributes[i]);
158                        }
159                }
160                return attributes; //Array
161        },
162
163        hasAttribute: function( /* item */ item,
164                                                        /* attribute-name-string */ attribute){
165                // summary:
166                //              See dojo.data.api.Read.hasAttribute()
167                //              The hasAttribute test is true if attribute has an index number within the item's array length
168                //              AND if the item has a value for that attribute. Note that for the CsvStore, an
169                //              empty string value is the same as no value.
170                this._assertIsItem(item);
171                if(typeof attribute === "string"){
172                        var attributeIndex = this._attributeIndexes[attribute];
173                        var itemData = this._dataArray[this._getIndex(item)];
174                        return (typeof attributeIndex !== "undefined" && attributeIndex < itemData.length && itemData[attributeIndex] !== ""); //Boolean
175                }else{
176                        throw new Error(this.declaredClass + ": a function was passed an attribute argument that was not a string");
177                }
178        },
179
180        containsValue: function(/* item */ item,
181                                                        /* attribute || attribute-name-string */ attribute,
182                                                        /* anything */ value){
183                // summary:
184                //              See dojo.data.api.Read.containsValue()
185                var regexp = undefined;
186                if(typeof value === "string"){
187                        regexp = filterUtil.patternToRegExp(value, false);
188                }
189                return this._containsValue(item, attribute, value, regexp); //boolean.
190        },
191
192        _containsValue: function(       /* item */ item,
193                                                                /* attribute || attribute-name-string */ attribute,
194                                                                /* anything */ value,
195                                                                /* RegExp?*/ regexp){
196                // summary:
197                //              Internal function for looking at the values contained by the item.
198                // description:
199                //              Internal function for looking at the values contained by the item.  This
200                //              function allows for denoting if the comparison should be case sensitive for
201                //              strings or not (for handling filtering cases where string case should not matter)
202                //
203                // item:
204                //              The data item to examine for attribute values.
205                // attribute:
206                //              The attribute to inspect.
207                // value:
208                //              The value to match.
209                // regexp:
210                //              Optional regular expression generated off value if value was of string type to handle wildcarding.
211                //              If present and attribute values are string, then it can be used for comparison instead of 'value'
212                // tags:
213                //              private
214                var values = this.getValues(item, attribute);
215                for(var i = 0; i < values.length; ++i){
216                        var possibleValue = values[i];
217                        if(typeof possibleValue === "string" && regexp){
218                                return (possibleValue.match(regexp) !== null);
219                        }else{
220                                //Non-string matching.
221                                if(value === possibleValue){
222                                        return true; // Boolean
223                                }
224                        }
225                }
226                return false; // Boolean
227        },
228
229        isItem: function(/* anything */ something){
230                // summary:
231                //              See dojo.data.api.Read.isItem()
232                if(something && something[this._storeProp] === this){
233                        var identity = something[this._idProp];
234                        //If an identifier was specified, we have to look it up via that and the mapping,
235                        //otherwise, just use row number.
236                        if(this.identifier){
237                                var data = this._dataArray[this._idMap[identity]];
238                                if(data){
239                                        return true;
240                                }
241                        }else{
242                                if(identity >= 0 && identity < this._dataArray.length){
243                                        return true; //Boolean
244                                }
245                        }
246                }
247                return false; //Boolean
248        },
249
250        isItemLoaded: function(/* anything */ something){
251                // summary:
252                //              See dojo.data.api.Read.isItemLoaded()
253                //              The CsvStore always loads all items, so if it's an item, then it's loaded.
254                return this.isItem(something); //Boolean
255        },
256
257        loadItem: function(/* item */ item){
258                // summary:
259                //              See dojo.data.api.Read.loadItem()
260                // description:
261                //              The CsvStore always loads all items, so if it's an item, then it's loaded.
262                //              From the dojo.data.api.Read.loadItem docs:
263                //                      If a call to isItemLoaded() returns true before loadItem() is even called,
264                //                      then loadItem() need not do any work at all and will not even invoke
265                //                      the callback handlers.
266        },
267
268        getFeatures: function(){
269                // summary:
270                //              See dojo.data.api.Read.getFeatures()
271                return this._features; //Object
272        },
273
274        getLabel: function(/* item */ item){
275                // summary:
276                //              See dojo.data.api.Read.getLabel()
277                if(this.label && this.isItem(item)){
278                        return this.getValue(item,this.label); //String
279                }
280                return undefined; //undefined
281        },
282
283        getLabelAttributes: function(/* item */ item){
284                // summary:
285                //              See dojo.data.api.Read.getLabelAttributes()
286                if(this.label){
287                        return [this.label]; //array
288                }
289                return null; //null
290        },
291
292
293        // The dojo.data.api.Read.fetch() function is implemented as
294        // a mixin from dojo.data.util.simpleFetch.
295        // That mixin requires us to define _fetchItems().
296        _fetchItems: function(  /* Object */ keywordArgs,
297                                                        /* Function */ findCallback,
298                                                        /* Function */ errorCallback){
299                // summary:
300                //              See dojo.data.util.simpleFetch.fetch()
301                // tags:
302                //              protected
303                var self = this;
304                var filter = function(requestArgs, arrayOfAllItems){
305                        var items = null;
306                        if(requestArgs.query){
307                                var key, value;
308                                items = [];
309                                var ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
310
311                                //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
312                                //same value for each item examined.  Much more efficient.
313                                var regexpList = {};
314                                for(key in requestArgs.query){
315                                        value = requestArgs.query[key];
316                                        if(typeof value === "string"){
317                                                regexpList[key] = filterUtil.patternToRegExp(value, ignoreCase);
318                                        }
319                                }
320
321                                for(var i = 0; i < arrayOfAllItems.length; ++i){
322                                        var match = true;
323                                        var candidateItem = arrayOfAllItems[i];
324                                        for(key in requestArgs.query){
325                                                value = requestArgs.query[key];
326                                                if(!self._containsValue(candidateItem, key, value, regexpList[key])){
327                                                        match = false;
328                                                }
329                                        }
330                                        if(match){
331                                                items.push(candidateItem);
332                                        }
333                                }
334                        }else{
335                                // We want a copy to pass back in case the parent wishes to sort the array.  We shouldn't allow resort
336                                // of the internal list so that multiple callers can get lists and sort without affecting each other.
337                                items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
338                               
339                        }
340                        findCallback(items, requestArgs);
341                };
342
343                if(this._loadFinished){
344                        filter(keywordArgs, this._arrayOfAllItems);
345                }else{
346                        if(this.url !== ""){
347                                //If fetches come in before the loading has finished, but while
348                                //a load is in progress, we have to defer the fetching to be
349                                //invoked in the callback.
350                                if(this._loadInProgress){
351                                        this._queuedFetches.push({args: keywordArgs, filter: filter});
352                                }else{
353                                        this._loadInProgress = true;
354                                        var getArgs = {
355                                                        url: self.url,
356                                                        handleAs: "text",
357                                                        preventCache: self.urlPreventCache
358                                                };
359                                        var getHandler = xhr.get(getArgs);
360                                        getHandler.addCallback(function(data){
361                                                try{
362                                                        self._processData(data);
363                                                        filter(keywordArgs, self._arrayOfAllItems);
364                                                        self._handleQueuedFetches();
365                                                }catch(e){
366                                                        errorCallback(e, keywordArgs);
367                                                }
368                                        });
369                                        getHandler.addErrback(function(error){
370                                                self._loadInProgress = false;
371                                                if(errorCallback){
372                                                        errorCallback(error, keywordArgs);
373                                                }else{
374                                                        throw error;
375                                                }
376                                        });
377                                        //Wire up the cancel to abort of the request
378                                        //This call cancel on the deferred if it hasn't been called
379                                        //yet and then will chain to the simple abort of the
380                                        //simpleFetch keywordArgs
381                                        var oldAbort = null;
382                                        if(keywordArgs.abort){
383                                                oldAbort = keywordArgs.abort;
384                                        }
385                                        keywordArgs.abort = function(){
386                                                var df = getHandler;
387                                                if(df && df.fired === -1){
388                                                        df.cancel();
389                                                        df = null;
390                                                }
391                                                if(oldAbort){
392                                                        oldAbort.call(keywordArgs);
393                                                }
394                                        };
395                                }
396                        }else if(this._csvData){
397                                try{
398                                        this._processData(this._csvData);
399                                        this._csvData = null;
400                                        filter(keywordArgs, this._arrayOfAllItems);
401                                }catch(e){
402                                        errorCallback(e, keywordArgs);
403                                }
404                        }else{
405                                var error = new Error(this.declaredClass + ": No CSV source data was provided as either URL or String data input.");
406                                if(errorCallback){
407                                        errorCallback(error, keywordArgs);
408                                }else{
409                                        throw error;
410                                }
411                        }
412                }
413        },
414       
415        close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
416                 //     summary:
417                 //             See dojo.data.api.Read.close()
418        },
419       
420       
421        // -------------------------------------------------------------------
422        // Private methods
423        _getArrayOfArraysFromCsvFileContents: function(/* string */ csvFileContents){
424                // summary:
425                //              Parses a string of CSV records into a nested array structure.
426                // description:
427                //              Given a string containing CSV records, this method parses
428                //              the string and returns a data structure containing the parsed
429                //              content.  The data structure we return is an array of length
430                //              R, where R is the number of rows (lines) in the CSV data.  The
431                //              return array contains one sub-array for each CSV line, and each
432                //              sub-array contains C string values, where C is the number of
433                //              columns in the CSV data.
434                // example:
435                //              For example, given this CSV string as input:
436                //                      "Title, Year, Producer \n Alien, 1979, Ridley Scott \n Blade Runner, 1982, Ridley Scott"
437                //              this._dataArray will be set to:
438                //                      [["Alien", "1979", "Ridley Scott"],
439                //                      ["Blade Runner", "1982", "Ridley Scott"]]
440                //              And this._attributes will be set to:
441                //                      ["Title", "Year", "Producer"]
442                //              And this._attributeIndexes will be set to:
443                //                      { "Title":0, "Year":1, "Producer":2 }
444                // tags:
445                //              private
446                if(lang.isString(csvFileContents)){
447                        var leadingWhiteSpaceCharacters = new RegExp("^\\s+",'g');
448                        var trailingWhiteSpaceCharacters = new RegExp("\\s+$",'g');
449                        var doubleQuotes = new RegExp('""','g');
450                        var arrayOfOutputRecords = [];
451                        var i;
452                       
453                        var arrayOfInputLines = this._splitLines(csvFileContents);
454                        for(i = 0; i < arrayOfInputLines.length; ++i){
455                                var singleLine = arrayOfInputLines[i];
456                                if(singleLine.length > 0){
457                                        var listOfFields = singleLine.split(this.separator);
458                                        var j = 0;
459                                        while(j < listOfFields.length){
460                                                var space_field_space = listOfFields[j];
461                                                var field_space = space_field_space.replace(leadingWhiteSpaceCharacters, ''); // trim leading whitespace
462                                                var field = field_space.replace(trailingWhiteSpaceCharacters, ''); // trim trailing whitespace
463                                                var firstChar = field.charAt(0);
464                                                var lastChar = field.charAt(field.length - 1);
465                                                var secondToLastChar = field.charAt(field.length - 2);
466                                                var thirdToLastChar = field.charAt(field.length - 3);
467                                                if(field.length === 2 && field == "\"\""){
468                                                        listOfFields[j] = ""; //Special case empty string field.
469                                                }else if((firstChar == '"') &&
470                                                                ((lastChar != '"') ||
471                                                                 ((lastChar == '"') && (secondToLastChar == '"') && (thirdToLastChar != '"')))){
472                                                        if(j+1 === listOfFields.length){
473                                                                // alert("The last field in record " + i + " is corrupted:\n" + field);
474                                                                return; //null
475                                                        }
476                                                        var nextField = listOfFields[j+1];
477                                                        listOfFields[j] = field_space + this.separator + nextField;
478                                                        listOfFields.splice(j+1, 1); // delete element [j+1] from the list
479                                                }else{
480                                                        if((firstChar == '"') && (lastChar == '"')){
481                                                                field = field.slice(1, (field.length - 1)); // trim the " characters off the ends
482                                                                field = field.replace(doubleQuotes, '"'); // replace "" with "
483                                                        }
484                                                        listOfFields[j] = field;
485                                                        j += 1;
486                                                }
487                                        }
488                                        arrayOfOutputRecords.push(listOfFields);
489                                }
490                        }
491                       
492                        // The first item of the array must be the header row with attribute names.
493                        this._attributes = arrayOfOutputRecords.shift();
494                        for(i = 0; i<this._attributes.length; i++){
495                                // Store the index of each attribute
496                                this._attributeIndexes[this._attributes[i]] = i;
497                        }
498                        this._dataArray = arrayOfOutputRecords; //Array
499                }
500        },
501
502        _splitLines: function(csvContent){
503                // summary:
504                //              Function to split the CSV file contents into separate lines.
505                //              Since line breaks can occur inside quotes, a Regexp didn't
506                //              work as well.  A quick passover parse should be just as efficient.
507                // tags:
508                //              private
509                var split = [];
510                var i;
511                var line = "";
512                var inQuotes = false;
513                for(i = 0; i < csvContent.length; i++){
514                        var c = csvContent.charAt(i);
515                        switch(c){
516                                case '\"':
517                                        inQuotes = !inQuotes;
518                                        line += c;
519                                        break;
520                                case '\r':
521                                        if(inQuotes){
522                                                line += c;
523                                        }else{
524                                                split.push(line);
525                                                line = "";
526                                                if(i < (csvContent.length - 1) && csvContent.charAt(i + 1) == '\n'){
527                                                        i++; //Skip it, it's CRLF
528                                                }
529                                        }
530                                        break;
531                                case '\n':
532                                        if(inQuotes){
533                                                line += c;
534                                        }else{
535                                                split.push(line);
536                                                line = "";
537                                        }
538                                        break;
539                                default:
540                                        line +=c;
541                        }
542                }
543                if(line !== ""){
544                        split.push(line);
545                }
546                return split;
547        },
548       
549        _processData: function(/* String */ data){
550                // summary:
551                //              Function for processing the string data from the server.
552                // data: String
553                //              The CSV data.
554                // tags:
555                //              private
556                this._getArrayOfArraysFromCsvFileContents(data);
557                this._arrayOfAllItems = [];
558
559                //Check that the specified Identifier is actually a column title, if provided.
560                if(this.identifier){
561                        if(this._attributeIndexes[this.identifier] === undefined){
562                                throw new Error(this.declaredClass + ": Identity specified is not a column header in the data set.");
563                        }
564                }
565
566                for(var i=0; i<this._dataArray.length; i++){
567                        var id = i;
568                        //Associate the identifier to a row in this case
569                        //for o(1) lookup.
570                        if(this.identifier){
571                                var iData = this._dataArray[i];
572                                id = iData[this._attributeIndexes[this.identifier]];
573                                this._idMap[id] = i;
574                        }
575                        this._arrayOfAllItems.push(this._createItemFromIdentity(id));
576                }
577                this._loadFinished = true;
578                this._loadInProgress = false;
579        },
580       
581        _createItemFromIdentity: function(/* String */ identity){
582                // summary:
583                //              Function for creating a new item from its identifier.
584                // identity: String
585                //              The identity
586                // tags:
587                //              private
588                var item = {};
589                item[this._storeProp] = this;
590                item[this._idProp] = identity;
591                return item; //Object
592        },
593       
594       
595/***************************************
596     dojo.data.api.Identity API
597***************************************/
598        getIdentity: function(/* item */ item){
599                // summary:
600                //              See dojo.data.api.Identity.getIdentity()
601                // tags:
602                //              public
603                if(this.isItem(item)){
604                        return item[this._idProp]; //String
605                }
606                return null; //null
607        },
608
609        fetchItemByIdentity: function(/* Object */ keywordArgs){
610                // summary:
611                //              See dojo.data.api.Identity.fetchItemByIdentity()
612                // tags:
613                //              public
614                var item;
615                var scope = keywordArgs.scope?keywordArgs.scope:winUtil.global;
616                //Hasn't loaded yet, we have to trigger the load.
617                if(!this._loadFinished){
618                        var self = this;
619                        if(this.url !== ""){
620                                //If fetches come in before the loading has finished, but while
621                                //a load is in progress, we have to defer the fetching to be
622                                //invoked in the callback.
623                                if(this._loadInProgress){
624                                        this._queuedFetches.push({args: keywordArgs});
625                                }else{
626                                        this._loadInProgress = true;
627                                        var getArgs = {
628                                                        url: self.url,
629                                                        handleAs: "text"
630                                                };
631                                        var getHandler = xhr.get(getArgs);
632                                        getHandler.addCallback(function(data){
633                                                try{
634                                                        self._processData(data);
635                                                        var item = self._createItemFromIdentity(keywordArgs.identity);
636                                                        if(!self.isItem(item)){
637                                                                item = null;
638                                                        }
639                                                        if(keywordArgs.onItem){
640                                                                keywordArgs.onItem.call(scope, item);
641                                                        }
642                                                        self._handleQueuedFetches();
643                                                }catch(error){
644                                                        if(keywordArgs.onError){
645                                                                keywordArgs.onError.call(scope, error);
646                                                        }
647                                                }
648                                        });
649                                        getHandler.addErrback(function(error){
650                                                this._loadInProgress = false;
651                                                if(keywordArgs.onError){
652                                                        keywordArgs.onError.call(scope, error);
653                                                }
654                                        });
655                                }
656                        }else if(this._csvData){
657                                try{
658                                        self._processData(self._csvData);
659                                        self._csvData = null;
660                                        item = self._createItemFromIdentity(keywordArgs.identity);
661                                        if(!self.isItem(item)){
662                                                item = null;
663                                        }
664                                        if(keywordArgs.onItem){
665                                                keywordArgs.onItem.call(scope, item);
666                                        }
667                                }catch(e){
668                                        if(keywordArgs.onError){
669                                                keywordArgs.onError.call(scope, e);
670                                        }
671                                }
672                        }
673                }else{
674                        //Already loaded.  We can just look it up and call back.
675                        item = this._createItemFromIdentity(keywordArgs.identity);
676                        if(!this.isItem(item)){
677                                item = null;
678                        }
679                        if(keywordArgs.onItem){
680                                keywordArgs.onItem.call(scope, item);
681                        }
682                }
683        },
684
685        getIdentityAttributes: function(/* item */ item){
686                // summary:
687                //              See dojo.data.api.Identity.getIdentifierAttributes()
688                // tags:
689                //              public
690                 
691                //Identity isn't a public attribute in the item, it's the row position index.
692                //So, return null.
693                if(this.identifier){
694                        return [this.identifier];
695                }else{
696                        return null;
697                }
698        },
699
700        _handleQueuedFetches: function(){
701                // summary:
702                //              Internal function to execute delayed request in the store.
703                // tags:
704                //              private
705
706                //Execute any deferred fetches now.
707                if(this._queuedFetches.length > 0){
708                        for(var i = 0; i < this._queuedFetches.length; i++){
709                                var fData = this._queuedFetches[i];
710                                var delayedFilter = fData.filter;
711                                var delayedQuery = fData.args;
712                                if(delayedFilter){
713                                        delayedFilter(delayedQuery, this._arrayOfAllItems);
714                                }else{
715                                        this.fetchItemByIdentity(fData.args);
716                                }
717                        }
718                        this._queuedFetches = [];
719                }
720        }
721});
722//Mix in the simple fetch implementation to this class.
723lang.extend(CsvStore, simpleFetch);
724
725return CsvStore;
726});
Note: See TracBrowser for help on using the repository browser.