Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / DragTracker.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="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../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-dd-DragTracker'>/**
19 </span> * @class Ext.dd.DragTracker
20  * A DragTracker listens for drag events on an Element and fires events at the start and end of the drag,
21  * as well as during the drag. This is useful for components such as {@link Ext.slider.Multi}, where there is
22  * an element that can be dragged around to change the Slider's value.
23  * DragTracker provides a series of template methods that should be overridden to provide functionality
24  * in response to detected drag operations. These are onBeforeStart, onStart, onDrag and onEnd.
25  * See {@link Ext.slider.Multi}'s initEvents function for an example implementation.
26  */
27 Ext.define('Ext.dd.DragTracker', {
28
29     uses: ['Ext.util.Region'],
30
31     mixins: {
32         observable: 'Ext.util.Observable'
33     },
34
35 <span id='Ext-dd-DragTracker-property-active'>    /**
36 </span>     * @property active
37      * @type Boolean
38      * Read-only property indicated whether the user is currently dragging this
39      * tracker.
40      */
41     active: false,
42
43 <span id='Ext-dd-DragTracker-property-dragTarget'>    /**
44 </span>     * @property dragTarget
45      * @type HtmlElement
46      * &lt;p&gt;&lt;b&gt;Only valid during drag operations. Read-only.&lt;/b&gt;&lt;/p&gt;
47      * &lt;p&gt;The element being dragged.&lt;/p&gt;
48      * &lt;p&gt;If the {@link #delegate} option is used, this will be the delegate element which was mousedowned.&lt;/p&gt;
49      */
50
51 <span id='Ext-dd-DragTracker-cfg-trackOver'>    /**
52 </span>     * @cfg {Boolean} trackOver
53      * &lt;p&gt;Defaults to &lt;code&gt;false&lt;/code&gt;. Set to true to fire mouseover and mouseout events when the mouse enters or leaves the target element.&lt;/p&gt;
54      * &lt;p&gt;This is implicitly set when an {@link #overCls} is specified.&lt;/p&gt;
55      * &lt;b&gt;If the {@link #delegate} option is used, these events fire only when a delegate element is entered of left.&lt;/b&gt;.
56      */
57     trackOver: false,
58
59 <span id='Ext-dd-DragTracker-cfg-overCls'>    /**
60 </span>     * @cfg {String} overCls
61      * &lt;p&gt;A CSS class to add to the DragTracker's target element when the element (or, if the {@link #delegate} option is used,
62      * when a delegate element) is mouseovered.&lt;/p&gt;
63      * &lt;b&gt;If the {@link #delegate} option is used, these events fire only when a delegate element is entered of left.&lt;/b&gt;.
64      */
65
66 <span id='Ext-dd-DragTracker-cfg-constrainTo'>    /**
67 </span>     * @cfg {Ext.util.Region/Element} constrainTo
68      * &lt;p&gt;A {@link Ext.util.Region Region} (Or an element from which a Region measurement will be read) which is used to constrain
69      * the result of the {@link #getOffset} call.&lt;/p&gt;
70      * &lt;p&gt;This may be set any time during the DragTracker's lifecycle to set a dynamic constraining region.&lt;/p&gt;
71      */
72
73 <span id='Ext-dd-DragTracker-cfg-tolerance'>    /**
74 </span>     * @cfg {Number} tolerance
75      * Number of pixels the drag target must be moved before dragging is
76      * considered to have started. Defaults to &lt;code&gt;5&lt;/code&gt;.
77      */
78     tolerance: 5,
79
80 <span id='Ext-dd-DragTracker-cfg-autoStart'>    /**
81 </span>     * @cfg {Boolean/Number} autoStart
82      * Defaults to &lt;code&gt;false&lt;/code&gt;. Specify &lt;code&gt;true&lt;/code&gt; to defer trigger start by 1000 ms.
83      * Specify a Number for the number of milliseconds to defer trigger start.
84      */
85     autoStart: false,
86
87 <span id='Ext-dd-DragTracker-cfg-delegate'>    /**
88 </span>     * @cfg {String} delegate
89      * Optional. &lt;p&gt;A {@link Ext.DomQuery DomQuery} selector which identifies child elements within the DragTracker's encapsulating
90      * Element which are the tracked elements. This limits tracking to only begin when the matching elements are mousedowned.&lt;/p&gt;
91      * &lt;p&gt;This may also be a specific child element within the DragTracker's encapsulating element to use as the tracked element.&lt;/p&gt;
92      */
93
94 <span id='Ext-dd-DragTracker-cfg-preventDefault'>    /**
95 </span>     * @cfg {Boolean} preventDefault
96      * Specify &lt;code&gt;false&lt;/code&gt; to enable default actions on onMouseDown events. Defaults to &lt;code&gt;true&lt;/code&gt;.
97      */
98
99 <span id='Ext-dd-DragTracker-cfg-stopEvent'>    /**
100 </span>     * @cfg {Boolean} stopEvent
101      * Specify &lt;code&gt;true&lt;/code&gt; to stop the &lt;code&gt;mousedown&lt;/code&gt; event from bubbling to outer listeners from the target element (or its delegates). Defaults to &lt;code&gt;false&lt;/code&gt;.
102      */
103
104     constructor : function(config){
105         Ext.apply(this, config);
106         this.addEvents(
107 <span id='Ext-dd-DragTracker-event-mouseover'>            /**
108 </span>             * @event mouseover &lt;p&gt;&lt;b&gt;Only available when {@link #trackOver} is &lt;code&gt;true&lt;/code&gt;&lt;/b&gt;&lt;/p&gt;
109              * &lt;p&gt;Fires when the mouse enters the DragTracker's target element (or if {@link #delegate} is
110              * used, when the mouse enters a delegate element).&lt;/p&gt;
111              * @param {Object} this
112              * @param {Object} e event object
113              * @param {HtmlElement} target The element mouseovered.
114              */
115             'mouseover',
116
117 <span id='Ext-dd-DragTracker-event-mouseout'>            /**
118 </span>             * @event mouseout &lt;p&gt;&lt;b&gt;Only available when {@link #trackOver} is &lt;code&gt;true&lt;/code&gt;&lt;/b&gt;&lt;/p&gt;
119              * &lt;p&gt;Fires when the mouse exits the DragTracker's target element (or if {@link #delegate} is
120              * used, when the mouse exits a delegate element).&lt;/p&gt;
121              * @param {Object} this
122              * @param {Object} e event object
123              */
124             'mouseout',
125
126 <span id='Ext-dd-DragTracker-event-mousedown'>            /**
127 </span>             * @event mousedown &lt;p&gt;Fires when the mouse button is pressed down, but before a drag operation begins. The
128              * drag operation begins after either the mouse has been moved by {@link #tolerance} pixels, or after
129              * the {@link #autoStart} timer fires.&lt;/p&gt;
130              * &lt;p&gt;Return false to veto the drag operation.&lt;/p&gt;
131              * @param {Object} this
132              * @param {Object} e event object
133              */
134             'mousedown',
135
136 <span id='Ext-dd-DragTracker-event-mouseup'>            /**
137 </span>             * @event mouseup
138              * @param {Object} this
139              * @param {Object} e event object
140              */
141             'mouseup',
142
143 <span id='Ext-dd-DragTracker-event-mousemove'>            /**
144 </span>             * @event mousemove Fired when the mouse is moved. Returning false cancels the drag operation.
145              * @param {Object} this
146              * @param {Object} e event object
147              */
148             'mousemove',
149
150 <span id='Ext-dd-DragTracker-event-beforestart'>            /**
151 </span>             * @event beforestart
152              * @param {Object} this
153              * @param {Object} e event object
154              */
155             'beforedragstart',
156
157 <span id='Ext-dd-DragTracker-event-dragstart'>            /**
158 </span>             * @event dragstart
159              * @param {Object} this
160              * @param {Object} e event object
161              */
162             'dragstart',
163
164 <span id='Ext-dd-DragTracker-event-dragend'>            /**
165 </span>             * @event dragend
166              * @param {Object} this
167              * @param {Object} e event object
168              */
169             'dragend',
170
171 <span id='Ext-dd-DragTracker-event-drag'>            /**
172 </span>             * @event drag
173              * @param {Object} this
174              * @param {Object} e event object
175              */
176             'drag'
177         );
178
179         this.dragRegion = Ext.create('Ext.util.Region', 0,0,0,0);
180
181         if (this.el) {
182             this.initEl(this.el);
183         }
184
185         // Dont pass the config so that it is not applied to 'this' again
186         this.mixins.observable.constructor.call(this);
187         if (this.disabled) {
188             this.disable();
189         }
190
191     },
192
193 <span id='Ext-dd-DragTracker-method-initEl'>    /**
194 </span>     * Initializes the DragTracker on a given element.
195      * @param {Ext.core.Element/HTMLElement} el The element
196      */
197     initEl: function(el) {
198         this.el = Ext.get(el);
199
200         // The delegate option may also be an element on which to listen
201         this.handle = Ext.get(this.delegate);
202
203         // If delegate specified an actual element to listen on, we do not use the delegate listener option
204         this.delegate = this.handle ? undefined : this.delegate;
205
206         if (!this.handle) {
207             this.handle = this.el;
208         }
209
210         // Add a mousedown listener which reacts only on the elements targeted by the delegate config.
211         // We process mousedown to begin tracking.
212         this.mon(this.handle, {
213             mousedown: this.onMouseDown,
214             delegate: this.delegate,
215             scope: this
216         });
217
218         // If configured to do so, track mouse entry and exit into the target (or delegate).
219         // The mouseover and mouseout CANNOT be replaced with mouseenter and mouseleave
220         // because delegate cannot work with those pseudoevents. Entry/exit checking is done in the handler.
221         if (this.trackOver || this.overCls) {
222             this.mon(this.handle, {
223                 mouseover: this.onMouseOver,
224                 mouseout: this.onMouseOut,
225                 delegate: this.delegate,
226                 scope: this
227             });
228         }
229     },
230
231     disable: function() {
232         this.disabled = true;
233     },
234
235     enable: function() {
236         this.disabled = false;
237     },
238
239     destroy : function() {
240         this.clearListeners();
241         delete this.el;
242     },
243
244     // When the pointer enters a tracking element, fire a mouseover if the mouse entered from outside.
245     // This is mouseenter functionality, but we cannot use mouseenter because we are using &quot;delegate&quot; to filter mouse targets
246     onMouseOver: function(e, target) {
247         var me = this;
248         if (!me.disabled) {
249             if (Ext.EventManager.contains(e) || me.delegate) {
250                 me.mouseIsOut = false;
251                 if (me.overCls) {
252                     me.el.addCls(me.overCls);
253                 }
254                 me.fireEvent('mouseover', me, e, me.delegate ? e.getTarget(me.delegate, target) : me.handle);
255             }
256         }
257     },
258
259     // When the pointer exits a tracking element, fire a mouseout.
260     // This is mouseleave functionality, but we cannot use mouseleave because we are using &quot;delegate&quot; to filter mouse targets
261     onMouseOut: function(e) {
262         if (this.mouseIsDown) {
263             this.mouseIsOut = true;
264         } else {
265             if (this.overCls) {
266                 this.el.removeCls(this.overCls);
267             }
268             this.fireEvent('mouseout', this, e);
269         }
270     },
271
272     onMouseDown: function(e, target){
273         // If this is disabled, or the mousedown has been processed by an upstream DragTracker, return
274         if (this.disabled ||e.dragTracked) {
275             return;
276         }
277
278         // This information should be available in mousedown listener and onBeforeStart implementations
279         this.dragTarget = this.delegate ? target : this.handle.dom;
280         this.startXY = this.lastXY = e.getXY();
281         this.startRegion = Ext.fly(this.dragTarget).getRegion();
282
283         if (this.fireEvent('mousedown', this, e) === false ||
284             this.fireEvent('beforedragstart', this, e) === false ||
285             this.onBeforeStart(e) === false) {
286             return;
287         }
288
289         // Track when the mouse is down so that mouseouts while the mouse is down are not processed.
290         // The onMouseOut method will only ever be called after mouseup.
291         this.mouseIsDown = true;
292
293         // Flag for downstream DragTracker instances that the mouse is being tracked.
294         e.dragTracked = true;
295
296         if (this.preventDefault !== false) {
297             e.preventDefault();
298         }
299         Ext.getDoc().on({
300             scope: this,
301             mouseup: this.onMouseUp,
302             mousemove: this.onMouseMove,
303             selectstart: this.stopSelect
304         });
305         if (this.autoStart) {
306             this.timer =  Ext.defer(this.triggerStart, this.autoStart === true ? 1000 : this.autoStart, this, [e]);
307         }
308     },
309
310     onMouseMove: function(e, target){
311         // BrowserBug: IE hack to see if button was released outside of window.
312         // Needed in IE6-9 in quirks and strictmode
313         if (this.active &amp;&amp; Ext.isIE &amp;&amp; !e.browserEvent.button) {
314             e.preventDefault();
315             this.onMouseUp(e);
316             return;
317         }
318
319         e.preventDefault();
320         var xy = e.getXY(),
321             s = this.startXY;
322
323         this.lastXY = xy;
324         if (!this.active) {
325             if (Math.max(Math.abs(s[0]-xy[0]), Math.abs(s[1]-xy[1])) &gt; this.tolerance) {
326                 this.triggerStart(e);
327             } else {
328                 return;
329             }
330         }
331
332         // Returning false from a mousemove listener deactivates 
333         if (this.fireEvent('mousemove', this, e) === false) {
334             this.onMouseUp(e);
335         } else {
336             this.onDrag(e);
337             this.fireEvent('drag', this, e);
338         }
339     },
340
341     onMouseUp: function(e) {
342         // Clear the flag which ensures onMouseOut fires only after the mouse button
343         // is lifted if the mouseout happens *during* a drag.
344         this.mouseIsDown = false;
345
346         // Remove flag from event singleton
347         delete e.dragTracked;
348
349         // If we mouseouted the el *during* the drag, the onMouseOut method will not have fired. Ensure that it gets processed.
350         if (this.mouseIsOut) {
351             this.mouseIsOut = false;
352             this.onMouseOut(e);
353         }
354         e.preventDefault();
355         this.fireEvent('mouseup', this, e);
356         this.endDrag(e);
357     },
358
359 <span id='Ext-dd-DragTracker-method-endDrag'>    /**
360 </span>     * @private
361      * Stop the drag operation, and remove active mouse listeners.
362      */
363     endDrag: function(e) {
364         var doc = Ext.getDoc(),
365         wasActive = this.active;
366
367         doc.un('mousemove', this.onMouseMove, this);
368         doc.un('mouseup', this.onMouseUp, this);
369         doc.un('selectstart', this.stopSelect, this);
370         this.clearStart();
371         this.active = false;
372         if (wasActive) {
373             this.onEnd(e);
374             this.fireEvent('dragend', this, e);
375         }
376         // Private property calculated when first required and only cached during a drag
377         delete this._constrainRegion;
378     },
379
380     triggerStart: function(e) {
381         this.clearStart();
382         this.active = true;
383         this.onStart(e);
384         this.fireEvent('dragstart', this, e);
385     },
386
387     clearStart : function() {
388         if (this.timer) {
389             clearTimeout(this.timer);
390             delete this.timer;
391         }
392     },
393
394     stopSelect : function(e) {
395         e.stopEvent();
396         return false;
397     },
398
399 <span id='Ext-dd-DragTracker-method-onBeforeStart'>    /**
400 </span>     * Template method which should be overridden by each DragTracker instance. Called when the user first clicks and
401      * holds the mouse button down. Return false to disallow the drag
402      * @param {Ext.EventObject} e The event object
403      */
404     onBeforeStart : function(e) {
405
406     },
407
408 <span id='Ext-dd-DragTracker-method-onStart'>    /**
409 </span>     * Template method which should be overridden by each DragTracker instance. Called when a drag operation starts
410      * (e.g. the user has moved the tracked element beyond the specified tolerance)
411      * @param {Ext.EventObject} e The event object
412      */
413     onStart : function(xy) {
414
415     },
416
417 <span id='Ext-dd-DragTracker-method-onDrag'>    /**
418 </span>     * Template method which should be overridden by each DragTracker instance. Called whenever a drag has been detected.
419      * @param {Ext.EventObject} e The event object
420      */
421     onDrag : function(e) {
422
423     },
424
425 <span id='Ext-dd-DragTracker-method-onEnd'>    /**
426 </span>     * Template method which should be overridden by each DragTracker instance. Called when a drag operation has been completed
427      * (e.g. the user clicked and held the mouse down, dragged the element and then released the mouse button)
428      * @param {Ext.EventObject} e The event object
429      */
430     onEnd : function(e) {
431
432     },
433
434 <span id='Ext-dd-DragTracker-method-getDragTarget'>    /**
435 </span>     * &lt;/p&gt;Returns the drag target. This is usually the DragTracker's encapsulating element.&lt;/p&gt;
436      * &lt;p&gt;If the {@link #delegate} option is being used, this may be a child element which matches the
437      * {@link #delegate} selector.&lt;/p&gt;
438      * @return {Ext.core.Element} The element currently being tracked.
439      */
440     getDragTarget : function(){
441         return this.dragTarget;
442     },
443
444 <span id='Ext-dd-DragTracker-method-getDragCt'>    /**
445 </span>     * @private
446      * @returns {Element} The DragTracker's encapsulating element.
447      */
448     getDragCt : function(){
449         return this.el;
450     },
451
452 <span id='Ext-dd-DragTracker-method-getConstrainRegion'>    /**
453 </span>     * @private
454      * Return the Region into which the drag operation is constrained.
455      * Either the XY pointer itself can be constrained, or the dragTarget element
456      * The private property _constrainRegion is cached until onMouseUp
457      */
458     getConstrainRegion: function() {
459         if (this.constrainTo) {
460             if (this.constrainTo instanceof Ext.util.Region) {
461                 return this.constrainTo;
462             }
463             if (!this._constrainRegion) {
464                 this._constrainRegion = Ext.fly(this.constrainTo).getViewRegion();
465             }
466         } else {
467             if (!this._constrainRegion) {
468                 this._constrainRegion = this.getDragCt().getViewRegion();
469             }
470         }
471         return this._constrainRegion;
472     },
473
474     getXY : function(constrain){
475         return constrain ? this.constrainModes[constrain](this, this.lastXY) : this.lastXY;
476     },
477
478 <span id='Ext-dd-DragTracker-method-getOffset'>    /**
479 </span>     * &lt;p&gt;Returns the X, Y offset of the current mouse position from the mousedown point.&lt;/p&gt;
480      * &lt;p&gt;This method may optionally constrain the real offset values, and returns a point coerced in one
481      * of two modes:&lt;/p&gt;&lt;ul&gt;
482      * &lt;li&gt;&lt;code&gt;point&lt;/code&gt;&lt;div class=&quot;sub-desc&quot;&gt;The current mouse position is coerced into the
483      * {@link #constrainRegion}, and the resulting position is returned.&lt;/div&gt;&lt;/li&gt;
484      * &lt;li&gt;&lt;code&gt;dragTarget&lt;/code&gt;&lt;div class=&quot;sub-desc&quot;&gt;The new {@link Ext.util.Region Region} of the
485      * {@link #getDragTarget dragTarget} is calculated based upon the current mouse position, and then
486      * coerced into the {@link #constrainRegion}. The returned mouse position is then adjusted by the
487      * same delta as was used to coerce the region.&lt;/div&gt;&lt;/li&gt;
488      * &lt;/ul&gt;
489      * @param constrainMode {String} Optional. If omitted the true mouse position is returned. May be passed
490      * as &lt;code&gt;'point'&lt;/code&gt; or &lt;code&gt;'dragTarget'. See above.&lt;/code&gt;.
491      * @returns {Array} The &lt;code&gt;X, Y&lt;/code&gt; offset from the mousedown point, optionally constrained.
492      */
493     getOffset : function(constrain){
494         var xy = this.getXY(constrain),
495             s = this.startXY;
496
497         return [xy[0]-s[0], xy[1]-s[1]];
498     },
499
500     constrainModes: {
501         // Constrain the passed point to within the constrain region
502         point: function(me, xy) {
503             var dr = me.dragRegion,
504                 constrainTo = me.getConstrainRegion();
505
506             // No constraint
507             if (!constrainTo) {
508                 return xy;
509             }
510
511             dr.x = dr.left = dr[0] = dr.right = xy[0];
512             dr.y = dr.top = dr[1] = dr.bottom = xy[1];
513             dr.constrainTo(constrainTo);
514
515             return [dr.left, dr.top];
516         },
517
518         // Constrain the dragTarget to within the constrain region. Return the passed xy adjusted by the same delta.
519         dragTarget: function(me, xy) {
520             var s = me.startXY,
521                 dr = me.startRegion.copy(),
522                 constrainTo = me.getConstrainRegion(),
523                 adjust;
524
525             // No constraint
526             if (!constrainTo) {
527                 return xy;
528             }
529
530             // See where the passed XY would put the dragTarget if translated by the unconstrained offset.
531             // If it overflows, we constrain the passed XY to bring the potential
532             // region back within the boundary.
533             dr.translateBy(xy[0]-s[0], xy[1]-s[1]);
534
535             // Constrain the X coordinate by however much the dragTarget overflows
536             if (dr.right &gt; constrainTo.right) {
537                 xy[0] += adjust = (constrainTo.right - dr.right);    // overflowed the right
538                 dr.left += adjust;
539             }
540             if (dr.left &lt; constrainTo.left) {
541                 xy[0] += (constrainTo.left - dr.left);      // overflowed the left
542             }
543
544             // Constrain the Y coordinate by however much the dragTarget overflows
545             if (dr.bottom &gt; constrainTo.bottom) {
546                 xy[1] += adjust = (constrainTo.bottom - dr.bottom);  // overflowed the bottom
547                 dr.top += adjust;
548             }
549             if (dr.top &lt; constrainTo.top) {
550                 xy[1] += (constrainTo.top - dr.top);        // overflowed the top
551             }
552             return xy;
553         }
554     }
555 });</pre>
556 </body>
557 </html>