AppStore.js 23 KB

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