1 | /*! |
---|
2 | * Ext JS Library 3.4.0 |
---|
3 | * Copyright(c) 2006-2011 Sencha Inc. |
---|
4 | * licensing@sencha.com |
---|
5 | * http://www.sencha.com/license |
---|
6 | */ |
---|
7 | /** |
---|
8 | * @class Ext.tree.TreeDropZone |
---|
9 | * @extends Ext.dd.DropZone |
---|
10 | * @constructor |
---|
11 | * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping |
---|
12 | * @param {Object} config |
---|
13 | */ |
---|
14 | if(Ext.dd.DropZone){ |
---|
15 | |
---|
16 | Ext.tree.TreeDropZone = function(tree, config){ |
---|
17 | /** |
---|
18 | * @cfg {Boolean} allowParentInsert |
---|
19 | * Allow inserting a dragged node between an expanded parent node and its first child that will become a |
---|
20 | * sibling of the parent when dropped (defaults to false) |
---|
21 | */ |
---|
22 | this.allowParentInsert = config.allowParentInsert || false; |
---|
23 | /** |
---|
24 | * @cfg {String} allowContainerDrop |
---|
25 | * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false) |
---|
26 | */ |
---|
27 | this.allowContainerDrop = config.allowContainerDrop || false; |
---|
28 | /** |
---|
29 | * @cfg {String} appendOnly |
---|
30 | * True if the tree should only allow append drops (use for trees which are sorted, defaults to false) |
---|
31 | */ |
---|
32 | this.appendOnly = config.appendOnly || false; |
---|
33 | |
---|
34 | Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config); |
---|
35 | /** |
---|
36 | * The TreePanel for this drop zone |
---|
37 | * @type Ext.tree.TreePanel |
---|
38 | * @property |
---|
39 | */ |
---|
40 | this.tree = tree; |
---|
41 | /** |
---|
42 | * Arbitrary data that can be associated with this tree and will be included in the event object that gets |
---|
43 | * passed to any nodedragover event handler (defaults to {}) |
---|
44 | * @type Ext.tree.TreePanel |
---|
45 | * @property |
---|
46 | */ |
---|
47 | this.dragOverData = {}; |
---|
48 | // private |
---|
49 | this.lastInsertClass = "x-tree-no-status"; |
---|
50 | }; |
---|
51 | |
---|
52 | Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, { |
---|
53 | /** |
---|
54 | * @cfg {String} ddGroup |
---|
55 | * A named drag drop group to which this object belongs. If a group is specified, then this object will only |
---|
56 | * interact with other drag drop objects in the same group (defaults to 'TreeDD'). |
---|
57 | */ |
---|
58 | ddGroup : "TreeDD", |
---|
59 | |
---|
60 | /** |
---|
61 | * @cfg {String} expandDelay |
---|
62 | * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node |
---|
63 | * over the target (defaults to 1000) |
---|
64 | */ |
---|
65 | expandDelay : 1000, |
---|
66 | |
---|
67 | // private |
---|
68 | expandNode : function(node){ |
---|
69 | if(node.hasChildNodes() && !node.isExpanded()){ |
---|
70 | node.expand(false, null, this.triggerCacheRefresh.createDelegate(this)); |
---|
71 | } |
---|
72 | }, |
---|
73 | |
---|
74 | // private |
---|
75 | queueExpand : function(node){ |
---|
76 | this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]); |
---|
77 | }, |
---|
78 | |
---|
79 | // private |
---|
80 | cancelExpand : function(){ |
---|
81 | if(this.expandProcId){ |
---|
82 | clearTimeout(this.expandProcId); |
---|
83 | this.expandProcId = false; |
---|
84 | } |
---|
85 | }, |
---|
86 | |
---|
87 | // private |
---|
88 | isValidDropPoint : function(n, pt, dd, e, data){ |
---|
89 | if(!n || !data){ return false; } |
---|
90 | var targetNode = n.node; |
---|
91 | var dropNode = data.node; |
---|
92 | // default drop rules |
---|
93 | if(!(targetNode && targetNode.isTarget && pt)){ |
---|
94 | return false; |
---|
95 | } |
---|
96 | if(pt == "append" && targetNode.allowChildren === false){ |
---|
97 | return false; |
---|
98 | } |
---|
99 | if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){ |
---|
100 | return false; |
---|
101 | } |
---|
102 | if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){ |
---|
103 | return false; |
---|
104 | } |
---|
105 | // reuse the object |
---|
106 | var overEvent = this.dragOverData; |
---|
107 | overEvent.tree = this.tree; |
---|
108 | overEvent.target = targetNode; |
---|
109 | overEvent.data = data; |
---|
110 | overEvent.point = pt; |
---|
111 | overEvent.source = dd; |
---|
112 | overEvent.rawEvent = e; |
---|
113 | overEvent.dropNode = dropNode; |
---|
114 | overEvent.cancel = false; |
---|
115 | var result = this.tree.fireEvent("nodedragover", overEvent); |
---|
116 | return overEvent.cancel === false && result !== false; |
---|
117 | }, |
---|
118 | |
---|
119 | // private |
---|
120 | getDropPoint : function(e, n, dd){ |
---|
121 | var tn = n.node; |
---|
122 | if(tn.isRoot){ |
---|
123 | return tn.allowChildren !== false ? "append" : false; // always append for root |
---|
124 | } |
---|
125 | var dragEl = n.ddel; |
---|
126 | var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight; |
---|
127 | var y = Ext.lib.Event.getPageY(e); |
---|
128 | var noAppend = tn.allowChildren === false || tn.isLeaf(); |
---|
129 | if(this.appendOnly || tn.parentNode.allowChildren === false){ |
---|
130 | return noAppend ? false : "append"; |
---|
131 | } |
---|
132 | var noBelow = false; |
---|
133 | if(!this.allowParentInsert){ |
---|
134 | noBelow = tn.hasChildNodes() && tn.isExpanded(); |
---|
135 | } |
---|
136 | var q = (b - t) / (noAppend ? 2 : 3); |
---|
137 | if(y >= t && y < (t + q)){ |
---|
138 | return "above"; |
---|
139 | }else if(!noBelow && (noAppend || y >= b-q && y <= b)){ |
---|
140 | return "below"; |
---|
141 | }else{ |
---|
142 | return "append"; |
---|
143 | } |
---|
144 | }, |
---|
145 | |
---|
146 | // private |
---|
147 | onNodeEnter : function(n, dd, e, data){ |
---|
148 | this.cancelExpand(); |
---|
149 | }, |
---|
150 | |
---|
151 | onContainerOver : function(dd, e, data) { |
---|
152 | if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) { |
---|
153 | return this.dropAllowed; |
---|
154 | } |
---|
155 | return this.dropNotAllowed; |
---|
156 | }, |
---|
157 | |
---|
158 | // private |
---|
159 | onNodeOver : function(n, dd, e, data){ |
---|
160 | var pt = this.getDropPoint(e, n, dd); |
---|
161 | var node = n.node; |
---|
162 | |
---|
163 | // auto node expand check |
---|
164 | if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){ |
---|
165 | this.queueExpand(node); |
---|
166 | }else if(pt != "append"){ |
---|
167 | this.cancelExpand(); |
---|
168 | } |
---|
169 | |
---|
170 | // set the insert point style on the target node |
---|
171 | var returnCls = this.dropNotAllowed; |
---|
172 | if(this.isValidDropPoint(n, pt, dd, e, data)){ |
---|
173 | if(pt){ |
---|
174 | var el = n.ddel; |
---|
175 | var cls; |
---|
176 | if(pt == "above"){ |
---|
177 | returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between"; |
---|
178 | cls = "x-tree-drag-insert-above"; |
---|
179 | }else if(pt == "below"){ |
---|
180 | returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between"; |
---|
181 | cls = "x-tree-drag-insert-below"; |
---|
182 | }else{ |
---|
183 | returnCls = "x-tree-drop-ok-append"; |
---|
184 | cls = "x-tree-drag-append"; |
---|
185 | } |
---|
186 | if(this.lastInsertClass != cls){ |
---|
187 | Ext.fly(el).replaceClass(this.lastInsertClass, cls); |
---|
188 | this.lastInsertClass = cls; |
---|
189 | } |
---|
190 | } |
---|
191 | } |
---|
192 | return returnCls; |
---|
193 | }, |
---|
194 | |
---|
195 | // private |
---|
196 | onNodeOut : function(n, dd, e, data){ |
---|
197 | this.cancelExpand(); |
---|
198 | this.removeDropIndicators(n); |
---|
199 | }, |
---|
200 | |
---|
201 | // private |
---|
202 | onNodeDrop : function(n, dd, e, data){ |
---|
203 | var point = this.getDropPoint(e, n, dd); |
---|
204 | var targetNode = n.node; |
---|
205 | targetNode.ui.startDrop(); |
---|
206 | if(!this.isValidDropPoint(n, point, dd, e, data)){ |
---|
207 | targetNode.ui.endDrop(); |
---|
208 | return false; |
---|
209 | } |
---|
210 | // first try to find the drop node |
---|
211 | var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null); |
---|
212 | return this.processDrop(targetNode, data, point, dd, e, dropNode); |
---|
213 | }, |
---|
214 | |
---|
215 | onContainerDrop : function(dd, e, data){ |
---|
216 | if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) { |
---|
217 | var targetNode = this.tree.getRootNode(); |
---|
218 | targetNode.ui.startDrop(); |
---|
219 | var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null); |
---|
220 | return this.processDrop(targetNode, data, 'append', dd, e, dropNode); |
---|
221 | } |
---|
222 | return false; |
---|
223 | }, |
---|
224 | |
---|
225 | // private |
---|
226 | processDrop: function(target, data, point, dd, e, dropNode){ |
---|
227 | var dropEvent = { |
---|
228 | tree : this.tree, |
---|
229 | target: target, |
---|
230 | data: data, |
---|
231 | point: point, |
---|
232 | source: dd, |
---|
233 | rawEvent: e, |
---|
234 | dropNode: dropNode, |
---|
235 | cancel: !dropNode, |
---|
236 | dropStatus: false |
---|
237 | }; |
---|
238 | var retval = this.tree.fireEvent("beforenodedrop", dropEvent); |
---|
239 | if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){ |
---|
240 | target.ui.endDrop(); |
---|
241 | return dropEvent.dropStatus; |
---|
242 | } |
---|
243 | |
---|
244 | target = dropEvent.target; |
---|
245 | if(point == 'append' && !target.isExpanded()){ |
---|
246 | target.expand(false, null, function(){ |
---|
247 | this.completeDrop(dropEvent); |
---|
248 | }.createDelegate(this)); |
---|
249 | }else{ |
---|
250 | this.completeDrop(dropEvent); |
---|
251 | } |
---|
252 | return true; |
---|
253 | }, |
---|
254 | |
---|
255 | // private |
---|
256 | completeDrop : function(de){ |
---|
257 | var ns = de.dropNode, p = de.point, t = de.target; |
---|
258 | if(!Ext.isArray(ns)){ |
---|
259 | ns = [ns]; |
---|
260 | } |
---|
261 | var n; |
---|
262 | for(var i = 0, len = ns.length; i < len; i++){ |
---|
263 | n = ns[i]; |
---|
264 | if(p == "above"){ |
---|
265 | t.parentNode.insertBefore(n, t); |
---|
266 | }else if(p == "below"){ |
---|
267 | t.parentNode.insertBefore(n, t.nextSibling); |
---|
268 | }else{ |
---|
269 | t.appendChild(n); |
---|
270 | } |
---|
271 | } |
---|
272 | n.ui.focus(); |
---|
273 | if(Ext.enableFx && this.tree.hlDrop){ |
---|
274 | n.ui.highlight(); |
---|
275 | } |
---|
276 | t.ui.endDrop(); |
---|
277 | this.tree.fireEvent("nodedrop", de); |
---|
278 | }, |
---|
279 | |
---|
280 | // private |
---|
281 | afterNodeMoved : function(dd, data, e, targetNode, dropNode){ |
---|
282 | if(Ext.enableFx && this.tree.hlDrop){ |
---|
283 | dropNode.ui.focus(); |
---|
284 | dropNode.ui.highlight(); |
---|
285 | } |
---|
286 | this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e); |
---|
287 | }, |
---|
288 | |
---|
289 | // private |
---|
290 | getTree : function(){ |
---|
291 | return this.tree; |
---|
292 | }, |
---|
293 | |
---|
294 | // private |
---|
295 | removeDropIndicators : function(n){ |
---|
296 | if(n && n.ddel){ |
---|
297 | var el = n.ddel; |
---|
298 | Ext.fly(el).removeClass([ |
---|
299 | "x-tree-drag-insert-above", |
---|
300 | "x-tree-drag-insert-below", |
---|
301 | "x-tree-drag-append"]); |
---|
302 | this.lastInsertClass = "_noclass"; |
---|
303 | } |
---|
304 | }, |
---|
305 | |
---|
306 | // private |
---|
307 | beforeDragDrop : function(target, e, id){ |
---|
308 | this.cancelExpand(); |
---|
309 | return true; |
---|
310 | }, |
---|
311 | |
---|
312 | // private |
---|
313 | afterRepair : function(data){ |
---|
314 | if(data && Ext.enableFx){ |
---|
315 | data.node.ui.highlight(); |
---|
316 | } |
---|
317 | this.hideProxy(); |
---|
318 | } |
---|
319 | }); |
---|
320 | |
---|
321 | } |
---|