Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / data / Tree.js
1 /**
2  * @class Ext.data.Tree
3  * 
4  * This class is used as a container for a series of nodes. The nodes themselves maintain
5  * the relationship between parent/child. The tree itself acts as a manager. It gives functionality
6  * to retrieve a node by its identifier: {@link #getNodeById}. 
7  *
8  * The tree also relays events from any of it's child nodes, allowing them to be handled in a 
9  * centralized fashion. In general this class is not used directly, rather used internally 
10  * by other parts of the framework.
11  *
12  * @constructor
13  * @param {Node} root (optional) The root node
14  */
15 Ext.define('Ext.data.Tree', {
16     alias: 'data.tree',
17     
18     mixins: {
19         observable: "Ext.util.Observable"
20     },
21
22     /**
23      * The root node for this tree
24      * @type Node
25      */
26     root: null,
27         
28     constructor: function(root) {
29         var me = this;
30         
31         me.nodeHash = {};
32
33         me.mixins.observable.constructor.call(me);
34                         
35         if (root) {
36             me.setRootNode(root);
37         }
38     },
39
40     /**
41      * Returns the root node for this tree.
42      * @return {Ext.data.NodeInterface}
43      */
44     getRootNode : function() {
45         return this.root;
46     },
47
48     /**
49      * Sets the root node for this tree.
50      * @param {Ext.data.NodeInterface} node
51      * @return {Ext.data.NodeInterface} The root node
52      */
53     setRootNode : function(node) {
54         var me = this;
55         
56         me.root = node;
57         Ext.data.NodeInterface.decorate(node);
58         
59         if (me.fireEvent('beforeappend', null, node) !== false) {
60             node.set('root', true);
61             node.updateInfo();
62             
63             me.relayEvents(node, [
64                 /**
65                  * @event append
66                  * Fires when a new child node is appended to a node in this tree.
67                  * @param {Tree} tree The owner tree
68                  * @param {Node} parent The parent node
69                  * @param {Node} node The newly appended node
70                  * @param {Number} index The index of the newly appended node
71                  */
72                 "append",
73
74                 /**
75                  * @event remove
76                  * Fires when a child node is removed from a node in this tree.
77                  * @param {Tree} tree The owner tree
78                  * @param {Node} parent The parent node
79                  * @param {Node} node The child node removed
80                  */
81                 "remove",
82
83                 /**
84                  * @event move
85                  * Fires when a node is moved to a new location in the tree
86                  * @param {Tree} tree The owner tree
87                  * @param {Node} node The node moved
88                  * @param {Node} oldParent The old parent of this node
89                  * @param {Node} newParent The new parent of this node
90                  * @param {Number} index The index it was moved to
91                  */
92                 "move",
93
94                 /**
95                  * @event insert
96                  * Fires when a new child node is inserted in a node in this tree.
97                  * @param {Tree} tree The owner tree
98                  * @param {Node} parent The parent node
99                  * @param {Node} node The child node inserted
100                  * @param {Node} refNode The child node the node was inserted before
101                  */
102                 "insert",
103
104                 /**
105                  * @event beforeappend
106                  * Fires before a new child is appended to a node in this tree, return false to cancel the append.
107                  * @param {Tree} tree The owner tree
108                  * @param {Node} parent The parent node
109                  * @param {Node} node The child node to be appended
110                  */
111                 "beforeappend",
112
113                 /**
114                  * @event beforeremove
115                  * Fires before a child is removed from a node in this tree, return false to cancel the remove.
116                  * @param {Tree} tree The owner tree
117                  * @param {Node} parent The parent node
118                  * @param {Node} node The child node to be removed
119                  */
120                 "beforeremove",
121
122                 /**
123                  * @event beforemove
124                  * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
125                  * @param {Tree} tree The owner tree
126                  * @param {Node} node The node being moved
127                  * @param {Node} oldParent The parent of the node
128                  * @param {Node} newParent The new parent the node is moving to
129                  * @param {Number} index The index it is being moved to
130                  */
131                 "beforemove",
132
133                 /**
134                  * @event beforeinsert
135                  * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
136                  * @param {Tree} tree The owner tree
137                  * @param {Node} parent The parent node
138                  * @param {Node} node The child node to be inserted
139                  * @param {Node} refNode The child node the node is being inserted before
140                  */
141                 "beforeinsert",
142
143                  /**
144                   * @event expand
145                   * Fires when this node is expanded.
146                   * @param {Node} this The expanding node
147                   */
148                  "expand",
149
150                  /**
151                   * @event collapse
152                   * Fires when this node is collapsed.
153                   * @param {Node} this The collapsing node
154                   */
155                  "collapse",
156
157                  /**
158                   * @event beforeexpand
159                   * Fires before this node is expanded.
160                   * @param {Node} this The expanding node
161                   */
162                  "beforeexpand",
163
164                  /**
165                   * @event beforecollapse
166                   * Fires before this node is collapsed.
167                   * @param {Node} this The collapsing node
168                   */
169                  "beforecollapse" ,
170
171                  /**
172                   * @event rootchange
173                   * Fires whenever the root node is changed in the tree.
174                   * @param {Ext.data.Model} root The new root
175                   */
176                  "rootchange"
177             ]);
178             
179             node.on({
180                 scope: me,
181                 insert: me.onNodeInsert,
182                 append: me.onNodeAppend,
183                 remove: me.onNodeRemove
184             });
185
186             me.registerNode(node);        
187             me.fireEvent('append', null, node);
188             me.fireEvent('rootchange', node);
189         }
190             
191         return node;
192     },
193     
194     /**
195      * Flattens all the nodes in the tree into an array.
196      * @private
197      * @return {Array} The flattened nodes.
198      */
199     flatten: function(){
200         var nodes = [],
201             hash = this.nodeHash,
202             key;
203             
204         for (key in hash) {
205             if (hash.hasOwnProperty(key)) {
206                 nodes.push(hash[key]);
207             }
208         }
209         return nodes;
210     },
211     
212     /**
213      * Fired when a node is inserted into the root or one of it's children
214      * @private
215      * @param {Ext.data.NodeInterface} parent The parent node
216      * @param {Ext.data.NodeInterface} node The inserted node
217      */
218     onNodeInsert: function(parent, node) {
219         this.registerNode(node);
220     },
221     
222     /**
223      * Fired when a node is appended into the root or one of it's children
224      * @private
225      * @param {Ext.data.NodeInterface} parent The parent node
226      * @param {Ext.data.NodeInterface} node The appended node
227      */
228     onNodeAppend: function(parent, node) {
229         this.registerNode(node);
230     },
231     
232     /**
233      * Fired when a node is removed from the root or one of it's children
234      * @private
235      * @param {Ext.data.NodeInterface} parent The parent node
236      * @param {Ext.data.NodeInterface} node The removed node
237      */
238     onNodeRemove: function(parent, node) {
239         this.unregisterNode(node);
240     },
241
242     /**
243      * Gets a node in this tree by its id.
244      * @param {String} id
245      * @return {Ext.data.NodeInterface} The match node.
246      */
247     getNodeById : function(id) {
248         return this.nodeHash[id];
249     },
250
251     /**
252      * Registers a node with the tree
253      * @private
254      * @param {Ext.data.NodeInterface} The node to register
255      */
256     registerNode : function(node) {
257         this.nodeHash[node.getId() || node.internalId] = node;
258     },
259
260     /**
261      * Unregisters a node with the tree
262      * @private
263      * @param {Ext.data.NodeInterface} The node to unregister
264      */
265     unregisterNode : function(node) {
266         delete this.nodeHash[node.getId() || node.internalId];
267     },
268     
269     /**
270      * Sorts this tree
271      * @private
272      * @param {Function} sorterFn The function to use for sorting
273      * @param {Boolean} recursive True to perform recursive sorting
274      */
275     sort: function(sorterFn, recursive) {
276         this.getRootNode().sort(sorterFn, recursive);
277     },
278     
279      /**
280      * Filters this tree
281      * @private
282      * @param {Function} sorterFn The function to use for filtering
283      * @param {Boolean} recursive True to perform recursive filtering
284      */
285     filter: function(filters, recursive) {
286         this.getRootNode().filter(filters, recursive);
287     }
288 });