ForestStoreModel.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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["dijit.tree.ForestStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dijit.tree.ForestStoreModel"] = true;
  8. dojo.provide("dijit.tree.ForestStoreModel");
  9. dojo.require("dijit.tree.TreeStoreModel");
  10. dojo.declare("dijit.tree.ForestStoreModel", dijit.tree.TreeStoreModel, {
  11. // summary:
  12. // Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
  13. // a.k.a. a store that has multiple "top level" items.
  14. //
  15. // description
  16. // Use this class to wrap a dojo.data store, making all the items matching the specified query
  17. // appear as children of a fabricated "root item". If no query is specified then all the
  18. // items returned by fetch() on the underlying store become children of the root item.
  19. // This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
  20. //
  21. // When using this class the developer must override a number of methods according to their app and
  22. // data, including:
  23. // - onNewRootItem
  24. // - onAddToRoot
  25. // - onLeaveRoot
  26. // - onNewItem
  27. // - onSetItem
  28. // Parameters to constructor
  29. // rootId: String
  30. // ID of fabricated root item
  31. rootId: "$root$",
  32. // rootLabel: String
  33. // Label of fabricated root item
  34. rootLabel: "ROOT",
  35. // query: String
  36. // Specifies the set of children of the root item.
  37. // example:
  38. // | {type:'continent'}
  39. query: null,
  40. // End of parameters to constructor
  41. constructor: function(params){
  42. // summary:
  43. // Sets up variables, etc.
  44. // tags:
  45. // private
  46. // Make dummy root item
  47. this.root = {
  48. store: this,
  49. root: true,
  50. id: params.rootId,
  51. label: params.rootLabel,
  52. children: params.rootChildren // optional param
  53. };
  54. },
  55. // =======================================================================
  56. // Methods for traversing hierarchy
  57. mayHaveChildren: function(/*dojo.data.Item*/ item){
  58. // summary:
  59. // Tells if an item has or may have children. Implementing logic here
  60. // avoids showing +/- expando icon for nodes that we know don't have children.
  61. // (For efficiency reasons we may not want to check if an element actually
  62. // has children until user clicks the expando node)
  63. // tags:
  64. // extension
  65. return item === this.root || this.inherited(arguments);
  66. },
  67. getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
  68. // summary:
  69. // Calls onComplete() with array of child items of given parent item, all loaded.
  70. if(parentItem === this.root){
  71. if(this.root.children){
  72. // already loaded, just return
  73. callback(this.root.children);
  74. }else{
  75. this.store.fetch({
  76. query: this.query,
  77. onComplete: dojo.hitch(this, function(items){
  78. this.root.children = items;
  79. callback(items);
  80. }),
  81. onError: onError
  82. });
  83. }
  84. }else{
  85. this.inherited(arguments);
  86. }
  87. },
  88. // =======================================================================
  89. // Inspecting items
  90. isItem: function(/* anything */ something){
  91. return (something === this.root) ? true : this.inherited(arguments);
  92. },
  93. fetchItemByIdentity: function(/* object */ keywordArgs){
  94. if(keywordArgs.identity == this.root.id){
  95. var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
  96. if(keywordArgs.onItem){
  97. keywordArgs.onItem.call(scope, this.root);
  98. }
  99. }else{
  100. this.inherited(arguments);
  101. }
  102. },
  103. getIdentity: function(/* item */ item){
  104. return (item === this.root) ? this.root.id : this.inherited(arguments);
  105. },
  106. getLabel: function(/* item */ item){
  107. return (item === this.root) ? this.root.label : this.inherited(arguments);
  108. },
  109. // =======================================================================
  110. // Write interface
  111. newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
  112. // summary:
  113. // Creates a new item. See dojo.data.api.Write for details on args.
  114. // Used in drag & drop when item from external source dropped onto tree.
  115. if(parent === this.root){
  116. this.onNewRootItem(args);
  117. return this.store.newItem(args);
  118. }else{
  119. return this.inherited(arguments);
  120. }
  121. },
  122. onNewRootItem: function(args){
  123. // summary:
  124. // User can override this method to modify a new element that's being
  125. // added to the root of the tree, for example to add a flag like root=true
  126. },
  127. pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
  128. // summary:
  129. // Move or copy an item from one parent item to another.
  130. // Used in drag & drop
  131. if(oldParentItem === this.root){
  132. if(!bCopy){
  133. // It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
  134. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  135. // that this element is no longer a child of the root node
  136. this.onLeaveRoot(childItem);
  137. }
  138. }
  139. dijit.tree.TreeStoreModel.prototype.pasteItem.call(this, childItem,
  140. oldParentItem === this.root ? null : oldParentItem,
  141. newParentItem === this.root ? null : newParentItem,
  142. bCopy,
  143. insertIndex
  144. );
  145. if(newParentItem === this.root){
  146. // It's onAddToRoot()'s responsibility to modify the item so it matches
  147. // this.query... thus triggering an onChildrenChange() event to notify the Tree
  148. // that this element is now a child of the root node
  149. this.onAddToRoot(childItem);
  150. }
  151. },
  152. // =======================================================================
  153. // Handling for top level children
  154. onAddToRoot: function(/* item */ item){
  155. // summary:
  156. // Called when item added to root of tree; user must override this method
  157. // to modify the item so that it matches the query for top level items
  158. // example:
  159. // | store.setValue(item, "root", true);
  160. // tags:
  161. // extension
  162. console.log(this, ": item ", item, " added to root");
  163. },
  164. onLeaveRoot: function(/* item */ item){
  165. // summary:
  166. // Called when item removed from root of tree; user must override this method
  167. // to modify the item so it doesn't match the query for top level items
  168. // example:
  169. // | store.unsetAttribute(item, "root");
  170. // tags:
  171. // extension
  172. console.log(this, ": item ", item, " removed from root");
  173. },
  174. // =======================================================================
  175. // Events from data store
  176. _requeryTop: function(){
  177. // reruns the query for the children of the root node,
  178. // sending out an onSet notification if those children have changed
  179. var oldChildren = this.root.children || [];
  180. this.store.fetch({
  181. query: this.query,
  182. onComplete: dojo.hitch(this, function(newChildren){
  183. this.root.children = newChildren;
  184. // If the list of children or the order of children has changed...
  185. if(oldChildren.length != newChildren.length ||
  186. dojo.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
  187. this.onChildrenChange(this.root, newChildren);
  188. }
  189. })
  190. });
  191. },
  192. onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){
  193. // summary:
  194. // Handler for when new items appear in the store. Developers should override this
  195. // method to be more efficient based on their app/data.
  196. // description:
  197. // Note that the default implementation requeries the top level items every time
  198. // a new item is created, since any new item could be a top level item (even in
  199. // addition to being a child of another item, since items can have multiple parents).
  200. //
  201. // If developers can detect which items are possible top level items (based on the item and the
  202. // parentInfo parameters), they should override this method to only call _requeryTop() for top
  203. // level items. Often all top level items have parentInfo==null, but
  204. // that will depend on which store you use and what your data is like.
  205. // tags:
  206. // extension
  207. this._requeryTop();
  208. this.inherited(arguments);
  209. },
  210. onDeleteItem: function(/*Object*/ item){
  211. // summary:
  212. // Handler for delete notifications from underlying store
  213. // check if this was a child of root, and if so send notification that root's children
  214. // have changed
  215. if(dojo.indexOf(this.root.children, item) != -1){
  216. this._requeryTop();
  217. }
  218. this.inherited(arguments);
  219. },
  220. onSetItem: function(/* item */ item,
  221. /* attribute-name-string */ attribute,
  222. /* object | array */ oldValue,
  223. /* object | array */ newValue){
  224. // summary:
  225. // Updates the tree view according to changes to an item in the data store.
  226. // Developers should override this method to be more efficient based on their app/data.
  227. // description:
  228. // Handles updates to an item's children by calling onChildrenChange(), and
  229. // other updates to an item by calling onChange().
  230. //
  231. // Also, any change to any item re-executes the query for the tree's top-level items,
  232. // since this modified item may have started/stopped matching the query for top level items.
  233. //
  234. // If possible, developers should override this function to only call _requeryTop() when
  235. // the change to the item has caused it to stop/start being a top level item in the tree.
  236. // tags:
  237. // extension
  238. this._requeryTop();
  239. this.inherited(arguments);
  240. }
  241. });
  242. }