2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The Carousel module provides a widget for browsing among a set of like
9 * objects represented pictorially.
12 * @requires yahoo, dom, event, element
14 * @namespace YAHOO.widget
15 * @title Carousel Widget
20 var WidgetName; // forward declaration
23 * The Carousel widget.
26 * @extends YAHOO.util.Element
28 * @param el {HTMLElement | String} The HTML element that represents the
29 * the container that houses the Carousel.
30 * @param cfg {Object} (optional) The configuration values
32 YAHOO.widget.Carousel = function (el, cfg) {
34 YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg);
38 * Private variables of the Carousel component
41 /* Some abbreviations to avoid lengthy typing and lookups. */
42 var Carousel = YAHOO.widget.Carousel,
44 Event = YAHOO.util.Event,
52 WidgetName = "Carousel";
55 * The internal table of Carousel instances.
62 * Custom events of the Carousel component
67 * @description Fires when the Carousel has scrolled to the previous or
68 * next page. Passes back the index of the first and last visible items in
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
74 afterScrollEvent = "afterScroll",
77 * @event allItemsRemovedEvent
78 * @description Fires when all items have been removed from the Carousel.
80 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
81 * for more information on listening for this event.
82 * @type YAHOO.util.CustomEvent
84 allItemsRemovedEvent = "allItemsRemoved",
88 * @description Fires before the Carousel is hidden. See
89 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
90 * for more information on listening for this event.
91 * @type YAHOO.util.CustomEvent
93 beforeHideEvent = "beforeHide",
96 * @event beforePageChange
97 * @description Fires when the Carousel is about to scroll to the previous
98 * or next page. Passes back the page number of the current page. Note
99 * that the first page number is zero. See
100 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
101 * for more information on listening for this event.
102 * @type YAHOO.util.CustomEvent
104 beforePageChangeEvent = "beforePageChange",
107 * @event beforeScroll
108 * @description Fires when the Carousel is about to scroll to the previous
109 * or next page. Passes back the index of the first and last visible items
110 * in the Carousel and the direction (backward/forward) of the scroll. See
111 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
112 * for more information on listening for this event.
113 * @type YAHOO.util.CustomEvent
115 beforeScrollEvent = "beforeScroll",
119 * @description Fires when the Carousel is about to be shown. See
120 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
121 * for more information on listening for this event.
122 * @type YAHOO.util.CustomEvent
124 beforeShowEvent = "beforeShow",
128 * @description Fires when the Carousel loses focus. See
129 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
130 * for more information on listening for this event.
131 * @type YAHOO.util.CustomEvent
137 * @description Fires when the Carousel gains focus. See
138 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
139 * for more information on listening for this event.
140 * @type YAHOO.util.CustomEvent
142 focusEvent = "focus",
146 * @description Fires when the Carousel is hidden. See
147 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
148 * for more information on listening for this event.
149 * @type YAHOO.util.CustomEvent
155 * @description Fires when an item has been added to the Carousel. Passes
156 * back the content of the item that would be added, the index at which the
157 * item would be added, and the event itself. See
158 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
159 * for more information on listening for this event.
160 * @type YAHOO.util.CustomEvent
162 itemAddedEvent = "itemAdded",
166 * @description Fires when an item has been removed from the Carousel.
167 * Passes back the content of the item that would be removed, the index
168 * from which the item would be removed, and the event itself. See
169 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
170 * for more information on listening for this event.
171 * @type YAHOO.util.CustomEvent
173 itemRemovedEvent = "itemRemoved",
176 * @event itemSelected
177 * @description Fires when an item has been selected in the Carousel.
178 * Passes back the index of the selected item in the Carousel. Note, that
179 * the index begins from zero. See
180 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
181 * for more information on listening for this event.
182 * @type YAHOO.util.CustomEvent
184 itemSelectedEvent = "itemSelected",
188 * @description Fires when the Carousel needs more items to be loaded for
189 * displaying them. Passes back the first and last visible items in the
190 * Carousel, and the number of items needed to be loaded. 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
195 loadItemsEvent = "loadItems",
198 * @event navigationStateChange
199 * @description Fires when the state of either one of the navigation
200 * buttons are changed from enabled to disabled or vice versa. Passes back
201 * the state (true/false) of the previous and next buttons. The value true
202 * signifies the button is enabled, false signifies disabled. See
203 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
204 * for more information on listening for this event.
205 * @type YAHOO.util.CustomEvent
207 navigationStateChangeEvent = "navigationStateChange",
211 * @description Fires after the Carousel has scrolled to the previous or
212 * next page. Passes back the page number of the current page. Note
213 * that the first page number is zero. See
214 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
215 * for more information on listening for this event.
216 * @type YAHOO.util.CustomEvent
218 pageChangeEvent = "pageChange",
223 * @description Fires when the Carousel is rendered. See
224 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
225 * for more information on listening for this event.
226 * @type YAHOO.util.CustomEvent
228 renderEvent = "render",
232 * @description Fires when the Carousel is shown. See
233 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
234 * for more information on listening for this event.
235 * @type YAHOO.util.CustomEvent
240 * @event startAutoPlay
241 * @description Fires when the auto play has started in the Carousel. See
242 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
243 * for more information on listening for this event.
244 * @type YAHOO.util.CustomEvent
246 startAutoPlayEvent = "startAutoPlay",
249 * @event stopAutoPlay
250 * @description Fires when the auto play has been stopped in the Carousel.
252 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
253 * for more information on listening for this event.
254 * @type YAHOO.util.CustomEvent
256 stopAutoPlayEvent = "stopAutoPlay",
260 * @event uiUpdateEvent
261 * @description Fires when the UI has been updated.
263 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
264 * for more information on listening for this event.
265 * @type YAHOO.util.CustomEvent
267 uiUpdateEvent = "uiUpdate";
270 * Private helper functions used by the Carousel component
274 * Create an element, set its class name and optionally install the element
276 * @method createElement
277 * @param el {String} The element to be created
278 * @param attrs {Object} Configuration of parent, class and id attributes.
279 * If the content is specified, it is inserted after creation of the
280 * element. The content can also be an HTML element in which case it would
281 * be appended as a child node of the created element.
284 function createElement(el, attrs) {
285 var newEl = document.createElement(el);
288 if (attrs.className) {
289 Dom.addClass(newEl, attrs.className);
293 attrs.parent.appendChild(newEl);
297 newEl.setAttribute("id", attrs.id);
301 if (attrs.content.nodeName) {
302 newEl.appendChild(attrs.content);
304 newEl.innerHTML = attrs.content;
312 * Get the computed style of an element.
315 * @param el {HTMLElement} The element for which the style needs to be
317 * @param style {String} The style attribute
318 * @param type {String} "int", "float", etc. (defaults to int)
321 function getStyle(el, style, type) {
328 function getStyleIntVal(el, style) {
332 * XXX: Safari calculates incorrect marginRight for an element
333 * which has its parent element style set to overflow: hidden
334 * https://bugs.webkit.org/show_bug.cgi?id=13343
335 * Let us assume marginLeft == marginRight
337 if (style == "marginRight" && YAHOO.env.ua.webkit) {
338 val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
340 val = parseInt(Dom.getStyle(el, style), 10);
343 return JS.isNumber(val) ? val : 0;
346 function getStyleFloatVal(el, style) {
350 * XXX: Safari calculates incorrect marginRight for an element
351 * which has its parent element style set to overflow: hidden
352 * https://bugs.webkit.org/show_bug.cgi?id=13343
353 * Let us assume marginLeft == marginRight
355 if (style == "marginRight" && YAHOO.env.ua.webkit) {
356 val = parseFloat(Dom.getStyle(el, "marginLeft"));
358 val = parseFloat(Dom.getStyle(el, style));
361 return JS.isNumber(val) ? val : 0;
364 if (typeof type == "undefined") {
370 value = el.offsetHeight;
372 value += getStyleIntVal(el, "marginTop") +
373 getStyleIntVal(el, "marginBottom");
375 value = getStyleFloatVal(el, "height") +
376 getStyleIntVal(el, "marginTop") +
377 getStyleIntVal(el, "marginBottom") +
378 getStyleIntVal(el, "borderTopWidth") +
379 getStyleIntVal(el, "borderBottomWidth") +
380 getStyleIntVal(el, "paddingTop") +
381 getStyleIntVal(el, "paddingBottom");
385 value = el.offsetWidth;
387 value += getStyleIntVal(el, "marginLeft") +
388 getStyleIntVal(el, "marginRight");
390 value = getStyleFloatVal(el, "width") +
391 getStyleIntVal(el, "marginLeft") +
392 getStyleIntVal(el, "marginRight") +
393 getStyleIntVal(el, "borderLeftWidth") +
394 getStyleIntVal(el, "borderRightWidth") +
395 getStyleIntVal(el, "paddingLeft") +
396 getStyleIntVal(el, "paddingRight");
401 value = getStyleIntVal(el, style);
402 } else if (type == "float") {
403 value = getStyleFloatVal(el, style);
405 value = Dom.getStyle(el, style);
414 * Compute and return the height or width of a single Carousel item
415 * depending upon the orientation.
417 * @method getCarouselItemSize
418 * @param which {String} "height" or "width" to be returned. If this is
419 * passed explicitly, the calculated size is not cached.
422 function getCarouselItemSize(which) {
428 if (carousel._itemsTable.numItems === 0) {
432 if (typeof which == "undefined") {
433 if (carousel._itemsTable.size > 0) {
434 return carousel._itemsTable.size;
438 if (JS.isUndefined(carousel._itemsTable.items[0])) {
442 child = Dom.get(carousel._itemsTable.items[0].id);
444 if (typeof which == "undefined") {
445 vertical = carousel.get("isVertical");
447 vertical = which == "height";
451 size = getStyle(child, "height");
453 size = getStyle(child, "width");
456 if (typeof which == "undefined") {
457 carousel._itemsTable.size = size; // save the size for later
464 * Return the index of the first item in the view port for displaying item
467 * @method getFirstVisibleForPosition
468 * @param pos {Number} The position of the item to be displayed
471 function getFirstVisibleForPosition(pos) {
472 var num = this.get("numVisible");
474 return Math.floor(pos / num) * num;
478 * Return the scrolling offset size given the number of elements to
481 * @method getScrollOffset
482 * @param delta {Number} The delta number of elements to scroll by.
485 function getScrollOffset(delta) {
489 itemSize = getCarouselItemSize.call(this);
490 size = itemSize * delta;
492 // XXX: really, when the orientation is vertical, the scrolling
493 // is not exactly the number of elements into element size.
494 if (this.get("isVertical")) {
502 * Scroll the Carousel by a page backward.
504 * @method scrollPageBackward
505 * @param {Event} ev The event object
506 * @param {Object} obj The context object
509 function scrollPageBackward(ev, obj) {
510 obj.scrollPageBackward();
511 Event.preventDefault(ev);
515 * Scroll the Carousel by a page forward.
517 * @method scrollPageForward
518 * @param {Event} ev The event object
519 * @param {Object} obj The context object
522 function scrollPageForward(ev, obj) {
523 obj.scrollPageForward();
524 Event.preventDefault(ev);
528 * Set the selected item.
530 * @method setItemSelection
531 * @param {Number} newpos The index of the new position
532 * @param {Number} oldpos The index of the previous position
535 function setItemSelection(newpos, oldpos) {
537 cssClass = carousel.CLASSES,
539 firstItem = carousel._firstItem,
540 isCircular = carousel.get("isCircular"),
541 numItems = carousel.get("numItems"),
542 numVisible = carousel.get("numVisible"),
544 sentinel = firstItem + numVisible - 1;
546 if (position >= 0 && position < numItems) {
547 if (!JS.isUndefined(carousel._itemsTable.items[position])) {
548 el = Dom.get(carousel._itemsTable.items[position].id);
550 Dom.removeClass(el, cssClass.SELECTED_ITEM);
555 if (JS.isNumber(newpos)) {
556 newpos = parseInt(newpos, 10);
557 newpos = JS.isNumber(newpos) ? newpos : 0;
562 if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
563 newpos = getFirstVisibleForPosition.call(carousel, newpos);
564 carousel.scrollTo(newpos); // still loading the item
567 if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
568 el = Dom.get(carousel._itemsTable.items[newpos].id);
570 Dom.addClass(el, cssClass.SELECTED_ITEM);
574 if (newpos < firstItem || newpos > sentinel) { // out of focus
575 newpos = getFirstVisibleForPosition.call(carousel, newpos);
576 carousel.scrollTo(newpos);
581 * Fire custom events for enabling/disabling navigation elements.
583 * @method syncNavigation
586 function syncNavigation() {
589 cssClass = carousel.CLASSES,
594 // Don't do anything if the Carousel is not rendered
595 if (!carousel._hasRendered) {
599 navigation = carousel.get("navigation");
600 sentinel = carousel._firstItem + carousel.get("numVisible");
602 if (navigation.prev) {
603 if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
604 if (carousel.get("numItems") === 0 ||
605 !carousel.get("isCircular")) {
606 Event.removeListener(navigation.prev, "click",
608 Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
609 for (i = 0; i < carousel._navBtns.prev.length; i++) {
610 carousel._navBtns.prev[i].setAttribute("disabled",
613 carousel._prevEnabled = false;
615 attach = !carousel._prevEnabled;
618 attach = !carousel._prevEnabled;
622 Event.on(navigation.prev, "click", scrollPageBackward,
624 Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
625 for (i = 0; i < carousel._navBtns.prev.length; i++) {
626 carousel._navBtns.prev[i].removeAttribute("disabled");
628 carousel._prevEnabled = true;
633 if (navigation.next) {
634 if (sentinel >= carousel.get("numItems")) {
635 if (!carousel.get("isCircular")) {
636 Event.removeListener(navigation.next, "click",
638 Dom.addClass(navigation.next, cssClass.DISABLED);
639 for (i = 0; i < carousel._navBtns.next.length; i++) {
640 carousel._navBtns.next[i].setAttribute("disabled",
643 carousel._nextEnabled = false;
645 attach = !carousel._nextEnabled;
648 attach = !carousel._nextEnabled;
652 Event.on(navigation.next, "click", scrollPageForward,
654 Dom.removeClass(navigation.next, cssClass.DISABLED);
655 for (i = 0; i < carousel._navBtns.next.length; i++) {
656 carousel._navBtns.next[i].removeAttribute("disabled");
658 carousel._nextEnabled = true;
662 carousel.fireEvent(navigationStateChangeEvent,
663 { next: carousel._nextEnabled, prev: carousel._prevEnabled });
667 * Synchronize and redraw the Pager UI if necessary.
669 * @method syncPagerUi
672 function syncPagerUi(page) {
673 var carousel = this, numPages, numVisible;
675 // Don't do anything if the Carousel is not rendered
676 if (!carousel._hasRendered) {
680 numVisible = carousel.get("numVisible");
682 if (!JS.isNumber(page)) {
683 page = Math.ceil(carousel.get("selectedItem") / numVisible);
685 numPages = Math.ceil(carousel.get("numItems") / numVisible);
687 carousel._pages.num = numPages;
688 carousel._pages.cur = page;
690 if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
691 carousel._updatePagerMenu();
693 carousel._updatePagerButtons();
699 * Call the appropriate methods on events fired when an item is added, or
700 * removed for synchronizing the DOM.
703 * @param {Object} o The item that needs to be added or removed
709 if (!JS.isObject(o)) {
715 carousel._syncUiForItemAdd(o);
717 case itemRemovedEvent:
718 carousel._syncUiForItemRemove(o);
721 carousel._syncUiForLazyLoading(o);
725 carousel.fireEvent(uiUpdateEvent);
729 * Update the state variables after scrolling the Carousel view port.
731 * @method updateStateAfterScroll
732 * @param {Integer} item The index to which the Carousel has scrolled to.
733 * @param {Integer} sentinel The last element in the view port.
736 function updateStateAfterScroll(item, sentinel) {
738 page = carousel.get("currentPage"),
740 numPerPage = carousel.get("numVisible");
742 newPage = parseInt(carousel._firstItem / numPerPage, 10);
743 if (newPage != page) {
744 carousel.setAttributeConfig("currentPage", { value: newPage });
745 carousel.fireEvent(pageChangeEvent, newPage);
748 if (carousel.get("selectOnScroll")) {
749 if (carousel.get("selectedItem") != carousel._selectedItem) {
750 carousel.set("selectedItem", carousel._selectedItem);
754 clearTimeout(carousel._autoPlayTimer);
755 delete carousel._autoPlayTimer;
756 if (carousel.isAutoPlayOn()) {
757 carousel.startAutoPlay();
760 carousel.fireEvent(afterScrollEvent,
761 { first: carousel._firstItem,
767 * Static members and methods of the Carousel component
771 * Return the appropriate Carousel object based on the id associated with
772 * the Carousel element or false if none match.
777 Carousel.getById = function (id) {
778 return instances[id] ? instances[id].object : false;
781 YAHOO.extend(Carousel, YAHOO.util.Element, {
784 * Internal variables used within the Carousel component
788 * The Animation object.
796 * The Carousel element.
798 * @property _carouselEl
804 * The Carousel clipping container element.
812 * The current first index of the Carousel.
814 * @property _firstItem
820 * Does the Carousel element have focus?
822 * @property _hasFocus
828 * Is the Carousel rendered already?
830 * @property _hasRendered
836 * Is the animation still in progress?
838 * @property _isAnimationInProgress
841 _isAnimationInProgress: false,
844 * Is the auto-scrolling of Carousel in progress?
846 * @property _isAutoPlayInProgress
849 _isAutoPlayInProgress: false,
852 * The table of items in the Carousel.
853 * The numItems is the number of items in the Carousel, items being the
854 * array of items in the Carousel. The size is the size of a single
855 * item in the Carousel. It is cached here for efficiency (to avoid
856 * computing the size multiple times).
858 * @property _itemsTable
864 * The Carousel navigation buttons.
872 * The Carousel navigation.
880 * Status of the next navigation item.
882 * @property _nextEnabled
888 * The Carousel pages structure.
889 * This is an object of the total number of pages and the current page.
897 * Status of the previous navigation item.
899 * @property _prevEnabled
905 * Whether the Carousel size needs to be recomputed or not?
907 * @property _recomputeSize
910 _recomputeSize: true,
913 * CSS classes used by the Carousel component
919 * The class name of the Carousel navigation buttons.
922 * @default "yui-carousel-button"
924 BUTTON: "yui-carousel-button",
927 * The class name of the Carousel element.
930 * @default "yui-carousel"
932 CAROUSEL: "yui-carousel",
935 * The class name of the container of the items in the Carousel.
937 * @property CAROUSEL_EL
938 * @default "yui-carousel-element"
940 CAROUSEL_EL: "yui-carousel-element",
943 * The class name of the Carousel's container element.
945 * @property CONTAINER
946 * @default "yui-carousel-container"
948 CONTAINER: "yui-carousel-container",
951 * The class name of the Carousel's container element.
954 * @default "yui-carousel-content"
956 CONTENT: "yui-carousel-content",
959 * The class name of a disabled navigation button.
962 * @default "yui-carousel-button-disabled"
964 DISABLED: "yui-carousel-button-disabled",
967 * The class name of the first Carousel navigation button.
969 * @property FIRST_NAV
970 * @default " yui-carousel-first-button"
972 FIRST_NAV: " yui-carousel-first-button",
975 * The class name of a first disabled navigation button.
977 * @property FIRST_NAV_DISABLED
978 * @default "yui-carousel-first-button-disabled"
980 FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
983 * The class name of a first page element.
985 * @property FIRST_PAGE
986 * @default "yui-carousel-nav-first-page"
988 FIRST_PAGE: "yui-carousel-nav-first-page",
991 * The class name of the Carousel navigation button that has focus.
993 * @property FOCUSSED_BUTTON
994 * @default "yui-carousel-button-focus"
996 FOCUSSED_BUTTON: "yui-carousel-button-focus",
999 * The class name of a horizontally oriented Carousel.
1001 * @property HORIZONTAL
1002 * @default "yui-carousel-horizontal"
1004 HORIZONTAL: "yui-carousel-horizontal",
1007 * The element to be used as the progress indicator when the item
1008 * is still being loaded.
1010 * @property ITEM_LOADING
1011 * @default The progress indicator (spinner) image CSS class
1013 ITEM_LOADING: "yui-carousel-item-loading",
1016 * The class name that will be set if the Carousel adjusts itself
1017 * for a minimum width.
1019 * @property MIN_WIDTH
1020 * @default "yui-carousel-min-width"
1022 MIN_WIDTH: "yui-carousel-min-width",
1025 * The navigation element container class name.
1027 * @property NAVIGATION
1028 * @default "yui-carousel-nav"
1030 NAVIGATION: "yui-carousel-nav",
1033 * The class name of the next Carousel navigation button.
1035 * @property NEXT_NAV
1036 * @default " yui-carousel-next-button"
1038 NEXT_NAV: " yui-carousel-next-button",
1041 * The class name of the next navigation link. This variable is
1042 * not only used for styling, but also for identifying the link
1043 * within the Carousel container.
1045 * @property NEXT_PAGE
1046 * @default "yui-carousel-next"
1048 NEXT_PAGE: "yui-carousel-next",
1051 * The class name for the navigation container for prev/next.
1053 * @property NAV_CONTAINER
1054 * @default "yui-carousel-buttons"
1056 NAV_CONTAINER: "yui-carousel-buttons",
1059 * The class name of the focussed page navigation. This class is
1060 * specifically used for the ugly focus handling in Opera.
1062 * @property PAGE_FOCUS
1063 * @default "yui-carousel-nav-page-focus"
1065 PAGE_FOCUS: "yui-carousel-nav-page-focus",
1068 * The class name of the previous navigation link. This variable
1069 * is not only used for styling, but also for identifying the link
1070 * within the Carousel container.
1072 * @property PREV_PAGE
1073 * @default "yui-carousel-prev"
1075 PREV_PAGE: "yui-carousel-prev",
1078 * The class name of the selected item.
1080 * @property SELECTED_ITEM
1081 * @default "yui-carousel-item-selected"
1083 SELECTED_ITEM: "yui-carousel-item-selected",
1086 * The class name of the selected paging navigation.
1088 * @property SELECTED_NAV
1089 * @default "yui-carousel-nav-page-selected"
1091 SELECTED_NAV: "yui-carousel-nav-page-selected",
1094 * The class name of a vertically oriented Carousel.
1096 * @property VERTICAL
1097 * @default "yui-carousel-vertical"
1099 VERTICAL: "yui-carousel-vertical",
1102 * The class name of the (vertical) Carousel's container element.
1104 * @property VERTICAL_CONTAINER
1105 * @default "yui-carousel-vertical-container"
1107 VERTICAL_CONTAINER: "yui-carousel-vertical-container",
1110 * The class name of a visible Carousel.
1113 * @default "yui-carousel-visible"
1115 VISIBLE: "yui-carousel-visible"
1120 * Configuration attributes for configuring the Carousel component
1126 * The offset of the first visible item in the Carousel.
1128 * @property FIRST_VISIBLE
1134 * The minimum width of the horizontal Carousel container to support
1135 * the navigation buttons.
1137 * @property HORZ_MIN_WIDTH
1140 HORZ_MIN_WIDTH: 180,
1143 * The maximum number of pager buttons allowed beyond which the UI
1144 * of the pager would be a drop-down of pages instead of buttons.
1146 * @property MAX_PAGER_BUTTONS
1149 MAX_PAGER_BUTTONS: 5,
1152 * The minimum width of the vertical Carousel container to support
1153 * the navigation buttons.
1155 * @property VERT_MIN_WIDTH
1161 * The number of visible items in the Carousel.
1163 * @property NUM_VISIBLE
1171 * Internationalizable strings in the Carousel component
1177 * The content to be used as the progress indicator when the item
1178 * is still being loaded.
1180 * @property ITEM_LOADING_CONTENT
1181 * @default "Loading"
1183 ITEM_LOADING_CONTENT: "Loading",
1186 * The next navigation button name/text.
1188 * @property NEXT_BUTTON_TEXT
1189 * @default "Next Page"
1191 NEXT_BUTTON_TEXT: "Next Page",
1194 * The prefix text for the pager in case the UI is a drop-down.
1196 * @property PAGER_PREFIX_TEXT
1197 * @default "Go to page "
1199 PAGER_PREFIX_TEXT: "Go to page ",
1202 * The previous navigation button name/text.
1204 * @property PREVIOUS_BUTTON_TEXT
1205 * @default "Previous Page"
1207 PREVIOUS_BUTTON_TEXT: "Previous Page"
1212 * Public methods of the Carousel component
1216 * Insert or append an item to the Carousel.
1220 * @param item {String | Object | HTMLElement} The item to be appended
1221 * to the Carousel. If the parameter is a string, it is assumed to be
1222 * the content of the newly created item. If the parameter is an
1223 * object, it is assumed to supply the content and an optional class
1224 * and an optional id of the newly created item.
1225 * @param index {Number} optional The position to where in the list
1226 * (starts from zero).
1227 * @return {Boolean} Return true on success, false otherwise
1229 addItem: function (item, index) {
1230 var carousel = this,
1234 numItems = carousel.get("numItems");
1240 if (JS.isString(item) || item.nodeName) {
1241 content = item.nodeName ? item.innerHTML : item;
1242 } else if (JS.isObject(item)) {
1243 content = item.content;
1248 className = item.className || "";
1249 elId = item.id ? item.id : Dom.generateId();
1251 if (JS.isUndefined(index)) {
1252 carousel._itemsTable.items.push({
1254 className : className,
1258 if (index < 0 || index >= numItems) {
1261 carousel._itemsTable.items.splice(index, 0, {
1263 className : className,
1267 carousel._itemsTable.numItems++;
1269 if (numItems < carousel._itemsTable.items.length) {
1270 carousel.set("numItems", carousel._itemsTable.items.length);
1273 carousel.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent });
1279 * Insert or append multiple items to the Carousel.
1283 * @param items {Array} An array of items to be added with each item
1284 * representing an item, index pair [{item, index}, ...]
1285 * @return {Boolean} Return true on success, false otherwise
1287 addItems: function (items) {
1288 var i, n, rv = true;
1290 if (!JS.isArray(items)) {
1294 for (i = 0, n = items.length; i < n; i++) {
1295 if (this.addItem(items[i][0], items[i][1]) === false) {
1304 * Remove focus from the Carousel.
1310 this._carouselEl.blur();
1311 this.fireEvent(blurEvent);
1315 * Clears the items from Carousel.
1317 * @method clearItems
1320 clearItems: function () {
1321 var carousel = this, n = carousel.get("numItems");
1324 if (!carousel.removeItem(0)) {
1327 For dynamic loading, the numItems may be much larger than
1328 the actual number of items in the table. So, set the
1329 numItems to zero, and break out of the loop if the table
1332 if (carousel._itemsTable.numItems === 0) {
1333 carousel.set("numItems", 0);
1339 carousel.fireEvent(allItemsRemovedEvent);
1343 * Set focus on the Carousel.
1348 focus: function () {
1349 var carousel = this,
1352 isSelectionInvisible,
1360 // Don't do anything if the Carousel is not rendered
1361 if (!carousel._hasRendered) {
1365 if (carousel.isAnimating()) {
1366 // this messes up real bad!
1370 selItem = carousel.get("selectedItem");
1371 numVisible = carousel.get("numVisible");
1372 selectOnScroll = carousel.get("selectOnScroll");
1373 selected = (selItem >= 0) ?
1374 carousel.getItem(selItem) : null;
1375 first = carousel.get("firstVisible");
1376 last = first + numVisible - 1;
1377 isSelectionInvisible = (selItem < first || selItem > last);
1378 focusEl = (selected && selected.id) ?
1379 Dom.get(selected.id) : null;
1380 itemsTable = carousel._itemsTable;
1382 if (!selectOnScroll && isSelectionInvisible) {
1383 focusEl = (itemsTable && itemsTable.items &&
1384 itemsTable.items[first]) ?
1385 Dom.get(itemsTable.items[first].id) : null;
1392 // ignore focus errors
1396 carousel.fireEvent(focusEvent);
1400 * Hide the Carousel.
1406 var carousel = this;
1408 if (carousel.fireEvent(beforeHideEvent) !== false) {
1409 carousel.removeClass(carousel.CLASSES.VISIBLE);
1410 carousel.fireEvent(hideEvent);
1415 * Initialize the Carousel.
1419 * @param el {HTMLElement | String} The html element that represents
1420 * the Carousel container.
1421 * @param attrs {Object} The set of configuration attributes for
1422 * creating the Carousel.
1424 init: function (el, attrs) {
1425 var carousel = this,
1426 elId = el, // save for a rainy day
1433 carousel._hasRendered = false;
1434 carousel._navBtns = { prev: [], next: [] };
1435 carousel._pages = { el: null, num: 0, cur: 0 };
1436 carousel._itemsTable = { loading: {}, numItems: 0,
1437 items: [], size: 0 };
1440 if (JS.isString(el)) {
1442 } else if (!el.nodeName) {
1446 Carousel.superclass.init.call(carousel, el, attrs);
1449 if (!el.id) { // in case the HTML element is passed
1450 el.setAttribute("id", Dom.generateId());
1452 parse = carousel._parseCarousel(el);
1454 carousel._createCarousel(elId);
1457 el = carousel._createCarousel(elId);
1461 carousel.initEvents();
1464 carousel._parseCarouselItems();
1467 if (!attrs || typeof attrs.isVertical == "undefined") {
1468 carousel.set("isVertical", false);
1471 carousel._parseCarouselNavigation(el);
1472 carousel._navEl = carousel._setupCarouselNavigation();
1474 instances[elId] = { object: carousel };
1476 carousel._loadItems();
1480 * Initialize the configuration attributes used to create the Carousel.
1482 * @method initAttributes
1484 * @param attrs {Object} The set of configuration attributes for
1485 * creating the Carousel.
1487 initAttributes: function (attrs) {
1488 var carousel = this;
1490 attrs = attrs || {};
1491 Carousel.superclass.initAttributes.call(carousel, attrs);
1494 * @attribute carouselEl
1495 * @description The type of the Carousel element.
1499 carousel.setAttributeConfig("carouselEl", {
1500 validator : JS.isString,
1501 value : attrs.carouselEl || "OL"
1505 * @attribute carouselItemEl
1506 * @description The type of the list of items within the Carousel.
1510 carousel.setAttributeConfig("carouselItemEl", {
1511 validator : JS.isString,
1512 value : attrs.carouselItemEl || "LI"
1516 * @attribute currentPage
1517 * @description The current page number (read-only.)
1520 carousel.setAttributeConfig("currentPage", {
1526 * @attribute firstVisible
1527 * @description The index to start the Carousel from (indexes begin
1532 carousel.setAttributeConfig("firstVisible", {
1533 method : carousel._setFirstVisible,
1534 validator : carousel._validateFirstVisible,
1536 attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
1540 * @attribute selectOnScroll
1541 * @description Set this to true to automatically set focus to
1542 * follow scrolling in the Carousel.
1546 carousel.setAttributeConfig("selectOnScroll", {
1547 validator : JS.isBoolean,
1548 value : attrs.selectOnScroll || true
1552 * @attribute numVisible
1553 * @description The number of visible items in the Carousel's
1558 carousel.setAttributeConfig("numVisible", {
1559 method : carousel._setNumVisible,
1560 validator : carousel._validateNumVisible,
1561 value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
1565 * @attribute numItems
1566 * @description The number of items in the Carousel.
1569 carousel.setAttributeConfig("numItems", {
1570 method : carousel._setNumItems,
1571 validator : carousel._validateNumItems,
1572 value : carousel._itemsTable.numItems
1576 * @attribute scrollIncrement
1577 * @description The number of items to scroll by for arrow keys.
1581 carousel.setAttributeConfig("scrollIncrement", {
1582 validator : carousel._validateScrollIncrement,
1583 value : attrs.scrollIncrement || 1
1587 * @attribute selectedItem
1588 * @description The index of the selected item.
1591 carousel.setAttributeConfig("selectedItem", {
1592 method : carousel._setSelectedItem,
1593 validator : JS.isNumber,
1598 * @attribute revealAmount
1599 * @description The percentage of the item to be revealed on each
1600 * side of the Carousel (before and after the first and last item
1601 * in the Carousel's viewport.)
1605 carousel.setAttributeConfig("revealAmount", {
1606 method : carousel._setRevealAmount,
1607 validator : carousel._validateRevealAmount,
1608 value : attrs.revealAmount || 0
1612 * @attribute isCircular
1613 * @description Set this to true to wrap scrolling of the contents
1618 carousel.setAttributeConfig("isCircular", {
1619 validator : JS.isBoolean,
1620 value : attrs.isCircular || false
1624 * @attribute isVertical
1625 * @description True if the orientation of the Carousel is vertical
1629 carousel.setAttributeConfig("isVertical", {
1630 method : carousel._setOrientation,
1631 validator : JS.isBoolean,
1632 value : attrs.isVertical || false
1636 * @attribute navigation
1637 * @description The set of navigation controls for Carousel
1639 * { prev: null, // the previous navigation element<br>
1640 * next: null } // the next navigation element
1643 carousel.setAttributeConfig("navigation", {
1644 method : carousel._setNavigation,
1645 validator : carousel._validateNavigation,
1647 attrs.navigation || {prev: null,next: null,page: null}
1651 * @attribute animation
1652 * @description The optional animation attributes for the Carousel.
1654 * { speed: 0, // the animation speed (in seconds)<br>
1655 * effect: null } // the animation effect (like
1656 * YAHOO.util.Easing.easeOut)
1659 carousel.setAttributeConfig("animation", {
1660 validator : carousel._validateAnimation,
1661 value : attrs.animation || { speed: 0, effect: null }
1665 * @attribute autoPlay
1666 * @description Set this to time in milli-seconds to have the
1667 * Carousel automatically scroll the contents.
1669 * @deprecated Use autoPlayInterval instead.
1671 carousel.setAttributeConfig("autoPlay", {
1672 validator : JS.isNumber,
1673 value : attrs.autoPlay || 0
1677 * @attribute autoPlayInterval
1678 * @description The delay in milli-seconds for scrolling the
1679 * Carousel during auto-play.
1680 * Note: The startAutoPlay() method needs to be invoked to trigger
1681 * automatic scrolling of Carousel.
1684 carousel.setAttributeConfig("autoPlayInterval", {
1685 validator : JS.isNumber,
1686 value : attrs.autoPlayInterval || 0
1691 * Initialize and bind the event handlers.
1693 * @method initEvents
1696 initEvents: function () {
1697 var carousel = this,
1698 cssClass = carousel.CLASSES,
1701 carousel.on("keydown", carousel._keyboardEventHandler);
1703 carousel.on(afterScrollEvent, syncNavigation);
1705 carousel.on(itemAddedEvent, syncUi);
1707 carousel.on(itemRemovedEvent, syncUi);
1709 carousel.on(itemSelectedEvent, function () {
1710 if (carousel._hasFocus) {
1715 carousel.on(loadItemsEvent, syncUi);
1717 carousel.on(allItemsRemovedEvent, function (ev) {
1718 carousel.scrollTo(0);
1719 syncNavigation.call(carousel);
1720 syncPagerUi.call(carousel);
1723 carousel.on(pageChangeEvent, syncPagerUi, carousel);
1725 carousel.on(renderEvent, function (ev) {
1726 carousel.set("selectedItem", carousel.get("firstVisible"));
1727 syncNavigation.call(carousel, ev);
1728 syncPagerUi.call(carousel, ev);
1729 carousel._setClipContainerSize();
1732 carousel.on("selectedItemChange", function (ev) {
1733 setItemSelection.call(carousel, ev.newValue, ev.prevValue);
1734 if (ev.newValue >= 0) {
1735 carousel._updateTabIndex(
1736 carousel.getElementForItem(ev.newValue));
1738 carousel.fireEvent(itemSelectedEvent, ev.newValue);
1741 carousel.on(uiUpdateEvent, function (ev) {
1742 syncNavigation.call(carousel, ev);
1743 syncPagerUi.call(carousel, ev);
1746 carousel.on("firstVisibleChange", function (ev) {
1747 if (!carousel.get("selectOnScroll")) {
1748 if (ev.newValue >= 0) {
1749 carousel._updateTabIndex(
1750 carousel.getElementForItem(ev.newValue));
1755 // Handle item selection on mouse click
1756 carousel.on("click", function (ev) {
1757 if (carousel.isAutoPlayOn()) {
1758 carousel.stopAutoPlay();
1760 carousel._itemClickHandler(ev);
1761 carousel._pagerClickHandler(ev);
1764 // Restore the focus on the navigation buttons
1766 Event.onFocus(carousel.get("element"), function (ev, obj) {
1767 var target = Event.getTarget(ev);
1769 if (target && target.nodeName.toUpperCase() == "A" &&
1770 Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
1772 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
1774 focussedLi = target.parentNode;
1775 Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
1778 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
1782 obj._hasFocus = true;
1783 obj._updateNavButtons(Event.getTarget(ev), true);
1786 Event.onBlur(carousel.get("element"), function (ev, obj) {
1787 obj._hasFocus = false;
1788 obj._updateNavButtons(Event.getTarget(ev), false);
1793 * Return true if the Carousel is still animating, or false otherwise.
1795 * @method isAnimating
1796 * @return {Boolean} Return true if animation is still in progress, or
1800 isAnimating: function () {
1801 return this._isAnimationInProgress;
1805 * Return true if the auto-scrolling of Carousel is "on", or false
1808 * @method isAutoPlayOn
1809 * @return {Boolean} Return true if autoPlay is "on", or false
1813 isAutoPlayOn: function () {
1814 return this._isAutoPlayInProgress;
1818 * Return the carouselItemEl at index or null if the index is not
1821 * @method getElementForItem
1822 * @param index {Number} The index of the item to be returned
1823 * @return {Element} Return the item at index or null if not found
1826 getElementForItem: function (index) {
1827 var carousel = this;
1829 if (index < 0 || index >= carousel.get("numItems")) {
1833 // TODO: may be cache the item
1834 if (carousel._itemsTable.numItems > index) {
1835 if (!JS.isUndefined(carousel._itemsTable.items[index])) {
1836 return Dom.get(carousel._itemsTable.items[index].id);
1844 * Return the carouselItemEl for all items in the Carousel.
1846 * @method getElementForItems
1847 * @return {Array} Return all the items
1850 getElementForItems: function () {
1851 var carousel = this, els = [], i;
1853 for (i = 0; i < carousel._itemsTable.numItems; i++) {
1854 els.push(carousel.getElementForItem(i));
1861 * Return the item at index or null if the index is not found.
1864 * @param index {Number} The index of the item to be returned
1865 * @return {Object} Return the item at index or null if not found
1868 getItem: function (index) {
1869 var carousel = this;
1871 if (index < 0 || index >= carousel.get("numItems")) {
1875 if (carousel._itemsTable.numItems > index) {
1876 if (!JS.isUndefined(carousel._itemsTable.items[index])) {
1877 return carousel._itemsTable.items[index];
1885 * Return all items as an array.
1888 * @return {Array} Return all items in the Carousel
1891 getItems: function (index) {
1892 return this._itemsTable.items;
1896 * Return the position of the Carousel item that has the id "id", or -1
1897 * if the id is not found.
1899 * @method getItemPositionById
1900 * @param index {Number} The index of the item to be returned
1903 getItemPositionById: function (id) {
1904 var carousel = this, i = 0, n = carousel._itemsTable.numItems;
1907 if (!JS.isUndefined(carousel._itemsTable.items[i])) {
1908 if (carousel._itemsTable.items[i].id == id) {
1919 * Return all visible items as an array.
1921 * @method getVisibleItems
1922 * @return {Array} The array of visible items
1925 getVisibleItems: function () {
1926 var carousel = this,
1927 i = carousel.get("firstVisible"),
1928 n = i + carousel.get("numVisible"),
1932 r.push(carousel.getElementForItem(i));
1940 * Remove an item at index from the Carousel.
1942 * @method removeItem
1944 * @param index {Number} The position to where in the list (starts from
1946 * @return {Boolean} Return true on success, false otherwise
1948 removeItem: function (index) {
1949 var carousel = this,
1951 num = carousel.get("numItems");
1953 if (index < 0 || index >= num) {
1957 item = carousel._itemsTable.items.splice(index, 1);
1958 if (item && item.length == 1) {
1959 carousel._itemsTable.numItems--;
1960 carousel.set("numItems", num - 1);
1962 carousel.fireEvent(itemRemovedEvent,
1963 { item: item[0], pos: index, ev: itemRemovedEvent });
1971 * Render the Carousel.
1975 * @param appendTo {HTMLElement | String} The element to which the
1976 * Carousel should be appended prior to rendering.
1977 * @return {Boolean} Status of the operation
1979 render: function (appendTo) {
1980 var carousel = this,
1981 cssClass = carousel.CLASSES;
1983 carousel.addClass(cssClass.CAROUSEL);
1985 if (!carousel._clipEl) {
1986 carousel._clipEl = carousel._createCarouselClip();
1987 carousel._clipEl.appendChild(carousel._carouselEl);
1991 carousel.appendChild(carousel._clipEl);
1992 carousel.appendTo(appendTo);
1994 if (!Dom.inDocument(carousel.get("element"))) {
1997 carousel.appendChild(carousel._clipEl);
2000 if (carousel.get("isVertical")) {
2001 carousel.addClass(cssClass.VERTICAL);
2003 carousel.addClass(cssClass.HORIZONTAL);
2006 if (carousel.get("numItems") < 1) {
2010 carousel._refreshUi();
2016 * Scroll the Carousel by an item backward.
2018 * @method scrollBackward
2021 scrollBackward: function () {
2022 var carousel = this;
2024 carousel.scrollTo(carousel._firstItem -
2025 carousel.get("scrollIncrement"));
2029 * Scroll the Carousel by an item forward.
2031 * @method scrollForward
2034 scrollForward: function () {
2035 var carousel = this;
2037 carousel.scrollTo(carousel._firstItem +
2038 carousel.get("scrollIncrement"));
2042 * Scroll the Carousel by a page backward.
2044 * @method scrollPageBackward
2047 scrollPageBackward: function () {
2048 var carousel = this,
2049 item = carousel._firstItem - carousel.get("numVisible");
2051 if (carousel.get("selectOnScroll")) {
2052 carousel._selectedItem = carousel._getSelectedItem(item);
2054 item = carousel._getValidIndex(item);
2056 carousel.scrollTo(item);
2060 * Scroll the Carousel by a page forward.
2062 * @method scrollPageForward
2065 scrollPageForward: function () {
2066 var carousel = this,
2067 item = carousel._firstItem + carousel.get("numVisible");
2069 if (carousel.get("selectOnScroll")) {
2070 carousel._selectedItem = carousel._getSelectedItem(item);
2072 item = carousel._getValidIndex(item);
2074 carousel.scrollTo(item);
2078 * Scroll the Carousel to make the item the first visible item.
2082 * @param item Number The index of the element to position at.
2083 * @param dontSelect Boolean True if select should be avoided
2085 scrollTo: function (item, dontSelect) {
2086 var carousel = this,
2087 animate, animCfg, isCircular, delta, direction, firstItem,
2088 numItems, numPerPage, offset, page, rv, sentinel,
2091 if (JS.isUndefined(item) || item == carousel._firstItem ||
2092 carousel.isAnimating()) {
2093 return; // nothing to do!
2096 animCfg = carousel.get("animation");
2097 isCircular = carousel.get("isCircular");
2098 firstItem = carousel._firstItem;
2099 numItems = carousel.get("numItems");
2100 numPerPage = carousel.get("numVisible");
2101 page = carousel.get("currentPage");
2102 stopAutoScroll = function () {
2103 if (carousel.isAutoPlayOn()) {
2104 carousel.stopAutoPlay();
2110 item = numItems + item;
2112 stopAutoScroll.call(carousel);
2115 } else if (numItems > 0 && item > numItems - 1) {
2116 if (carousel.get("isCircular")) {
2117 item = numItems - item;
2119 stopAutoScroll.call(carousel);
2124 direction = (carousel._firstItem > item) ? "backward" : "forward";
2126 sentinel = firstItem + numPerPage;
2127 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2128 rv = carousel.fireEvent(beforeScrollEvent,
2129 { dir: direction, first: firstItem, last: sentinel });
2130 if (rv === false) { // scrolling is prevented
2134 carousel.fireEvent(beforePageChangeEvent, { page: page });
2136 delta = firstItem - item; // yes, the delta is reverse
2137 carousel._firstItem = item;
2138 carousel.set("firstVisible", item);
2141 carousel._loadItems(); // do we have all the items to display?
2143 sentinel = item + numPerPage;
2144 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2146 offset = getScrollOffset.call(carousel, delta);
2148 animate = animCfg.speed > 0;
2151 carousel._animateAndSetCarouselOffset(offset, item, sentinel,
2154 carousel._setCarouselOffset(offset);
2155 updateStateAfterScroll.call(carousel, item, sentinel);
2160 * Select the previous item in the Carousel.
2162 * @method selectPreviousItem
2165 selectPreviousItem: function () {
2166 var carousel = this,
2168 selected = carousel.get("selectedItem");
2170 if (selected == this._firstItem) {
2171 newpos = selected - carousel.get("numVisible");
2172 carousel._selectedItem = carousel._getSelectedItem(selected-1);
2173 carousel.scrollTo(newpos);
2175 newpos = carousel.get("selectedItem") -
2176 carousel.get("scrollIncrement");
2177 carousel.set("selectedItem",carousel._getSelectedItem(newpos));
2182 * Select the next item in the Carousel.
2184 * @method selectNextItem
2187 selectNextItem: function () {
2188 var carousel = this, newpos = 0;
2190 newpos = carousel.get("selectedItem") +
2191 carousel.get("scrollIncrement");
2192 carousel.set("selectedItem", carousel._getSelectedItem(newpos));
2196 * Display the Carousel.
2202 var carousel = this,
2203 cssClass = carousel.CLASSES;
2205 if (carousel.fireEvent(beforeShowEvent) !== false) {
2206 carousel.addClass(cssClass.VISIBLE);
2207 carousel.fireEvent(showEvent);
2212 * Start auto-playing the Carousel.
2214 * @method startAutoPlay
2217 startAutoPlay: function () {
2218 var carousel = this, timer;
2220 if (JS.isUndefined(carousel._autoPlayTimer)) {
2221 timer = carousel.get("autoPlayInterval");
2225 carousel._isAutoPlayInProgress = true;
2226 carousel.fireEvent(startAutoPlayEvent);
2227 carousel._autoPlayTimer = setTimeout(function () {
2228 carousel._autoScroll();
2234 * Stop auto-playing the Carousel.
2236 * @method stopAutoPlay
2239 stopAutoPlay: function () {
2240 var carousel = this;
2242 if (!JS.isUndefined(carousel._autoPlayTimer)) {
2243 clearTimeout(carousel._autoPlayTimer);
2244 delete carousel._autoPlayTimer;
2245 carousel._isAutoPlayInProgress = false;
2246 carousel.fireEvent(stopAutoPlayEvent);
2251 * Return the string representation of the Carousel.
2257 toString: function () {
2258 return WidgetName + (this.get ? " (#" + this.get("id") + ")" : "");
2262 * Protected methods of the Carousel component
2266 * Set the Carousel offset to the passed offset after animating.
2268 * @method _animateAndSetCarouselOffset
2269 * @param {Integer} offset The offset to which the Carousel has to be
2271 * @param {Integer} item The index to which the Carousel will scroll.
2272 * @param {Integer} sentinel The last element in the view port.
2275 _animateAndSetCarouselOffset: function (offset, item, sentinel) {
2276 var carousel = this,
2277 animCfg = carousel.get("animation"),
2280 if (carousel.get("isVertical")) {
2281 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2282 { points: { by: [0, offset] } },
2283 animCfg.speed, animCfg.effect);
2285 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2286 { points: { by: [offset, 0] } },
2287 animCfg.speed, animCfg.effect);
2290 carousel._isAnimationInProgress = true;
2291 animObj.onComplete.subscribe(carousel._animationCompleteHandler,
2292 { scope: carousel, item: item,
2298 * Handle the animation complete event.
2300 * @method _animationCompleteHandler
2301 * @param {Event} ev The event.
2302 * @param {Array} p The event parameters.
2303 * @param {Object} o The object that has the state of the Carousel
2306 _animationCompleteHandler: function (ev, p, o) {
2307 o.scope._isAnimationInProgress = false;
2308 updateStateAfterScroll.call(o.scope, o.item, o.last);
2312 * Automatically scroll the contents of the Carousel.
2313 * @method _autoScroll
2316 _autoScroll: function() {
2317 var carousel = this,
2318 currIndex = carousel._firstItem,
2321 if (currIndex >= carousel.get("numItems") - 1) {
2322 if (carousel.get("isCircular")) {
2325 carousel.stopAutoPlay();
2328 index = currIndex + carousel.get("numVisible");
2331 carousel._selectedItem = carousel._getSelectedItem(index);
2332 carousel.scrollTo.call(carousel, index);
2336 * Create the Carousel.
2338 * @method createCarousel
2339 * @param elId {String} The id of the element to be created
2342 _createCarousel: function (elId) {
2343 var carousel = this,
2344 cssClass = carousel.CLASSES,
2348 el = createElement("DIV", {
2349 className : cssClass.CAROUSEL,
2354 if (!carousel._carouselEl) {
2355 carousel._carouselEl=createElement(carousel.get("carouselEl"),
2356 { className: cssClass.CAROUSEL_EL });
2363 * Create the Carousel clip container.
2365 * @method createCarouselClip
2368 _createCarouselClip: function () {
2369 return createElement("DIV", { className: this.CLASSES.CONTENT });
2373 * Create the Carousel item.
2375 * @method createCarouselItem
2376 * @param obj {Object} The attributes of the element to be created
2379 _createCarouselItem: function (obj) {
2380 return createElement(this.get("carouselItemEl"), {
2381 className : obj.className,
2382 content : obj.content,
2388 * Return a valid item for a possibly out of bounds index considering
2389 * the isCircular property.
2391 * @method _getValidIndex
2392 * @param index {Number} The index of the item to be returned
2393 * @return {Object} Return a valid item index
2396 _getValidIndex: function (index) {
2397 var carousel = this,
2398 isCircular = carousel.get("isCircular"),
2399 numItems = carousel.get("numItems"),
2400 sentinel = numItems - 1;
2403 index = isCircular ? numItems + index : 0;
2404 } else if (index > sentinel) {
2405 index = isCircular ? index - numItems : sentinel;
2412 * Get the value for the selected item.
2414 * @method _getSelectedItem
2415 * @param val {Number} The new value for "selected" item
2416 * @return {Number} The new value that would be set
2419 _getSelectedItem: function (val) {
2420 var carousel = this,
2421 isCircular = carousel.get("isCircular"),
2422 numItems = carousel.get("numItems"),
2423 sentinel = numItems - 1;
2427 val = numItems + val;
2429 val = carousel.get("selectedItem");
2431 } else if (val > sentinel) {
2433 val = val - numItems;
2435 val = carousel.get("selectedItem");
2443 * The "click" handler for the item.
2445 * @method _itemClickHandler
2446 * @param {Event} ev The event object
2449 _itemClickHandler: function (ev) {
2450 var carousel = this,
2451 container = carousel.get("element"),
2454 target = YAHOO.util.Event.getTarget(ev);
2456 while (target && target != container &&
2457 target.id != carousel._carouselEl) {
2458 el = target.nodeName;
2459 if (el.toUpperCase() == carousel.get("carouselItemEl")) {
2462 target = target.parentNode;
2465 if ((item = carousel.getItemPositionById(target.id)) >= 0) {
2466 carousel.set("selectedItem", carousel._getSelectedItem(item));
2472 * The keyboard event handler for Carousel.
2474 * @method _keyboardEventHandler
2475 * @param ev {Event} The event that is being handled.
2478 _keyboardEventHandler: function (ev) {
2479 var carousel = this,
2480 key = Event.getCharCode(ev),
2483 if (carousel.isAnimating()) {
2484 return; // do not mess while animation is in progress
2488 case 0x25: // left arrow
2489 case 0x26: // up arrow
2490 carousel.selectPreviousItem();
2493 case 0x27: // right arrow
2494 case 0x28: // down arrow
2495 carousel.selectNextItem();
2498 case 0x21: // page-up
2499 carousel.scrollPageBackward();
2502 case 0x22: // page-down
2503 carousel.scrollPageForward();
2509 if (carousel.isAutoPlayOn()) {
2510 carousel.stopAutoPlay();
2512 Event.preventDefault(ev);
2517 * The load the required set of items that are needed for display.
2519 * @method _loadItems
2522 _loadItems: function() {
2523 var carousel = this,
2524 first = carousel.get("firstVisible"),
2526 numItems = carousel.get("numItems"),
2527 numVisible = carousel.get("numVisible"),
2528 reveal = carousel.get("revealAmount");
2530 last = first + numVisible - 1 + (reveal ? 1 : 0);
2531 last = last > numItems - 1 ? numItems - 1 : last;
2533 if (!carousel.getItem(first) || !carousel.getItem(last)) {
2534 carousel.fireEvent(loadItemsEvent, {
2535 ev: loadItemsEvent, first: first, last: last,
2542 * The "click" handler for the pager navigation.
2544 * @method _pagerClickHandler
2545 * @param {Event} ev The event object
2548 _pagerClickHandler: function (ev) {
2549 var carousel = this,
2551 target = Event.getTarget(ev),
2554 function getPagerNode(el) {
2555 var itemEl = carousel.get("carouselItemEl");
2557 if (el.nodeName.toUpperCase() == itemEl.toUpperCase()) {
2558 el = Dom.getChildrenBy(el, function (node) {
2559 // either an anchor or select at least
2560 return node.href || node.value;
2565 } else if (el.href || el.value) {
2573 target = getPagerNode(target);
2577 val = target.href || target.value;
2578 if (JS.isString(val) && val) {
2579 pos = val.lastIndexOf("#");
2581 val = carousel.getItemPositionById(
2582 val.substring(pos + 1));
2583 carousel._selectedItem = val;
2584 carousel.scrollTo(val);
2585 if (!target.value) { // not a select element
2588 Event.preventDefault(ev);
2595 * Find the Carousel within a container. The Carousel is identified by
2596 * the first element that matches the carousel element tag or the
2597 * element that has the Carousel class.
2599 * @method parseCarousel
2600 * @param parent {HTMLElement} The parent element to look under
2601 * @return {Boolean} True if Carousel is found, false otherwise
2604 _parseCarousel: function (parent) {
2605 var carousel = this, child, cssClass, domEl, found, node;
2607 cssClass = carousel.CLASSES;
2608 domEl = carousel.get("carouselEl");
2611 for (child = parent.firstChild; child; child = child.nextSibling) {
2612 if (child.nodeType == 1) {
2613 node = child.nodeName;
2614 if (node.toUpperCase() == domEl) {
2615 carousel._carouselEl = child;
2616 Dom.addClass(carousel._carouselEl,
2617 carousel.CLASSES.CAROUSEL_EL);
2627 * Find the items within the Carousel and add them to the items table.
2628 * A Carousel item is identified by elements that matches the carousel
2631 * @method parseCarouselItems
2634 _parseCarouselItems: function () {
2635 var carousel = this,
2640 parent = carousel._carouselEl;
2642 domItemEl = carousel.get("carouselItemEl");
2644 for (child = parent.firstChild; child; child = child.nextSibling) {
2645 if (child.nodeType == 1) {
2646 node = child.nodeName;
2647 if (node.toUpperCase() == domItemEl) {
2651 elId = Dom.generateId();
2652 child.setAttribute("id", elId);
2654 carousel.addItem(child);
2661 * Find the Carousel navigation within a container. The navigation
2662 * elements need to match the carousel navigation class names.
2664 * @method parseCarouselNavigation
2665 * @param parent {HTMLElement} The parent element to look under
2666 * @return {Boolean} True if at least one is found, false otherwise
2669 _parseCarouselNavigation: function (parent) {
2670 var carousel = this,
2672 cssClass = carousel.CLASSES,
2679 nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent);
2680 if (nav.length > 0) {
2682 if (nav.hasOwnProperty(i)) {
2684 if (el.nodeName == "INPUT" ||
2685 el.nodeName == "BUTTON") {
2686 carousel._navBtns.prev.push(el);
2688 j = el.getElementsByTagName("INPUT");
2689 if (JS.isArray(j) && j.length > 0) {
2690 carousel._navBtns.prev.push(j[0]);
2692 j = el.getElementsByTagName("BUTTON");
2693 if (JS.isArray(j) && j.length > 0) {
2694 carousel._navBtns.prev.push(j[0]);
2700 cfg = { prev: nav };
2703 nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent);
2704 if (nav.length > 0) {
2706 if (nav.hasOwnProperty(i)) {
2708 if (el.nodeName == "INPUT" ||
2709 el.nodeName == "BUTTON") {
2710 carousel._navBtns.next.push(el);
2712 j = el.getElementsByTagName("INPUT");
2713 if (JS.isArray(j) && j.length > 0) {
2714 carousel._navBtns.next.push(j[0]);
2716 j = el.getElementsByTagName("BUTTON");
2717 if (JS.isArray(j) && j.length > 0) {
2718 carousel._navBtns.next.push(j[0]);
2727 cfg = { next: nav };
2732 carousel.set("navigation", cfg);
2740 * Refresh the widget UI if it is not already rendered, on first item
2743 * @method _refreshUi
2746 _refreshUi: function () {
2747 var carousel = this;
2749 // Set the rendered state appropriately.
2750 carousel._hasRendered = true;
2751 carousel.fireEvent(renderEvent);
2755 * Set the Carousel offset to the passed offset.
2757 * @method _setCarouselOffset
2760 _setCarouselOffset: function (offset) {
2761 var carousel = this, which;
2763 which = carousel.get("isVertical") ? "top" : "left";
2764 offset += offset !== 0 ? getStyle(carousel._carouselEl, which) : 0;
2765 Dom.setStyle(carousel._carouselEl, which, offset + "px");
2769 * Setup/Create the Carousel navigation element (if needed).
2771 * @method _setupCarouselNavigation
2774 _setupCarouselNavigation: function () {
2775 var carousel = this,
2776 btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
2778 cssClass = carousel.CLASSES;
2780 // TODO: can the _navBtns be tested against instead?
2781 navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
2782 "DIV", carousel.get("element"));
2784 if (navContainer.length === 0) {
2785 navContainer = createElement("DIV",
2786 { className: cssClass.NAVIGATION });
2787 carousel.insertBefore(navContainer,
2788 Dom.getFirstChild(carousel.get("element")));
2790 navContainer = navContainer[0];
2793 carousel._pages.el = createElement("UL");
2794 navContainer.appendChild(carousel._pages.el);
2796 nav = carousel.get("navigation");
2797 if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
2798 if (JS.isString(nav.prev)) {
2799 nav.prev = [nav.prev];
2801 for (btn in nav.prev) {
2802 if (nav.prev.hasOwnProperty(btn)) {
2803 carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
2807 // TODO: separate method for creating a navigation button
2808 prevButton = createElement("SPAN",
2809 { className: cssClass.BUTTON + cssClass.FIRST_NAV });
2811 Dom.setStyle(prevButton, "visibility", "visible");
2812 btn = Dom.generateId();
2813 prevButton.innerHTML = "<button type=\"button\" " +
2814 "id=\"" + btn + "\" name=\"" +
2815 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">" +
2816 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>";
2817 navContainer.appendChild(prevButton);
2819 carousel._navBtns.prev = [btn];
2820 cfg = { prev: [prevButton] };
2823 if (JS.isString(nav.next) || JS.isArray(nav.next)) {
2824 if (JS.isString(nav.next)) {
2825 nav.next = [nav.next];
2827 for (btn in nav.next) {
2828 if (nav.next.hasOwnProperty(btn)) {
2829 carousel._navBtns.next.push(Dom.get(nav.next[btn]));
2833 // TODO: separate method for creating a navigation button
2834 nextButton = createElement("SPAN",
2835 { className: cssClass.BUTTON + cssClass.NEXT_NAV });
2837 Dom.setStyle(nextButton, "visibility", "visible");
2838 btn = Dom.generateId();
2839 nextButton.innerHTML = "<button type=\"button\" " +
2840 "id=\"" + btn + "\" name=\"" +
2841 carousel.STRINGS.NEXT_BUTTON_TEXT + "\">" +
2842 carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>";
2843 navContainer.appendChild(nextButton);
2845 carousel._navBtns.next = [btn];
2847 cfg.next = [nextButton];
2849 cfg = { next: [nextButton] };
2854 carousel.set("navigation", cfg);
2857 return navContainer;
2861 * Set the clip container size (based on the new numVisible value).
2863 * @method _setClipContainerSize
2864 * @param clip {HTMLElement} The clip container element.
2865 * @param num {Number} optional The number of items per page.
2868 _setClipContainerSize: function (clip, num) {
2869 var carousel = this,
2870 attr, currVal, isVertical, itemSize, reveal, size, which;
2872 isVertical = carousel.get("isVertical");
2873 reveal = carousel.get("revealAmount");
2874 which = isVertical ? "height" : "width";
2875 attr = isVertical ? "top" : "left";
2877 clip = clip || carousel._clipEl;
2882 num = num || carousel.get("numVisible");
2883 itemSize = getCarouselItemSize.call(carousel, which);
2884 size = itemSize * num;
2886 // TODO: try to re-use the _hasRendered indicator
2887 carousel._recomputeSize = (size === 0); // bleh!
2888 if (carousel._recomputeSize) {
2889 carousel._hasRendered = false;
2890 return; // no use going further, bail out!
2894 reveal = itemSize * (reveal / 100) * 2;
2896 // TODO: set the Carousel's initial offset somwehere
2897 currVal = parseFloat(Dom.getStyle(carousel._carouselEl, attr));
2898 currVal = JS.isNumber(currVal) ? currVal : 0;
2899 Dom.setStyle(carousel._carouselEl,
2900 attr, currVal + (reveal / 2) + "px");
2904 size += getStyle(carousel._carouselEl, "marginTop") +
2905 getStyle(carousel._carouselEl, "marginBottom") +
2906 getStyle(carousel._carouselEl, "paddingTop") +
2907 getStyle(carousel._carouselEl, "paddingBottom") +
2908 getStyle(carousel._carouselEl, "borderTopWidth") +
2909 getStyle(carousel._carouselEl, "borderBottomWidth");
2910 // XXX: for vertical Carousel
2911 Dom.setStyle(clip, which, (size - (num - 1)) + "px");
2913 size += getStyle(carousel._carouselEl, "marginLeft") +
2914 getStyle(carousel._carouselEl, "marginRight") +
2915 getStyle(carousel._carouselEl, "paddingLeft") +
2916 getStyle(carousel._carouselEl, "paddingRight") +
2917 getStyle(carousel._carouselEl, "borderLeftWidth") +
2918 getStyle(carousel._carouselEl, "borderRightWidth");
2919 Dom.setStyle(clip, which, size + "px");
2922 carousel._setContainerSize(clip); // adjust the container size too
2926 * Set the container size.
2928 * @method _setContainerSize
2929 * @param clip {HTMLElement} The clip container element.
2930 * @param attr {String} Either set the height or width.
2933 _setContainerSize: function (clip, attr) {
2934 var carousel = this,
2935 config = carousel.CONFIG,
2936 cssClass = carousel.CLASSES,
2940 isVertical = carousel.get("isVertical");
2941 clip = clip || carousel._clipEl;
2942 attr = attr || (isVertical ? "height" : "width");
2943 size = parseFloat(Dom.getStyle(clip, attr), 10);
2945 size = JS.isNumber(size) ? size : 0;
2948 size += getStyle(carousel._carouselEl, "marginTop") +
2949 getStyle(carousel._carouselEl, "marginBottom") +
2950 getStyle(carousel._carouselEl, "paddingTop") +
2951 getStyle(carousel._carouselEl, "paddingBottom") +
2952 getStyle(carousel._carouselEl, "borderTopWidth") +
2953 getStyle(carousel._carouselEl, "borderBottomWidth") +
2954 getStyle(carousel._navEl, "height");
2956 size += getStyle(clip, "marginLeft") +
2957 getStyle(clip, "marginRight") +
2958 getStyle(clip, "paddingLeft") +
2959 getStyle(clip, "paddingRight") +
2960 getStyle(clip, "borderLeftWidth") +
2961 getStyle(clip, "borderRightWidth");
2965 if (size < config.HORZ_MIN_WIDTH) {
2966 size = config.HORZ_MIN_WIDTH;
2967 carousel.addClass(cssClass.MIN_WIDTH);
2970 carousel.setStyle(attr, size + "px");
2972 // Additionally the width of the container should be set for
2973 // the vertical Carousel
2975 size = getCarouselItemSize.call(carousel, "width");
2976 if (size < config.VERT_MIN_WIDTH) {
2977 size = config.VERT_MIN_WIDTH;
2978 carousel.addClass(cssClass.MIN_WIDTH);
2980 carousel.setStyle("width", size + "px");
2985 * Set the value for the Carousel's first visible item.
2987 * @method _setFirstVisible
2988 * @param val {Number} The new value for firstVisible
2989 * @return {Number} The new value that would be set
2992 _setFirstVisible: function (val) {
2993 var carousel = this;
2995 if (val >= 0 && val < carousel.get("numItems")) {
2996 carousel.scrollTo(val);
2998 val = carousel.get("firstVisible");
3004 * Set the value for the Carousel's navigation.
3006 * @method _setNavigation
3007 * @param cfg {Object} The navigation configuration
3008 * @return {Object} The new value that would be set
3011 _setNavigation: function (cfg) {
3012 var carousel = this;
3015 Event.on(cfg.prev, "click", scrollPageBackward, carousel);
3018 Event.on(cfg.next, "click", scrollPageForward, carousel);
3023 * Set the value for the number of visible items in the Carousel.
3025 * @method _setNumVisible
3026 * @param val {Number} The new value for numVisible
3027 * @return {Number} The new value that would be set
3030 _setNumVisible: function (val) {
3031 var carousel = this;
3033 carousel._setClipContainerSize(carousel._clipEl, val);
3037 * Set the number of items in the Carousel.
3038 * Warning: Setting this to a lower number than the current removes
3039 * items from the end.
3041 * @method _setNumItems
3042 * @param val {Number} The new value for numItems
3043 * @return {Number} The new value that would be set
3046 _setNumItems: function (val) {
3047 var carousel = this,
3048 num = carousel._itemsTable.numItems;
3050 if (JS.isArray(carousel._itemsTable.items)) {
3051 if (carousel._itemsTable.items.length != num) { // out of sync
3052 num = carousel._itemsTable.items.length;
3053 carousel._itemsTable.numItems = num;
3059 carousel.removeItem(num - 1);
3068 * Set the orientation of the Carousel.
3070 * @method _setOrientation
3071 * @param val {Boolean} The new value for isVertical
3072 * @return {Boolean} The new value that would be set
3075 _setOrientation: function (val) {
3076 var carousel = this,
3077 cssClass = carousel.CLASSES;
3080 carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
3082 carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
3084 carousel._itemsTable.size = 0; // force recalculation next time
3089 * Set the value for the reveal amount percentage in the Carousel.
3091 * @method _setRevealAmount
3092 * @param val {Number} The new value for revealAmount
3093 * @return {Number} The new value that would be set
3096 _setRevealAmount: function (val) {
3097 var carousel = this;
3099 if (val >= 0 && val <= 100) {
3100 val = parseInt(val, 10);
3101 val = JS.isNumber(val) ? val : 0;
3102 carousel._setClipContainerSize();
3104 val = carousel.get("revealAmount");
3110 * Set the value for the selected item.
3112 * @method _setSelectedItem
3113 * @param val {Number} The new value for "selected" item
3116 _setSelectedItem: function (val) {
3117 this._selectedItem = val;
3121 * Synchronize and redraw the UI after an item is added.
3123 * @method _syncUiForItemAdd
3126 _syncUiForItemAdd: function (obj) {
3127 var carousel = this,
3128 carouselEl = carousel._carouselEl,
3131 itemsTable = carousel._itemsTable,
3136 pos = JS.isUndefined(obj.pos) ? itemsTable.numItems - 1 : obj.pos;
3137 if (!JS.isUndefined(itemsTable.items[pos])) {
3138 item = itemsTable.items[pos];
3139 if (item && !JS.isUndefined(item.id)) {
3140 oel = Dom.get(item.id);
3144 el = carousel._createCarouselItem({
3145 className : item.className,
3146 content : item.item,
3149 if (JS.isUndefined(obj.pos)) {
3150 if (!JS.isUndefined(itemsTable.loading[pos])) {
3151 oel = itemsTable.loading[pos];
3152 // if oel is null, it is a problem ...
3156 carouselEl.replaceChild(el, oel);
3157 // ... and remove the item from the data structure
3158 delete itemsTable.loading[pos];
3160 carouselEl.appendChild(el);
3163 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3164 sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
3167 carouselEl.insertBefore(el, sibling);
3172 if (JS.isUndefined(obj.pos)) {
3173 if (!Dom.isAncestor(carousel._carouselEl, oel)) {
3174 carouselEl.appendChild(oel);
3177 if (!Dom.isAncestor(carouselEl, oel)) {
3178 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3179 carouselEl.insertBefore(oel,
3180 Dom.get(itemsTable.items[obj.pos + 1].id));
3186 if (!carousel._hasRendered) {
3187 carousel._refreshUi();
3190 if (carousel.get("selectedItem") < 0) {
3191 carousel.set("selectedItem", carousel.get("firstVisible"));
3196 * Synchronize and redraw the UI after an item is removed.
3198 * @method _syncUiForItemAdd
3201 _syncUiForItemRemove: function (obj) {
3202 var carousel = this,
3203 carouselEl = carousel._carouselEl,
3206 num = carousel.get("numItems");
3210 if (item && (el = Dom.get(item.id))) {
3211 if (el && Dom.isAncestor(carouselEl, el)) {
3212 Event.purgeElement(el, true);
3213 carouselEl.removeChild(el);
3216 if (carousel.get("selectedItem") == pos) {
3217 pos = pos >= num ? num - 1 : pos;
3218 carousel.set("selectedItem", pos);
3225 * Synchronize and redraw the UI for lazy loading.
3227 * @method _syncUiForLazyLoading
3230 _syncUiForLazyLoading: function (obj) {
3231 var carousel = this,
3232 carouselEl = carousel._carouselEl,
3235 itemsTable = carousel._itemsTable,
3238 for (i = obj.first; i <= obj.last; i++) {
3239 el = carousel._createCarouselItem({
3240 className : carousel.CLASSES.ITEM_LOADING,
3241 content : carousel.STRINGS.ITEM_LOADING_CONTENT,
3242 id : Dom.generateId()
3245 if (!JS.isUndefined(itemsTable.items[obj.last + 1])) {
3246 sibling = Dom.get(itemsTable.items[obj.last + 1].id);
3248 carouselEl.insertBefore(el, sibling);
3252 carouselEl.appendChild(el);
3255 itemsTable.loading[i] = el;
3260 * Set the correct class for the navigation buttons.
3262 * @method _updateNavButtons
3263 * @param el {Object} The target button
3264 * @param setFocus {Boolean} True to set focus ring, false otherwise.
3267 _updateNavButtons: function (el, setFocus) {
3269 cssClass = this.CLASSES,
3271 parent = el.parentNode;
3276 grandParent = parent.parentNode;
3278 if (el.nodeName.toUpperCase() == "BUTTON" &&
3279 Dom.hasClass(parent, cssClass.BUTTON)) {
3282 children = Dom.getChildren(grandParent);
3284 Dom.removeClass(children, cssClass.FOCUSSED_BUTTON);
3287 Dom.addClass(parent, cssClass.FOCUSSED_BUTTON);
3289 Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON);
3295 * Update the UI for the pager buttons based on the current page and
3296 * the number of pages.
3298 * @method _updatePagerButtons
3301 _updatePagerButtons: function () {
3302 var carousel = this,
3303 css = carousel.CLASSES,
3304 cur = carousel._pages.cur, // current page
3309 n = carousel.get("numVisible"),
3310 num = carousel._pages.num, // total pages
3311 pager = carousel._pages.el; // the pager container element
3313 if (num === 0 || !pager) {
3314 return; // don't do anything if number of pages is 0
3317 // Hide the pager before redrawing it
3318 Dom.setStyle(pager, "visibility", "hidden");
3320 // Remove all nodes from the pager
3321 while (pager.firstChild) {
3322 pager.removeChild(pager.firstChild);
3325 for (i = 0; i < num; i++) {
3326 if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
3327 Dom.setStyle(pager, "visibility", "visible");
3330 item = carousel._itemsTable.items[i * n].id;
3332 el = document.createElement("LI");
3334 Dom.setStyle(pager, "visibility", "visible");
3339 Dom.addClass(el, css.FIRST_PAGE);
3342 Dom.addClass(el, css.SELECTED_NAV);
3345 // TODO: use a template string for i18N compliance
3346 html = "<a href=\"#" + item + "\" tabindex=\"0\"><em>" +
3347 carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
3349 el.innerHTML = html;
3351 pager.appendChild(el);
3354 // Show the pager now
3355 Dom.setStyle(pager, "visibility", "visible");
3359 * Update the UI for the pager menu based on the current page and
3360 * the number of pages. If the number of pages is greater than
3361 * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
3362 * down menu instead of a set of buttons.
3364 * @method _updatePagerMenu
3367 _updatePagerMenu: function () {
3368 var carousel = this,
3369 cur = carousel._pages.cur, // current page
3373 n = carousel.get("numVisible"),
3374 num = carousel._pages.num, // total pages
3375 pager = carousel._pages.el, // the pager container element
3379 return; // don't do anything if number of pages is 0
3382 sel = document.createElement("SELECT");
3387 // Hide the pager before redrawing it
3388 Dom.setStyle(pager, "visibility", "hidden");
3390 // Remove all nodes from the pager
3391 while (pager.firstChild) {
3392 pager.removeChild(pager.firstChild);
3395 for (i = 0; i < num; i++) {
3396 if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
3397 Dom.setStyle(pager, "visibility", "visible");
3400 item = carousel._itemsTable.items[i * n].id;
3402 el = document.createElement("OPTION");
3404 Dom.setStyle(pager, "visibility", "visible");
3407 el.value = "#" + item;
3408 // TODO: use a template string for i18N compliance
3409 el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
3412 el.setAttribute("selected", "selected");
3415 sel.appendChild(el);
3418 el = document.createElement("FORM");
3421 el.appendChild(sel);
3422 pager.appendChild(el);
3425 // Show the pager now
3426 Dom.setStyle(pager, "visibility", "visible");
3430 * Set the correct tab index for the Carousel items.
3432 * @method _updateTabIndex
3433 * @param el {Object} The element to be focussed
3436 _updateTabIndex: function (el) {
3437 var carousel = this;
3440 if (carousel._focusableItemEl) {
3441 carousel._focusableItemEl.tabIndex = -1;
3443 carousel._focusableItemEl = el;
3449 * Validate animation parameters.
3451 * @method _validateAnimation
3452 * @param cfg {Object} The animation configuration
3453 * @return {Boolean} The status of the validation
3456 _validateAnimation: function (cfg) {
3459 if (JS.isObject(cfg)) {
3461 rv = rv && JS.isNumber(cfg.speed);
3464 rv = rv && JS.isFunction(cfg.effect);
3465 } else if (!JS.isUndefined(YAHOO.util.Easing)) {
3466 cfg.effect = YAHOO.util.Easing.easeOut;
3476 * Validate the firstVisible value.
3478 * @method _validateFirstVisible
3479 * @param val {Number} The first visible value
3480 * @return {Boolean} The status of the validation
3483 _validateFirstVisible: function (val) {
3484 var carousel = this, numItems = carousel.get("numItems");
3486 if (JS.isNumber(val)) {
3487 if (numItems === 0 && val == numItems) {
3490 return (val >= 0 && val < numItems);
3498 * Validate and navigation parameters.
3500 * @method _validateNavigation
3501 * @param cfg {Object} The navigation configuration
3502 * @return {Boolean} The status of the validation
3505 _validateNavigation : function (cfg) {
3508 if (!JS.isObject(cfg)) {
3513 if (!JS.isArray(cfg.prev)) {
3516 for (i in cfg.prev) {
3517 if (cfg.prev.hasOwnProperty(i)) {
3518 if (!JS.isString(cfg.prev[i].nodeName)) {
3526 if (!JS.isArray(cfg.next)) {
3529 for (i in cfg.next) {
3530 if (cfg.next.hasOwnProperty(i)) {
3531 if (!JS.isString(cfg.next[i].nodeName)) {
3542 * Validate the numItems value.
3544 * @method _validateNumItems
3545 * @param val {Number} The numItems value
3546 * @return {Boolean} The status of the validation
3549 _validateNumItems: function (val) {
3550 return JS.isNumber(val) && (val >= 0);
3554 * Validate the numVisible value.
3556 * @method _validateNumVisible
3557 * @param val {Number} The numVisible value
3558 * @return {Boolean} The status of the validation
3561 _validateNumVisible: function (val) {
3564 if (JS.isNumber(val)) {
3565 rv = val > 0 && val <= this.get("numItems");
3572 * Validate the revealAmount value.
3574 * @method _validateRevealAmount
3575 * @param val {Number} The revealAmount value
3576 * @return {Boolean} The status of the validation
3579 _validateRevealAmount: function (val) {
3582 if (JS.isNumber(val)) {
3583 rv = val >= 0 && val < 100;
3590 * Validate the scrollIncrement value.
3592 * @method _validateScrollIncrement
3593 * @param val {Number} The scrollIncrement value
3594 * @return {Boolean} The status of the validation
3597 _validateScrollIncrement: function (val) {
3600 if (JS.isNumber(val)) {
3601 rv = (val > 0 && val < this.get("numItems"));
3611 ;; Local variables: **
3613 ;; indent-tabs-mode: nil **
3616 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.7.0", build: "1799"});