- /**
- * Provides a plugin, which adds support for a scroll indicator to ScrollView instances
- *
- * @module scrollview
- * @submodule scrollview-scrollbars
- */
-
- var getClassName = Y.ClassNameManager.getClassName,
- _classNames,
-
- Transition = Y.Transition,
- NATIVE_TRANSITIONS = Transition.useNative,
- SCROLLBAR = 'scrollbar',
- SCROLLVIEW = 'scrollview',
-
- VERTICAL_NODE = "verticalNode",
- HORIZONTAL_NODE = "horizontalNode",
-
- CHILD_CACHE = "childCache",
-
- TOP = "top",
- LEFT = "left",
- WIDTH = "width",
- HEIGHT = "height",
-
- HORIZ_CACHE = "_sbh",
- VERT_CACHE = "_sbv",
-
- TRANSITION_PROPERTY = Y.ScrollView._TRANSITION.PROPERTY,
- TRANSFORM = "transform",
-
- TRANSLATE_X = "translateX(",
- TRANSLATE_Y = "translateY(",
-
- SCALE_X = "scaleX(",
- SCALE_Y = "scaleY(",
-
- SCROLL_X = "scrollX",
- SCROLL_Y = "scrollY",
-
- PX = "px",
- CLOSE = ")",
- PX_CLOSE = PX + CLOSE;
-
- /**
- * ScrollView plugin that adds scroll indicators to ScrollView instances
- *
- * @class ScrollViewScrollbars
- * @namespace Plugin
- * @extends Plugin.Base
- * @constructor
- */
- function ScrollbarsPlugin() {
- ScrollbarsPlugin.superclass.constructor.apply(this, arguments);
- }
-
- ScrollbarsPlugin.CLASS_NAMES = {
- showing: getClassName(SCROLLVIEW, SCROLLBAR, 'showing'),
- scrollbar: getClassName(SCROLLVIEW, SCROLLBAR),
- scrollbarV: getClassName(SCROLLVIEW, SCROLLBAR, 'vert'),
- scrollbarH: getClassName(SCROLLVIEW, SCROLLBAR, 'horiz'),
- scrollbarVB: getClassName(SCROLLVIEW, SCROLLBAR, 'vert', 'basic'),
- scrollbarHB: getClassName(SCROLLVIEW, SCROLLBAR, 'horiz', 'basic'),
- child: getClassName(SCROLLVIEW, 'child'),
- first: getClassName(SCROLLVIEW, 'first'),
- middle: getClassName(SCROLLVIEW, 'middle'),
- last: getClassName(SCROLLVIEW, 'last')
- };
-
- _classNames = ScrollbarsPlugin.CLASS_NAMES;
-
- /**
- * The identity of the plugin
- *
- * @property NAME
- * @type String
- * @default 'pluginScrollViewScrollbars'
- * @static
- */
- ScrollbarsPlugin.NAME = 'pluginScrollViewScrollbars';
-
- /**
- * The namespace on which the plugin will reside.
- *
- * @property NS
- * @type String
- * @default 'scrollbars'
- * @static
- */
- ScrollbarsPlugin.NS = 'scrollbars';
-
- /**
- * HTML template for the scrollbar
- *
- * @property SCROLLBAR_TEMPLATE
- * @type Object
- * @static
- */
- ScrollbarsPlugin.SCROLLBAR_TEMPLATE = [
- '<div>',
- '<span class="' + _classNames.child + ' ' + _classNames.first + '"></span>',
- '<span class="' + _classNames.child + ' ' + _classNames.middle + '"></span>',
- '<span class="' + _classNames.child + ' ' + _classNames.last + '"></span>',
- '</div>'
- ].join('');
-
- /**
- * The default attribute configuration for the plugin
- *
- * @property ATTRS
- * @type Object
- * @static
- */
- ScrollbarsPlugin.ATTRS = {
-
- /**
- * Vertical scrollbar node
- *
- * @attribute verticalNode
- * @type Y.Node
- */
- verticalNode: {
- setter: '_setNode',
- valueFn: '_defaultNode'
- },
-
- /**
- * Horizontal scrollbar node
- *
- * @attribute horizontalNode
- * @type Y.Node
- */
- horizontalNode: {
- setter: '_setNode',
- valueFn: '_defaultNode'
- }
- };
-
- Y.namespace("Plugin").ScrollViewScrollbars = Y.extend(ScrollbarsPlugin, Y.Plugin.Base, {
-
- /**
- * Designated initializer
- *
- * @method initializer
- */
- initializer: function() {
- this._host = this.get("host");
-
- this.afterHostEvent('scrollEnd', this._hostScrollEnd);
- this.afterHostMethod('scrollTo', this._update);
- this.afterHostMethod('_uiDimensionsChange', this._hostDimensionsChange);
- },
-
- /**
- * Set up the DOM nodes for the scrollbars. This method is invoked whenever the
- * host's _uiDimensionsChange fires, giving us the opportunity to remove un-needed
- * scrollbars, as well as add one if necessary.
- *
- * @method _hostDimensionsChange
- * @protected
- */
- _hostDimensionsChange: function() {
- var host = this._host,
- axis = host._cAxis,
- scrollX = host.get(SCROLL_X),
- scrollY = host.get(SCROLL_Y);
-
- this._dims = host._getScrollDims();
-
- if (axis && axis.y) {
- this._renderBar(this.get(VERTICAL_NODE), true, 'vert');
- }
-
- if (axis && axis.x) {
- this._renderBar(this.get(HORIZONTAL_NODE), true, 'horiz');
- }
-
- this._update(scrollX, scrollY);
-
- Y.later(500, this, 'flash', true);
- },
-
- /**
- * Handler for the scrollEnd event fired by the host. Default implementation flashes the scrollbar
- *
- * @method _hostScrollEnd
- * @param {EventFacade} e The event facade.
- * @protected
- */
- _hostScrollEnd : function() {
- var host = this._host,
- scrollX = host.get(SCROLL_X),
- scrollY = host.get(SCROLL_Y);
-
- this.flash();
-
- this._update(scrollX, scrollY);
- },
-
- /**
- * Adds or removes a scrollbar node from the document.
- *
- * @method _renderBar
- * @private
- * @param {Node} bar The scrollbar node
- * @param {boolean} add true, to add the node, false to remove it
- */
- _renderBar: function(bar, add) {
- var inDoc = bar.inDoc(),
- bb = this._host._bb,
- className = bar.getData("isHoriz") ? _classNames.scrollbarHB : _classNames.scrollbarVB;
-
- if (add && !inDoc) {
- bb.append(bar);
- bar.toggleClass(className, this._basic);
- this._setChildCache(bar);
- } else if(!add && inDoc) {
- bar.remove();
- this._clearChildCache(bar);
- }
- },
-
- /**
- * Caches scrollbar child element information,
- * to optimize _update implementation
- *
- * @method _setChildCache
- * @private
- * @param {Node} node
- */
- _setChildCache : function(node) {
- var c = node.get("children"),
- fc = c.item(0),
- mc = c.item(1),
- lc = c.item(2),
- size = node.getData("isHoriz") ? "offsetWidth" : "offsetHeight";
-
- node.setStyle(TRANSITION_PROPERTY, TRANSFORM);
- mc.setStyle(TRANSITION_PROPERTY, TRANSFORM);
- lc.setStyle(TRANSITION_PROPERTY, TRANSFORM);
-
- node.setData(CHILD_CACHE, {
- fc : fc,
- lc : lc,
- mc : mc,
- fcSize : fc && fc.get(size),
- lcSize : lc && lc.get(size)
- });
- },
-
- /**
- * Clears child cache
- *
- * @method _clearChildCache
- * @private
- * @param {Node} node
- */
- _clearChildCache : function(node) {
- node.clearData(CHILD_CACHE);
- },
-
- /**
- * Utility method, to move/resize either vertical or horizontal scrollbars
- *
- * @method _updateBar
- * @private
- *
- * @param {Node} scrollbar The scrollbar node.
- * @param {Number} current The current scroll position.
- * @param {Number} duration The transition duration.
- * @param {boolean} horiz true if horizontal, false if vertical.
- */
- _updateBar : function(scrollbar, current, duration, horiz) {
-
- var host = this._host,
- basic = this._basic,
-
- scrollbarSize = 0,
- scrollbarPos = 1,
-
- childCache = scrollbar.getData(CHILD_CACHE),
- lastChild = childCache.lc,
- middleChild = childCache.mc,
- firstChildSize = childCache.fcSize,
- lastChildSize = childCache.lcSize,
- middleChildSize,
- lastChildPosition,
-
- transition,
- translate,
- scale,
-
- dim,
- dimOffset,
- dimCache,
- widgetSize,
- contentSize;
-
- if (horiz) {
- dim = WIDTH;
- dimOffset = LEFT;
- dimCache = HORIZ_CACHE;
- widgetSize = this._dims.offsetWidth;
- contentSize = this._dims.scrollWidth;
- translate = TRANSLATE_X;
- scale = SCALE_X;
- current = (current !== undefined) ? current : host.get(SCROLL_X);
- } else {
- dim = HEIGHT;
- dimOffset = TOP;
- dimCache = VERT_CACHE;
- widgetSize = this._dims.offsetHeight;
- contentSize = this._dims.scrollHeight;
- translate = TRANSLATE_Y;
- scale = SCALE_Y;
- current = (current !== undefined) ? current : host.get(SCROLL_Y);
- }
-
- scrollbarSize = Math.floor(widgetSize * (widgetSize/contentSize));
- scrollbarPos = Math.floor((current/(contentSize - widgetSize)) * (widgetSize - scrollbarSize));
- if (scrollbarSize > widgetSize) {
- scrollbarSize = 1;
- }
-
- if (scrollbarPos > (widgetSize - scrollbarSize)) {
- scrollbarSize = scrollbarSize - (scrollbarPos - (widgetSize - scrollbarSize));
- } else if (scrollbarPos < 0) {
- scrollbarSize = scrollbarPos + scrollbarSize;
- scrollbarPos = 0;
- } else if (isNaN(scrollbarPos)) {
- scrollbarPos = 0;
- }
-
- middleChildSize = (scrollbarSize - (firstChildSize + lastChildSize));
-
- if (middleChildSize < 0) {
- middleChildSize = 0;
- }
-
- if (middleChildSize === 0 && scrollbarPos !== 0) {
- scrollbarPos = widgetSize - (firstChildSize + lastChildSize) - 1;
- }
-
- if (duration !== 0) {
- // Position Scrollbar
- transition = {
- duration : duration
- };
-
- if (NATIVE_TRANSITIONS) {
- transition.transform = translate + scrollbarPos + PX_CLOSE;
- } else {
- transition[dimOffset] = scrollbarPos + PX;
- }
-
- scrollbar.transition(transition);
-
- } else {
- if (NATIVE_TRANSITIONS) {
- scrollbar.setStyle(TRANSFORM, translate + scrollbarPos + PX_CLOSE);
- } else {
- scrollbar.setStyle(dimOffset, scrollbarPos + PX);
- }
- }
-
- // Resize Scrollbar Middle Child
- if (this[dimCache] !== middleChildSize) {
- this[dimCache] = middleChildSize;
-
- if (middleChildSize > 0) {
-
- if (duration !== 0) {
- transition = {
- duration : duration
- };
-
- if(NATIVE_TRANSITIONS) {
- transition.transform = scale + middleChildSize + CLOSE;
- } else {
- transition[dim] = middleChildSize + PX;
- }
-
- middleChild.transition(transition);
- } else {
- if (NATIVE_TRANSITIONS) {
- middleChild.setStyle(TRANSFORM, scale + middleChildSize + CLOSE);
- } else {
- middleChild.setStyle(dim, middleChildSize + PX);
- }
- }
-
- // Position Last Child
- if (!horiz || !basic) {
-
- lastChildPosition = scrollbarSize - lastChildSize;
-
- if(duration !== 0) {
- transition = {
- duration : duration
- };
-
- if (NATIVE_TRANSITIONS) {
- transition.transform = translate + lastChildPosition + PX_CLOSE;
- } else {
- transition[dimOffset] = lastChildPosition;
- }
-
- lastChild.transition(transition);
- } else {
- if (NATIVE_TRANSITIONS) {
- lastChild.setStyle(TRANSFORM, translate + lastChildPosition + PX_CLOSE);
- } else {
- lastChild.setStyle(dimOffset, lastChildPosition + PX);
- }
- }
- }
- }
- }
- },
-
- /**
- * AOP method, invoked after the host's _uiScrollTo method,
- * to position and resize the scroll bars
- *
- * @method _update
- * @param x {Number} The current scrollX value
- * @param y {Number} The current scrollY value
- * @param duration {Number} Number of ms of animation (optional) - used when snapping to bounds
- * @param easing {String} Optional easing equation to use during the animation, if duration is set
- * @protected
- */
- _update: function(x, y, duration) {
- var vNode = this.get(VERTICAL_NODE),
- hNode = this.get(HORIZONTAL_NODE),
- host = this._host,
- axis = host._cAxis;
-
- duration = (duration || 0)/1000;
-
- if (!this._showing) {
- this.show();
- }
-
- if (axis && axis.y && vNode && y !== null) {
- this._updateBar(vNode, y, duration, false);
- }
-
- if (axis && axis.x && hNode && x !== null) {
- this._updateBar(hNode, x, duration, true);
- }
- },
-
- /**
- * Show the scroll bar indicators
- *
- * @method show
- * @param animated {Boolean} Whether or not to animate the showing
- */
- show: function(animated) {
- this._show(true, animated);
- },
-
- /**
- * Hide the scroll bar indicators
- *
- * @method hide
- * @param animated {Boolean} Whether or not to animate the hiding
- */
- hide: function(animated) {
- this._show(false, animated);
- },
-
- /**
- * Internal hide/show implementation utility method
- *
- * @method _show
- * @param {boolean} show Whether to show or hide the scrollbar
- * @param {bolean} animated Whether or not to animate while showing/hide
- * @protected
- */
- _show : function(show, animated) {
-
- var verticalNode = this.get(VERTICAL_NODE),
- horizontalNode = this.get(HORIZONTAL_NODE),
-
- duration = (animated) ? 0.6 : 0,
- opacity = (show) ? 1 : 0,
-
- transition;
-
- this._showing = show;
-
- if (this._flashTimer) {
- this._flashTimer.cancel();
- }
-
- transition = {
- duration : duration,
- opacity : opacity
- };
-
- if (verticalNode && verticalNode._node) {
- verticalNode.transition(transition);
- }
-
- if (horizontalNode && horizontalNode._node) {
- horizontalNode.transition(transition);
- }
- },
-
- /**
- * Momentarily flash the scroll bars to indicate current scroll position
- *
- * @method flash
- */
- flash: function() {
- this.show(true);
- this._flashTimer = Y.later(800, this, 'hide', true);
- },
-
- /**
- * Setter for the verticalNode and horizontalNode attributes
- *
- * @method _setNode
- * @param node {Node} The Y.Node instance for the scrollbar
- * @param name {String} The attribute name
- * @return {Node} The Y.Node instance for the scrollbar
- *
- * @protected
- */
- _setNode: function(node, name) {
- var horiz = (name === HORIZONTAL_NODE);
- node = Y.one(node);
-
- if (node) {
- node.addClass(_classNames.scrollbar);
- node.addClass( (horiz) ? _classNames.scrollbarH : _classNames.scrollbarV );
- node.setData("isHoriz", horiz);
- }
-
- return node;
- },
-
- /**
- * Creates default node instances for scrollbars
- *
- * @method _defaultNode
- * @return {Node} The Y.Node instance for the scrollbar
- *
- * @protected
- */
- _defaultNode: function() {
- return Y.Node.create(ScrollbarsPlugin.SCROLLBAR_TEMPLATE);
- },
-
- _basic: Y.UA.ie && Y.UA.ie <= 8
-
- });
-
-