Roo/tree/TreeDropZone.js
[roojs1] / Roo / tree / TreeDropZone.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12 if(Roo.dd.DropZone){
13     
14 Roo.tree.TreeDropZone = function(tree, config){
15     this.allowParentInsert = false;
16     this.allowContainerDrop = false;
17     this.appendOnly = false;
18     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
19     this.tree = tree;
20     this.lastInsertClass = "x-tree-no-status";
21     this.dragOverData = {};
22 };
23
24 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
25     ddGroup : "TreeDD",
26     
27     expandDelay : 1000,
28     
29     expandNode : function(node){
30         if(node.hasChildNodes() && !node.isExpanded()){
31             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32         }
33     },
34     
35     queueExpand : function(node){
36         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37     },
38     
39     cancelExpand : function(){
40         if(this.expandProcId){
41             clearTimeout(this.expandProcId);
42             this.expandProcId = false;
43         }
44     },
45     
46     isValidDropPoint : function(n, pt, dd, e, data){
47         if(!n || !data){ return false; }
48         var targetNode = n.node;
49         var dropNode = data.node;
50         // default drop rules
51         if(!(targetNode && targetNode.isTarget && pt)){
52             return false;
53         }
54         if(pt == "append" && targetNode.allowChildren === false){
55             return false;
56         }
57         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
58             return false;
59         }
60         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
61             return false;
62         }
63         // reuse the object
64         var overEvent = this.dragOverData;
65         overEvent.tree = this.tree;
66         overEvent.target = targetNode;
67         overEvent.data = data;
68         overEvent.point = pt;
69         overEvent.source = dd;
70         overEvent.rawEvent = e;
71         overEvent.dropNode = dropNode;
72         overEvent.cancel = false;  
73         var result = this.tree.fireEvent("nodedragover", overEvent);
74         return overEvent.cancel === false && result !== false;
75     },
76     
77     getDropPoint : function(e, n, dd)
78     {
79         var tn = n.node;
80         if(tn.isRoot){
81             return tn.allowChildren !== false ? "append" : false; // always append for root
82         }
83         var dragEl = n.ddel;
84         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
85         var y = Roo.lib.Event.getPageY(e);
86         //var noAppend = tn.allowChildren === false || tn.isLeaf();
87         
88         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
89         var noAppend = tn.allowChildren === false;
90         if(this.appendOnly || tn.parentNode.allowChildren === false){
91             return noAppend ? false : "append";
92         }
93         var noBelow = false;
94         if(!this.allowParentInsert){
95             noBelow = tn.hasChildNodes() && tn.isExpanded();
96         }
97         var q = (b - t) / (noAppend ? 2 : 3);
98         if(y >= t && y < (t + q)){
99             return "above";
100         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
101             return "below";
102         }else{
103             return "append";
104         }
105     },
106     
107     onNodeEnter : function(n, dd, e, data)
108     {
109         this.cancelExpand();
110     },
111     
112     onNodeOver : function(n, dd, e, data)
113     {
114        
115         var pt = this.getDropPoint(e, n, dd);
116         var node = n.node;
117         
118         // auto node expand check
119         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
120             this.queueExpand(node);
121         }else if(pt != "append"){
122             this.cancelExpand();
123         }
124         
125         // set the insert point style on the target node
126         var returnCls = this.dropNotAllowed;
127         if(this.isValidDropPoint(n, pt, dd, e, data)){
128            if(pt){
129                var el = n.ddel;
130                var cls;
131                if(pt == "above"){
132                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
133                    cls = "x-tree-drag-insert-above";
134                }else if(pt == "below"){
135                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
136                    cls = "x-tree-drag-insert-below";
137                }else{
138                    returnCls = "x-tree-drop-ok-append";
139                    cls = "x-tree-drag-append";
140                }
141                if(this.lastInsertClass != cls){
142                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
143                    this.lastInsertClass = cls;
144                }
145            }
146        }
147        return returnCls;
148     },
149     
150     onNodeOut : function(n, dd, e, data){
151         
152         this.cancelExpand();
153         this.removeDropIndicators(n);
154     },
155     
156     onNodeDrop : function(n, dd, e, data){
157         var point = this.getDropPoint(e, n, dd);
158         var targetNode = n.node;
159         targetNode.ui.startDrop();
160         if(!this.isValidDropPoint(n, point, dd, e, data)){
161             targetNode.ui.endDrop();
162             return false;
163         }
164         // first try to find the drop node
165         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
166         var dropEvent = {
167             tree : this.tree,
168             target: targetNode,
169             data: data,
170             point: point,
171             source: dd,
172             rawEvent: e,
173             dropNode: dropNode,
174             cancel: !dropNode   
175         };
176         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
177         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
178             targetNode.ui.endDrop();
179             return false;
180         }
181         // allow target changing
182         targetNode = dropEvent.target;
183         if(point == "append" && !targetNode.isExpanded()){
184             targetNode.expand(false, null, function(){
185                 this.completeDrop(dropEvent);
186             }.createDelegate(this));
187         }else{
188             this.completeDrop(dropEvent);
189         }
190         return true;
191     },
192     
193     completeDrop : function(de){
194         var ns = de.dropNode, p = de.point, t = de.target;
195         if(!(ns instanceof Array)){
196             ns = [ns];
197         }
198         var n;
199         for(var i = 0, len = ns.length; i < len; i++){
200             n = ns[i];
201             if(p == "above"){
202                 t.parentNode.insertBefore(n, t);
203             }else if(p == "below"){
204                 t.parentNode.insertBefore(n, t.nextSibling);
205             }else{
206                 t.appendChild(n);
207             }
208         }
209         n.ui.focus();
210         if(this.tree.hlDrop){
211             n.ui.highlight();
212         }
213         t.ui.endDrop();
214         this.tree.fireEvent("nodedrop", de);
215     },
216     
217     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
218         if(this.tree.hlDrop){
219             dropNode.ui.focus();
220             dropNode.ui.highlight();
221         }
222         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
223     },
224     
225     getTree : function(){
226         return this.tree;
227     },
228     
229     removeDropIndicators : function(n){
230         if(n && n.ddel){
231             var el = n.ddel;
232             Roo.fly(el).removeClass([
233                     "x-tree-drag-insert-above",
234                     "x-tree-drag-insert-below",
235                     "x-tree-drag-append"]);
236             this.lastInsertClass = "_noclass";
237         }
238     },
239     
240     beforeDragDrop : function(target, e, id){
241         this.cancelExpand();
242         return true;
243     },
244     
245     afterRepair : function(data){
246         if(data && Roo.enableFx){
247             data.node.ui.highlight();
248         }
249         this.hideProxy();
250     } ,
251     
252     beforeDragOut : function(target, e, id){
253         Roo.log('before drag out');
254         return true;
255     },
256     
257 });
258
259 }