AppStore.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828
  1. define("dojox/data/AppStore", ["dojo", "dojox", "dojo/data/util/simpleFetch", "dojo/data/util/filter", "dojox/atom/io/Connection"], function(dojo, dojox) {
  2. dojo.experimental("dojox.data.AppStore");
  3. dojo.declare("dojox.data.AppStore",
  4. null,{
  5. // url: [public] string
  6. // So the parser can instantiate the store via markup.
  7. url: "",
  8. // urlPreventCache: [public] boolean
  9. // Whether or not to pass the preventCache parameter to the connection
  10. urlPreventCache: false,
  11. // xmethod: [public] boolean
  12. // Whether to use X-Method-Override for PUT/DELETE.
  13. xmethod: false,
  14. _atomIO: null,
  15. _feed: null,
  16. _requests: null,
  17. _processing: null,
  18. _updates: null,
  19. _adds: null,
  20. _deletes: null,
  21. constructor: function(/*Object*/args){
  22. // summary:
  23. // The APP data store.
  24. // description:
  25. // The APP Store is instantiated either in markup or programmatically by supplying a
  26. // url of the Collection to be used.
  27. //
  28. // args:
  29. // An anonymous object to initialize properties. It expects the following values:
  30. // url: The url of the Collection to load.
  31. // urlPreventCache: Whether or not to append on cache prevention params (as defined by dojo.xhr*)
  32. if(args && args.url){
  33. this.url = args.url;
  34. }
  35. if(args && args.urlPreventCache){
  36. this.urlPreventCache = args.urlPreventCache;
  37. }
  38. if(!this.url){
  39. throw new Error("A URL is required to instantiate an APP Store object");
  40. }
  41. },
  42. _setFeed: function(feed, data){
  43. // summary:
  44. // Sets the internal feed using a dojox.atom.io.model.Feed object.
  45. // description:
  46. // Sets the internal feed using a dojox.atom.io.model.Feed object. Also adds
  47. // a property to the entries to track that they belong to this store. It
  48. // also parses stored requests (since we were waiting on a callback) and
  49. // executes those as well.
  50. //
  51. // feed: dojox.atom.io.model.Feed object
  52. // The Feed to use for this data store.
  53. // data: unused
  54. // Signature for this function is defined by AtomIO.getFeed, since this is a callback.
  55. this._feed = feed;
  56. var i;
  57. for(i=0; i<this._feed.entries.length; i++){
  58. this._feed.entries[i].store = this;
  59. }
  60. if(this._requests){
  61. for(i=0; i<this._requests.length; i++){
  62. var request = this._requests[i];
  63. if(request.request && request.fh && request.eh){
  64. this._finishFetchItems(request.request, request.fh, request.eh);
  65. }else if(request.clear){
  66. this._feed = null;
  67. }else if(request.add){
  68. this._feed.addEntry(request.add);
  69. }else if(request.remove){
  70. this._feed.removeEntry(request.remove);
  71. }
  72. }
  73. }
  74. this._requests = null;
  75. },
  76. _getAllItems: function(){
  77. // summary:
  78. // Function to return all entries in the Feed as an array of items.
  79. // description:
  80. // Function to return all entries in the Feed as an array of items.
  81. //
  82. // returns:
  83. // Array of all entries in the feed.
  84. var items = [];
  85. for(var i=0; i<this._feed.entries.length; i++){
  86. items.push(this._feed.entries[i]);
  87. }
  88. return items; //array
  89. },
  90. _assertIsItem: function(/* item */ item){
  91. // summary:
  92. // This function tests whether the item is an item.
  93. // description:
  94. // This function tests whether the item passed in is indeed an item
  95. // in the store.
  96. //
  97. // item:
  98. // The item to test for being contained by the store.
  99. if(!this.isItem(item)){
  100. throw new Error("This error message is provided when a function is called in the following form: "
  101. + "getAttribute(argument, attributeName). The argument variable represents the member "
  102. + "or owner of the object. The error is created when an item that does not belong "
  103. + "to this store is specified as an argument.");
  104. }
  105. },
  106. _assertIsAttribute: function(/* String */ attribute){
  107. // summary:
  108. // This function tests whether the item is an attribute.
  109. // description:
  110. // This function tests whether the item passed in is indeed a valid
  111. // 'attribute' like type for the store.
  112. // attribute:
  113. // The attribute to test for being contained by the store.
  114. //
  115. // returns:
  116. // Returns a boolean indicating whether this is a valid attribute.
  117. if(typeof attribute !== "string"){
  118. throw new Error("The attribute argument must be a string. The error is created "
  119. + "when a different type of variable is specified such as an array or object.");
  120. }
  121. for(var key in dojox.atom.io.model._actions){
  122. if(key == attribute){
  123. return true;
  124. }
  125. }
  126. return false;
  127. },
  128. _addUpdate: function(/* Object */ update){
  129. // summary:
  130. // Internal function to add an updated entry to our updates array
  131. // description:
  132. // Internal function to add an updated entry to our updates array
  133. //
  134. // update: dojox.atom.io.model.Entry object
  135. // The updated Entry we've changed.
  136. if(!this._updates){
  137. this._updates = [update];
  138. }else{
  139. this._updates.push(update);
  140. }
  141. },
  142. /***************************************
  143. dojo.data.api.Read API
  144. ***************************************/
  145. getValue: function( /* item */ item,
  146. /* attribute-name-string */ attribute,
  147. /* value? */ defaultValue){
  148. // summary:
  149. // See dojo.data.api.Read.getValue()
  150. var values = this.getValues(item, attribute);
  151. return (values.length > 0)?values[0]:defaultValue; //Object || int || Boolean
  152. },
  153. getValues: function(/* item */ item,
  154. /* attribute-name-string */ attribute){
  155. // summary:
  156. // See dojo.data.api.Read.getValues()
  157. this._assertIsItem(item);
  158. var flag = this._assertIsAttribute(attribute);
  159. if(flag){
  160. if((attribute === "author" || attribute === "contributor" || attribute === "link") && item[attribute+"s"]){
  161. return item[attribute+"s"];
  162. }
  163. if(attribute === "category" && item.categories){
  164. return item.categories;
  165. }
  166. if(item[attribute]){
  167. item = item[attribute];
  168. if(item.nodeType == "Content"){
  169. return [item.value];
  170. }
  171. return [item] ;
  172. }
  173. }
  174. return []; //Array
  175. },
  176. getAttributes: function(/* item */ item){
  177. // summary:
  178. // See dojo.data.api.Read.getAttributes()
  179. this._assertIsItem(item);
  180. var attributes = [];
  181. for(var key in dojox.atom.io.model._actions){
  182. if(this.hasAttribute(item, key)){
  183. attributes.push(key);
  184. }
  185. }
  186. return attributes; //Array
  187. },
  188. hasAttribute: function( /* item */ item,
  189. /* attribute-name-string */ attribute){
  190. // summary:
  191. // See dojo.data.api.Read.hasAttribute()
  192. return this.getValues(item, attribute).length > 0;
  193. },
  194. containsValue: function(/* item */ item,
  195. /* attribute-name-string */ attribute,
  196. /* anything */ value){
  197. // summary:
  198. // See dojo.data.api.Read.containsValue()
  199. var regexp = undefined;
  200. if(typeof value === "string"){
  201. regexp = dojo.data.util.filter.patternToRegExp(value, false);
  202. }
  203. return this._containsValue(item, attribute, value, regexp); //boolean.
  204. },
  205. _containsValue: function( /* item */ item,
  206. /* attribute-name-string */ attribute,
  207. /* anything */ value,
  208. /* RegExp?*/ regexp,
  209. /* Boolean?*/ trim){
  210. // summary:
  211. // Internal function for looking at the values contained by the item.
  212. // description:
  213. // Internal function for looking at the values contained by the item. This
  214. // function allows for denoting if the comparison should be case sensitive for
  215. // strings or not (for handling filtering cases where string case should not matter)
  216. //
  217. // item:
  218. // The data item to examine for attribute values.
  219. // attribute:
  220. // The attribute to inspect.
  221. // value:
  222. // The value to match.
  223. // regexp:
  224. // Optional regular expression generated off value if value was of string type to handle wildcarding.
  225. // If present and attribute values are string, then it can be used for comparison instead of 'value'
  226. var values = this.getValues(item, attribute);
  227. for(var i = 0; i < values.length; ++i){
  228. var possibleValue = values[i];
  229. if(typeof possibleValue === "string" && regexp){
  230. if(trim){
  231. possibleValue = possibleValue.replace(new RegExp(/^\s+/),""); // START
  232. possibleValue = possibleValue.replace(new RegExp(/\s+$/),""); // END
  233. }
  234. possibleValue = possibleValue.replace(/\r|\n|\r\n/g, "");
  235. return (possibleValue.match(regexp) !== null);
  236. }else{
  237. //Non-string matching.
  238. if(value === possibleValue){
  239. return true; // Boolean
  240. }
  241. }
  242. }
  243. return false; // Boolean
  244. },
  245. isItem: function(/* anything */ something){
  246. // summary:
  247. // See dojo.data.api.Read.isItem()
  248. return something && something.store && something.store === this; //boolean
  249. },
  250. isItemLoaded: function(/* anything */ something){
  251. // summary:
  252. // See dojo.data.api.Read.isItemLoaded()
  253. return this.isItem(something);
  254. },
  255. loadItem: function(/* Object */ keywordArgs){
  256. // summary:
  257. // See dojo.data.api.Read.loadItem()
  258. this._assertIsItem(keywordArgs.item);
  259. },
  260. _fetchItems: function(request, fetchHandler, errorHandler){
  261. // summary:
  262. // Fetch items (Atom entries) that match to a query
  263. // description:
  264. // Fetch items (Atom entries) that match to a query
  265. // request:
  266. // A request object
  267. // fetchHandler:
  268. // A function to call for fetched items
  269. // errorHandler:
  270. // A function to call on error
  271. if(this._feed){
  272. this._finishFetchItems(request, fetchHandler, errorHandler);
  273. }else{
  274. var flag = false;
  275. if(!this._requests){
  276. this._requests = [];
  277. flag = true;
  278. }
  279. this._requests.push({request: request, fh: fetchHandler, eh: errorHandler});
  280. if(flag){
  281. this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
  282. this._atomIO.getFeed(this.url,this._setFeed, null, this);
  283. }
  284. }
  285. },
  286. _finishFetchItems: function(request, fetchHandler, errorHandler){
  287. // summary:
  288. // Internal function for finishing a fetch request.
  289. // description:
  290. // Internal function for finishing a fetch request. Needed since the feed
  291. // might not have been loaded, so we finish the fetch in a callback.
  292. //
  293. // request:
  294. // A request object
  295. // fetchHandler:
  296. // A function to call for fetched items
  297. // errorHandler:
  298. // A function to call on error
  299. var items = null;
  300. var arrayOfAllItems = this._getAllItems();
  301. if(request.query){
  302. var ignoreCase = request.queryOptions ? request.queryOptions.ignoreCase : false;
  303. items = [];
  304. //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
  305. //same value for each item examined. Much more efficient.
  306. var regexpList = {};
  307. var key;
  308. var value;
  309. for(key in request.query){
  310. value = request.query[key]+'';
  311. if(typeof value === "string"){
  312. regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
  313. }
  314. }
  315. for(var i = 0; i < arrayOfAllItems.length; ++i){
  316. var match = true;
  317. var candidateItem = arrayOfAllItems[i];
  318. for(key in request.query){
  319. value = request.query[key]+'';
  320. if(!this._containsValue(candidateItem, key, value, regexpList[key], request.trim)){
  321. match = false;
  322. }
  323. }
  324. if(match){
  325. items.push(candidateItem);
  326. }
  327. }
  328. }else{
  329. // We want a copy to pass back in case the parent wishes to sort the array. We shouldn't allow resort
  330. // of the internal list so that multiple callers can get listsand sort without affecting each other.
  331. if(arrayOfAllItems.length> 0){
  332. items = arrayOfAllItems.slice(0,arrayOfAllItems.length);
  333. }
  334. }
  335. try{
  336. fetchHandler(items, request);
  337. }catch(e){
  338. errorHandler(e, request);
  339. }
  340. },
  341. getFeatures: function(){
  342. // summary:
  343. // See dojo.data.api.Read.getFeatures()
  344. return {
  345. 'dojo.data.api.Read': true,
  346. 'dojo.data.api.Write': true,
  347. 'dojo.data.api.Identity': true
  348. };
  349. },
  350. close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
  351. // summary:
  352. // See dojo.data.api.Read.close()
  353. // nothing to do here!
  354. this._feed = null;
  355. },
  356. getLabel: function(/* item */ item){
  357. // summary:
  358. // See dojo.data.api.Read.getLabel()
  359. if(this.isItem(item)){
  360. return this.getValue(item, "title", "No Title");
  361. }
  362. return undefined;
  363. },
  364. getLabelAttributes: function(/* item */ item){
  365. // summary:
  366. // See dojo.data.api.Read.getLabelAttributes()
  367. return ["title"];
  368. },
  369. /***************************************
  370. dojo.data.api.Identity API
  371. ***************************************/
  372. getIdentity: function(/* item */ item){
  373. // summary:
  374. // See dojo.data.api.Identity.getIdentity()
  375. this._assertIsItem(item);
  376. return this.getValue(item, "id");
  377. },
  378. getIdentityAttributes: function(/* item */ item){
  379. // summary:
  380. // See dojo.data.api.Identity.getIdentityAttributes()
  381. return ["id"];
  382. },
  383. fetchItemByIdentity: function(keywordArgs){
  384. // summary:
  385. // See dojo.data.api.Identity.fetchItemByIdentity()
  386. this._fetchItems({query:{id:keywordArgs.identity}, onItem: keywordArgs.onItem, scope: keywordArgs.scope},
  387. function(items, request){
  388. var scope = request.scope;
  389. if(!scope){
  390. scope = dojo.global;
  391. }
  392. if(items.length < 1){
  393. request.onItem.call(scope, null);
  394. }else{
  395. request.onItem.call(scope, items[0]);
  396. }
  397. }, keywordArgs.onError);
  398. },
  399. /***************************************
  400. dojo.data.api.Identity API
  401. ***************************************/
  402. newItem: function(/* Object? */ keywordArgs){
  403. // summary:
  404. // See dojo.data.api.Write.newItem()
  405. var entry = new dojox.atom.io.model.Entry();
  406. var value = null;
  407. var temp = null;
  408. var i;
  409. for(var key in keywordArgs){
  410. if(this._assertIsAttribute(key)){
  411. value = keywordArgs[key];
  412. switch(key){
  413. case "link":
  414. for(i in value){
  415. temp = value[i];
  416. entry.addLink(temp.href,temp.rel,temp.hrefLang,temp.title,temp.type);
  417. }
  418. break;
  419. case "author":
  420. for(i in value){
  421. temp = value[i];
  422. entry.addAuthor(temp.name, temp.email, temp.uri);
  423. }
  424. break;
  425. case "contributor":
  426. for(i in value){
  427. temp = value[i];
  428. entry.addContributor(temp.name, temp.email, temp.uri);
  429. }
  430. break;
  431. case "category":
  432. for(i in value){
  433. temp = value[i];
  434. entry.addCategory(temp.scheme, temp.term, temp.label);
  435. }
  436. break;
  437. case "icon":
  438. case "id":
  439. case "logo":
  440. case "xmlBase":
  441. case "rights":
  442. entry[key] = value;
  443. break;
  444. case "updated":
  445. case "published":
  446. case "issued":
  447. case "modified":
  448. entry[key] = dojox.atom.io.model.util.createDate(value);
  449. break;
  450. case "content":
  451. case "summary":
  452. case "title":
  453. case "subtitle":
  454. entry[key] = new dojox.atom.io.model.Content(key);
  455. entry[key].value = value;
  456. break;
  457. default:
  458. entry[key] = value;
  459. break;
  460. }
  461. }
  462. }
  463. entry.store = this;
  464. entry.isDirty = true;
  465. if(!this._adds){
  466. this._adds = [entry];
  467. }else{
  468. this._adds.push(entry);
  469. }
  470. if(this._feed){
  471. this._feed.addEntry(entry);
  472. }else{
  473. if(this._requests){
  474. this._requests.push({add:entry});
  475. }else{
  476. this._requests = [{add:entry}];
  477. this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
  478. this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
  479. }
  480. }
  481. return true;
  482. },
  483. deleteItem: function(/* item */ item){
  484. // summary:
  485. // See dojo.data.api.Write.deleteItem()
  486. this._assertIsItem(item);
  487. if(!this._deletes){
  488. this._deletes = [item];
  489. }else{
  490. this._deletes.push(item);
  491. }
  492. if(this._feed){
  493. this._feed.removeEntry(item);
  494. }else{
  495. if(this._requests){
  496. this._requests.push({remove:item});
  497. }else{
  498. this._requests = [{remove:item}];
  499. this._atomIO = new dojox.atom.io.Connection(false, this.urlPreventCache);
  500. this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
  501. }
  502. }
  503. item = null;
  504. return true;
  505. },
  506. setValue: function( /* item */ item,
  507. /* string */ attribute,
  508. /* almost anything */ value){
  509. // summary:
  510. // See dojo.data.api.Write.setValue()
  511. this._assertIsItem(item);
  512. var update = {item: item};
  513. if(this._assertIsAttribute(attribute)){
  514. switch(attribute){
  515. case "link":
  516. update.links = item.links;
  517. this._addUpdate(update);
  518. item.links = null;
  519. item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
  520. item.isDirty = true;
  521. return true;
  522. case "author":
  523. update.authors = item.authors;
  524. this._addUpdate(update);
  525. item.authors = null;
  526. item.addAuthor(value.name, value.email, value.uri);
  527. item.isDirty = true;
  528. return true;
  529. case "contributor":
  530. update.contributors = item.contributors;
  531. this._addUpdate(update);
  532. item.contributors = null;
  533. item.addContributor(value.name, value.email, value.uri);
  534. item.isDirty = true;
  535. return true;
  536. case "category":
  537. update.categories = item.categories;
  538. this._addUpdate(update);
  539. item.categories = null;
  540. item.addCategory(value.scheme, value.term, value.label);
  541. item.isDirty = true;
  542. return true;
  543. case "icon":
  544. case "id":
  545. case "logo":
  546. case "xmlBase":
  547. case "rights":
  548. update[attribute] = item[attribute];
  549. this._addUpdate(update);
  550. item[attribute] = value;
  551. item.isDirty = true;
  552. return true;
  553. case "updated":
  554. case "published":
  555. case "issued":
  556. case "modified":
  557. update[attribute] = item[attribute];
  558. this._addUpdate(update);
  559. item[attribute] = dojox.atom.io.model.util.createDate(value);
  560. item.isDirty = true;
  561. return true;
  562. case "content":
  563. case "summary":
  564. case "title":
  565. case "subtitle":
  566. update[attribute] = item[attribute];
  567. this._addUpdate(update);
  568. item[attribute] = new dojox.atom.io.model.Content(attribute);
  569. item[attribute].value = value;
  570. item.isDirty = true;
  571. return true;
  572. default:
  573. update[attribute] = item[attribute];
  574. this._addUpdate(update);
  575. item[attribute] = value;
  576. item.isDirty = true;
  577. return true;
  578. }
  579. }
  580. return false;
  581. },
  582. setValues: function(/* item */ item,
  583. /* string */ attribute,
  584. /* array */ values){
  585. // summary:
  586. // See dojo.data.api.Write.setValues()
  587. if(values.length === 0){
  588. return this.unsetAttribute(item, attribute);
  589. }
  590. this._assertIsItem(item);
  591. var update = {item: item};
  592. var value;
  593. var i;
  594. if(this._assertIsAttribute(attribute)){
  595. switch(attribute){
  596. case "link":
  597. update.links = item.links;
  598. item.links = null;
  599. for(i in values){
  600. value = values[i];
  601. item.addLink(value.href,value.rel,value.hrefLang,value.title,value.type);
  602. }
  603. item.isDirty = true;
  604. return true;
  605. case "author":
  606. update.authors = item.authors;
  607. item.authors = null;
  608. for(i in values){
  609. value = values[i];
  610. item.addAuthor(value.name, value.email, value.uri);
  611. }
  612. item.isDirty = true;
  613. return true;
  614. case "contributor":
  615. update.contributors = item.contributors;
  616. item.contributors = null;
  617. for(i in values){
  618. value = values[i];
  619. item.addContributor(value.name, value.email, value.uri);
  620. }
  621. item.isDirty = true;
  622. return true;
  623. case "categories":
  624. update.categories = item.categories;
  625. item.categories = null;
  626. for(i in values){
  627. value = values[i];
  628. item.addCategory(value.scheme, value.term, value.label);
  629. }
  630. item.isDirty = true;
  631. return true;
  632. case "icon":
  633. case "id":
  634. case "logo":
  635. case "xmlBase":
  636. case "rights":
  637. update[attribute] = item[attribute];
  638. item[attribute] = values[0];
  639. item.isDirty = true;
  640. return true;
  641. case "updated":
  642. case "published":
  643. case "issued":
  644. case "modified":
  645. update[attribute] = item[attribute];
  646. item[attribute] = dojox.atom.io.model.util.createDate(values[0]);
  647. item.isDirty = true;
  648. return true;
  649. case "content":
  650. case "summary":
  651. case "title":
  652. case "subtitle":
  653. update[attribute] = item[attribute];
  654. item[attribute] = new dojox.atom.io.model.Content(attribute);
  655. item[attribute].values[0] = values[0];
  656. item.isDirty = true;
  657. return true;
  658. default:
  659. update[attribute] = item[attribute];
  660. item[attribute] = values[0];
  661. item.isDirty = true;
  662. return true;
  663. }
  664. }
  665. this._addUpdate(update);
  666. return false;
  667. },
  668. unsetAttribute: function( /* item */ item,
  669. /* string */ attribute){
  670. // summary:
  671. // See dojo.data.api.Write.unsetAttribute()
  672. this._assertIsItem(item);
  673. if(this._assertIsAttribute(attribute)){
  674. if(item[attribute] !== null){
  675. var update = {item: item};
  676. switch(attribute){
  677. case "author":
  678. case "contributor":
  679. case "link":
  680. update[attribute+"s"] = item[attribute+"s"];
  681. break;
  682. case "category":
  683. update.categories = item.categories;
  684. break;
  685. default:
  686. update[attribute] = item[attribute];
  687. break;
  688. }
  689. item.isDirty = true;
  690. item[attribute] = null;
  691. this._addUpdate(update);
  692. return true;
  693. }
  694. }
  695. return false; // boolean
  696. },
  697. save: function(/* object */ keywordArgs){
  698. // summary:
  699. // See dojo.data.api.Write.save()
  700. // keywordArgs:
  701. // {
  702. // onComplete: function
  703. // onError: function
  704. // scope: object
  705. // }
  706. var i;
  707. for(i in this._adds){
  708. this._atomIO.addEntry(this._adds[i], null, function(){}, keywordArgs.onError, false, keywordArgs.scope);
  709. }
  710. this._adds = null;
  711. for(i in this._updates){
  712. this._atomIO.updateEntry(this._updates[i].item, function(){}, keywordArgs.onError, false, this.xmethod, keywordArgs.scope);
  713. }
  714. this._updates = null;
  715. for(i in this._deletes){
  716. this._atomIO.removeEntry(this._deletes[i], function(){}, keywordArgs.onError, this.xmethod, keywordArgs.scope);
  717. }
  718. this._deletes = null;
  719. this._atomIO.getFeed(this.url,dojo.hitch(this,this._setFeed));
  720. if(keywordArgs.onComplete){
  721. var scope = keywordArgs.scope || dojo.global;
  722. keywordArgs.onComplete.call(scope);
  723. }
  724. },
  725. revert: function(){
  726. // summary:
  727. // See dojo.data.api.Write.revert()
  728. var i;
  729. for(i in this._adds){
  730. this._feed.removeEntry(this._adds[i]);
  731. }
  732. this._adds = null;
  733. var update, item, key;
  734. for(i in this._updates){
  735. update = this._updates[i];
  736. item = update.item;
  737. for(key in update){
  738. if(key !== "item"){
  739. item[key] = update[key];
  740. }
  741. }
  742. }
  743. this._updates = null;
  744. for(i in this._deletes){
  745. this._feed.addEntry(this._deletes[i]);
  746. }
  747. this._deletes = null;
  748. return true;
  749. },
  750. isDirty: function(/* item? */ item){
  751. // summary:
  752. // See dojo.data.api.Write.isDirty()
  753. if(item){
  754. this._assertIsItem(item);
  755. return item.isDirty?true:false; //boolean
  756. }
  757. return (this._adds !== null || this._updates !== null); //boolean
  758. }
  759. });
  760. dojo.extend(dojox.data.AppStore,dojo.data.util.simpleFetch);
  761. return dojox.data.AppStore;
  762. });