API Docs for: 3.18.1
Show:

File: scrollview/js/scrollbars-plugin.js

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