NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / scrollview-scrollbars / scrollview-scrollbars.js
blobf22f6ff6297f7aa32fa14c805387784ce1d635e4
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('scrollview-scrollbars', function (Y, NAME) {
10 /**
11  * Provides a plugin, which adds support for a scroll indicator to ScrollView instances
12  *
13  * @module scrollview
14  * @submodule scrollview-scrollbars
15  */
17 var getClassName = Y.ClassNameManager.getClassName,
18     _classNames,
20     Transition = Y.Transition,
21     NATIVE_TRANSITIONS = Transition.useNative,
22     SCROLLBAR = 'scrollbar',
23     SCROLLVIEW = 'scrollview',
25     VERTICAL_NODE = "verticalNode",
26     HORIZONTAL_NODE = "horizontalNode",
28     CHILD_CACHE = "childCache",
30     TOP = "top",
31     LEFT = "left",
32     WIDTH = "width",
33     HEIGHT = "height",
35     HORIZ_CACHE = "_sbh",
36     VERT_CACHE = "_sbv",
38     TRANSITION_PROPERTY = Y.ScrollView._TRANSITION.PROPERTY,
39     TRANSFORM = "transform",
41     TRANSLATE_X = "translateX(",
42     TRANSLATE_Y = "translateY(",
44     SCALE_X = "scaleX(",
45     SCALE_Y = "scaleY(",
47     SCROLL_X = "scrollX",
48     SCROLL_Y = "scrollY",
50     PX = "px",
51     CLOSE = ")",
52     PX_CLOSE = PX + CLOSE;
54 /**
55  * ScrollView plugin that adds scroll indicators to ScrollView instances
56  *
57  * @class ScrollViewScrollbars
58  * @namespace Plugin
59  * @extends Plugin.Base
60  * @constructor
61  */
62 function ScrollbarsPlugin() {
63     ScrollbarsPlugin.superclass.constructor.apply(this, arguments);
66 ScrollbarsPlugin.CLASS_NAMES = {
67     showing: getClassName(SCROLLVIEW, SCROLLBAR, 'showing'),
68     scrollbar: getClassName(SCROLLVIEW, SCROLLBAR),
69     scrollbarV: getClassName(SCROLLVIEW, SCROLLBAR, 'vert'),
70     scrollbarH: getClassName(SCROLLVIEW, SCROLLBAR, 'horiz'),
71     scrollbarVB: getClassName(SCROLLVIEW, SCROLLBAR, 'vert', 'basic'),
72     scrollbarHB: getClassName(SCROLLVIEW, SCROLLBAR, 'horiz', 'basic'),
73     child: getClassName(SCROLLVIEW, 'child'),
74     first: getClassName(SCROLLVIEW, 'first'),
75     middle: getClassName(SCROLLVIEW, 'middle'),
76     last: getClassName(SCROLLVIEW, 'last')
79 _classNames = ScrollbarsPlugin.CLASS_NAMES;
81 /**
82  * The identity of the plugin
83  *
84  * @property NAME
85  * @type String
86  * @default 'pluginScrollViewScrollbars'
87  * @static
88  */
89 ScrollbarsPlugin.NAME = 'pluginScrollViewScrollbars';
91 /**
92  * The namespace on which the plugin will reside.
93  *
94  * @property NS
95  * @type String
96  * @default 'scrollbars'
97  * @static
98  */
99 ScrollbarsPlugin.NS = 'scrollbars';
102  * HTML template for the scrollbar
104  * @property SCROLLBAR_TEMPLATE
105  * @type Object
106  * @static
107  */
108 ScrollbarsPlugin.SCROLLBAR_TEMPLATE = [
109     '<div>',
110     '<span class="' + _classNames.child + ' ' + _classNames.first + '"></span>',
111     '<span class="' + _classNames.child + ' ' + _classNames.middle + '"></span>',
112     '<span class="' + _classNames.child + ' ' + _classNames.last + '"></span>',
113     '</div>'
114 ].join('');
117  * The default attribute configuration for the plugin
119  * @property ATTRS
120  * @type Object
121  * @static
122  */
123 ScrollbarsPlugin.ATTRS = {
125     /**
126      * Vertical scrollbar node
127      *
128      * @attribute verticalNode
129      * @type Y.Node
130      */
131     verticalNode: {
132         setter: '_setNode',
133         valueFn: '_defaultNode'
134     },
136     /**
137      * Horizontal scrollbar node
138      *
139      * @attribute horizontalNode
140      * @type Y.Node
141      */
142     horizontalNode: {
143         setter: '_setNode',
144         valueFn: '_defaultNode'
145     }
148 Y.namespace("Plugin").ScrollViewScrollbars = Y.extend(ScrollbarsPlugin, Y.Plugin.Base, {
150     /**
151      * Designated initializer
152      *
153      * @method initializer
154      */
155     initializer: function() {
156         this._host = this.get("host");
158         this.afterHostEvent('scrollEnd', this._hostScrollEnd);
159         this.afterHostMethod('scrollTo', this._update);
160         this.afterHostMethod('_uiDimensionsChange', this._hostDimensionsChange);
161     },
163     /**
164      * Set up the DOM nodes for the scrollbars. This method is invoked whenever the
165      * host's _uiDimensionsChange fires, giving us the opportunity to remove un-needed
166      * scrollbars, as well as add one if necessary.
167      *
168      * @method _hostDimensionsChange
169      * @protected
170      */
171     _hostDimensionsChange: function() {
172         var host = this._host,
173             axis = host._cAxis,
174             scrollX = host.get(SCROLL_X),
175             scrollY = host.get(SCROLL_Y);
177         this._dims = host._getScrollDims();
179         if (axis && axis.y) {
180             this._renderBar(this.get(VERTICAL_NODE), true, 'vert');
181         }
183         if (axis && axis.x) {
184             this._renderBar(this.get(HORIZONTAL_NODE), true, 'horiz');
185         }
187         this._update(scrollX, scrollY);
189         Y.later(500, this, 'flash', true);
190     },
192     /**
193      * Handler for the scrollEnd event fired by the host. Default implementation flashes the scrollbar
194      *
195      * @method _hostScrollEnd
196      * @param {Event.Facade} e The event facade.
197      * @protected
198      */
199     _hostScrollEnd : function() {
200         var host = this._host,
201             scrollX = host.get(SCROLL_X),
202             scrollY = host.get(SCROLL_Y);
204         this.flash();
206         this._update(scrollX, scrollY);
207     },
209     /**
210      * Adds or removes a scrollbar node from the document.
211      *
212      * @method _renderBar
213      * @private
214      * @param {Node} bar The scrollbar node
215      * @param {boolean} add true, to add the node, false to remove it
216      */
217     _renderBar: function(bar, add) {
218         var inDoc = bar.inDoc(),
219             bb = this._host._bb,
220             className = bar.getData("isHoriz") ? _classNames.scrollbarHB : _classNames.scrollbarVB;
222         if (add && !inDoc) {
223             bb.append(bar);
224             bar.toggleClass(className, this._basic);
225             this._setChildCache(bar);
226         } else if(!add && inDoc) {
227             bar.remove();
228             this._clearChildCache(bar);
229         }
230     },
232     /**
233      * Caches scrollbar child element information,
234      * to optimize _update implementation
235      *
236      * @method _setChildCache
237      * @private
238      * @param {Node} node
239      */
240     _setChildCache : function(node) {
241         var c = node.get("children"),
242             fc = c.item(0),
243             mc = c.item(1),
244             lc = c.item(2),
245             size = node.getData("isHoriz") ? "offsetWidth" : "offsetHeight";
247         node.setStyle(TRANSITION_PROPERTY, TRANSFORM);
248         mc.setStyle(TRANSITION_PROPERTY, TRANSFORM);
249         lc.setStyle(TRANSITION_PROPERTY, TRANSFORM);
251         node.setData(CHILD_CACHE, {
252             fc : fc,
253             lc : lc,
254             mc : mc,
255             fcSize : fc && fc.get(size),
256             lcSize : lc && lc.get(size)
257         });
258     },
260     /**
261      * Clears child cache
262      *
263      * @method _clearChildCache
264      * @private
265      * @param {Node} node
266      */
267     _clearChildCache : function(node) {
268         node.clearData(CHILD_CACHE);
269     },
271     /**
272      * Utility method, to move/resize either vertical or horizontal scrollbars
273      *
274      * @method _updateBar
275      * @private
276      *
277      * @param {Node} scrollbar The scrollbar node.
278      * @param {Number} current The current scroll position.
279      * @param {Number} duration The transition duration.
280      * @param {boolean} horiz true if horizontal, false if vertical.
281      */
282     _updateBar : function(scrollbar, current, duration, horiz) {
284         var host = this._host,
285             basic = this._basic,
287             scrollbarSize = 0,
288             scrollbarPos = 1,
290             childCache = scrollbar.getData(CHILD_CACHE),
291             lastChild = childCache.lc,
292             middleChild = childCache.mc,
293             firstChildSize = childCache.fcSize,
294             lastChildSize = childCache.lcSize,
295             middleChildSize,
296             lastChildPosition,
298             transition,
299             translate,
300             scale,
302             dim,
303             dimOffset,
304             dimCache,
305             widgetSize,
306             contentSize;
308         if (horiz) {
309             dim = WIDTH;
310             dimOffset = LEFT;
311             dimCache = HORIZ_CACHE;
312             widgetSize = this._dims.offsetWidth;
313             contentSize = this._dims.scrollWidth;
314             translate = TRANSLATE_X;
315             scale = SCALE_X;
316             current = (current !== undefined) ? current : host.get(SCROLL_X);
317         } else {
318             dim = HEIGHT;
319             dimOffset = TOP;
320             dimCache = VERT_CACHE;
321             widgetSize = this._dims.offsetHeight;
322             contentSize = this._dims.scrollHeight;
323             translate = TRANSLATE_Y;
324             scale = SCALE_Y;
325             current = (current !== undefined) ? current : host.get(SCROLL_Y);
326         }
328         scrollbarSize = Math.floor(widgetSize * (widgetSize/contentSize));
329         scrollbarPos = Math.floor((current/(contentSize - widgetSize)) * (widgetSize - scrollbarSize));
330         if (scrollbarSize > widgetSize) {
331             scrollbarSize = 1;
332         }
334         if (scrollbarPos > (widgetSize - scrollbarSize)) {
335             scrollbarSize = scrollbarSize - (scrollbarPos - (widgetSize - scrollbarSize));
336         } else if (scrollbarPos < 0) {
337             scrollbarSize = scrollbarPos + scrollbarSize;
338             scrollbarPos = 0;
339         } else if (isNaN(scrollbarPos)) {
340             scrollbarPos = 0;
341         }
343         middleChildSize = (scrollbarSize - (firstChildSize + lastChildSize));
345         if (middleChildSize < 0) {
346             middleChildSize = 0;
347         }
349         if (middleChildSize === 0 && scrollbarPos !== 0) {
350             scrollbarPos = widgetSize - (firstChildSize + lastChildSize) - 1;
351         }
353         if (duration !== 0) {
354             // Position Scrollbar
355             transition = {
356                 duration : duration
357             };
359             if (NATIVE_TRANSITIONS) {
360                 transition.transform = translate + scrollbarPos + PX_CLOSE;
361             } else {
362                 transition[dimOffset] = scrollbarPos + PX;
363             }
365             scrollbar.transition(transition);
367         } else {
368             if (NATIVE_TRANSITIONS) {
369                 scrollbar.setStyle(TRANSFORM, translate + scrollbarPos + PX_CLOSE);
370             } else {
371                 scrollbar.setStyle(dimOffset, scrollbarPos + PX);
372             }
373         }
375         // Resize Scrollbar Middle Child
376         if (this[dimCache] !== middleChildSize) {
377             this[dimCache] = middleChildSize;
379             if (middleChildSize > 0) {
381                 if (duration !== 0) {
382                     transition = {
383                         duration : duration
384                     };
386                     if(NATIVE_TRANSITIONS) {
387                         transition.transform = scale + middleChildSize + CLOSE;
388                     } else {
389                         transition[dim] = middleChildSize + PX;
390                     }
392                     middleChild.transition(transition);
393                 } else {
394                     if (NATIVE_TRANSITIONS) {
395                         middleChild.setStyle(TRANSFORM, scale + middleChildSize + CLOSE);
396                     } else {
397                         middleChild.setStyle(dim, middleChildSize + PX);
398                     }
399                 }
401                 // Position Last Child
402                 if (!horiz || !basic) {
404                     lastChildPosition = scrollbarSize - lastChildSize;
406                     if(duration !== 0) {
407                         transition = {
408                             duration : duration
409                         };
411                         if (NATIVE_TRANSITIONS) {
412                             transition.transform = translate + lastChildPosition + PX_CLOSE;
413                         } else {
414                             transition[dimOffset] = lastChildPosition;
415                         }
417                         lastChild.transition(transition);
418                     } else {
419                         if (NATIVE_TRANSITIONS) {
420                             lastChild.setStyle(TRANSFORM, translate + lastChildPosition + PX_CLOSE);
421                         } else {
422                             lastChild.setStyle(dimOffset, lastChildPosition + PX);
423                         }
424                     }
425                 }
426             }
427         }
428     },
430     /**
431      * AOP method, invoked after the host's _uiScrollTo method,
432      *  to position and resize the scroll bars
433      *
434      * @method _update
435      * @param x {Number} The current scrollX value
436      * @param y {Number} The current scrollY value
437      * @param duration {Number} Number of ms of animation (optional) - used when snapping to bounds
438      * @param easing {String} Optional easing equation to use during the animation, if duration is set
439      * @protected
440      */
441     _update: function(x, y, duration) {
442         var vNode = this.get(VERTICAL_NODE),
443             hNode = this.get(HORIZONTAL_NODE),
444             host = this._host,
445             axis = host._cAxis;
447         duration = (duration || 0)/1000;
449         if (!this._showing) {
450             this.show();
451         }
453         if (axis && axis.y && vNode && y !== null) {
454             this._updateBar(vNode, y, duration, false);
455         }
457         if (axis && axis.x && hNode && x !== null) {
458             this._updateBar(hNode, x, duration, true);
459         }
460     },
462     /**
463      * Show the scroll bar indicators
464      *
465      * @method show
466      * @param animated {Boolean} Whether or not to animate the showing
467      */
468     show: function(animated) {
469         this._show(true, animated);
470     },
472     /**
473      * Hide the scroll bar indicators
474      *
475      * @method hide
476      * @param animated {Boolean} Whether or not to animate the hiding
477      */
478     hide: function(animated) {
479         this._show(false, animated);
480     },
482     /**
483      * Internal hide/show implementation utility method
484      *
485      * @method _show
486      * @param {boolean} show Whether to show or hide the scrollbar
487      * @param {bolean} animated Whether or not to animate while showing/hide
488      * @protected
489      */
490     _show : function(show, animated) {
492         var verticalNode = this.get(VERTICAL_NODE),
493             horizontalNode = this.get(HORIZONTAL_NODE),
495             duration = (animated) ? 0.6 : 0,
496             opacity = (show) ? 1 : 0,
498             transition;
500         this._showing = show;
502         if (this._flashTimer) {
503             this._flashTimer.cancel();
504         }
506         transition = {
507             duration : duration,
508             opacity : opacity
509         };
511         if (verticalNode && verticalNode._node) {
512             verticalNode.transition(transition);
513         }
515         if (horizontalNode && horizontalNode._node) {
516             horizontalNode.transition(transition);
517         }
518     },
520     /**
521      * Momentarily flash the scroll bars to indicate current scroll position
522      *
523      * @method flash
524      */
525     flash: function() {
526         this.show(true);
527         this._flashTimer = Y.later(800, this, 'hide', true);
528     },
530     /**
531      * Setter for the verticalNode and horizontalNode attributes
532      *
533      * @method _setNode
534      * @param node {Node} The Y.Node instance for the scrollbar
535      * @param name {String} The attribute name
536      * @return {Node} The Y.Node instance for the scrollbar
537      *
538      * @protected
539      */
540     _setNode: function(node, name) {
541         var horiz = (name === HORIZONTAL_NODE);
542             node = Y.one(node);
544         if (node) {
545             node.addClass(_classNames.scrollbar);
546             node.addClass( (horiz) ? _classNames.scrollbarH : _classNames.scrollbarV );
547             node.setData("isHoriz", horiz);
548         }
550         return node;
551     },
553     /**
554      * Creates default node instances for scrollbars
555      *
556      * @method _defaultNode
557      * @return {Node} The Y.Node instance for the scrollbar
558      *
559      * @protected
560      */
561     _defaultNode: function() {
562         return Y.Node.create(ScrollbarsPlugin.SCROLLBAR_TEMPLATE);
563     },
565     _basic: Y.UA.ie && Y.UA.ie <= 8
570 }, '3.13.0', {"requires": ["classnamemanager", "transition", "plugin"], "skinnable": true});