CsvStore.js 23 KB

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