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/
8 YUI.add('scrollview-scrollbars', function (Y, NAME) {
11 * Provides a plugin, which adds support for a scroll indicator to ScrollView instances
14 * @submodule scrollview-scrollbars
17 var getClassName = Y.ClassNameManager.getClassName,
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",
38 TRANSITION_PROPERTY = Y.ScrollView._TRANSITION.PROPERTY,
39 TRANSFORM = "transform",
41 TRANSLATE_X = "translateX(",
42 TRANSLATE_Y = "translateY(",
52 PX_CLOSE = PX + CLOSE;
55 * ScrollView plugin that adds scroll indicators to ScrollView instances
57 * @class ScrollViewScrollbars
59 * @extends Plugin.Base
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;
82 * The identity of the plugin
86 * @default 'pluginScrollViewScrollbars'
89 ScrollbarsPlugin.NAME = 'pluginScrollViewScrollbars';
92 * The namespace on which the plugin will reside.
96 * @default 'scrollbars'
99 ScrollbarsPlugin.NS = 'scrollbars';
102 * HTML template for the scrollbar
104 * @property SCROLLBAR_TEMPLATE
108 ScrollbarsPlugin.SCROLLBAR_TEMPLATE = [
110 '<span class="' + _classNames.child + ' ' + _classNames.first + '"></span>',
111 '<span class="' + _classNames.child + ' ' + _classNames.middle + '"></span>',
112 '<span class="' + _classNames.child + ' ' + _classNames.last + '"></span>',
117 * The default attribute configuration for the plugin
123 ScrollbarsPlugin.ATTRS = {
126 * Vertical scrollbar node
128 * @attribute verticalNode
133 valueFn: '_defaultNode'
137 * Horizontal scrollbar node
139 * @attribute horizontalNode
144 valueFn: '_defaultNode'
148 Y.namespace("Plugin").ScrollViewScrollbars = Y.extend(ScrollbarsPlugin, Y.Plugin.Base, {
151 * Designated initializer
153 * @method initializer
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);
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.
168 * @method _hostDimensionsChange
171 _hostDimensionsChange: function() {
172 var host = this._host,
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');
183 if (axis && axis.x) {
184 this._renderBar(this.get(HORIZONTAL_NODE), true, 'horiz');
187 this._update(scrollX, scrollY);
189 Y.later(500, this, 'flash', true);
193 * Handler for the scrollEnd event fired by the host. Default implementation flashes the scrollbar
195 * @method _hostScrollEnd
196 * @param {Event.Facade} e The event facade.
199 _hostScrollEnd : function() {
200 var host = this._host,
201 scrollX = host.get(SCROLL_X),
202 scrollY = host.get(SCROLL_Y);
206 this._update(scrollX, scrollY);
210 * Adds or removes a scrollbar node from the document.
214 * @param {Node} bar The scrollbar node
215 * @param {boolean} add true, to add the node, false to remove it
217 _renderBar: function(bar, add) {
218 var inDoc = bar.inDoc(),
220 className = bar.getData("isHoriz") ? _classNames.scrollbarHB : _classNames.scrollbarVB;
224 bar.toggleClass(className, this._basic);
225 this._setChildCache(bar);
226 } else if(!add && inDoc) {
228 this._clearChildCache(bar);
233 * Caches scrollbar child element information,
234 * to optimize _update implementation
236 * @method _setChildCache
240 _setChildCache : function(node) {
241 var c = node.get("children"),
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, {
255 fcSize : fc && fc.get(size),
256 lcSize : lc && lc.get(size)
263 * @method _clearChildCache
267 _clearChildCache : function(node) {
268 node.clearData(CHILD_CACHE);
272 * Utility method, to move/resize either vertical or horizontal scrollbars
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.
282 _updateBar : function(scrollbar, current, duration, horiz) {
284 var host = this._host,
290 childCache = scrollbar.getData(CHILD_CACHE),
291 lastChild = childCache.lc,
292 middleChild = childCache.mc,
293 firstChildSize = childCache.fcSize,
294 lastChildSize = childCache.lcSize,
311 dimCache = HORIZ_CACHE;
312 widgetSize = this._dims.offsetWidth;
313 contentSize = this._dims.scrollWidth;
314 translate = TRANSLATE_X;
316 current = (current !== undefined) ? current : host.get(SCROLL_X);
320 dimCache = VERT_CACHE;
321 widgetSize = this._dims.offsetHeight;
322 contentSize = this._dims.scrollHeight;
323 translate = TRANSLATE_Y;
325 current = (current !== undefined) ? current : host.get(SCROLL_Y);
328 scrollbarSize = Math.floor(widgetSize * (widgetSize/contentSize));
329 scrollbarPos = Math.floor((current/(contentSize - widgetSize)) * (widgetSize - scrollbarSize));
330 if (scrollbarSize > widgetSize) {
334 if (scrollbarPos > (widgetSize - scrollbarSize)) {
335 scrollbarSize = scrollbarSize - (scrollbarPos - (widgetSize - scrollbarSize));
336 } else if (scrollbarPos < 0) {
337 scrollbarSize = scrollbarPos + scrollbarSize;
339 } else if (isNaN(scrollbarPos)) {
343 middleChildSize = (scrollbarSize - (firstChildSize + lastChildSize));
345 if (middleChildSize < 0) {
349 if (middleChildSize === 0 && scrollbarPos !== 0) {
350 scrollbarPos = widgetSize - (firstChildSize + lastChildSize) - 1;
353 if (duration !== 0) {
354 // Position Scrollbar
359 if (NATIVE_TRANSITIONS) {
360 transition.transform = translate + scrollbarPos + PX_CLOSE;
362 transition[dimOffset] = scrollbarPos + PX;
365 scrollbar.transition(transition);
368 if (NATIVE_TRANSITIONS) {
369 scrollbar.setStyle(TRANSFORM, translate + scrollbarPos + PX_CLOSE);
371 scrollbar.setStyle(dimOffset, scrollbarPos + PX);
375 // Resize Scrollbar Middle Child
376 if (this[dimCache] !== middleChildSize) {
377 this[dimCache] = middleChildSize;
379 if (middleChildSize > 0) {
381 if (duration !== 0) {
386 if(NATIVE_TRANSITIONS) {
387 transition.transform = scale + middleChildSize + CLOSE;
389 transition[dim] = middleChildSize + PX;
392 middleChild.transition(transition);
394 if (NATIVE_TRANSITIONS) {
395 middleChild.setStyle(TRANSFORM, scale + middleChildSize + CLOSE);
397 middleChild.setStyle(dim, middleChildSize + PX);
401 // Position Last Child
402 if (!horiz || !basic) {
404 lastChildPosition = scrollbarSize - lastChildSize;
411 if (NATIVE_TRANSITIONS) {
412 transition.transform = translate + lastChildPosition + PX_CLOSE;
414 transition[dimOffset] = lastChildPosition;
417 lastChild.transition(transition);
419 if (NATIVE_TRANSITIONS) {
420 lastChild.setStyle(TRANSFORM, translate + lastChildPosition + PX_CLOSE);
422 lastChild.setStyle(dimOffset, lastChildPosition + PX);
431 * AOP method, invoked after the host's _uiScrollTo method,
432 * to position and resize the scroll bars
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
441 _update: function(x, y, duration) {
442 var vNode = this.get(VERTICAL_NODE),
443 hNode = this.get(HORIZONTAL_NODE),
447 duration = (duration || 0)/1000;
449 if (!this._showing) {
453 if (axis && axis.y && vNode && y !== null) {
454 this._updateBar(vNode, y, duration, false);
457 if (axis && axis.x && hNode && x !== null) {
458 this._updateBar(hNode, x, duration, true);
463 * Show the scroll bar indicators
466 * @param animated {Boolean} Whether or not to animate the showing
468 show: function(animated) {
469 this._show(true, animated);
473 * Hide the scroll bar indicators
476 * @param animated {Boolean} Whether or not to animate the hiding
478 hide: function(animated) {
479 this._show(false, animated);
483 * Internal hide/show implementation utility method
486 * @param {boolean} show Whether to show or hide the scrollbar
487 * @param {bolean} animated Whether or not to animate while showing/hide
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,
500 this._showing = show;
502 if (this._flashTimer) {
503 this._flashTimer.cancel();
511 if (verticalNode && verticalNode._node) {
512 verticalNode.transition(transition);
515 if (horizontalNode && horizontalNode._node) {
516 horizontalNode.transition(transition);
521 * Momentarily flash the scroll bars to indicate current scroll position
527 this._flashTimer = Y.later(800, this, 'hide', true);
531 * Setter for the verticalNode and horizontalNode attributes
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
540 _setNode: function(node, name) {
541 var horiz = (name === HORIZONTAL_NODE);
545 node.addClass(_classNames.scrollbar);
546 node.addClass( (horiz) ? _classNames.scrollbarH : _classNames.scrollbarV );
547 node.setData("isHoriz", horiz);
554 * Creates default node instances for scrollbars
556 * @method _defaultNode
557 * @return {Node} The Y.Node instance for the scrollbar
561 _defaultNode: function() {
562 return Y.Node.create(ScrollbarsPlugin.SCROLLBAR_TEMPLATE);
565 _basic: Y.UA.ie && Y.UA.ie <= 8
570 }, '3.13.0', {"requires": ["classnamemanager", "transition", "plugin"], "skinnable": true});