Upgrade to ExtJS 4.0.7 - Released 10/19/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  * Note: This class is designed to be used in "Point Mode
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 {String/HTMLElement/Ext.Element/String[]/HTMLElement[]/Ext.Element[]} el
153      * The id of or the element to be scrolled or an array of either
154      */
155     register : function(el){
156         if (Ext.isArray(el)) {
157             for(var i = 0, len = el.length; i < len; i++) {
158                     this.register(el[i]);
159             }
160         } else {
161             el = Ext.get(el);
162             this.els[el.id] = el;
163         }
164     },
165
166     /**
167      * Unregisters overflow element(s) so they are no longer scrolled
168      * @param {String/HTMLElement/Ext.Element/String[]/HTMLElement[]/Ext.Element[]} el
169      * The id of or the element to be removed or an array of either
170      */
171     unregister : function(el){
172         if(Ext.isArray(el)){
173             for (var i = 0, len = el.length; i < len; i++) {
174                 this.unregister(el[i]);
175             }
176         }else{
177             el = Ext.get(el);
178             delete this.els[el.id];
179         }
180     },
181
182     /**
183      * The number of pixels from the top or bottom edge of a container the pointer needs to be to
184      * trigger scrolling
185      * @type Number
186      */
187     vthresh : 25,
188     /**
189      * The number of pixels from the right or left edge of a container the pointer needs to be to
190      * trigger scrolling
191      * @type Number
192      */
193     hthresh : 25,
194
195     /**
196      * The number of pixels to scroll in each scroll increment
197      * @type Number
198      */
199     increment : 100,
200
201     /**
202      * The frequency of scrolls in milliseconds
203      * @type Number
204      */
205     frequency : 500,
206
207     /**
208      * True to animate the scroll
209      * @type Boolean
210      */
211     animate: true,
212
213     /**
214      * The animation duration in seconds - MUST BE less than Ext.dd.ScrollManager.frequency!
215      * @type Number
216      */
217     animDuration: 0.4,
218
219     /**
220      * The named drag drop {@link Ext.dd.DragSource#ddGroup group} to which this container belongs.
221      * If a ddGroup is specified, then container scrolling will only occur when a dragged object is in the same ddGroup.
222      * @type String
223      */
224     ddGroup: undefined,
225
226     /**
227      * Manually trigger a cache refresh.
228      */
229     refreshCache : function(){
230         var els = this.els,
231             id;
232         for (id in els) {
233             if(typeof els[id] == 'object'){ // for people extending the object prototype
234                 els[id]._region = els[id].getRegion();
235             }
236         }
237     }
238 });
239