Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / dd / DD.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /*
16  * This is a derivative of the similarly named class in the YUI Library.
17  * The original license:
18  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19  * Code licensed under the BSD License:
20  * http://developer.yahoo.net/yui/license.txt
21  */
22
23
24 /**
25  * @class Ext.dd.DD
26  * A DragDrop implementation where the linked element follows the
27  * mouse cursor during a drag.
28  * @extends Ext.dd.DragDrop
29  */
30 Ext.define('Ext.dd.DD', {
31     extend: 'Ext.dd.DragDrop',
32     requires: ['Ext.dd.DragDropManager'],
33
34     /**
35      * Creates new DD instance.
36      * @param {String} id the id of the linked element
37      * @param {String} sGroup the group of related DragDrop items
38      * @param {object} config an object containing configurable attributes.
39      * Valid properties for DD: scroll
40      */
41     constructor: function(id, sGroup, config) {
42         if (id) {
43             this.init(id, sGroup, config);
44         }
45     },
46
47     /**
48      * When set to true, the utility automatically tries to scroll the browser
49      * window when a drag and drop element is dragged near the viewport boundary.
50      * Defaults to true.
51      * @property scroll
52      * @type boolean
53      */
54     scroll: true,
55
56     /**
57      * Sets the pointer offset to the distance between the linked element's top
58      * left corner and the location the element was clicked
59      * @method autoOffset
60      * @param {int} iPageX the X coordinate of the click
61      * @param {int} iPageY the Y coordinate of the click
62      */
63     autoOffset: function(iPageX, iPageY) {
64         var x = iPageX - this.startPageX;
65         var y = iPageY - this.startPageY;
66         this.setDelta(x, y);
67     },
68
69     /**
70      * Sets the pointer offset.  You can call this directly to force the
71      * offset to be in a particular location (e.g., pass in 0,0 to set it
72      * to the center of the object)
73      * @method setDelta
74      * @param {int} iDeltaX the distance from the left
75      * @param {int} iDeltaY the distance from the top
76      */
77     setDelta: function(iDeltaX, iDeltaY) {
78         this.deltaX = iDeltaX;
79         this.deltaY = iDeltaY;
80     },
81
82     /**
83      * Sets the drag element to the location of the mousedown or click event,
84      * maintaining the cursor location relative to the location on the element
85      * that was clicked.  Override this if you want to place the element in a
86      * location other than where the cursor is.
87      * @method setDragElPos
88      * @param {int} iPageX the X coordinate of the mousedown or drag event
89      * @param {int} iPageY the Y coordinate of the mousedown or drag event
90      */
91     setDragElPos: function(iPageX, iPageY) {
92         // the first time we do this, we are going to check to make sure
93         // the element has css positioning
94
95         var el = this.getDragEl();
96         this.alignElWithMouse(el, iPageX, iPageY);
97     },
98
99     /**
100      * Sets the element to the location of the mousedown or click event,
101      * maintaining the cursor location relative to the location on the element
102      * that was clicked.  Override this if you want to place the element in a
103      * location other than where the cursor is.
104      * @method alignElWithMouse
105      * @param {HTMLElement} el the element to move
106      * @param {int} iPageX the X coordinate of the mousedown or drag event
107      * @param {int} iPageY the Y coordinate of the mousedown or drag event
108      */
109     alignElWithMouse: function(el, iPageX, iPageY) {
110         var oCoord = this.getTargetCoord(iPageX, iPageY),
111             fly = el.dom ? el : Ext.fly(el, '_dd'),
112             elSize = fly.getSize(),
113             EL = Ext.core.Element,
114             vpSize;
115
116         if (!this.deltaSetXY) {
117             vpSize = this.cachedViewportSize = { width: EL.getDocumentWidth(), height: EL.getDocumentHeight() };
118             var aCoord = [
119                 Math.max(0, Math.min(oCoord.x, vpSize.width - elSize.width)),
120                 Math.max(0, Math.min(oCoord.y, vpSize.height - elSize.height))
121             ];
122             fly.setXY(aCoord);
123             var newLeft = fly.getLeft(true);
124             var newTop  = fly.getTop(true);
125             this.deltaSetXY = [newLeft - oCoord.x, newTop - oCoord.y];
126         } else {
127             vpSize = this.cachedViewportSize;
128             fly.setLeftTop(
129                 Math.max(0, Math.min(oCoord.x + this.deltaSetXY[0], vpSize.width - elSize.width)),
130                 Math.max(0, Math.min(oCoord.y + this.deltaSetXY[1], vpSize.height - elSize.height))
131             );
132         }
133
134         this.cachePosition(oCoord.x, oCoord.y);
135         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
136         return oCoord;
137     },
138
139     /**
140      * Saves the most recent position so that we can reset the constraints and
141      * tick marks on-demand.  We need to know this so that we can calculate the
142      * number of pixels the element is offset from its original position.
143      * @method cachePosition
144      * @param iPageX the current x position (optional, this just makes it so we
145      * don't have to look it up again)
146      * @param iPageY the current y position (optional, this just makes it so we
147      * don't have to look it up again)
148      */
149     cachePosition: function(iPageX, iPageY) {
150         if (iPageX) {
151             this.lastPageX = iPageX;
152             this.lastPageY = iPageY;
153         } else {
154             var aCoord = Ext.core.Element.getXY(this.getEl());
155             this.lastPageX = aCoord[0];
156             this.lastPageY = aCoord[1];
157         }
158     },
159
160     /**
161      * Auto-scroll the window if the dragged object has been moved beyond the
162      * visible window boundary.
163      * @method autoScroll
164      * @param {int} x the drag element's x position
165      * @param {int} y the drag element's y position
166      * @param {int} h the height of the drag element
167      * @param {int} w the width of the drag element
168      * @private
169      */
170     autoScroll: function(x, y, h, w) {
171
172         if (this.scroll) {
173             // The client height
174             var clientH = Ext.core.Element.getViewHeight();
175
176             // The client width
177             var clientW = Ext.core.Element.getViewWidth();
178
179             // The amt scrolled down
180             var st = this.DDMInstance.getScrollTop();
181
182             // The amt scrolled right
183             var sl = this.DDMInstance.getScrollLeft();
184
185             // Location of the bottom of the element
186             var bot = h + y;
187
188             // Location of the right of the element
189             var right = w + x;
190
191             // The distance from the cursor to the bottom of the visible area,
192             // adjusted so that we don't scroll if the cursor is beyond the
193             // element drag constraints
194             var toBot = (clientH + st - y - this.deltaY);
195
196             // The distance from the cursor to the right of the visible area
197             var toRight = (clientW + sl - x - this.deltaX);
198
199
200             // How close to the edge the cursor must be before we scroll
201             // var thresh = (document.all) ? 100 : 40;
202             var thresh = 40;
203
204             // How many pixels to scroll per autoscroll op.  This helps to reduce
205             // clunky scrolling. IE is more sensitive about this ... it needs this
206             // value to be higher.
207             var scrAmt = (document.all) ? 80 : 30;
208
209             // Scroll down if we are near the bottom of the visible page and the
210             // obj extends below the crease
211             if ( bot > clientH && toBot < thresh ) {
212                 window.scrollTo(sl, st + scrAmt);
213             }
214
215             // Scroll up if the window is scrolled down and the top of the object
216             // goes above the top border
217             if ( y < st && st > 0 && y - st < thresh ) {
218                 window.scrollTo(sl, st - scrAmt);
219             }
220
221             // Scroll right if the obj is beyond the right border and the cursor is
222             // near the border.
223             if ( right > clientW && toRight < thresh ) {
224                 window.scrollTo(sl + scrAmt, st);
225             }
226
227             // Scroll left if the window has been scrolled to the right and the obj
228             // extends past the left border
229             if ( x < sl && sl > 0 && x - sl < thresh ) {
230                 window.scrollTo(sl - scrAmt, st);
231             }
232         }
233     },
234
235     /**
236      * Finds the location the element should be placed if we want to move
237      * it to where the mouse location less the click offset would place us.
238      * @method getTargetCoord
239      * @param {int} iPageX the X coordinate of the click
240      * @param {int} iPageY the Y coordinate of the click
241      * @return an object that contains the coordinates (Object.x and Object.y)
242      * @private
243      */
244     getTargetCoord: function(iPageX, iPageY) {
245         var x = iPageX - this.deltaX;
246         var y = iPageY - this.deltaY;
247
248         if (this.constrainX) {
249             if (x < this.minX) {
250                 x = this.minX;
251             }
252             if (x > this.maxX) {
253                 x = this.maxX;
254             }
255         }
256
257         if (this.constrainY) {
258             if (y < this.minY) {
259                 y = this.minY;
260             }
261             if (y > this.maxY) {
262                 y = this.maxY;
263             }
264         }
265
266         x = this.getTick(x, this.xTicks);
267         y = this.getTick(y, this.yTicks);
268
269
270         return {x: x, y: y};
271     },
272
273     /**
274      * Sets up config options specific to this class. Overrides
275      * Ext.dd.DragDrop, but all versions of this method through the
276      * inheritance chain are called
277      */
278     applyConfig: function() {
279         this.callParent();
280         this.scroll = (this.config.scroll !== false);
281     },
282
283     /**
284      * Event that fires prior to the onMouseDown event.  Overrides
285      * Ext.dd.DragDrop.
286      */
287     b4MouseDown: function(e) {
288         // this.resetConstraints();
289         this.autoOffset(e.getPageX(), e.getPageY());
290     },
291
292     /**
293      * Event that fires prior to the onDrag event.  Overrides
294      * Ext.dd.DragDrop.
295      */
296     b4Drag: function(e) {
297         this.setDragElPos(e.getPageX(), e.getPageY());
298     },
299
300     toString: function() {
301         return ("DD " + this.id);
302     }
303
304     //////////////////////////////////////////////////////////////////////////
305     // Debugging ygDragDrop events that can be overridden
306     //////////////////////////////////////////////////////////////////////////
307     /*
308     startDrag: function(x, y) {
309     },
310
311     onDrag: function(e) {
312     },
313
314     onDragEnter: function(e, id) {
315     },
316
317     onDragOver: function(e, id) {
318     },
319
320     onDragOut: function(e, id) {
321     },
322
323     onDragDrop: function(e, id) {
324     },
325
326     endDrag: function(e) {
327     }
328
329     */
330
331 });
332