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