MDL-32843 import YUI 3.5.1
[moodle.git] / lib / yui / 3.5.1 / build / dd-scroll / dd-scroll.js
blobd5245486c0e25b6c205cab0bd68125fda31dd54e
1 /*
2 YUI 3.5.1 (build 22)
3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
7 YUI.add('dd-scroll', function(Y) {
10     /**
11      * Base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
12      * This class should not be called on it's own, it's designed to be a plugin.
13      * @module dd
14      * @submodule dd-scroll
15      */
16     /**
17      * Base scroller class used to create the Plugin.DDNodeScroll and Plugin.DDWinScroll.
18      * This class should not be called on it's own, it's designed to be a plugin.
19      * @class Scroll
20      * @extends Base
21      * @namespace DD
22      * @constructor
23      */
25     var S = function() {
26         S.superclass.constructor.apply(this, arguments);
28     },
29     WS, NS,
30     HOST = 'host',
31     BUFFER = 'buffer',
32     PARENT_SCROLL = 'parentScroll',
33     WINDOW_SCROLL = 'windowScroll',
34     SCROLL_TOP = 'scrollTop',
35     SCROLL_LEFT = 'scrollLeft',
36     OFFSET_WIDTH = 'offsetWidth',
37     OFFSET_HEIGHT = 'offsetHeight';
40     S.ATTRS = {
41         /**
42         * @attribute parentScroll
43         * @description Internal config option to hold the node that we are scrolling. Should not be set by the developer.
44         * @type Node
45         */
46         parentScroll: {
47             value: false,
48             setter: function(node) {
49                 if (node) {
50                     return node;
51                 }
52                 return false;
53             }
54         },
55         /**
56         * @attribute buffer
57         * @description The number of pixels from the edge of the screen to turn on scrolling. Default: 30
58         * @type Number
59         */
60         buffer: {
61             value: 30,
62             validator: Y.Lang.isNumber
63         },
64         /**
65         * @attribute scrollDelay
66         * @description The number of milliseconds delay to pass to the auto scroller. Default: 235
67         * @type Number
68         */
69         scrollDelay: {
70             value: 235,
71             validator: Y.Lang.isNumber
72         },
73         /**
74         * @attribute host
75         * @description The host we are plugged into.
76         * @type Object
77         */
78         host: {
79             value: null
80         },
81         /**
82         * @attribute windowScroll
83         * @description Turn on window scroll support, default: false
84         * @type Boolean
85         */
86         windowScroll: {
87             value: false,
88             validator: Y.Lang.isBoolean
89         },
90         /**
91         * @attribute vertical
92         * @description Allow vertical scrolling, default: true.
93         * @type Boolean
94         */
95         vertical: {
96             value: true,
97             validator: Y.Lang.isBoolean
98         },
99         /**
100         * @attribute horizontal
101         * @description Allow horizontal scrolling, default: true.
102         * @type Boolean
103         */
104         horizontal: {
105             value: true,
106             validator: Y.Lang.isBoolean
107         }
108     };
110     Y.extend(S, Y.Base, {
111         /**
112         * @private
113         * @property _scrolling
114         * @description Tells if we are actively scrolling or not.
115         * @type Boolean
116         */
117         _scrolling: null,
118         /**
119         * @private
120         * @property _vpRegionCache
121         * @description Cache of the Viewport dims.
122         * @type Object
123         */
124         _vpRegionCache: null,
125         /**
126         * @private
127         * @property _dimCache
128         * @description Cache of the dragNode dims.
129         * @type Object
130         */
131         _dimCache: null,
132         /**
133         * @private
134         * @property _scrollTimer
135         * @description Holder for the Timer object returned from Y.later.
136         * @type {Y.later}
137         */
138         _scrollTimer: null,
139         /**
140         * @private
141         * @method _getVPRegion
142         * @description Sets the _vpRegionCache property with an Object containing the dims from the viewport.
143         */        
144         _getVPRegion: function() {
145             var r = {},
146                 n = this.get(PARENT_SCROLL),
147             b = this.get(BUFFER),
148             ws = this.get(WINDOW_SCROLL),
149             xy = ((ws) ? [] : n.getXY()),
150             w = ((ws) ? 'winWidth' : OFFSET_WIDTH),
151             h = ((ws) ? 'winHeight' : OFFSET_HEIGHT),
152             t = ((ws) ? n.get(SCROLL_TOP) : xy[1]),
153             l = ((ws) ? n.get(SCROLL_LEFT) : xy[0]);
155             r = {
156                 top: t + b,
157                 right: (n.get(w) + l) - b,
158                 bottom: (n.get(h) + t) - b,
159                 left: l + b
160             };
161             this._vpRegionCache = r;
162             return r;
163         },
164         initializer: function() {
165             var h = this.get(HOST);
166             h.after('drag:start', Y.bind(this.start, this));
167             h.after('drag:end', Y.bind(this.end, this));
168             h.on('drag:align', Y.bind(this.align, this));
170             //TODO - This doesn't work yet??
171             Y.one('win').on('scroll', Y.bind(function() {
172                 this._vpRegionCache = null;
173             }, this));
174         },
175         /**
176         * @private
177         * @method _checkWinScroll
178         * @description Check to see if we need to fire the scroll timer. If scroll timer is running this will scroll the window.
179         * @param {Boolean} move Should we move the window. From Y.later
180         */        
181         _checkWinScroll: function(move) {
182             var r = this._getVPRegion(),
183                 ho = this.get(HOST),
184                 ws = this.get(WINDOW_SCROLL),
185                 xy = ho.lastXY,
186                 scroll = false,
187                 b = this.get(BUFFER),
188                 win = this.get(PARENT_SCROLL),
189                 sTop = win.get(SCROLL_TOP),
190                 sLeft = win.get(SCROLL_LEFT),
191                 w = this._dimCache.w,
192                 h = this._dimCache.h,
193                 bottom = xy[1] + h,
194                 top = xy[1],
195                 right = xy[0] + w,
196                 left = xy[0],
197                 nt = top,
198                 nl = left,
199                 st = sTop,
200                 sl = sLeft;
201             
202             if (this.get('horizontal')) {
203                 if (left <= r.left) {
204                     scroll = true;
205                     nl = xy[0] - ((ws) ? b : 0);
206                     sl = sLeft - b;
207                 }
208                 if (right >= r.right) {
209                     scroll = true;
210                     nl = xy[0] + ((ws) ? b : 0);
211                     sl = sLeft + b;
212                 }
213             }
214             if (this.get('vertical')) {
215                 if (bottom >= r.bottom) {
216                     scroll = true;
217                     nt = xy[1] + ((ws) ? b : 0);
218                     st = sTop + b;
220                 }
221                 if (top <= r.top) {
222                     scroll = true;
223                     nt = xy[1] - ((ws) ? b : 0);
224                     st = sTop - b;
225                 }
226             }
228             if (st < 0) {
229                 st = 0;
230                 nt = xy[1];
231             }
233             if (sl < 0) {
234                 sl = 0;
235                 nl = xy[0];
236             }
238             if (nt < 0) {
239                 nt = xy[1];
240             }
241             if (nl < 0) {
242                 nl = xy[0];
243             }
244             if (move) {
245                 ho.actXY = [nl, nt];
246                 ho._moveNode({ node: win, top: st, left: sl});
247                 if (!st && !sl) {
248                     this._cancelScroll();
249                 }
250             } else {
251                 if (scroll) {
252                     this._initScroll();
253                 } else {
254                     this._cancelScroll();
255                 }
256             }
257         },
258         /**
259         * @private
260         * @method _initScroll
261         * @description Cancel a previous scroll timer and init a new one.
262         */        
263         _initScroll: function() {
264             this._cancelScroll();
265             this._scrollTimer = Y.Lang.later(this.get('scrollDelay'), this, this._checkWinScroll, [true], true);
267         },
268         /**
269         * @private
270         * @method _cancelScroll
271         * @description Cancel a currently running scroll timer.
272         */        
273         _cancelScroll: function() {
274             this._scrolling = false;
275             if (this._scrollTimer) {
276                 this._scrollTimer.cancel();
277                 delete this._scrollTimer;
278             }
279         },
280         /**
281         * @method align
282         * @description Called from the drag:align event to determine if we need to scroll.
283         */        
284         align: function(e) {
285             if (this._scrolling) {
286                 this._cancelScroll();
287                 e.preventDefault();
288             }
289             if (!this._scrolling) {
290                 this._checkWinScroll();
291             }
292         },
293         /**
294         * @private
295         * @method _setDimCache
296         * @description Set the cache of the dragNode dims.
297         */        
298         _setDimCache: function() {
299             var node = this.get(HOST).get('dragNode');
300             this._dimCache = {
301                 h: node.get(OFFSET_HEIGHT),
302                 w: node.get(OFFSET_WIDTH)
303             };
304         },
305         /**
306         * @method start
307         * @description Called from the drag:start event
308         */
309         start: function() {
310             this._setDimCache();
311         },
312         /**
313         * @method end
314         * @description Called from the drag:end event
315         */
316         end: function(xy) {
317             this._dimCache = null;
318             this._cancelScroll();
319         }
320     });
322     Y.namespace('Plugin');
324     
325     /**
326      * Extends the Scroll class to make the window scroll while dragging.
327      * @class DDWindowScroll
328      * @extends Scroll
329      * @namespace Plugin
330      * @constructor
331      */
332     WS = function() {
333         WS.superclass.constructor.apply(this, arguments);
334     };
335     WS.ATTRS = Y.merge(S.ATTRS, {
336         /**
337         * @attribute windowScroll
338         * @description Turn on window scroll support, default: true
339         * @type Boolean
340         */
341         windowScroll: {
342             value: true,
343             setter: function(scroll) {
344                 if (scroll) {
345                     this.set(PARENT_SCROLL, Y.one('win'));
346                 }
347                 return scroll;
348             }
349         }
350     });
351     Y.extend(WS, S, {
352         //Shouldn't have to do this..
353         initializer: function() {
354             this.set('windowScroll', this.get('windowScroll'));
355         }
356     });
357     /**
358     * @property NS
359     * @default winscroll
360     * @readonly
361     * @protected
362     * @static
363     * @description The Scroll instance will be placed on the Drag instance under the winscroll namespace.
364     * @type {String}
365     */
366     WS.NAME = WS.NS = 'winscroll';
367     Y.Plugin.DDWinScroll = WS;
368     
370     /**
371      * Extends the Scroll class to make a parent node scroll while dragging.
372      * @class DDNodeScroll
373      * @extends Scroll
374      * @namespace Plugin
375      * @constructor
376      */
377     NS = function() {
378         NS.superclass.constructor.apply(this, arguments);
380     };
381     NS.ATTRS = Y.merge(S.ATTRS, {
382         /**
383         * @attribute node
384         * @description The node we want to scroll. Used to set the internal parentScroll attribute.
385         * @type Node
386         */
387         node: {
388             value: false,
389             setter: function(node) {
390                 var n = Y.one(node);
391                 if (!n) {
392                     if (node !== false) {
393                         Y.error('DDNodeScroll: Invalid Node Given: ' + node);
394                     }
395                 } else {
396                     this.set(PARENT_SCROLL, n);
397                 }
398                 return n;
399             }
400         }
401     });
402     Y.extend(NS, S, {
403         //Shouldn't have to do this..
404         initializer: function() {
405             this.set('node', this.get('node'));
406         }
407     });
408     /**
409     * @property NS
410     * @default nodescroll
411     * @readonly
412     * @protected
413     * @static
414     * @description The NodeScroll instance will be placed on the Drag instance under the nodescroll namespace.
415     * @type {String}
416     */
417     NS.NAME = NS.NS = 'nodescroll';
418     Y.Plugin.DDNodeScroll = NS;
420     Y.DD.Scroll = S;    
425 }, '3.5.1' ,{skinnable:false, optional:['dd-proxy'], requires:['dd-drag']});