Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / dd / ScrollManager.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  * @class Ext.dd.ScrollManager
17  * <p>Provides automatic scrolling of overflow regions in the page during drag operations.</p>
18  * <p>The ScrollManager configs will be used as the defaults for any scroll container registered with it,
19  * but you can also override most of the configs per scroll container by adding a
20  * <tt>ddScrollConfig</tt> object to the target element that contains these properties: {@link #hthresh},
21  * {@link #vthresh}, {@link #increment} and {@link #frequency}.  Example usage:
22  * <pre><code>
23 var el = Ext.get('scroll-ct');
24 el.ddScrollConfig = {
25     vthresh: 50,
26     hthresh: -1,
27     frequency: 100,
28     increment: 200
29 };
30 Ext.dd.ScrollManager.register(el);
31 </code></pre>
32  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
33  * @singleton
34  */
35 Ext.define('Ext.dd.ScrollManager', {
36     singleton: true,
37     requires: [
38         'Ext.dd.DragDropManager'
39     ],
40
41     constructor: function() {
42         var ddm = Ext.dd.DragDropManager;
43         ddm.fireEvents = Ext.Function.createSequence(ddm.fireEvents, this.onFire, this);
44         ddm.stopDrag = Ext.Function.createSequence(ddm.stopDrag, this.onStop, this);
45         this.doScroll = Ext.Function.bind(this.doScroll, this);
46         this.ddmInstance = ddm;
47         this.els = {};
48         this.dragEl = null;
49         this.proc = {};
50     },
51
52     onStop: function(e){
53         var sm = Ext.dd.ScrollManager;
54         sm.dragEl = null;
55         sm.clearProc();
56     },
57
58     triggerRefresh: function() {
59         if (this.ddmInstance.dragCurrent) {
60             this.ddmInstance.refreshCache(this.ddmInstance.dragCurrent.groups);
61         }
62     },
63
64     doScroll: function() {
65         if (this.ddmInstance.dragCurrent) {
66             var proc   = this.proc,
67                 procEl = proc.el,
68                 ddScrollConfig = proc.el.ddScrollConfig,
69                 inc = ddScrollConfig ? ddScrollConfig.increment : this.increment;
70
71             if (!this.animate) {
72                 if (procEl.scroll(proc.dir, inc)) {
73                     this.triggerRefresh();
74                 }
75             } else {
76                 procEl.scroll(proc.dir, inc, true, this.animDuration, this.triggerRefresh);
77             }
78         }
79     },
80
81     clearProc: function() {
82         var proc = this.proc;
83         if (proc.id) {
84             clearInterval(proc.id);
85         }
86         proc.id = 0;
87         proc.el = null;
88         proc.dir = "";
89     },
90
91     startProc: function(el, dir) {
92         this.clearProc();
93         this.proc.el = el;
94         this.proc.dir = dir;
95         var group = el.ddScrollConfig ? el.ddScrollConfig.ddGroup : undefined,
96             freq  = (el.ddScrollConfig && el.ddScrollConfig.frequency)
97                   ? el.ddScrollConfig.frequency
98                   : this.frequency;
99
100         if (group === undefined || this.ddmInstance.dragCurrent.ddGroup == group) {
101             this.proc.id = setInterval(this.doScroll, freq);
102         }
103     },
104
105     onFire: function(e, isDrop) {
106         if (isDrop || !this.ddmInstance.dragCurrent) {
107             return;
108         }
109         if (!this.dragEl || this.dragEl != this.ddmInstance.dragCurrent) {
110             this.dragEl = this.ddmInstance.dragCurrent;
111             // refresh regions on drag start
112             this.refreshCache();
113         }
114
115         var xy = e.getXY(),
116             pt = e.getPoint(),
117             proc = this.proc,
118             els = this.els;
119
120         for (var id in els) {
121             var el = els[id], r = el._region;
122             var c = el.ddScrollConfig ? el.ddScrollConfig : this;
123             if (r && r.contains(pt) && el.isScrollable()) {
124                 if (r.bottom - pt.y <= c.vthresh) {
125                     if(proc.el != el){
126                         this.startProc(el, "down");
127                     }
128                     return;
129                 }else if (r.right - pt.x <= c.hthresh) {
130                     if (proc.el != el) {
131                         this.startProc(el, "left");
132                     }
133                     return;
134                 } else if(pt.y - r.top <= c.vthresh) {
135                     if (proc.el != el) {
136                         this.startProc(el, "up");
137                     }
138                     return;
139                 } else if(pt.x - r.left <= c.hthresh) {
140                     if (proc.el != el) {
141                         this.startProc(el, "right");
142                     }
143                     return;
144                 }
145             }
146         }
147         this.clearProc();
148     },
149
150     /**
151      * Registers new overflow element(s) to auto scroll
152      * @param {Mixed/Array} el The id of or the element to be scrolled or an array of either
153      */
154     register : function(el){
155         if (Ext.isArray(el)) {
156             for(var i = 0, len = el.length; i < len; i++) {
157                     this.register(el[i]);
158             }
159         } else {
160             el = Ext.get(el);
161             this.els[el.id] = el;
162         }
163     },
164
165     /**
166      * Unregisters overflow element(s) so they are no longer scrolled
167      * @param {Mixed/Array} el The id of or the element to be removed or an array of either
168      */
169     unregister : function(el){
170         if(Ext.isArray(el)){
171             for (var i = 0, len = el.length; i < len; i++) {
172                 this.unregister(el[i]);
173             }
174         }else{
175             el = Ext.get(el);
176             delete this.els[el.id];
177         }
178     },
179
180     /**
181      * The number of pixels from the top or bottom edge of a container the pointer needs to be to
182      * trigger scrolling (defaults to 25)
183      * @type Number
184      */
185     vthresh : 25,
186     /**
187      * The number of pixels from the right or left edge of a container the pointer needs to be to
188      * trigger scrolling (defaults to 25)
189      * @type Number
190      */
191     hthresh : 25,
192
193     /**
194      * The number of pixels to scroll in each scroll increment (defaults to 100)
195      * @type Number
196      */
197     increment : 100,
198
199     /**
200      * The frequency of scrolls in milliseconds (defaults to 500)
201      * @type Number
202      */
203     frequency : 500,
204
205     /**
206      * True to animate the scroll (defaults to true)
207      * @type Boolean
208      */
209     animate: true,
210
211     /**
212      * The animation duration in seconds -
213      * MUST BE less than Ext.dd.ScrollManager.frequency! (defaults to .4)
214      * @type Number
215      */
216     animDuration: 0.4,
217
218     /**
219      * The named drag drop {@link Ext.dd.DragSource#ddGroup group} to which this container belongs (defaults to undefined).
220      * If a ddGroup is specified, then container scrolling will only occur when a dragged object is in the same ddGroup.
221      * @type String
222      */
223     ddGroup: undefined,
224
225     /**
226      * Manually trigger a cache refresh.
227      */
228     refreshCache : function(){
229         var els = this.els,
230             id;
231         for (id in els) {
232             if(typeof els[id] == 'object'){ // for people extending the object prototype
233                 els[id]._region = els[id].getRegion();
234             }
235         }
236     }
237 });
238