source: Dev/trunk/src/client/dojox/data/CsvStore.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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