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