/hippo/src/main/webapp/yui/carousel/carousel.js

http://hdbc.googlecode.com/ · JavaScript · 2065 lines · 846 code · 269 blank · 950 comment · 176 complexity · 2e960d2d73124b5fd8b077b5c0d2c0ab MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. Copyright (c) 2009, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.7.0
  6. */
  7. /**
  8. * The Carousel module provides a widget for browsing among a set of like
  9. * objects represented pictorially.
  10. *
  11. * @module carousel
  12. * @requires yahoo, dom, event, element
  13. * @optional animation
  14. * @namespace YAHOO.widget
  15. * @title Carousel Widget
  16. * @beta
  17. */
  18. (function () {
  19. var WidgetName; // forward declaration
  20. /**
  21. * The Carousel widget.
  22. *
  23. * @class Carousel
  24. * @extends YAHOO.util.Element
  25. * @constructor
  26. * @param el {HTMLElement | String} The HTML element that represents the
  27. * the container that houses the Carousel.
  28. * @param cfg {Object} (optional) The configuration values
  29. */
  30. YAHOO.widget.Carousel = function (el, cfg) {
  31. YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg);
  32. };
  33. /*
  34. * Private variables of the Carousel component
  35. */
  36. /* Some abbreviations to avoid lengthy typing and lookups. */
  37. var Carousel = YAHOO.widget.Carousel,
  38. Dom = YAHOO.util.Dom,
  39. Event = YAHOO.util.Event,
  40. JS = YAHOO.lang;
  41. /**
  42. * The widget name.
  43. * @private
  44. * @static
  45. */
  46. WidgetName = "Carousel";
  47. /**
  48. * The internal table of Carousel instances.
  49. * @private
  50. * @static
  51. */
  52. var instances = {},
  53. /*
  54. * Custom events of the Carousel component
  55. */
  56. /**
  57. * @event afterScroll
  58. * @description Fires when the Carousel has scrolled to the previous or
  59. * next page. Passes back the index of the first and last visible items in
  60. * the Carousel. See
  61. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  62. * for more information on listening for this event.
  63. * @type YAHOO.util.CustomEvent
  64. */
  65. afterScrollEvent = "afterScroll",
  66. /**
  67. * @event allItemsRemovedEvent
  68. * @description Fires when all items have been removed from the Carousel.
  69. * See
  70. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  71. * for more information on listening for this event.
  72. * @type YAHOO.util.CustomEvent
  73. */
  74. allItemsRemovedEvent = "allItemsRemoved",
  75. /**
  76. * @event beforeHide
  77. * @description Fires before the Carousel is hidden. See
  78. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  79. * for more information on listening for this event.
  80. * @type YAHOO.util.CustomEvent
  81. */
  82. beforeHideEvent = "beforeHide",
  83. /**
  84. * @event beforePageChange
  85. * @description Fires when the Carousel is about to scroll to the previous
  86. * or next page. Passes back the page number of the current page. Note
  87. * that the first page number is zero. See
  88. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  89. * for more information on listening for this event.
  90. * @type YAHOO.util.CustomEvent
  91. */
  92. beforePageChangeEvent = "beforePageChange",
  93. /**
  94. * @event beforeScroll
  95. * @description Fires when the Carousel is about to scroll to the previous
  96. * or next page. Passes back the index of the first and last visible items
  97. * in the Carousel and the direction (backward/forward) of the scroll. See
  98. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  99. * for more information on listening for this event.
  100. * @type YAHOO.util.CustomEvent
  101. */
  102. beforeScrollEvent = "beforeScroll",
  103. /**
  104. * @event beforeShow
  105. * @description Fires when the Carousel is about to be shown. See
  106. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  107. * for more information on listening for this event.
  108. * @type YAHOO.util.CustomEvent
  109. */
  110. beforeShowEvent = "beforeShow",
  111. /**
  112. * @event blur
  113. * @description Fires when the Carousel loses focus. See
  114. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  115. * for more information on listening for this event.
  116. * @type YAHOO.util.CustomEvent
  117. */
  118. blurEvent = "blur",
  119. /**
  120. * @event focus
  121. * @description Fires when the Carousel gains focus. See
  122. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  123. * for more information on listening for this event.
  124. * @type YAHOO.util.CustomEvent
  125. */
  126. focusEvent = "focus",
  127. /**
  128. * @event hide
  129. * @description Fires when the Carousel is hidden. See
  130. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  131. * for more information on listening for this event.
  132. * @type YAHOO.util.CustomEvent
  133. */
  134. hideEvent = "hide",
  135. /**
  136. * @event itemAdded
  137. * @description Fires when an item has been added to the Carousel. Passes
  138. * back the content of the item that would be added, the index at which the
  139. * item would be added, and the event itself. See
  140. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  141. * for more information on listening for this event.
  142. * @type YAHOO.util.CustomEvent
  143. */
  144. itemAddedEvent = "itemAdded",
  145. /**
  146. * @event itemRemoved
  147. * @description Fires when an item has been removed from the Carousel.
  148. * Passes back the content of the item that would be removed, the index
  149. * from which the item would be removed, and the event itself. See
  150. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  151. * for more information on listening for this event.
  152. * @type YAHOO.util.CustomEvent
  153. */
  154. itemRemovedEvent = "itemRemoved",
  155. /**
  156. * @event itemSelected
  157. * @description Fires when an item has been selected in the Carousel.
  158. * Passes back the index of the selected item in the Carousel. Note, that
  159. * the index begins from zero. See
  160. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  161. * for more information on listening for this event.
  162. * @type YAHOO.util.CustomEvent
  163. */
  164. itemSelectedEvent = "itemSelected",
  165. /**
  166. * @event loadItems
  167. * @description Fires when the Carousel needs more items to be loaded for
  168. * displaying them. Passes back the first and last visible items in the
  169. * Carousel, and the number of items needed to be loaded. See
  170. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  171. * for more information on listening for this event.
  172. * @type YAHOO.util.CustomEvent
  173. */
  174. loadItemsEvent = "loadItems",
  175. /**
  176. * @event navigationStateChange
  177. * @description Fires when the state of either one of the navigation
  178. * buttons are changed from enabled to disabled or vice versa. Passes back
  179. * the state (true/false) of the previous and next buttons. The value true
  180. * signifies the button is enabled, false signifies disabled. See
  181. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  182. * for more information on listening for this event.
  183. * @type YAHOO.util.CustomEvent
  184. */
  185. navigationStateChangeEvent = "navigationStateChange",
  186. /**
  187. * @event pageChange
  188. * @description Fires after the Carousel has scrolled to the previous or
  189. * next page. Passes back the page number of the current page. Note
  190. * that the first page number is zero. See
  191. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  192. * for more information on listening for this event.
  193. * @type YAHOO.util.CustomEvent
  194. */
  195. pageChangeEvent = "pageChange",
  196. /*
  197. * Internal event.
  198. * @event render
  199. * @description Fires when the Carousel is rendered. See
  200. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  201. * for more information on listening for this event.
  202. * @type YAHOO.util.CustomEvent
  203. */
  204. renderEvent = "render",
  205. /**
  206. * @event show
  207. * @description Fires when the Carousel is shown. See
  208. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  209. * for more information on listening for this event.
  210. * @type YAHOO.util.CustomEvent
  211. */
  212. showEvent = "show",
  213. /**
  214. * @event startAutoPlay
  215. * @description Fires when the auto play has started in the Carousel. See
  216. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  217. * for more information on listening for this event.
  218. * @type YAHOO.util.CustomEvent
  219. */
  220. startAutoPlayEvent = "startAutoPlay",
  221. /**
  222. * @event stopAutoPlay
  223. * @description Fires when the auto play has been stopped in the Carousel.
  224. * See
  225. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  226. * for more information on listening for this event.
  227. * @type YAHOO.util.CustomEvent
  228. */
  229. stopAutoPlayEvent = "stopAutoPlay",
  230. /*
  231. * Internal event.
  232. * @event uiUpdateEvent
  233. * @description Fires when the UI has been updated.
  234. * See
  235. * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  236. * for more information on listening for this event.
  237. * @type YAHOO.util.CustomEvent
  238. */
  239. uiUpdateEvent = "uiUpdate";
  240. /*
  241. * Private helper functions used by the Carousel component
  242. */
  243. /**
  244. * Create an element, set its class name and optionally install the element
  245. * to its parent.
  246. * @method createElement
  247. * @param el {String} The element to be created
  248. * @param attrs {Object} Configuration of parent, class and id attributes.
  249. * If the content is specified, it is inserted after creation of the
  250. * element. The content can also be an HTML element in which case it would
  251. * be appended as a child node of the created element.
  252. * @private
  253. */
  254. function createElement(el, attrs) {
  255. var newEl = document.createElement(el);
  256. attrs = attrs || {};
  257. if (attrs.className) {
  258. Dom.addClass(newEl, attrs.className);
  259. }
  260. if (attrs.parent) {
  261. attrs.parent.appendChild(newEl);
  262. }
  263. if (attrs.id) {
  264. newEl.setAttribute("id", attrs.id);
  265. }
  266. if (attrs.content) {
  267. if (attrs.content.nodeName) {
  268. newEl.appendChild(attrs.content);
  269. } else {
  270. newEl.innerHTML = attrs.content;
  271. }
  272. }
  273. return newEl;
  274. }
  275. /**
  276. * Get the computed style of an element.
  277. *
  278. * @method getStyle
  279. * @param el {HTMLElement} The element for which the style needs to be
  280. * returned.
  281. * @param style {String} The style attribute
  282. * @param type {String} "int", "float", etc. (defaults to int)
  283. * @private
  284. */
  285. function getStyle(el, style, type) {
  286. var value;
  287. if (!el) {
  288. return 0;
  289. }
  290. function getStyleIntVal(el, style) {
  291. var val;
  292. /*
  293. * XXX: Safari calculates incorrect marginRight for an element
  294. * which has its parent element style set to overflow: hidden
  295. * https://bugs.webkit.org/show_bug.cgi?id=13343
  296. * Let us assume marginLeft == marginRight
  297. */
  298. if (style == "marginRight" && YAHOO.env.ua.webkit) {
  299. val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
  300. } else {
  301. val = parseInt(Dom.getStyle(el, style), 10);
  302. }
  303. return JS.isNumber(val) ? val : 0;
  304. }
  305. function getStyleFloatVal(el, style) {
  306. var val;
  307. /*
  308. * XXX: Safari calculates incorrect marginRight for an element
  309. * which has its parent element style set to overflow: hidden
  310. * https://bugs.webkit.org/show_bug.cgi?id=13343
  311. * Let us assume marginLeft == marginRight
  312. */
  313. if (style == "marginRight" && YAHOO.env.ua.webkit) {
  314. val = parseFloat(Dom.getStyle(el, "marginLeft"));
  315. } else {
  316. val = parseFloat(Dom.getStyle(el, style));
  317. }
  318. return JS.isNumber(val) ? val : 0;
  319. }
  320. if (typeof type == "undefined") {
  321. type = "int";
  322. }
  323. switch (style) {
  324. case "height":
  325. value = el.offsetHeight;
  326. if (value > 0) {
  327. value += getStyleIntVal(el, "marginTop") +
  328. getStyleIntVal(el, "marginBottom");
  329. } else {
  330. value = getStyleFloatVal(el, "height") +
  331. getStyleIntVal(el, "marginTop") +
  332. getStyleIntVal(el, "marginBottom") +
  333. getStyleIntVal(el, "borderTopWidth") +
  334. getStyleIntVal(el, "borderBottomWidth") +
  335. getStyleIntVal(el, "paddingTop") +
  336. getStyleIntVal(el, "paddingBottom");
  337. }
  338. break;
  339. case "width":
  340. value = el.offsetWidth;
  341. if (value > 0) {
  342. value += getStyleIntVal(el, "marginLeft") +
  343. getStyleIntVal(el, "marginRight");
  344. } else {
  345. value = getStyleFloatVal(el, "width") +
  346. getStyleIntVal(el, "marginLeft") +
  347. getStyleIntVal(el, "marginRight") +
  348. getStyleIntVal(el, "borderLeftWidth") +
  349. getStyleIntVal(el, "borderRightWidth") +
  350. getStyleIntVal(el, "paddingLeft") +
  351. getStyleIntVal(el, "paddingRight");
  352. }
  353. break;
  354. default:
  355. if (type == "int") {
  356. value = getStyleIntVal(el, style);
  357. } else if (type == "float") {
  358. value = getStyleFloatVal(el, style);
  359. } else {
  360. value = Dom.getStyle(el, style);
  361. }
  362. break;
  363. }
  364. return value;
  365. }
  366. /**
  367. * Compute and return the height or width of a single Carousel item
  368. * depending upon the orientation.
  369. *
  370. * @method getCarouselItemSize
  371. * @param which {String} "height" or "width" to be returned. If this is
  372. * passed explicitly, the calculated size is not cached.
  373. * @private
  374. */
  375. function getCarouselItemSize(which) {
  376. var carousel = this,
  377. child,
  378. size = 0,
  379. vertical = false;
  380. if (carousel._itemsTable.numItems === 0) {
  381. return 0;
  382. }
  383. if (typeof which == "undefined") {
  384. if (carousel._itemsTable.size > 0) {
  385. return carousel._itemsTable.size;
  386. }
  387. }
  388. if (JS.isUndefined(carousel._itemsTable.items[0])) {
  389. return 0;
  390. }
  391. child = Dom.get(carousel._itemsTable.items[0].id);
  392. if (typeof which == "undefined") {
  393. vertical = carousel.get("isVertical");
  394. } else {
  395. vertical = which == "height";
  396. }
  397. if (vertical) {
  398. size = getStyle(child, "height");
  399. } else {
  400. size = getStyle(child, "width");
  401. }
  402. if (typeof which == "undefined") {
  403. carousel._itemsTable.size = size; // save the size for later
  404. }
  405. return size;
  406. }
  407. /**
  408. * Return the index of the first item in the view port for displaying item
  409. * in "pos".
  410. *
  411. * @method getFirstVisibleForPosition
  412. * @param pos {Number} The position of the item to be displayed
  413. * @private
  414. */
  415. function getFirstVisibleForPosition(pos) {
  416. var num = this.get("numVisible");
  417. return Math.floor(pos / num) * num;
  418. }
  419. /**
  420. * Return the scrolling offset size given the number of elements to
  421. * scroll.
  422. *
  423. * @method getScrollOffset
  424. * @param delta {Number} The delta number of elements to scroll by.
  425. * @private
  426. */
  427. function getScrollOffset(delta) {
  428. var itemSize = 0,
  429. size = 0;
  430. itemSize = getCarouselItemSize.call(this);
  431. size = itemSize * delta;
  432. // XXX: really, when the orientation is vertical, the scrolling
  433. // is not exactly the number of elements into element size.
  434. if (this.get("isVertical")) {
  435. size -= delta;
  436. }
  437. return size;
  438. }
  439. /**
  440. * Scroll the Carousel by a page backward.
  441. *
  442. * @method scrollPageBackward
  443. * @param {Event} ev The event object
  444. * @param {Object} obj The context object
  445. * @private
  446. */
  447. function scrollPageBackward(ev, obj) {
  448. obj.scrollPageBackward();
  449. Event.preventDefault(ev);
  450. }
  451. /**
  452. * Scroll the Carousel by a page forward.
  453. *
  454. * @method scrollPageForward
  455. * @param {Event} ev The event object
  456. * @param {Object} obj The context object
  457. * @private
  458. */
  459. function scrollPageForward(ev, obj) {
  460. obj.scrollPageForward();
  461. Event.preventDefault(ev);
  462. }
  463. /**
  464. * Set the selected item.
  465. *
  466. * @method setItemSelection
  467. * @param {Number} newpos The index of the new position
  468. * @param {Number} oldpos The index of the previous position
  469. * @private
  470. */
  471. function setItemSelection(newpos, oldpos) {
  472. var carousel = this,
  473. cssClass = carousel.CLASSES,
  474. el,
  475. firstItem = carousel._firstItem,
  476. isCircular = carousel.get("isCircular"),
  477. numItems = carousel.get("numItems"),
  478. numVisible = carousel.get("numVisible"),
  479. position = oldpos,
  480. sentinel = firstItem + numVisible - 1;
  481. if (position >= 0 && position < numItems) {
  482. if (!JS.isUndefined(carousel._itemsTable.items[position])) {
  483. el = Dom.get(carousel._itemsTable.items[position].id);
  484. if (el) {
  485. Dom.removeClass(el, cssClass.SELECTED_ITEM);
  486. }
  487. }
  488. }
  489. if (JS.isNumber(newpos)) {
  490. newpos = parseInt(newpos, 10);
  491. newpos = JS.isNumber(newpos) ? newpos : 0;
  492. } else {
  493. newpos = firstItem;
  494. }
  495. if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
  496. newpos = getFirstVisibleForPosition.call(carousel, newpos);
  497. carousel.scrollTo(newpos); // still loading the item
  498. }
  499. if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
  500. el = Dom.get(carousel._itemsTable.items[newpos].id);
  501. if (el) {
  502. Dom.addClass(el, cssClass.SELECTED_ITEM);
  503. }
  504. }
  505. if (newpos < firstItem || newpos > sentinel) { // out of focus
  506. newpos = getFirstVisibleForPosition.call(carousel, newpos);
  507. carousel.scrollTo(newpos);
  508. }
  509. }
  510. /**
  511. * Fire custom events for enabling/disabling navigation elements.
  512. *
  513. * @method syncNavigation
  514. * @private
  515. */
  516. function syncNavigation() {
  517. var attach = false,
  518. carousel = this,
  519. cssClass = carousel.CLASSES,
  520. i,
  521. navigation,
  522. sentinel;
  523. // Don't do anything if the Carousel is not rendered
  524. if (!carousel._hasRendered) {
  525. return;
  526. }
  527. navigation = carousel.get("navigation");
  528. sentinel = carousel._firstItem + carousel.get("numVisible");
  529. if (navigation.prev) {
  530. if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
  531. if (carousel.get("numItems") === 0 ||
  532. !carousel.get("isCircular")) {
  533. Event.removeListener(navigation.prev, "click",
  534. scrollPageBackward);
  535. Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
  536. for (i = 0; i < carousel._navBtns.prev.length; i++) {
  537. carousel._navBtns.prev[i].setAttribute("disabled",
  538. "true");
  539. }
  540. carousel._prevEnabled = false;
  541. } else {
  542. attach = !carousel._prevEnabled;
  543. }
  544. } else {
  545. attach = !carousel._prevEnabled;
  546. }
  547. if (attach) {
  548. Event.on(navigation.prev, "click", scrollPageBackward,
  549. carousel);
  550. Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
  551. for (i = 0; i < carousel._navBtns.prev.length; i++) {
  552. carousel._navBtns.prev[i].removeAttribute("disabled");
  553. }
  554. carousel._prevEnabled = true;
  555. }
  556. }
  557. attach = false;
  558. if (navigation.next) {
  559. if (sentinel >= carousel.get("numItems")) {
  560. if (!carousel.get("isCircular")) {
  561. Event.removeListener(navigation.next, "click",
  562. scrollPageForward);
  563. Dom.addClass(navigation.next, cssClass.DISABLED);
  564. for (i = 0; i < carousel._navBtns.next.length; i++) {
  565. carousel._navBtns.next[i].setAttribute("disabled",
  566. "true");
  567. }
  568. carousel._nextEnabled = false;
  569. } else {
  570. attach = !carousel._nextEnabled;
  571. }
  572. } else {
  573. attach = !carousel._nextEnabled;
  574. }
  575. if (attach) {
  576. Event.on(navigation.next, "click", scrollPageForward,
  577. carousel);
  578. Dom.removeClass(navigation.next, cssClass.DISABLED);
  579. for (i = 0; i < carousel._navBtns.next.length; i++) {
  580. carousel._navBtns.next[i].removeAttribute("disabled");
  581. }
  582. carousel._nextEnabled = true;
  583. }
  584. }
  585. carousel.fireEvent(navigationStateChangeEvent,
  586. { next: carousel._nextEnabled, prev: carousel._prevEnabled });
  587. }
  588. /**
  589. * Synchronize and redraw the Pager UI if necessary.
  590. *
  591. * @method syncPagerUi
  592. * @private
  593. */
  594. function syncPagerUi(page) {
  595. var carousel = this, numPages, numVisible;
  596. // Don't do anything if the Carousel is not rendered
  597. if (!carousel._hasRendered) {
  598. return;
  599. }
  600. numVisible = carousel.get("numVisible");
  601. if (!JS.isNumber(page)) {
  602. page = Math.ceil(carousel.get("selectedItem") / numVisible);
  603. }
  604. numPages = Math.ceil(carousel.get("numItems") / numVisible);
  605. carousel._pages.num = numPages;
  606. carousel._pages.cur = page;
  607. if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
  608. carousel._updatePagerMenu();
  609. } else {
  610. carousel._updatePagerButtons();
  611. }
  612. }
  613. /**
  614. * Handle UI update.
  615. * Call the appropriate methods on events fired when an item is added, or
  616. * removed for synchronizing the DOM.
  617. *
  618. * @method syncUi
  619. * @param {Object} o The item that needs to be added or removed
  620. * @private
  621. */
  622. function syncUi(o) {
  623. var carousel = this;
  624. if (!JS.isObject(o)) {
  625. return;
  626. }
  627. switch (o.ev) {
  628. case itemAddedEvent:
  629. carousel._syncUiForItemAdd(o);
  630. break;
  631. case itemRemovedEvent:
  632. carousel._syncUiForItemRemove(o);
  633. break;
  634. case loadItemsEvent:
  635. carousel._syncUiForLazyLoading(o);
  636. break;
  637. }
  638. carousel.fireEvent(uiUpdateEvent);
  639. }
  640. /**
  641. * Update the state variables after scrolling the Carousel view port.
  642. *
  643. * @method updateStateAfterScroll
  644. * @param {Integer} item The index to which the Carousel has scrolled to.
  645. * @param {Integer} sentinel The last element in the view port.
  646. * @private
  647. */
  648. function updateStateAfterScroll(item, sentinel) {
  649. var carousel = this,
  650. page = carousel.get("currentPage"),
  651. newPage,
  652. numPerPage = carousel.get("numVisible");
  653. newPage = parseInt(carousel._firstItem / numPerPage, 10);
  654. if (newPage != page) {
  655. carousel.setAttributeConfig("currentPage", { value: newPage });
  656. carousel.fireEvent(pageChangeEvent, newPage);
  657. }
  658. if (carousel.get("selectOnScroll")) {
  659. if (carousel.get("selectedItem") != carousel._selectedItem) {
  660. carousel.set("selectedItem", carousel._selectedItem);
  661. }
  662. }
  663. clearTimeout(carousel._autoPlayTimer);
  664. delete carousel._autoPlayTimer;
  665. if (carousel.isAutoPlayOn()) {
  666. carousel.startAutoPlay();
  667. }
  668. carousel.fireEvent(afterScrollEvent,
  669. { first: carousel._firstItem,
  670. last: sentinel },
  671. carousel);
  672. }
  673. /*
  674. * Static members and methods of the Carousel component
  675. */
  676. /**
  677. * Return the appropriate Carousel object based on the id associated with
  678. * the Carousel element or false if none match.
  679. * @method getById
  680. * @public
  681. * @static
  682. */
  683. Carousel.getById = function (id) {
  684. return instances[id] ? instances[id].object : false;
  685. };
  686. YAHOO.extend(Carousel, YAHOO.util.Element, {
  687. /*
  688. * Internal variables used within the Carousel component
  689. */
  690. /**
  691. * The Animation object.
  692. *
  693. * @property _animObj
  694. * @private
  695. */
  696. _animObj: null,
  697. /**
  698. * The Carousel element.
  699. *
  700. * @property _carouselEl
  701. * @private
  702. */
  703. _carouselEl: null,
  704. /**
  705. * The Carousel clipping container element.
  706. *
  707. * @property _clipEl
  708. * @private
  709. */
  710. _clipEl: null,
  711. /**
  712. * The current first index of the Carousel.
  713. *
  714. * @property _firstItem
  715. * @private
  716. */
  717. _firstItem: 0,
  718. /**
  719. * Does the Carousel element have focus?
  720. *
  721. * @property _hasFocus
  722. * @private
  723. */
  724. _hasFocus: false,
  725. /**
  726. * Is the Carousel rendered already?
  727. *
  728. * @property _hasRendered
  729. * @private
  730. */
  731. _hasRendered: false,
  732. /**
  733. * Is the animation still in progress?
  734. *
  735. * @property _isAnimationInProgress
  736. * @private
  737. */
  738. _isAnimationInProgress: false,
  739. /**
  740. * Is the auto-scrolling of Carousel in progress?
  741. *
  742. * @property _isAutoPlayInProgress
  743. * @private
  744. */
  745. _isAutoPlayInProgress: false,
  746. /**
  747. * The table of items in the Carousel.
  748. * The numItems is the number of items in the Carousel, items being the
  749. * array of items in the Carousel. The size is the size of a single
  750. * item in the Carousel. It is cached here for efficiency (to avoid
  751. * computing the size multiple times).
  752. *
  753. * @property _itemsTable
  754. * @private
  755. */
  756. _itemsTable: null,
  757. /**
  758. * The Carousel navigation buttons.
  759. *
  760. * @property _navBtns
  761. * @private
  762. */
  763. _navBtns: null,
  764. /**
  765. * The Carousel navigation.
  766. *
  767. * @property _navEl
  768. * @private
  769. */
  770. _navEl: null,
  771. /**
  772. * Status of the next navigation item.
  773. *
  774. * @property _nextEnabled
  775. * @private
  776. */
  777. _nextEnabled: true,
  778. /**
  779. * The Carousel pages structure.
  780. * This is an object of the total number of pages and the current page.
  781. *
  782. * @property _pages
  783. * @private
  784. */
  785. _pages: null,
  786. /**
  787. * Status of the previous navigation item.
  788. *
  789. * @property _prevEnabled
  790. * @private
  791. */
  792. _prevEnabled: true,
  793. /**
  794. * Whether the Carousel size needs to be recomputed or not?
  795. *
  796. * @property _recomputeSize
  797. * @private
  798. */
  799. _recomputeSize: true,
  800. /*
  801. * CSS classes used by the Carousel component
  802. */
  803. CLASSES: {
  804. /**
  805. * The class name of the Carousel navigation buttons.
  806. *
  807. * @property BUTTON
  808. * @default "yui-carousel-button"
  809. */
  810. BUTTON: "yui-carousel-button",
  811. /**
  812. * The class name of the Carousel element.
  813. *
  814. * @property CAROUSEL
  815. * @default "yui-carousel"
  816. */
  817. CAROUSEL: "yui-carousel",
  818. /**
  819. * The class name of the container of the items in the Carousel.
  820. *
  821. * @property CAROUSEL_EL
  822. * @default "yui-carousel-element"
  823. */
  824. CAROUSEL_EL: "yui-carousel-element",
  825. /**
  826. * The class name of the Carousel's container element.
  827. *
  828. * @property CONTAINER
  829. * @default "yui-carousel-container"
  830. */
  831. CONTAINER: "yui-carousel-container",
  832. /**
  833. * The class name of the Carousel's container element.
  834. *
  835. * @property CONTENT
  836. * @default "yui-carousel-content"
  837. */
  838. CONTENT: "yui-carousel-content",
  839. /**
  840. * The class name of a disabled navigation button.
  841. *
  842. * @property DISABLED
  843. * @default "yui-carousel-button-disabled"
  844. */
  845. DISABLED: "yui-carousel-button-disabled",
  846. /**
  847. * The class name of the first Carousel navigation button.
  848. *
  849. * @property FIRST_NAV
  850. * @default " yui-carousel-first-button"
  851. */
  852. FIRST_NAV: " yui-carousel-first-button",
  853. /**
  854. * The class name of a first disabled navigation button.
  855. *
  856. * @property FIRST_NAV_DISABLED
  857. * @default "yui-carousel-first-button-disabled"
  858. */
  859. FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
  860. /**
  861. * The class name of a first page element.
  862. *
  863. * @property FIRST_PAGE
  864. * @default "yui-carousel-nav-first-page"
  865. */
  866. FIRST_PAGE: "yui-carousel-nav-first-page",
  867. /**
  868. * The class name of the Carousel navigation button that has focus.
  869. *
  870. * @property FOCUSSED_BUTTON
  871. * @default "yui-carousel-button-focus"
  872. */
  873. FOCUSSED_BUTTON: "yui-carousel-button-focus",
  874. /**
  875. * The class name of a horizontally oriented Carousel.
  876. *
  877. * @property HORIZONTAL
  878. * @default "yui-carousel-horizontal"
  879. */
  880. HORIZONTAL: "yui-carousel-horizontal",
  881. /**
  882. * The element to be used as the progress indicator when the item
  883. * is still being loaded.
  884. *
  885. * @property ITEM_LOADING
  886. * @default The progress indicator (spinner) image CSS class
  887. */
  888. ITEM_LOADING: "yui-carousel-item-loading",
  889. /**
  890. * The class name that will be set if the Carousel adjusts itself
  891. * for a minimum width.
  892. *
  893. * @property MIN_WIDTH
  894. * @default "yui-carousel-min-width"
  895. */
  896. MIN_WIDTH: "yui-carousel-min-width",
  897. /**
  898. * The navigation element container class name.
  899. *
  900. * @property NAVIGATION
  901. * @default "yui-carousel-nav"
  902. */
  903. NAVIGATION: "yui-carousel-nav",
  904. /**
  905. * The class name of the next Carousel navigation button.
  906. *
  907. * @property NEXT_NAV
  908. * @default " yui-carousel-next-button"
  909. */
  910. NEXT_NAV: " yui-carousel-next-button",
  911. /**
  912. * The class name of the next navigation link. This variable is
  913. * not only used for styling, but also for identifying the link
  914. * within the Carousel container.
  915. *
  916. * @property NEXT_PAGE
  917. * @default "yui-carousel-next"
  918. */
  919. NEXT_PAGE: "yui-carousel-next",
  920. /**
  921. * The class name for the navigation container for prev/next.
  922. *
  923. * @property NAV_CONTAINER
  924. * @default "yui-carousel-buttons"
  925. */
  926. NAV_CONTAINER: "yui-carousel-buttons",
  927. /**
  928. * The class name of the focussed page navigation. This class is
  929. * specifically used for the ugly focus handling in Opera.
  930. *
  931. * @property PAGE_FOCUS
  932. * @default "yui-carousel-nav-page-focus"
  933. */
  934. PAGE_FOCUS: "yui-carousel-nav-page-focus",
  935. /**
  936. * The class name of the previous navigation link. This variable
  937. * is not only used for styling, but also for identifying the link
  938. * within the Carousel container.
  939. *
  940. * @property PREV_PAGE
  941. * @default "yui-carousel-prev"
  942. */
  943. PREV_PAGE: "yui-carousel-prev",
  944. /**
  945. * The class name of the selected item.
  946. *
  947. * @property SELECTED_ITEM
  948. * @default "yui-carousel-item-selected"
  949. */
  950. SELECTED_ITEM: "yui-carousel-item-selected",
  951. /**
  952. * The class name of the selected paging navigation.
  953. *
  954. * @property SELECTED_NAV
  955. * @default "yui-carousel-nav-page-selected"
  956. */
  957. SELECTED_NAV: "yui-carousel-nav-page-selected",
  958. /**
  959. * The class name of a vertically oriented Carousel.
  960. *
  961. * @property VERTICAL
  962. * @default "yui-carousel-vertical"
  963. */
  964. VERTICAL: "yui-carousel-vertical",
  965. /**
  966. * The class name of the (vertical) Carousel's container element.
  967. *
  968. * @property VERTICAL_CONTAINER
  969. * @default "yui-carousel-vertical-container"
  970. */
  971. VERTICAL_CONTAINER: "yui-carousel-vertical-container",
  972. /**
  973. * The class name of a visible Carousel.
  974. *
  975. * @property VISIBLE
  976. * @default "yui-carousel-visible"
  977. */
  978. VISIBLE: "yui-carousel-visible"
  979. },
  980. /*
  981. * Configuration attributes for configuring the Carousel component
  982. */
  983. CONFIG: {
  984. /**
  985. * The offset of the first visible item in the Carousel.
  986. *
  987. * @property FIRST_VISIBLE
  988. * @default 0
  989. */
  990. FIRST_VISIBLE: 0,
  991. /**
  992. * The minimum width of the horizontal Carousel container to support
  993. * the navigation buttons.
  994. *
  995. * @property HORZ_MIN_WIDTH
  996. * @default 180
  997. */
  998. HORZ_MIN_WIDTH: 180,
  999. /**
  1000. * The maximum number of pager buttons allowed beyond which the UI
  1001. * of the pager would be a drop-down of pages instead of buttons.
  1002. *
  1003. * @property MAX_PAGER_BUTTONS
  1004. * @default 5
  1005. */
  1006. MAX_PAGER_BUTTONS: 5,
  1007. /**
  1008. * The minimum width of the vertical Carousel container to support
  1009. * the navigation buttons.
  1010. *
  1011. * @property VERT_MIN_WIDTH
  1012. * @default 99
  1013. */
  1014. VERT_MIN_WIDTH: 99,
  1015. /**
  1016. * The number of visible items in the Carousel.
  1017. *
  1018. * @property NUM_VISIBLE
  1019. * @default 3
  1020. */
  1021. NUM_VISIBLE: 3
  1022. },
  1023. /*
  1024. * Internationalizable strings in the Carousel component
  1025. */
  1026. STRINGS: {
  1027. /**
  1028. * The content to be used as the progress indicator when the item
  1029. * is still being loaded.
  1030. *
  1031. * @property ITEM_LOADING_CONTENT
  1032. * @default "Loading"
  1033. */
  1034. ITEM_LOADING_CONTENT: "Loading",
  1035. /**
  1036. * The next navigation button name/text.
  1037. *
  1038. * @property NEXT_BUTTON_TEXT
  1039. * @default "Next Page"
  1040. */
  1041. NEXT_BUTTON_TEXT: "Next Page",
  1042. /**
  1043. * The prefix text for the pager in case the UI is a drop-down.
  1044. *
  1045. * @property PAGER_PREFIX_TEXT
  1046. * @default "Go to page "
  1047. */
  1048. PAGER_PREFIX_TEXT: "Go to page ",
  1049. /**
  1050. * The previous navigation button name/text.
  1051. *
  1052. * @property PREVIOUS_BUTTON_TEXT
  1053. * @default "Previous Page"
  1054. */
  1055. PREVIOUS_BUTTON_TEXT: "Previous Page"
  1056. },
  1057. /*
  1058. * Public methods of the Carousel component
  1059. */
  1060. /**
  1061. * Insert or append an item to the Carousel.
  1062. *
  1063. * @method addItem
  1064. * @public
  1065. * @param item {String | Object | HTMLElement} The item to be appended
  1066. * to the Carousel. If the parameter is a string, it is assumed to be
  1067. * the content of the newly created item. If the parameter is an
  1068. * object, it is assumed to supply the content and an optional class
  1069. * and an optional id of the newly created item.
  1070. * @param index {Number} optional The position to where in the list
  1071. * (starts from zero).
  1072. * @return {Boolean} Return true on success, false otherwise
  1073. */
  1074. addItem: function (item, index) {
  1075. var carousel = this,
  1076. className,
  1077. content,
  1078. elId,
  1079. numItems = carousel.get("numItems");
  1080. if (!item) {
  1081. return false;
  1082. }
  1083. if (JS.isString(item) || item.nodeName) {
  1084. content = item.nodeName ? item.innerHTML : item;
  1085. } else if (JS.isObject(item)) {
  1086. content = item.content;
  1087. } else {
  1088. return false;
  1089. }
  1090. className = item.className || "";
  1091. elId = item.id ? item.id : Dom.generateId();
  1092. if (JS.isUndefined(index)) {
  1093. carousel._itemsTable.items.push({
  1094. item : content,
  1095. className : className,
  1096. id : elId
  1097. });
  1098. } else {
  1099. if (index < 0 || index >= numItems) {
  1100. return false;
  1101. }
  1102. carousel._itemsTable.items.splice(index, 0, {
  1103. item : content,
  1104. className : className,
  1105. id : elId
  1106. });
  1107. }
  1108. carousel._itemsTable.numItems++;
  1109. if (numItems < carousel._itemsTable.items.length) {
  1110. carousel.set("numItems", carousel._itemsTable.items.length);
  1111. }
  1112. carousel.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent });
  1113. return true;
  1114. },
  1115. /**
  1116. * Insert or append multiple items to the Carousel.
  1117. *
  1118. * @method addItems
  1119. * @public
  1120. * @param items {Array} An array of items to be added with each item
  1121. * representing an item, index pair [{item, index}, ...]
  1122. * @return {Boolean} Return true on success, false otherwise
  1123. */
  1124. addItems: function (items) {
  1125. var i, n, rv = true;
  1126. if (!JS.isArray(items)) {
  1127. return false;
  1128. }
  1129. for (i = 0, n = items.length; i < n; i++) {
  1130. if (this.addItem(items[i][0], items[i][1]) === false) {
  1131. rv = false;
  1132. }
  1133. }
  1134. return rv;
  1135. },
  1136. /**
  1137. * Remove focus from the Carousel.
  1138. *
  1139. * @method blur
  1140. * @public
  1141. */
  1142. blur: function () {
  1143. this._carouselEl.blur();
  1144. this.fireEvent(blurEvent);
  1145. },
  1146. /**
  1147. * Clears the items from Carousel.
  1148. *
  1149. * @method clearItems
  1150. * public
  1151. */
  1152. clearItems: function () {
  1153. var carousel = this, n = carousel.get("numItems");
  1154. while (n > 0) {
  1155. if (!carousel.removeItem(0)) {
  1156. }
  1157. /*
  1158. For dynamic loading, the numItems may be much larger than
  1159. the actual number of items in the table. So, set the
  1160. numItems to zero, and break out of the loop if the table
  1161. is already empty.
  1162. */
  1163. if (carousel._itemsTable.numItems === 0) {
  1164. carousel.set("numItems", 0);
  1165. break;
  1166. }
  1167. n--;
  1168. }
  1169. carousel.fireEvent(allItemsRemovedEvent);
  1170. },
  1171. /**
  1172. * Set focus on the Carousel.
  1173. *
  1174. * @method focus
  1175. * @public
  1176. */
  1177. focus: function () {
  1178. var carousel = this,
  1179. first,
  1180. focusEl,
  1181. isSelectionInvisible,
  1182. itemsTable,
  1183. last,
  1184. numVisible,
  1185. selectOnScroll,
  1186. selected,
  1187. selItem;
  1188. // Don't do anything if the Carousel is not rendered
  1189. if (!carousel._hasRendered) {
  1190. return;
  1191. }
  1192. if (carousel.isAnimating()) {
  1193. // this messes up real bad!
  1194. return;
  1195. }
  1196. selItem = carousel.get("selectedItem");
  1197. numVisible = carousel.get("numVisible");
  1198. selectOnScroll = carousel.get("selectOnScroll");
  1199. selected = (selItem >= 0) ?
  1200. carousel.getItem(selItem) : null;
  1201. first = carousel.get("firstVisible");
  1202. last = first + numVisible - 1;
  1203. isSelectionInvisible = (selItem < first || selItem > last);
  1204. focusEl = (selected && selected.id) ?
  1205. Dom.get(selected.id) : null;
  1206. itemsTable = carousel._itemsTable;
  1207. if (!selectOnScroll && isSelectionInvisible) {
  1208. focusEl = (itemsTable && itemsTable.items &&
  1209. itemsTable.items[first]) ?
  1210. Dom.get(itemsTable.items[first].id) : null;
  1211. }
  1212. if (focusEl) {
  1213. try {
  1214. focusEl.focus();
  1215. } catch (ex) {
  1216. // ignore focus errors
  1217. }
  1218. }
  1219. carousel.fireEvent(focusEvent);
  1220. },
  1221. /**
  1222. * Hide the Carousel.
  1223. *
  1224. * @method hide
  1225. * @public
  1226. */
  1227. hide: function () {
  1228. var carousel = this;
  1229. if (carousel.fireEvent(beforeHideEvent) !== false) {
  1230. carousel.removeClass(carousel.CLASSES.VISIBLE);
  1231. carousel.fireEvent(hideEvent);
  1232. }
  1233. },
  1234. /**
  1235. * Initialize the Carousel.
  1236. *
  1237. * @method init
  1238. * @public
  1239. * @param el {HTMLElement | String} The html element that represents
  1240. * the Carousel container.
  1241. * @param attrs {Object} The set of configuration attributes for
  1242. * creating the Carousel.
  1243. */
  1244. init: function (el, attrs) {
  1245. var carousel = this,
  1246. elId = el, // save for a rainy day
  1247. parse = false;
  1248. if (!el) {
  1249. return;
  1250. }
  1251. carousel._hasRendered = false;
  1252. carousel._navBtns = { prev: [], next: [] };
  1253. carousel._pages = { el: null, num: 0, cur: 0 };
  1254. carousel._itemsTable = { loading: {}, numItems: 0,
  1255. items: [], size: 0 };
  1256. if (JS.isString(el)) {
  1257. el = Dom.get(el);
  1258. } else if (!el.nodeName) {
  1259. return;
  1260. }
  1261. Carousel.superclass.init.call(carousel, el, attrs);
  1262. if (el) {
  1263. if (!el.id) { // in case the HTML element is passed
  1264. el.setAttribute("id", Dom.generateId());
  1265. }
  1266. parse = carousel._parseCarousel(el);
  1267. if (!parse) {
  1268. carousel._createCarousel(elId);
  1269. }
  1270. } else {
  1271. el = carousel._createCarousel(elId);
  1272. }
  1273. elId = el.id;
  1274. carousel.initEvents();
  1275. if (parse) {
  1276. carousel._parseCarouselItems();
  1277. }
  1278. if (!attrs || typeof attrs.isVertical == "undefined") {
  1279. carousel.set("isVertical", false);
  1280. }
  1281. carousel._parseCarouselNavigation(el);
  1282. carousel._navEl = carousel._setupCarouselNavigation();
  1283. instances[elId] = { object: carousel };
  1284. carousel._loadItems();
  1285. },
  1286. /**
  1287. * Initialize the configuration attributes used to create the Carousel.
  1288. *
  1289. * @method initAttributes
  1290. * @public
  1291. * @param attrs {Object} The set of configuration attributes for
  1292. * creating the Carousel.
  1293. */
  1294. initAttributes: function (attrs) {
  1295. var carousel = this;
  1296. attrs = attrs || {};
  1297. Carousel.superclass.initAttributes.call(carousel, attrs);
  1298. /**
  1299. * @attribute carouselEl
  1300. * @description The type of the Carousel element.
  1301. * @default OL
  1302. * @type Boolean
  1303. */
  1304. carousel.setAttributeConfig("carouselEl", {
  1305. validator : JS.isString,
  1306. value : attrs.carouselEl || "OL"
  1307. });
  1308. /**
  1309. * @attribute carouselItemEl
  1310. * @description The type of the list of items within the Carousel.
  1311. * @default LI
  1312. * @type Boolean
  1313. */
  1314. carousel.setAttributeConfig("carouselItemEl", {
  1315. validator : JS.isString,
  1316. value : attrs.carouselItemEl || "LI"
  1317. });
  1318. /**
  1319. * @attribute currentPage
  1320. * @description The current page number (read-only.)
  1321. * @type Number
  1322. */
  1323. carousel.setAttributeConfig("currentPage", {
  1324. readOnly : true,
  1325. value : 0
  1326. });
  1327. /**
  1328. * @attribute firstVisible
  1329. * @description The index to start the Carousel from (indexes begin
  1330. * from zero)
  1331. * @default 0
  1332. * @type Number
  1333. */
  1334. carousel.setAttributeConfig("firstVisible", {
  1335. method : carousel._setFirstVisible,
  1336. validator : carousel._validateFirstVisible,
  1337. value :
  1338. attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
  1339. });
  1340. /**
  1341. * @attribute selectOnScroll
  1342. * @description Set this to true to automatically set focus to
  1343. * follow scrolling in the Carousel.
  1344. * @default true
  1345. * @type Boolean
  1346. */
  1347. carousel.setAttributeConfig("selectOnScroll", {
  1348. validator : JS.isBoolean,
  1349. value : attrs.selectOnScroll || true
  1350. });
  1351. /**
  1352. * @attribute numVisible
  1353. * @description The number of visible items in the Carousel's
  1354. * viewport.
  1355. * @default 3
  1356. * @type Number
  1357. */
  1358. carousel.setAttributeConfig("numVisible", {
  1359. method : carousel._setNumVisible,
  1360. validator : carousel._validateNumVisible,
  1361. value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
  1362. });
  1363. /**
  1364. * @attribute numItems
  1365. * @description The number of items in the Carousel.
  1366. * @type Number
  1367. */
  1368. carousel.setAttributeConfig("numItems", {
  1369. method : carousel._setNumItems,
  1370. validator : carousel._validateNumItems,
  1371. value : carousel._itemsTable.numItems
  1372. });
  1373. /**
  1374. * @attribute scrollIncrement
  1375. * @description The number of items to scroll by for arrow keys.
  1376. * @default 1
  1377. * @type Number
  1378. */
  1379. carousel.setAttributeConfig("scrollIncrement", {
  1380. validator : carousel._validateScrollIncrement,
  1381. value : attrs.scrollIncrement || 1
  1382. });
  1383. /**
  1384. * @attribute selectedItem
  1385. * @description The index of the selected item.
  1386. * @type Number
  1387. */
  1388. car