CsvStore.js 23 KB

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