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