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