Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / ViewDropZone2.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-tree-ViewDropZone'>/**
19 </span> * @class Ext.tree.ViewDropZone
20  * @extends Ext.view.DropZone
21  * @private
22  */
23 Ext.define('Ext.tree.ViewDropZone', {
24     extend: 'Ext.view.DropZone',
25
26 <span id='Ext-tree-ViewDropZone-cfg-allowParentInsert'>    /**
27 </span>     * @cfg {Boolean} allowParentInsert
28      * Allow inserting a dragged node between an expanded parent node and its first child that will become a
29      * sibling of the parent when dropped.
30      */
31     allowParentInserts: false,
32  
33 <span id='Ext-tree-ViewDropZone-cfg-allowContainerDrop'>    /**
34 </span>     * @cfg {String} allowContainerDrop
35      * True if drops on the tree container (outside of a specific tree node) are allowed.
36      */
37     allowContainerDrops: false,
38
39 <span id='Ext-tree-ViewDropZone-cfg-appendOnly'>    /**
40 </span>     * @cfg {String} appendOnly
41      * True if the tree should only allow append drops (use for trees which are sorted).
42      */
43     appendOnly: false,
44
45 <span id='Ext-tree-ViewDropZone-cfg-expandDelay'>    /**
46 </span>     * @cfg {String} expandDelay
47      * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node
48      * over the target.
49      */
50     expandDelay : 500,
51
52     indicatorCls: 'x-tree-ddindicator',
53
54     // private
55     expandNode : function(node) {
56         var view = this.view;
57         if (!node.isLeaf() &amp;&amp; !node.isExpanded()) {
58             view.expand(node);
59             this.expandProcId = false;
60         }
61     },
62
63     // private
64     queueExpand : function(node) {
65         this.expandProcId = Ext.Function.defer(this.expandNode, this.expandDelay, this, [node]);
66     },
67
68     // private
69     cancelExpand : function() {
70         if (this.expandProcId) {
71             clearTimeout(this.expandProcId);
72             this.expandProcId = false;
73         }
74     },
75
76     getPosition: function(e, node) {
77         var view = this.view,
78             record = view.getRecord(node),
79             y = e.getPageY(),
80             noAppend = record.isLeaf(),
81             noBelow = false,
82             region = Ext.fly(node).getRegion(),
83             fragment;
84
85         // If we are dragging on top of the root node of the tree, we always want to append.
86         if (record.isRoot()) {
87             return 'append';
88         }
89
90         // Return 'append' if the node we are dragging on top of is not a leaf else return false.
91         if (this.appendOnly) {
92             return noAppend ? false : 'append';
93         }
94
95         if (!this.allowParentInsert) {
96             noBelow = record.hasChildNodes() &amp;&amp; record.isExpanded();
97         }
98
99         fragment = (region.bottom - region.top) / (noAppend ? 2 : 3);
100         if (y &gt;= region.top &amp;&amp; y &lt; (region.top + fragment)) {
101             return 'before';
102         }
103         else if (!noBelow &amp;&amp; (noAppend || (y &gt;= (region.bottom - fragment) &amp;&amp; y &lt;= region.bottom))) {
104             return 'after';
105         }
106         else {
107             return 'append';
108         }
109     },
110
111     isValidDropPoint : function(node, position, dragZone, e, data) {
112         if (!node || !data.item) {
113             return false;
114         }
115
116         var view = this.view,
117             targetNode = view.getRecord(node),
118             draggedRecords = data.records,
119             dataLength = draggedRecords.length,
120             ln = draggedRecords.length,
121             i, record;
122
123         // No drop position, or dragged records: invalid drop point
124         if (!(targetNode &amp;&amp; position &amp;&amp; dataLength)) {
125             return false;
126         }
127
128         // If the targetNode is within the folder we are dragging
129         for (i = 0; i &lt; ln; i++) {
130             record = draggedRecords[i];
131             if (record.isNode &amp;&amp; record.contains(targetNode)) {
132                 return false;
133             }
134         }
135         
136         // Respect the allowDrop field on Tree nodes
137         if (position === 'append' &amp;&amp; targetNode.get('allowDrop') === false) {
138             return false;
139         }
140         else if (position != 'append' &amp;&amp; targetNode.parentNode.get('allowDrop') === false) {
141             return false;
142         }
143
144         // If the target record is in the dragged dataset, then invalid drop
145         if (Ext.Array.contains(draggedRecords, targetNode)) {
146              return false;
147         }
148
149         // @TODO: fire some event to notify that there is a valid drop possible for the node you're dragging
150         // Yes: this.fireViewEvent(blah....) fires an event through the owning View.
151         return true;
152     },
153
154     onNodeOver : function(node, dragZone, e, data) {
155         var position = this.getPosition(e, node),
156             returnCls = this.dropNotAllowed,
157             view = this.view,
158             targetNode = view.getRecord(node),
159             indicator = this.getIndicator(),
160             indicatorX = 0,
161             indicatorY = 0;
162
163         // auto node expand check
164         this.cancelExpand();
165         if (position == 'append' &amp;&amp; !this.expandProcId &amp;&amp; !Ext.Array.contains(data.records, targetNode) &amp;&amp; !targetNode.isLeaf() &amp;&amp; !targetNode.isExpanded()) {
166             this.queueExpand(targetNode);
167         }
168             
169             
170         if (this.isValidDropPoint(node, position, dragZone, e, data)) {
171             this.valid = true;
172             this.currentPosition = position;
173             this.overRecord = targetNode;
174
175             indicator.setWidth(Ext.fly(node).getWidth());
176             indicatorY = Ext.fly(node).getY() - Ext.fly(view.el).getY() - 1;
177
178             /*
179              * In the code below we show the proxy again. The reason for doing this is showing the indicator will
180              * call toFront, causing it to get a new z-index which can sometimes push the proxy behind it. We always 
181              * want the proxy to be above, so calling show on the proxy will call toFront and bring it forward.
182              */
183             if (position == 'before') {
184                 returnCls = targetNode.isFirst() ? Ext.baseCSSPrefix + 'tree-drop-ok-above' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
185                 indicator.showAt(0, indicatorY);
186                 dragZone.proxy.show();
187             } else if (position == 'after') {
188                 returnCls = targetNode.isLast() ? Ext.baseCSSPrefix + 'tree-drop-ok-below' : Ext.baseCSSPrefix + 'tree-drop-ok-between';
189                 indicatorY += Ext.fly(node).getHeight();
190                 indicator.showAt(0, indicatorY);
191                 dragZone.proxy.show();
192             } else {
193                 returnCls = Ext.baseCSSPrefix + 'tree-drop-ok-append';
194                 // @TODO: set a class on the parent folder node to be able to style it
195                 indicator.hide();
196             }
197         } else {
198             this.valid = false;
199         }
200
201         this.currentCls = returnCls;
202         return returnCls;
203     },
204
205     onContainerOver : function(dd, e, data) {
206         return e.getTarget('.' + this.indicatorCls) ? this.currentCls : this.dropNotAllowed;
207     },
208     
209     notifyOut: function() {
210         this.callParent(arguments);
211         this.cancelExpand();
212     },
213
214     handleNodeDrop : function(data, targetNode, position) {
215         var me = this,
216             view = me.view,
217             parentNode = targetNode.parentNode,
218             store = view.getStore(),
219             recordDomNodes = [],
220             records, i, len,
221             insertionMethod, argList,
222             needTargetExpand,
223             transferData,
224             processDrop;
225
226         // If the copy flag is set, create a copy of the Models with the same IDs
227         if (data.copy) {
228             records = data.records;
229             data.records = [];
230             for (i = 0, len = records.length; i &lt; len; i++) {
231                 data.records.push(Ext.apply({}, records[i].data));
232             }
233         }
234
235         // Cancel any pending expand operation
236         me.cancelExpand();
237
238         // Grab a reference to the correct node insertion method.
239         // Create an arg list array intended for the apply method of the
240         // chosen node insertion method.
241         // Ensure the target object for the method is referenced by 'targetNode'
242         if (position == 'before') {
243             insertionMethod = parentNode.insertBefore;
244             argList = [null, targetNode];
245             targetNode = parentNode;
246         }
247         else if (position == 'after') {
248             if (targetNode.nextSibling) {
249                 insertionMethod = parentNode.insertBefore;
250                 argList = [null, targetNode.nextSibling];
251             }
252             else {
253                 insertionMethod = parentNode.appendChild;
254                 argList = [null];
255             }
256             targetNode = parentNode;
257         }
258         else {
259             if (!targetNode.isExpanded()) {
260                 needTargetExpand = true;
261             }
262             insertionMethod = targetNode.appendChild;
263             argList = [null];
264         }
265
266         // A function to transfer the data into the destination tree
267         transferData = function() {
268             var node;
269             for (i = 0, len = data.records.length; i &lt; len; i++) {
270                 argList[0] = data.records[i];
271                 node = insertionMethod.apply(targetNode, argList);
272                 
273                 if (Ext.enableFx &amp;&amp; me.dropHighlight) {
274                     recordDomNodes.push(view.getNode(node));
275                 }
276             }
277             
278             // Kick off highlights after everything's been inserted, so they are
279             // more in sync without insertion/render overhead.
280             if (Ext.enableFx &amp;&amp; me.dropHighlight) {
281                 //FIXME: the check for n.firstChild is not a great solution here. Ideally the line should simply read 
282                 //Ext.fly(n.firstChild) but this yields errors in IE6 and 7. See ticket EXTJSIV-1705 for more details
283                 Ext.Array.forEach(recordDomNodes, function(n) {
284                     if (n) {
285                         Ext.fly(n.firstChild ? n.firstChild : n).highlight(me.dropHighlightColor);
286                     }
287                 });
288             }
289         };
290
291         // If dropping right on an unexpanded node, transfer the data after it is expanded.
292         if (needTargetExpand) {
293             targetNode.expand(false, transferData);
294         }
295         // Otherwise, call the data transfer function immediately
296         else {
297             transferData();
298         }
299     }
300 });</pre>
301 </body>
302 </html>