Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / data / NodeStore.js
1 /**
2  * @class Ext.data.NodeStore
3  * @extends Ext.data.AbstractStore
4  * Node Store
5  * @ignore
6  */
7 Ext.define('Ext.data.NodeStore', {
8     extend: 'Ext.data.Store',
9     alias: 'store.node',
10     requires: ['Ext.data.NodeInterface'],
11     
12     /**
13      * @cfg {Ext.data.Record} node The Record you want to bind this Store to. Note that
14      * this record will be decorated with the Ext.data.NodeInterface if this is not the
15      * case yet.
16      */
17     node: null,
18     
19     /**
20      * @cfg {Boolean} recursive Set this to true if you want this NodeStore to represent
21      * all the descendents of the node in its flat data collection. This is useful for
22      * rendering a tree structure to a DataView and is being used internally by
23      * the TreeView. Any records that are moved, removed, inserted or appended to the
24      * node at any depth below the node this store is bound to will be automatically
25      * updated in this Store's internal flat data structure.
26      */
27     recursive: false,
28     
29     /** 
30      * @cfg {Boolean} rootVisible <tt>false</tt> to not include the root node in this Stores collection (defaults to <tt>true</tt>)
31      */    
32     rootVisible: false,
33     
34     constructor: function(config) {
35         var me = this,
36             node;
37             
38         config = config || {};
39         Ext.apply(me, config);
40         
41         //<debug>
42         if (Ext.isDefined(me.proxy)) {
43             Ext.Error.raise("A NodeStore cannot be bound to a proxy. Instead bind it to a record " +
44                             "decorated with the NodeInterface by setting the node config.");
45         }
46         //</debug>
47
48         config.proxy = {type: 'proxy'};
49         me.callParent([config]);
50
51         me.addEvents('expand', 'collapse', 'beforeexpand', 'beforecollapse');
52         
53         node = me.node;
54         if (node) {
55             me.node = null;
56             me.setNode(node);
57         }
58     },
59     
60     setNode: function(node) {
61         var me = this;
62         
63         if (me.node && me.node != node) {
64             // We want to unbind our listeners on the old node
65             me.mun(me.node, {
66                 expand: me.onNodeExpand,
67                 collapse: me.onNodeCollapse,
68                 append: me.onNodeAppend,
69                 insert: me.onNodeInsert,
70                 remove: me.onNodeRemove,
71                 sort: me.onNodeSort,
72                 scope: me
73             });
74             me.node = null;
75         }
76         
77         if (node) {
78             Ext.data.NodeInterface.decorate(node);
79             me.removeAll();
80             if (me.rootVisible) {
81                 me.add(node);
82             }
83             me.mon(node, {
84                 expand: me.onNodeExpand,
85                 collapse: me.onNodeCollapse,
86                 append: me.onNodeAppend,
87                 insert: me.onNodeInsert,
88                 remove: me.onNodeRemove,
89                 sort: me.onNodeSort,
90                 scope: me
91             });
92             me.node = node;
93             if (node.isExpanded() && node.isLoaded()) {
94                 me.onNodeExpand(node, node.childNodes, true);
95             }
96         }
97     },
98     
99     onNodeSort: function(node, childNodes) {
100         var me = this;
101         
102         if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) {
103             me.onNodeCollapse(node, childNodes, true);
104             me.onNodeExpand(node, childNodes, true);
105         }
106     },
107     
108     onNodeExpand: function(parent, records, suppressEvent) {
109         var me = this,
110             insertIndex = me.indexOf(parent) + 1,
111             ln = records ? records.length : 0,
112             i, record;
113             
114         if (!me.recursive && parent !== me.node) {
115             return;
116         }
117         
118         if (!me.isVisible(parent)) {
119             return;
120         }
121
122         if (!suppressEvent && me.fireEvent('beforeexpand', parent, records, insertIndex) === false) {
123             return;
124         }
125         
126         if (ln) {
127             me.insert(insertIndex, records);
128             for (i = 0; i < ln; i++) {
129                 record = records[i];
130                 if (record.isExpanded()) {
131                     if (record.isLoaded()) {
132                         // Take a shortcut                        
133                         me.onNodeExpand(record, record.childNodes, true);
134                     }
135                     else {
136                         record.set('expanded', false);
137                         record.expand();
138                     }
139                 }
140             }
141         }
142
143         if (!suppressEvent) {
144             me.fireEvent('expand', parent, records);
145         }
146     },
147
148     onNodeCollapse: function(parent, records, suppressEvent) {
149         var me = this,
150             ln = records.length,
151             collapseIndex = me.indexOf(parent) + 1,
152             i, record;
153             
154         if (!me.recursive && parent !== me.node) {
155             return;
156         }
157         
158         if (!suppressEvent && me.fireEvent('beforecollapse', parent, records, collapseIndex) === false) {
159             return;
160         }
161
162         for (i = 0; i < ln; i++) {
163             record = records[i];
164             me.remove(record);
165             if (record.isExpanded()) {
166                 me.onNodeCollapse(record, record.childNodes, true);
167             }
168         }
169         
170         if (!suppressEvent) {
171             me.fireEvent('collapse', parent, records, collapseIndex);
172         }
173     },
174     
175     onNodeAppend: function(parent, node, index) {
176         var me = this,
177             refNode, sibling;
178
179         if (me.isVisible(node)) {
180             if (index === 0) {
181                 refNode = parent;
182             } else {
183                 sibling = node.previousSibling;
184                 while (sibling.isExpanded() && sibling.lastChild) {
185                     sibling = sibling.lastChild;
186                 }
187                 refNode = sibling;
188             }
189             me.insert(me.indexOf(refNode) + 1, node);
190             if (!node.isLeaf() && node.isExpanded()) {
191                 if (node.isLoaded()) {
192                     // Take a shortcut                        
193                     me.onNodeExpand(node, node.childNodes, true);
194                 }
195                 else {
196                     node.set('expanded', false);
197                     node.expand();
198                 }
199             }
200         } 
201     },
202     
203     onNodeInsert: function(parent, node, refNode) {
204         var me = this,
205             index = this.indexOf(refNode);
206             
207         if (index != -1 && me.isVisible(node)) {
208             me.insert(index, node);
209             if (!node.isLeaf() && node.isExpanded()) {
210                 if (node.isLoaded()) {
211                     // Take a shortcut                        
212                     me.onNodeExpand(node, node.childNodes, true);
213                 }
214                 else {
215                     node.set('expanded', false);
216                     node.expand();
217                 }
218             }
219         }
220     },
221     
222     onNodeRemove: function(parent, node, index) {
223         var me = this;
224         if (me.indexOf(node) != -1) {
225             if (!node.isLeaf() && node.isExpanded()) {
226                 me.onNodeCollapse(node, node.childNodes, true);
227             }            
228             me.remove(node);
229         }
230     },
231     
232     isVisible: function(node) {
233         var parent = node.parentNode;
234         while (parent) {
235             if (parent === this.node && !this.rootVisible && parent.isExpanded()) {
236                 return true;
237             }
238             
239             if (this.indexOf(parent) === -1 || !parent.isExpanded()) {
240                 return false;
241             }
242             
243             parent = parent.parentNode;
244         }
245         return true;
246     }
247 });