]> git.proxmox.com Git - extjs.git/blob - extjs/packages/core/src/data/NodeStore.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / data / NodeStore.js
1 /**
2 * Node Store
3 * @private
4 */
5 Ext.define('Ext.data.NodeStore', {
6 extend: 'Ext.data.Store',
7 alias: 'store.node',
8 requires: [
9 'Ext.data.TreeModel',
10 'Ext.data.NodeInterface'
11 ],
12
13 /**
14 * @property {Boolean} isNodeStore
15 * `true` in this class to identify an object as an instantiated NodeStore, or subclass thereof.
16 */
17 isNodeStore: true,
18
19 config: {
20 /**
21 * @cfg {Ext.data.Model} node The Record you want to bind this Store to. Note that
22 * this record will be decorated with the {@link Ext.data.NodeInterface} if this is not the
23 * case yet.
24 * @accessor
25 */
26 node: null,
27
28 /**
29 * @cfg {Boolean} recursive Set this to `true` if you want this NodeStore to represent
30 * all the descendants of the node in its flat data collection. This is useful for
31 * rendering a tree structure to a DataView and is being used internally by
32 * the TreeView. Any records that are moved, removed, inserted or appended to the
33 * node at any depth below the node this store is bound to will be automatically
34 * updated in this Store's internal flat data structure.
35 * @accessor
36 */
37 recursive: false,
38
39 /**
40 * @cfg {Boolean} rootVisible `false` to not include the root node in this Stores collection.
41 * @accessor
42 */
43 rootVisible: false,
44
45 /**
46 * @cfg {Boolean} folderSort
47 * Set to `true` to automatically prepend a leaf sorter.
48 */
49 folderSort: false
50 },
51
52 implicitModel: 'Ext.data.TreeModel',
53
54 // NodeStores are never buffered or paged. They are loaded from the TreeStore to reflect all visible
55 // nodes.
56 // BufferedRenderer always asks for the *total* count, so this must return the count.
57 getTotalCount: function() {
58 return this.getCount();
59 },
60
61 updateFolderSort: function(folderSort) {
62 var data = this.getData();
63
64 data.setTrackGroups(false);
65 if (folderSort) {
66 data.setGrouper({
67 groupFn: this.folderSortFn
68 });
69 } else {
70 data.setGrouper(null);
71 }
72 },
73
74 folderSortFn: function(node) {
75 return node.data.leaf ? 1 : 0;
76 },
77
78 afterReject : function(record) {
79 var me = this;
80 // Must pass the 5th param (modifiedFieldNames) as null, otherwise the
81 // event firing machinery appends the listeners "options" object to the arg list
82 // which may get used as the modified fields array by a handler.
83 // This array is used for selective grid cell updating by Grid View.
84 // Null will be treated as though all cells need updating.
85 if (me.contains(record)) {
86 me.onUpdate(record, Ext.data.Model.REJECT, null);
87 me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);
88 }
89 },
90
91 afterCommit : function(record, modifiedFieldNames) {
92 var me = this;
93 if (!modifiedFieldNames) {
94 modifiedFieldNames = null;
95 }
96 if (me.contains(record)) {
97 me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);
98 me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);
99 }
100 },
101
102 onNodeAppend: function(parent, node) {
103 if (parent === this.getNode()) {
104 this.add([node].concat(this.retrieveChildNodes(node)));
105 }
106 },
107
108 onNodeInsert: function(parent, node, refNode) {
109 var me = this,
110 idx;
111
112 if (parent === me.getNode()) {
113 idx = me.indexOf(refNode) || 0;
114 me.insert(0, [node].concat(me.retrieveChildNodes(node)));
115 }
116 },
117
118 onNodeRemove: function(parent, node) {
119 if (parent === this.getNode()) {
120 this.remove([node].concat(this.retrieveChildNodes(node)));
121 }
122 },
123
124 onNodeExpand: function(parent, records) {
125 if (parent === this.getNode()) {
126 this.loadRecords(records);
127 }
128 },
129
130 applyNode: function(node) {
131 if (node) {
132 if (!node.isModel) {
133 node = new (this.getModel())(node);
134 }
135 if (!node.isNode) {
136 Ext.data.NodeInterface.decorate(node);
137 }
138 }
139 return node;
140 },
141
142 updateNode: function(node, oldNode) {
143 var me = this,
144 data;
145
146 if (oldNode && !oldNode.destroyed) {
147 oldNode.un({
148 append : 'onNodeAppend',
149 insert : 'onNodeInsert',
150 remove : 'onNodeRemove',
151 scope: me
152 });
153 oldNode.unjoin(me);
154 }
155
156 if (node) {
157 node.on({
158 scope : me,
159 append : 'onNodeAppend',
160 insert : 'onNodeInsert',
161 remove : 'onNodeRemove'
162 });
163
164 node.join(me);
165
166 data = [];
167 if (node.childNodes.length) {
168 data = data.concat(me.retrieveChildNodes(node));
169 }
170 if (me.getRootVisible()) {
171 data.push(node);
172 } else if (node.isLoaded() || node.isLoading()) {
173 node.set('expanded', true);
174 }
175
176 me.getData().clear();
177 me.fireEvent('clear', me);
178
179 me.suspendEvents();
180 if (me.isInitializing) {
181 me.inlineData = data;
182 } else {
183 me.add(data);
184 }
185 me.resumeEvents();
186
187 if (data.length === 0) {
188 me.loaded = node.loaded = true;
189 }
190
191 me.fireEvent('refresh', me, me.data);
192 }
193 },
194
195 /**
196 * @param {Object} node
197 * @return {Boolean}
198 */
199 isVisible: function(node) {
200 var parent = node.parentNode;
201
202 if (!this.getRecursive() && parent !== this.getNode()) {
203 return false;
204 }
205
206 while (parent) {
207 if (!parent.isExpanded()) {
208 return false;
209 }
210
211 //we need to check this because for a nodestore the node is not likely to be the root
212 //so we stop going up the chain when we hit the original node as we don't care about any
213 //ancestors above the configured node
214 if (parent === this.getNode()) {
215 break;
216 }
217
218 parent = parent.parentNode;
219 }
220 return true;
221 },
222
223 privates: {
224 /**
225 * Private method used to deeply retrieve the children of a record without recursion.
226 * @private
227 * @param {Ext.data.NodeInterface} root
228 * @return {Ext.data.NodeInterface[]}
229 */
230 retrieveChildNodes: function(root) {
231 var node = this.getNode(),
232 recursive = this.getRecursive(),
233 added = [],
234 child = root;
235
236 if (!root.childNodes.length || (!recursive && root !== node)) {
237 return added;
238 }
239
240 if (!recursive) {
241 return root.childNodes;
242 }
243
244 while (child) {
245 if (child._added) {
246 delete child._added;
247 if (child === root) {
248 break;
249 } else {
250 child = child.nextSibling || child.parentNode;
251 }
252 } else {
253 if (child !== root) {
254 added.push(child);
255 }
256 if (child.firstChild) {
257 child._added = true;
258 child = child.firstChild;
259 } else {
260 child = child.nextSibling || child.parentNode;
261 }
262 }
263 }
264
265 return added;
266 }
267 }
268 });