PageRenderTime 72ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/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
  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. carousel.setAttributeConfig("selectedItem", {
  1389. method : carousel._setSelectedItem,
  1390. validator : JS.isNumber,
  1391. value : -1
  1392. });
  1393. /**
  1394. * @attribute revealAmount
  1395. * @description The percentage of the item to be revealed on each
  1396. * side of the Carousel (before and after the first and last item
  1397. * in the Carousel's viewport.)
  1398. * @default 0
  1399. * @type Number
  1400. */
  1401. carousel.setAttributeConfig("revealAmount", {
  1402. method : carousel._setRevealAmount,
  1403. validator : carousel._validateRevealAmount,
  1404. value : attrs.revealAmount || 0
  1405. });
  1406. /**
  1407. * @attribute isCircular
  1408. * @description Set this to true to wrap scrolling of the contents
  1409. * in the Carousel.
  1410. * @default false
  1411. * @type Boolean
  1412. */
  1413. carousel.setAttributeConfig("isCircular", {
  1414. validator : JS.isBoolean,
  1415. value : attrs.isCircular || false
  1416. });
  1417. /**
  1418. * @attribute isVertical
  1419. * @description True if the orientation of the Carousel is vertical
  1420. * @default false
  1421. * @type Boolean
  1422. */
  1423. carousel.setAttributeConfig("isVertical", {
  1424. method : carousel._setOrientation,
  1425. validator : JS.isBoolean,
  1426. value : attrs.isVertical || false
  1427. });
  1428. /**
  1429. * @attribute navigation
  1430. * @description The set of navigation controls for Carousel
  1431. * @default <br>
  1432. * { prev: null, // the previous navigation element<br>
  1433. * next: null } // the next navigation element
  1434. * @type Object
  1435. */
  1436. carousel.setAttributeConfig("navigation", {
  1437. method : carousel._setNavigation,
  1438. validator : carousel._validateNavigation,
  1439. value :
  1440. attrs.navigation || {prev: null,next: null,page: null}
  1441. });
  1442. /**
  1443. * @attribute animation
  1444. * @description The optional animation attributes for the Carousel.
  1445. * @default <br>
  1446. * { speed: 0, // the animation speed (in seconds)<br>
  1447. * effect: null } // the animation effect (like
  1448. * YAHOO.util.Easing.easeOut)
  1449. * @type Object
  1450. */
  1451. carousel.setAttributeConfig("animation", {
  1452. validator : carousel._validateAnimation,
  1453. value : attrs.animation || { speed: 0, effect: null }
  1454. });
  1455. /**
  1456. * @attribute autoPlay
  1457. * @description Set this to time in milli-seconds to have the
  1458. * Carousel automatically scroll the contents.
  1459. * @type Number
  1460. * @deprecated Use autoPlayInterval instead.
  1461. */
  1462. carousel.setAttributeConfig("autoPlay", {
  1463. validator : JS.isNumber,
  1464. value : attrs.autoPlay || 0
  1465. });
  1466. /**
  1467. * @attribute autoPlayInterval
  1468. * @description The delay in milli-seconds for scrolling the
  1469. * Carousel during auto-play.
  1470. * Note: The startAutoPlay() method needs to be invoked to trigger
  1471. * automatic scrolling of Carousel.
  1472. * @type Number
  1473. */
  1474. carousel.setAttributeConfig("autoPlayInterval", {
  1475. validator : JS.isNumber,
  1476. value : attrs.autoPlayInterval || 0
  1477. });
  1478. },
  1479. /**
  1480. * Initialize and bind the event handlers.
  1481. *
  1482. * @method initEvents
  1483. * @public
  1484. */
  1485. initEvents: function () {
  1486. var carousel = this,
  1487. cssClass = carousel.CLASSES,
  1488. focussedLi;
  1489. carousel.on("keydown", carousel._keyboardEventHandler);
  1490. carousel.on(afterScrollEvent, syncNavigation);
  1491. carousel.on(itemAddedEvent, syncUi);
  1492. carousel.on(itemRemovedEvent, syncUi);
  1493. carousel.on(itemSelectedEvent, function () {
  1494. if (carousel._hasFocus) {
  1495. carousel.focus();
  1496. }
  1497. });
  1498. carousel.on(loadItemsEvent, syncUi);
  1499. carousel.on(allItemsRemovedEvent, function (ev) {
  1500. carousel.scrollTo(0);
  1501. syncNavigation.call(carousel);
  1502. syncPagerUi.call(carousel);
  1503. });
  1504. carousel.on(pageChangeEvent, syncPagerUi, carousel);
  1505. carousel.on(renderEvent, function (ev) {
  1506. carousel.set("selectedItem", carousel.get("firstVisible"));
  1507. syncNavigation.call(carousel, ev);
  1508. syncPagerUi.call(carousel, ev);
  1509. carousel._setClipContainerSize();
  1510. });
  1511. carousel.on("selectedItemChange", function (ev) {
  1512. setItemSelection.call(carousel, ev.newValue, ev.prevValue);
  1513. if (ev.newValue >= 0) {
  1514. carousel._updateTabIndex(
  1515. carousel.getElementForItem(ev.newValue));
  1516. }
  1517. carousel.fireEvent(itemSelectedEvent, ev.newValue);
  1518. });
  1519. carousel.on(uiUpdateEvent, function (ev) {
  1520. syncNavigation.call(carousel, ev);
  1521. syncPagerUi.call(carousel, ev);
  1522. });
  1523. carousel.on("firstVisibleChange", function (ev) {
  1524. if (!carousel.get("selectOnScroll")) {
  1525. if (ev.newValue >= 0) {
  1526. carousel._updateTabIndex(
  1527. carousel.getElementForItem(ev.newValue));
  1528. }
  1529. }
  1530. });
  1531. // Handle item selection on mouse click
  1532. carousel.on("click", function (ev) {
  1533. if (carousel.isAutoPlayOn()) {
  1534. carousel.stopAutoPlay();
  1535. }
  1536. carousel._itemClickHandler(ev);
  1537. carousel._pagerClickHandler(ev);
  1538. });
  1539. // Restore the focus on the navigation buttons
  1540. Event.onFocus(carousel.get("element"), function (ev, obj) {
  1541. var target = Event.getTarget(ev);
  1542. if (target && target.nodeName.toUpperCase() == "A" &&
  1543. Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
  1544. if (focussedLi) {
  1545. Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
  1546. }
  1547. focussedLi = target.parentNode;
  1548. Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
  1549. } else {
  1550. if (focussedLi) {
  1551. Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
  1552. }
  1553. }
  1554. obj._hasFocus = true;
  1555. obj._updateNavButtons(Event.getTarget(ev), true);
  1556. }, carousel);
  1557. Event.onBlur(carousel.get("element"), function (ev, obj) {
  1558. obj._hasFocus = false;
  1559. obj._updateNavButtons(Event.getTarget(ev), false);
  1560. }, carousel);
  1561. },
  1562. /**
  1563. * Return true if the Carousel is still animating, or false otherwise.
  1564. *
  1565. * @method isAnimating
  1566. * @return {Boolean} Return true if animation is still in progress, or
  1567. * false otherwise.
  1568. * @public
  1569. */
  1570. isAnimating: function () {
  1571. return this._isAnimationInProgress;
  1572. },
  1573. /**
  1574. * Return true if the auto-scrolling of Carousel is "on", or false
  1575. * otherwise.
  1576. *
  1577. * @method isAutoPlayOn
  1578. * @return {Boolean} Return true if autoPlay is "on", or false
  1579. * otherwise.
  1580. * @public
  1581. */
  1582. isAutoPlayOn: function () {
  1583. return this._isAutoPlayInProgress;
  1584. },
  1585. /**
  1586. * Return the carouselItemEl at index or null if the index is not
  1587. * found.
  1588. *
  1589. * @method getElementForItem
  1590. * @param index {Number} The index of the item to be returned
  1591. * @return {Element} Return the item at index or null if not found
  1592. * @public
  1593. */
  1594. getElementForItem: function (index) {
  1595. var carousel = this;
  1596. if (index < 0 || index >= carousel.get("numItems")) {
  1597. return null;
  1598. }
  1599. // TODO: may be cache the item
  1600. if (carousel._itemsTable.numItems > index) {
  1601. if (!JS.isUndefined(carousel._itemsTable.items[index])) {
  1602. return Dom.get(carousel._itemsTable.items[index].id);
  1603. }
  1604. }
  1605. return null;
  1606. },
  1607. /**
  1608. * Return the carouselItemEl for all items in the Carousel.
  1609. *
  1610. * @method getElementForItems
  1611. * @return {Array} Return all the items
  1612. * @public
  1613. */
  1614. getElementForItems: function () {
  1615. var carousel = this, els = [], i;
  1616. for (i = 0; i < carousel._itemsTable.numItems; i++) {
  1617. els.push(carousel.getElementForItem(i));
  1618. }
  1619. return els;
  1620. },
  1621. /**
  1622. * Return the item at index or null if the index is not found.
  1623. *
  1624. * @method getItem
  1625. * @param index {Number} The index of the item to be returned
  1626. * @return {Object} Return the item at index or null if not found
  1627. * @public
  1628. */
  1629. getItem: function (index) {
  1630. var carousel = this;
  1631. if (index < 0 || index >= carousel.get("numItems")) {
  1632. return null;
  1633. }
  1634. if (carousel._itemsTable.numItems > index) {
  1635. if (!JS.isUndefined(carousel._itemsTable.items[index])) {
  1636. return carousel._itemsTable.items[index];
  1637. }
  1638. }
  1639. return null;
  1640. },
  1641. /**
  1642. * Return all items as an array.
  1643. *
  1644. * @method getItems
  1645. * @return {Array} Return all items in the Carousel
  1646. * @public
  1647. */
  1648. getItems: function (index) {
  1649. return this._itemsTable.items;
  1650. },
  1651. /**
  1652. * Return the position of the Carousel item that has the id "id", or -1
  1653. * if the id is not found.
  1654. *
  1655. * @method getItemPositionById
  1656. * @param index {Number} The index of the item to be returned
  1657. * @public
  1658. */
  1659. getItemPositionById: function (id) {
  1660. var carousel = this, i = 0, n = carousel._itemsTable.numItems;
  1661. while (i < n) {
  1662. if (!JS.isUndefined(carousel._itemsTable.items[i])) {
  1663. if (carousel._itemsTable.items[i].id == id) {
  1664. return i;
  1665. }
  1666. }
  1667. i++;
  1668. }
  1669. return -1;
  1670. },
  1671. /**
  1672. * Return all visible items as an array.
  1673. *
  1674. * @method getVisibleItems
  1675. * @return {Array} The array of visible items
  1676. * @public
  1677. */
  1678. getVisibleItems: function () {
  1679. var carousel = this,
  1680. i = carousel.get("firstVisible"),
  1681. n = i + carousel.get("numVisible"),
  1682. r = [];
  1683. while (i < n) {
  1684. r.push(carousel.getElementForItem(i));
  1685. i++;
  1686. }
  1687. return r;
  1688. },
  1689. /**
  1690. * Remove an item at index from the Carousel.
  1691. *
  1692. * @method removeItem
  1693. * @public
  1694. * @param index {Number} The position to where in the list (starts from
  1695. * zero).
  1696. * @return {Boolean} Return true on success, false otherwise
  1697. */
  1698. removeItem: function (index) {
  1699. var carousel = this,
  1700. item,
  1701. num = carousel.get("numItems");
  1702. if (index < 0 || index >= num) {
  1703. return false;
  1704. }
  1705. item = carousel._itemsTable.items.splice(index, 1);
  1706. if (item && item.length == 1) {
  1707. carousel._itemsTable.numItems--;
  1708. carousel.set("numItems", num - 1);
  1709. carousel.fireEvent(itemRemovedEvent,
  1710. { item: item[0], pos: index, ev: itemRemovedEvent });
  1711. return true;
  1712. }
  1713. return false;
  1714. },
  1715. /**
  1716. * Render the Carousel.
  1717. *
  1718. * @method render
  1719. * @public
  1720. * @param appendTo {HTMLElement | String} The element to which the
  1721. * Carousel should be appended prior to rendering.
  1722. * @return {Boolean} Status of the operation
  1723. */
  1724. render: function (appendTo) {
  1725. var carousel = this,
  1726. cssClass = carousel.CLASSES;
  1727. carousel.addClass(cssClass.CAROUSEL);
  1728. if (!carousel._clipEl) {
  1729. carousel._clipEl = carousel._createCarouselClip();
  1730. carousel._clipEl.appendChild(carousel._carouselEl);
  1731. }
  1732. if (appendTo) {
  1733. carousel.appendChild(carousel._clipEl);
  1734. carousel.appendTo(appendTo);
  1735. } else {
  1736. if (!Dom.inDocument(carousel.get("element"))) {
  1737. return false;
  1738. }
  1739. carousel.appendChild(carousel._clipEl);
  1740. }
  1741. if (carousel.get("isVertical")) {
  1742. carousel.addClass(cssClass.VERTICAL);
  1743. } else {
  1744. carousel.addClass(cssClass.HORIZONTAL);
  1745. }
  1746. if (carousel.get("numItems") < 1) {
  1747. return false;
  1748. }
  1749. carousel._refreshUi();
  1750. return true;
  1751. },
  1752. /**
  1753. * Scroll the Carousel by an item backward.
  1754. *
  1755. * @method scrollBackward
  1756. * @public
  1757. */
  1758. scrollBackward: function () {
  1759. var carousel = this;
  1760. carousel.scrollTo(carousel._firstItem -
  1761. carousel.get("scrollIncrement"));
  1762. },
  1763. /**
  1764. * Scroll the Carousel by an item forward.
  1765. *
  1766. * @method scrollForward
  1767. * @public
  1768. */
  1769. scrollForward: function () {
  1770. var carousel = this;
  1771. carousel.scrollTo(carousel._firstItem +
  1772. carousel.get("scrollIncrement"));
  1773. },
  1774. /**
  1775. * Scroll the Carousel by a page backward.
  1776. *
  1777. * @method scrollPageBackward
  1778. * @public
  1779. */
  1780. scrollPageBackward: function () {
  1781. var carousel = this,
  1782. item = carousel._firstItem - carousel.get("numVisible");
  1783. if (carousel.get("selectOnScroll")) {
  1784. carousel._selectedItem = carousel._getSelectedItem(item);
  1785. } else {
  1786. item = carousel._getValidIndex(item);
  1787. }
  1788. carousel.scrollTo(item);
  1789. },
  1790. /**
  1791. * Scroll the Carousel by a page forward.
  1792. *
  1793. * @method scrollPageForward
  1794. * @public
  1795. */
  1796. scrollPageForwa