2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
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
19 var WidgetName = "Carousel"; // forward declaration
22 * The Carousel widget.
25 * @extends YAHOO.util.Element
27 * @param el {HTMLElement | String} The HTML element that represents the
28 * the container that houses the Carousel.
29 * @param cfg {Object} (optional) The configuration values
31 YAHOO.widget.Carousel = function (el, cfg) {
33 YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg);
37 * Private variables of the Carousel component
40 /* Some abbreviations to avoid lengthy typing and lookups. */
42 Carousel = YAHOO.widget.Carousel,
44 Event = YAHOO.util.Event,
48 * The internal table of Carousel instances.
53 syncUiOnItemInsert = true,
56 * Custom events of the Carousel component
61 * @description Fires when the Carousel has scrolled to the previous or
62 * next page. Passes back the index of the first and last visible items in
64 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
65 * for more information on listening for this event.
66 * @type YAHOO.util.CustomEvent
68 afterScrollEvent = "afterScroll",
71 * @event allItemsRemovedEvent
72 * @description Fires when all items have been removed from the Carousel.
74 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
75 * for more information on listening for this event.
76 * @type YAHOO.util.CustomEvent
78 allItemsRemovedEvent = "allItemsRemoved",
82 * @description Fires before the Carousel is hidden. See
83 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
84 * for more information on listening for this event.
85 * @type YAHOO.util.CustomEvent
87 beforeHideEvent = "beforeHide",
90 * @event beforePageChange
91 * @description Fires when the Carousel is about to scroll to the previous
92 * or next page. Passes back the page number of the current page. Note
93 * that the first page number is zero. See
94 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
95 * for more information on listening for this event.
96 * @type YAHOO.util.CustomEvent
98 beforePageChangeEvent = "beforePageChange",
101 * @event beforeScroll
102 * @description Fires when the Carousel is about to scroll to the previous
103 * or next page. Passes back the index of the first and last visible items
104 * in the Carousel and the direction (backward/forward) of the scroll. See
105 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
106 * for more information on listening for this event.
107 * @type YAHOO.util.CustomEvent
109 beforeScrollEvent = "beforeScroll",
113 * @description Fires when the Carousel is about to be shown. 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
118 beforeShowEvent = "beforeShow",
122 * @description Fires when the Carousel loses focus. See
123 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
124 * for more information on listening for this event.
125 * @type YAHOO.util.CustomEvent
131 * @description Fires when the Carousel gains focus. See
132 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
133 * for more information on listening for this event.
134 * @type YAHOO.util.CustomEvent
136 focusEvent = "focus",
140 * @description Fires when the Carousel is hidden. See
141 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
142 * for more information on listening for this event.
143 * @type YAHOO.util.CustomEvent
149 * @description Fires when an item has been added to the Carousel. Passes
150 * back the content of the item that would be added, the index at which the
151 * item would be added, and the event itself. See
152 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
153 * for more information on listening for this event.
154 * @type YAHOO.util.CustomEvent
156 itemAddedEvent = "itemAdded",
160 * @description Fires when an item has been removed from the Carousel.
161 * Passes back the content of the item that would be removed, the index
162 * from which the item would be removed, and the event itself. See
163 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
164 * for more information on listening for this event.
165 * @type YAHOO.util.CustomEvent
167 itemRemovedEvent = "itemRemoved",
170 * @event itemReplaced
171 * @description Fires when an item has been replaced in the Carousel.
172 * Passes back the content of the item that was replaced, the content
173 * of the new item, the index where the replacement occurred, and the event
175 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
176 * for more information on listening for this event.
177 * @type YAHOO.util.CustomEvent
179 itemReplacedEvent = "itemReplaced",
182 * @event itemSelected
183 * @description Fires when an item has been selected in the Carousel.
184 * Passes back the index of the selected item in the Carousel. Note, that
185 * the index begins from zero. See
186 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
187 * for more information on listening for this event.
188 * @type YAHOO.util.CustomEvent
190 itemSelectedEvent = "itemSelected",
194 * @description Fires when the Carousel needs more items to be loaded for
195 * displaying them. Passes back the first and last visible items in the
196 * Carousel, and the number of items needed to be loaded. See
197 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
198 * for more information on listening for this event.
199 * @type YAHOO.util.CustomEvent
201 loadItemsEvent = "loadItems",
204 * @event navigationStateChange
205 * @description Fires when the state of either one of the navigation
206 * buttons are changed from enabled to disabled or vice versa. Passes back
207 * the state (true/false) of the previous and next buttons. The value true
208 * signifies the button is enabled, false signifies disabled. See
209 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
210 * for more information on listening for this event.
211 * @type YAHOO.util.CustomEvent
213 navigationStateChangeEvent = "navigationStateChange",
217 * @description Fires after the Carousel has scrolled to the previous or
218 * next page. Passes back the page number of the current page. Note
219 * that the first page number is zero. See
220 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
221 * for more information on listening for this event.
222 * @type YAHOO.util.CustomEvent
224 pageChangeEvent = "pageChange",
229 * @description Fires when the Carousel is rendered. See
230 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
231 * for more information on listening for this event.
232 * @type YAHOO.util.CustomEvent
234 renderEvent = "render",
238 * @description Fires when the Carousel is shown. See
239 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
240 * for more information on listening for this event.
241 * @type YAHOO.util.CustomEvent
246 * @event startAutoPlay
247 * @description Fires when the auto play has started in the Carousel. See
248 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
249 * for more information on listening for this event.
250 * @type YAHOO.util.CustomEvent
252 startAutoPlayEvent = "startAutoPlay",
255 * @event stopAutoPlay
256 * @description Fires when the auto play has been stopped in the Carousel.
258 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
259 * for more information on listening for this event.
260 * @type YAHOO.util.CustomEvent
262 stopAutoPlayEvent = "stopAutoPlay",
266 * @event uiUpdateEvent
267 * @description Fires when the UI has been updated.
269 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
270 * for more information on listening for this event.
271 * @type YAHOO.util.CustomEvent
273 uiUpdateEvent = "uiUpdate";
276 * Private helper functions used by the Carousel component
280 * Set multiple styles on one element.
282 * @param el {HTMLElement} The element to set styles on
283 * @param style {Object} top:"10px", left:"0px", etc.
286 function setStyles(el, styles) {
289 for (which in styles) {
290 if (styles.hasOwnProperty(which)) {
291 Dom.setStyle(el, which, styles[which]);
297 * Create an element, set its class name and optionally install the element
299 * @method createElement
300 * @param el {String} The element to be created
301 * @param attrs {Object} Configuration of parent, class and id attributes.
302 * If the content is specified, it is inserted after creation of the
303 * element. The content can also be an HTML element in which case it would
304 * be appended as a child node of the created element.
307 function createElement(el, attrs) {
308 var newEl = document.createElement(el);
311 if (attrs.className) {
312 Dom.addClass(newEl, attrs.className);
316 setStyles(newEl, attrs.styles);
320 attrs.parent.appendChild(newEl);
324 newEl.setAttribute("id", attrs.id);
328 if (attrs.content.nodeName) {
329 newEl.appendChild(attrs.content);
331 newEl.innerHTML = attrs.content;
339 * Get the computed style of an element.
342 * @param el {HTMLElement} The element for which the style needs to be
344 * @param style {String} The style attribute
345 * @param type {String} "int", "float", etc. (defaults to int)
348 function getStyle(el, style, type) {
355 function getStyleIntVal(el, style) {
359 * XXX: Safari calculates incorrect marginRight for an element
360 * which has its parent element style set to overflow: hidden
361 * https://bugs.webkit.org/show_bug.cgi?id=13343
362 * Let us assume marginLeft == marginRight
364 * Seems like IE9 also has this issue!
366 if (style == "marginRight" && (YAHOO.env.ua.webkit ||
367 (YAHOO.env.ua.ie && YAHOO.env.ua.ie >= 9))) {
368 val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
370 val = parseInt(Dom.getStyle(el, style), 10);
373 return JS.isNumber(val) ? val : 0;
376 function getStyleFloatVal(el, style) {
380 * XXX: Safari calculates incorrect marginRight for an element
381 * which has its parent element style set to overflow: hidden
382 * https://bugs.webkit.org/show_bug.cgi?id=13343
383 * Let us assume marginLeft == marginRight
385 if (style == "marginRight" && YAHOO.env.ua.webkit) {
386 val = parseFloat(Dom.getStyle(el, "marginLeft"));
388 val = parseFloat(Dom.getStyle(el, style));
391 return JS.isNumber(val) ? val : 0;
394 if (typeof type == "undefined") {
400 value = el.offsetHeight;
402 value += getStyleIntVal(el, "marginTop") +
403 getStyleIntVal(el, "marginBottom");
405 value = getStyleFloatVal(el, "height") +
406 getStyleIntVal(el, "marginTop") +
407 getStyleIntVal(el, "marginBottom") +
408 getStyleIntVal(el, "borderTopWidth") +
409 getStyleIntVal(el, "borderBottomWidth") +
410 getStyleIntVal(el, "paddingTop") +
411 getStyleIntVal(el, "paddingBottom");
415 value = el.offsetWidth;
417 value += getStyleIntVal(el, "marginLeft") +
418 getStyleIntVal(el, "marginRight");
420 value = getStyleFloatVal(el, "width") +
421 getStyleIntVal(el, "marginLeft") +
422 getStyleIntVal(el, "marginRight") +
423 getStyleIntVal(el, "borderLeftWidth") +
424 getStyleIntVal(el, "borderRightWidth") +
425 getStyleIntVal(el, "paddingLeft") +
426 getStyleIntVal(el, "paddingRight");
431 value = getStyleIntVal(el, style);
432 } else if (type == "float") {
433 value = getStyleFloatVal(el, style);
435 value = Dom.getStyle(el, style);
444 * Compute and return the height or width of a single Carousel item
445 * depending upon the orientation.
447 * @method getCarouselItemSize
448 * @param which {String} "height" or "width" to be returned. If this is
449 * passed explicitly, the calculated size is not cached.
452 function getCarouselItemSize(which) {
459 if (carousel._itemAttrCache[which]) {
460 return carousel._itemAttrCache[which];
463 if (carousel._itemsTable.numItems === 0) {
467 // get first loaded item
468 item = carousel._findClosestSibling(-1);
470 if (JS.isUndefined(item)) {
474 child = Dom.get(item.id);
476 if (typeof which == "undefined") {
477 vertical = carousel.get("isVertical");
479 vertical = which == "height";
483 size = getStyle(child, "height");
485 size = getStyle(child, "width");
489 carousel._itemAttrCache[which] = size;
496 * Return the size of a part of the item (reveal).
498 * @method getRevealSize
501 function getRevealSize() {
502 var carousel = this, isVertical, sz;
504 isVertical = carousel.get("isVertical");
505 sz = getCarouselItemSize.call(carousel,
506 isVertical ? "height" : "width");
507 return (sz * carousel.get("revealAmount") / 100);
511 * Compute and return the position of a Carousel item based on its
514 * @method getCarouselItemPosition
515 * @param position {Number} The position of the Carousel item.
518 function getCarouselItemPosition(pos) {
520 itemsPerRow = carousel._cols,
521 itemsPerCol = carousel._rows,
533 itemsTable = carousel._itemsTable;
535 isVertical = carousel.get("isVertical");
536 sz = getCarouselItemSize.call(carousel,
537 isVertical ? "height" : "width");
538 rsz = getRevealSize.call(carousel);
541 page = this.getPageForItem(pos);
543 itemsRow = Math.floor(pos/itemsPerRow);
546 styles.top = (top + rsz) + "px";
548 sz = getCarouselItemSize.call(carousel, "width");
550 itemsCol = pos % itemsPerRow;
553 styles.left = left + "px";
555 itemsCol = pos % itemsPerRow;
556 sentinel = (page - 1) * itemsPerRow;
557 delta = itemsCol + sentinel;
559 styles.left = (left + rsz) + "px";
561 sz = getCarouselItemSize.call(carousel, "height");
563 itemsRow = Math.floor(pos/itemsPerRow);
564 sentinel = (page - 1) * itemsPerCol;
565 delta = itemsRow - sentinel;
568 styles.top = top + "px";
573 styles.top = ((pos * sz) + rsz) + "px";
576 styles.left = ((pos * sz) + rsz) + "px";
584 * Return the index of the first item in the view port for displaying item
587 * @method getFirstVisibleForPosition
588 * @param pos {Number} The position of the item to be displayed
591 function getFirstVisibleForPosition(pos) {
592 var num = this.get("numVisible");
593 return Math.floor(pos / num) * num;
597 * Return the scrolling offset size given the number of elements to
600 * @method getScrollOffset
601 * @param delta {Number} The delta number of elements to scroll by.
604 function getScrollOffset(delta) {
608 attr = carousel.get("isVertical") ? "height" : "width";
610 itemSize = getCarouselItemSize.call(carousel, attr);
612 size = itemSize * delta;
618 * Scroll the Carousel by a page backward.
620 * @method scrollPageBackward
621 * @param {Event} ev The event object
622 * @param {Object} obj The context object
625 function scrollPageBackward(ev, obj) {
626 obj.scrollPageBackward();
627 Event.preventDefault(ev);
631 * Scroll the Carousel by a page forward.
633 * @method scrollPageForward
634 * @param {Event} ev The event object
635 * @param {Object} obj The context object
638 function scrollPageForward(ev, obj) {
639 obj.scrollPageForward();
640 Event.preventDefault(ev);
644 * Set the selected item.
646 * @method setItemSelection
647 * @param {Number} newpos The index of the new position
648 * @param {Number} oldpos The index of the previous position
651 function setItemSelection(newpos, oldpos) {
653 cssClass = carousel.CLASSES,
655 firstItem = carousel._firstItem,
656 numItems = carousel.get("numItems"),
657 numVisible = carousel.get("numVisible"),
659 sentinel = firstItem + numVisible - 1;
661 if (position >= 0 && position < numItems) {
662 if (!JS.isUndefined(carousel._itemsTable.items[position])) {
663 el = Dom.get(carousel._itemsTable.items[position].id);
665 Dom.removeClass(el, cssClass.SELECTED_ITEM);
670 if (JS.isNumber(newpos)) {
671 newpos = parseInt(newpos, 10);
672 newpos = JS.isNumber(newpos) ? newpos : 0;
677 if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
678 newpos = getFirstVisibleForPosition.call(carousel, newpos);
679 carousel.scrollTo(newpos); // still loading the item
682 if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
683 el = Dom.get(carousel._itemsTable.items[newpos].id);
685 Dom.addClass(el, cssClass.SELECTED_ITEM);
689 if (newpos < firstItem || newpos > sentinel) { // out of focus
690 newpos = getFirstVisibleForPosition.call(carousel, newpos);
691 carousel.scrollTo(newpos);
696 * Show or hide navigation.
698 * @method showNavigation
701 function showNavigation(hide) {
703 cfg = carousel.get("navigation");
705 if (JS.isUndefined(cfg)) {
706 return; // can't do anything
709 if (JS.isUndefined(hide)) {
710 // show the navigation
711 if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) &&
712 !JS.isUndefined(cfg.prev[0])) {
713 Dom.setStyle(cfg.prev[0], "visibility", "visible");
715 if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) &&
716 !JS.isUndefined(cfg.next[0])) {
717 Dom.setStyle(cfg.next[0], "visibility", "visible");
719 if (!JS.isUndefined(carousel._pages) &&
720 !JS.isUndefined(carousel._pages.el)) {
721 Dom.setStyle(carousel._pages.el, "visibility", "visible");
724 // hide the navigation
725 if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) &&
726 !JS.isUndefined(cfg.prev[0])) {
727 Dom.setStyle(cfg.prev[0], "visibility", "hidden");
729 if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) &&
730 !JS.isUndefined(cfg.next[0])) {
731 Dom.setStyle(cfg.next[0], "visibility", "hidden");
733 if (!JS.isUndefined(carousel._pages) &&
734 !JS.isUndefined(carousel._pages.el)) {
735 Dom.setStyle(carousel._pages.el, "visibility", "hidden");
741 * Fire custom events for enabling/disabling navigation elements.
743 * @method syncNavigation
746 function syncNavigation() {
749 cssClass = carousel.CLASSES,
754 // Don't do anything if the Carousel is not rendered
755 if (!carousel._hasRendered) {
759 navigation = carousel.get("navigation");
760 sentinel = carousel._firstItem + carousel.get("numVisible");
762 if (navigation.prev) {
763 if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
764 if (carousel.get("numItems") === 0 ||
765 !carousel.get("isCircular")) {
766 Event.removeListener(navigation.prev, "click",
768 Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
769 for (i = 0; i < carousel._navBtns.prev.length; i++) {
770 carousel._navBtns.prev[i].setAttribute("disabled",
773 carousel._prevEnabled = false;
775 attach = !carousel._prevEnabled;
778 attach = !carousel._prevEnabled;
782 Event.on(navigation.prev, "click", scrollPageBackward,
784 Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
785 for (i = 0; i < carousel._navBtns.prev.length; i++) {
786 carousel._navBtns.prev[i].removeAttribute("disabled");
788 carousel._prevEnabled = true;
793 if (navigation.next) {
794 if (sentinel >= carousel.get("numItems")) {
795 if (!carousel.get("isCircular")) {
796 Event.removeListener(navigation.next, "click",
798 Dom.addClass(navigation.next, cssClass.DISABLED);
799 for (i = 0; i < carousel._navBtns.next.length; i++) {
800 carousel._navBtns.next[i].setAttribute("disabled",
803 carousel._nextEnabled = false;
805 attach = !carousel._nextEnabled;
808 attach = !carousel._nextEnabled;
812 Event.on(navigation.next, "click", scrollPageForward,
814 Dom.removeClass(navigation.next, cssClass.DISABLED);
815 for (i = 0; i < carousel._navBtns.next.length; i++) {
816 carousel._navBtns.next[i].removeAttribute("disabled");
818 carousel._nextEnabled = true;
822 carousel.fireEvent(navigationStateChangeEvent,
823 { next: carousel._nextEnabled, prev: carousel._prevEnabled });
827 * Synchronize and redraw the Pager UI if necessary.
829 * @method syncPagerUi
832 function syncPagerUi(page) {
833 var carousel = this, numPages, numVisible;
835 // Don't do anything if the Carousel is not rendered
836 if (!carousel._hasRendered) {
840 numVisible = carousel.get("numVisible");
842 if (!JS.isNumber(page)) {
843 page = Math.floor(carousel.get("selectedItem") / numVisible);
846 numPages = Math.ceil(carousel.get("numItems") / numVisible);
848 carousel._pages.num = numPages;
849 carousel._pages.cur = page;
851 if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
852 carousel._updatePagerMenu();
854 carousel._updatePagerButtons();
859 * Get full dimensions of an element.
861 * @method getDimensions
862 * @param {Object} el The element to get the dimensions of
863 * @param {String} which Get the height or width of an element
866 function getDimensions(el, which) {
869 return getStyle(el, "marginTop") +
870 getStyle(el, "marginBottom") +
871 getStyle(el, "paddingTop") +
872 getStyle(el, "paddingBottom") +
873 getStyle(el, "borderTopWidth") +
874 getStyle(el, "borderBottomWidth");
876 return getStyle(el, "marginLeft") +
877 getStyle(el, "marginRight") +
878 getStyle(el, "paddingLeft") +
879 getStyle(el, "paddingRight") +
880 getStyle(el, "borderLeftWidth") +
881 getStyle(el, "borderRightWidth");
886 return getStyle(el, which);
891 * Call the appropriate methods on events fired when an item is added, or
892 * removed for synchronizing the DOM.
895 * @param {Object} o The item that needs to be added or removed
901 if (!JS.isObject(o)) {
907 carousel._syncUiForItemAdd(o);
909 case itemRemovedEvent:
910 carousel._syncUiForItemRemove(o);
912 case itemReplacedEvent:
913 carousel._syncUiForItemReplace(o);
916 carousel._syncUiForLazyLoading(o);
920 carousel.fireEvent(uiUpdateEvent);
924 * Update the state variables after scrolling the Carousel view port.
926 * @method updateStateAfterScroll
927 * @param {Integer} item The index to which the Carousel has scrolled to.
928 * @param {Integer} sentinel The last element in the view port.
931 function updateStateAfterScroll(item, sentinel) {
933 page = carousel.get("currentPage"),
935 numPerPage = carousel.get("numVisible");
937 newPage = parseInt(carousel._firstItem / numPerPage, 10);
938 if (newPage != page) {
939 carousel.setAttributeConfig("currentPage", { value: newPage });
940 carousel.fireEvent(pageChangeEvent, newPage);
943 if (carousel.get("selectOnScroll")) {
944 if (carousel.get("selectedItem") != carousel._selectedItem) {
945 carousel.set("selectedItem", carousel._selectedItem);
949 clearTimeout(carousel._autoPlayTimer);
950 delete carousel._autoPlayTimer;
951 if (carousel.isAutoPlayOn()) {
952 carousel.startAutoPlay();
955 carousel.fireEvent(afterScrollEvent,
956 { first: carousel._firstItem,
962 * Static members and methods of the Carousel component
966 * Return the appropriate Carousel object based on the id associated with
967 * the Carousel element or false if none match.
972 Carousel.getById = function (id) {
973 return instances[id] ? instances[id].object : false;
976 YAHOO.extend(Carousel, YAHOO.util.Element, {
979 * Internal variables used within the Carousel component
983 * Number of rows for a multirow carousel.
991 * Number of cols for a multirow carousel.
999 * The Animation object.
1001 * @property _animObj
1007 * The Carousel element.
1009 * @property _carouselEl
1015 * The Carousel clipping container element.
1023 * The current first index of the Carousel.
1025 * @property _firstItem
1031 * Does the Carousel element have focus?
1033 * @property _hasFocus
1039 * Is the Carousel rendered already?
1041 * @property _hasRendered
1044 _hasRendered: false,
1047 * Is the animation still in progress?
1049 * @property _isAnimationInProgress
1052 _isAnimationInProgress: false,
1055 * Is the auto-scrolling of Carousel in progress?
1057 * @property _isAutoPlayInProgress
1060 _isAutoPlayInProgress: false,
1063 * The table of items in the Carousel.
1064 * The numItems is the number of items in the Carousel, items being the
1065 * array of items in the Carousel. The size is the size of a single
1066 * item in the Carousel. It is cached here for efficiency (to avoid
1067 * computing the size multiple times).
1069 * @property _itemsTable
1075 * The Carousel navigation buttons.
1077 * @property _navBtns
1083 * The Carousel navigation.
1091 * Status of the next navigation item.
1093 * @property _nextEnabled
1099 * The Carousel pages structure.
1100 * This is an object of the total number of pages and the current page.
1108 * The Carousel pagination structure.
1110 * @property _pagination
1116 * Status of the previous navigation item.
1118 * @property _prevEnabled
1124 * Whether the Carousel size needs to be recomputed or not?
1126 * @property _recomputeSize
1129 _recomputeSize: true,
1132 * Cache the Carousel item attributes.
1134 * @property _itemAttrCache
1137 _itemAttrCache: null,
1140 * CSS classes used by the Carousel component
1146 * The class name of the Carousel navigation buttons.
1149 * @default "yui-carousel-button"
1151 BUTTON: "yui-carousel-button",
1154 * The class name of the Carousel element.
1156 * @property CAROUSEL
1157 * @default "yui-carousel"
1159 CAROUSEL: "yui-carousel",
1162 * The class name of the container of the items in the Carousel.
1164 * @property CAROUSEL_EL
1165 * @default "yui-carousel-element"
1167 CAROUSEL_EL: "yui-carousel-element",
1170 * The class name of the Carousel's container element.
1172 * @property CONTAINER
1173 * @default "yui-carousel-container"
1175 CONTAINER: "yui-carousel-container",
1178 * The class name of the Carousel's container element.
1181 * @default "yui-carousel-content"
1183 CONTENT: "yui-carousel-content",
1186 * The class name of a disabled navigation button.
1188 * @property DISABLED
1189 * @default "yui-carousel-button-disabled"
1191 DISABLED: "yui-carousel-button-disabled",
1194 * The class name of the first Carousel navigation button.
1196 * @property FIRST_NAV
1197 * @default " yui-carousel-first-button"
1199 FIRST_NAV: " yui-carousel-first-button",
1202 * The class name of a first disabled navigation button.
1204 * @property FIRST_NAV_DISABLED
1205 * @default "yui-carousel-first-button-disabled"
1207 FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
1210 * The class name of a first page element.
1212 * @property FIRST_PAGE
1213 * @default "yui-carousel-nav-first-page"
1215 FIRST_PAGE: "yui-carousel-nav-first-page",
1218 * The class name of the Carousel navigation button that has focus.
1220 * @property FOCUSSED_BUTTON
1221 * @default "yui-carousel-button-focus"
1223 FOCUSSED_BUTTON: "yui-carousel-button-focus",
1226 * The class name of a horizontally oriented Carousel.
1228 * @property HORIZONTAL
1229 * @default "yui-carousel-horizontal"
1231 HORIZONTAL: "yui-carousel-horizontal",
1234 * The element to be used as the progress indicator when the item
1235 * is still being loaded.
1237 * @property ITEM_LOADING
1238 * @default The progress indicator (spinner) image CSS class
1240 ITEM_LOADING: "yui-carousel-item-loading",
1243 * The class name that will be set if the Carousel adjusts itself
1244 * for a minimum width.
1246 * @property MIN_WIDTH
1247 * @default "yui-carousel-min-width"
1249 MIN_WIDTH: "yui-carousel-min-width",
1252 * The navigation element container class name.
1254 * @property NAVIGATION
1255 * @default "yui-carousel-nav"
1257 NAVIGATION: "yui-carousel-nav",
1260 * The class name of the next Carousel navigation button.
1262 * @property NEXT_NAV
1263 * @default " yui-carousel-next-button"
1265 NEXT_NAV: " yui-carousel-next-button",
1268 * The class name of the next navigation link. This variable is
1269 * not only used for styling, but also for identifying the link
1270 * within the Carousel container.
1272 * @property NEXT_PAGE
1273 * @default "yui-carousel-next"
1275 NEXT_PAGE: "yui-carousel-next",
1278 * The class name for the navigation container for prev/next.
1280 * @property NAV_CONTAINER
1281 * @default "yui-carousel-buttons"
1283 NAV_CONTAINER: "yui-carousel-buttons",
1286 * The class name for an item in the pager UL or dropdown menu.
1288 * @property PAGER_ITEM
1289 * @default "yui-carousel-pager-item"
1291 PAGER_ITEM: "yui-carousel-pager-item",
1294 * The class name for the pagination container
1296 * @property PAGINATION
1297 * @default "yui-carousel-pagination"
1299 PAGINATION: "yui-carousel-pagination",
1302 * The class name of the focussed page navigation. This class is
1303 * specifically used for the ugly focus handling in Opera.
1305 * @property PAGE_FOCUS
1306 * @default "yui-carousel-nav-page-focus"
1308 PAGE_FOCUS: "yui-carousel-nav-page-focus",
1311 * The class name of the previous navigation link. This variable
1312 * is not only used for styling, but also for identifying the link
1313 * within the Carousel container.
1315 * @property PREV_PAGE
1316 * @default "yui-carousel-prev"
1318 PREV_PAGE: "yui-carousel-prev",
1321 * The class name of the item.
1324 * @default "yui-carousel-item"
1326 ITEM: "yui-carousel-item",
1329 * The class name of the selected item.
1331 * @property SELECTED_ITEM
1332 * @default "yui-carousel-item-selected"
1334 SELECTED_ITEM: "yui-carousel-item-selected",
1337 * The class name of the selected paging navigation.
1339 * @property SELECTED_NAV
1340 * @default "yui-carousel-nav-page-selected"
1342 SELECTED_NAV: "yui-carousel-nav-page-selected",
1345 * The class name of a vertically oriented Carousel.
1347 * @property VERTICAL
1348 * @default "yui-carousel-vertical"
1350 VERTICAL: "yui-carousel-vertical",
1353 * The class name of a multirow Carousel.
1355 * @property MULTI_ROW
1356 * @default "yui-carousel-multi-row"
1358 MULTI_ROW: "yui-carousel-multi-row",
1361 * The class name of a row in a multirow Carousel.
1364 * @default "yui-carousel-new-row"
1366 ROW: "yui-carousel-row",
1369 * The class name of a vertical Carousel's container element.
1371 * @property VERTICAL_CONTAINER
1372 * @default "yui-carousel-vertical-container"
1374 VERTICAL_CONTAINER: "yui-carousel-vertical-container",
1377 * The class name of a visible Carousel.
1380 * @default "yui-carousel-visible"
1382 VISIBLE: "yui-carousel-visible"
1387 * Configuration attributes for configuring the Carousel component
1393 * The offset of the first visible item in the Carousel.
1395 * @property FIRST_VISIBLE
1401 * The minimum width of the horizontal Carousel container to support
1402 * the navigation buttons.
1404 * @property HORZ_MIN_WIDTH
1407 HORZ_MIN_WIDTH: 180,
1410 * The maximum number of pager buttons allowed beyond which the UI
1411 * of the pager would be a drop-down of pages instead of buttons.
1413 * @property MAX_PAGER_BUTTONS
1416 MAX_PAGER_BUTTONS: 5,
1419 * The minimum width of the vertical Carousel container to support
1420 * the navigation buttons.
1422 * @property VERT_MIN_WIDTH
1425 VERT_MIN_WIDTH: 115,
1428 * The number of visible items in the Carousel.
1430 * @property NUM_VISIBLE
1438 * Internationalizable strings in the Carousel component
1444 * The content to be used as the progress indicator when the item
1445 * is still being loaded. Inserted into DOM with innerHTML.
1447 * @property ITEM_LOADING_CONTENT
1449 * @default "Loading"
1451 ITEM_LOADING_CONTENT: "Loading",
1454 * The next navigation button name/text. Inserted into DOM with innerHTML.
1456 * @property NEXT_BUTTON_TEXT
1458 * @default "Next Page"
1460 NEXT_BUTTON_TEXT: "Next Page",
1463 * The prefix text for the pager in case the UI is a drop-down.
1464 * Inserted into DOM with innerHTML.
1466 * @property PAGER_PREFIX_TEXT
1468 * @default "Go to page "
1470 PAGER_PREFIX_TEXT: "Go to page ",
1473 * The previous navigation button name/text. Inserted into DOM with innerHTML.
1475 * @property PREVIOUS_BUTTON_TEXT
1477 * @default "Previous Page"
1479 PREVIOUS_BUTTON_TEXT: "Previous Page"
1484 * Public methods of the Carousel component
1488 * Insert or append an item to the Carousel.
1489 * E.g. if Object: ({content:"Your Content", id:"", className:""}, index)
1493 * @param item {HTML | Object | HTMLElement} The item to be appended
1494 * to the Carousel. If the parameter is a string, it is assumed to be
1495 * the HTML content of the newly created item. If the parameter is an
1496 * object, it is assumed to supply the content and an optional class
1497 * and an optional id of the newly created item.
1498 * @param index {Number} optional The position to where in the list
1499 * (starts from zero).
1500 * @return {Boolean} Return true on success, false otherwise
1502 addItem: function (item, index) {
1503 var carousel = this,
1508 newIndex, // Add newIndex as workaround for undefined pos
1509 numItems = carousel.get("numItems");
1515 if (JS.isString(item) || item.nodeName) {
1516 content = item.nodeName ? item.innerHTML : item;
1517 } else if (JS.isObject(item)) {
1518 content = item.content;
1523 className = carousel.CLASSES.ITEM +
1524 (item.className ? " " + item.className : "");
1525 elId = item.id ? item.id : Dom.generateId();
1527 if (JS.isUndefined(index)) {
1528 carousel._itemsTable.items.push({
1530 className : className,
1533 // Add newIndex as workaround for undefined pos
1534 newIndex = carousel._itemsTable.items.length - 1;
1536 if (index < 0 || index > numItems) {
1540 // make sure we splice into the correct position
1541 if (!carousel._itemsTable.items[index]) {
1542 carousel._itemsTable.items[index] = undefined;
1546 carousel._itemsTable.items.splice(index, replaceItems, {
1548 className : className,
1552 carousel._itemsTable.numItems++;
1554 if (numItems < carousel._itemsTable.items.length) {
1555 carousel.set("numItems", carousel._itemsTable.items.length);
1558 // Add newPos as workaround for undefined pos
1559 carousel.fireEvent(itemAddedEvent,
1560 { pos: index, ev: itemAddedEvent, newPos: newIndex });
1566 * Insert or append multiple items to the Carousel.
1570 * @param items {Array} An array containing an array of new items each linked to the
1571 * index where the insertion should take place.
1572 * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]]
1573 * NOTE: An item at index must already exist.
1574 * @return {Boolean} Return true on success, false otherwise
1576 addItems: function (items) {
1577 var i, n, rv = true;
1579 if (!JS.isArray(items)) {
1583 syncUiOnItemInsert = false;
1584 for (i = 0, n = items.length; i < n; i++) {
1585 if (this.addItem(items[i][0], items[i][1]) === false) {
1589 syncUiOnItemInsert = true;
1591 this._syncUiItems();
1597 * Remove focus from the Carousel.
1603 this._carouselEl.blur();
1604 this.fireEvent(blurEvent);
1608 * Clears the items from Carousel.
1610 * @method clearItems
1613 clearItems: function () {
1614 var carousel = this, n = carousel.get("numItems");
1617 if (!carousel.removeItem(0)) {
1620 For dynamic loading, the numItems may be much larger than
1621 the actual number of items in the table. So, set the
1622 numItems to zero, and break out of the loop if the table
1625 if (carousel._itemsTable.numItems === 0) {
1626 carousel.set("numItems", 0);
1632 carousel.fireEvent(allItemsRemovedEvent);
1636 * Set focus on the Carousel.
1641 focus: function () {
1642 var carousel = this,
1645 isSelectionInvisible,
1653 // Don't do anything if the Carousel is not rendered
1654 if (!carousel._hasRendered) {
1658 if (carousel.isAnimating()) {
1659 // this messes up real bad!
1663 selItem = carousel.get("selectedItem");
1664 numVisible = carousel.get("numVisible");
1665 selectOnScroll = carousel.get("selectOnScroll");
1666 selected = (selItem >= 0) ?
1667 carousel.getItem(selItem) : null;
1668 first = carousel.get("firstVisible");
1669 last = first + numVisible - 1;
1670 isSelectionInvisible = (selItem < first || selItem > last);
1671 focusEl = (selected && selected.id) ?
1672 Dom.get(selected.id) : null;
1673 itemsTable = carousel._itemsTable;
1675 if (!selectOnScroll && isSelectionInvisible) {
1676 focusEl = (itemsTable && itemsTable.items &&
1677 itemsTable.items[first]) ?
1678 Dom.get(itemsTable.items[first].id) : null;
1685 // ignore focus errors
1689 carousel.fireEvent(focusEvent);
1693 * Hide the Carousel.
1699 var carousel = this;
1701 if (carousel.fireEvent(beforeHideEvent) !== false) {
1702 carousel.removeClass(carousel.CLASSES.VISIBLE);
1703 showNavigation.call(carousel, false);
1704 carousel.fireEvent(hideEvent);
1709 * Initialize the Carousel.
1713 * @param el {HTMLElement | String} The html element that represents
1714 * the Carousel container.
1715 * @param attrs {Object} The set of configuration attributes for
1716 * creating the Carousel.
1718 init: function (el, attrs) {
1719 var carousel = this,
1720 elId = el, // save for a rainy day
1728 carousel._hasRendered = false;
1729 carousel._navBtns = { prev: [], next: [] };
1730 carousel._pages = { el: null, num: 0, cur: 0 };
1731 carousel._pagination = {};
1732 carousel._itemAttrCache = {};
1734 carousel._itemsTable = { loading: {}, numItems: 0,
1735 items: [], size: 0 };
1738 if (JS.isString(el)) {
1740 } else if (!el.nodeName) {
1744 Carousel.superclass.init.call(carousel, el, attrs);
1746 // check if we're starting somewhere in the middle
1747 selected = carousel.get("selectedItem");
1749 carousel.set("firstVisible",getFirstVisibleForPosition.call(carousel,selected));
1753 if (!el.id) { // in case the HTML element is passed
1754 el.setAttribute("id", Dom.generateId());
1756 parse = carousel._parseCarousel(el);
1758 carousel._createCarousel(elId);
1761 el = carousel._createCarousel(elId);
1765 carousel.initEvents();
1768 carousel._parseCarouselItems();
1771 // add the selected class
1773 setItemSelection.call(carousel,selected,0);
1776 if (!attrs || typeof attrs.isVertical == "undefined") {
1777 carousel.set("isVertical", false);
1780 carousel._parseCarouselNavigation(el);
1781 carousel._navEl = carousel._setupCarouselNavigation();
1783 instances[elId] = { object: carousel };
1784 carousel._loadItems(Math.min(carousel.get("firstVisible")+carousel.get("numVisible"),carousel.get("numItems"))-1);
1788 * Initialize the configuration attributes used to create the Carousel.
1790 * @method initAttributes
1792 * @param attrs {Object} The set of configuration attributes for
1793 * creating the Carousel.
1795 initAttributes: function (attrs) {
1796 var carousel = this;
1798 attrs = attrs || {};
1799 Carousel.superclass.initAttributes.call(carousel, attrs);
1802 * @attribute carouselEl
1803 * @description The type of the Carousel element.
1807 carousel.setAttributeConfig("carouselEl", {
1808 validator : JS.isString,
1809 value : attrs.carouselEl || "OL"
1813 * @attribute carouselItemEl
1814 * @description The type of the list of items within the Carousel.
1818 carousel.setAttributeConfig("carouselItemEl", {
1819 validator : JS.isString,
1820 value : attrs.carouselItemEl || "LI"
1824 * @attribute currentPage
1825 * @description The current page number (read-only.)
1828 carousel.setAttributeConfig("currentPage", {
1834 * @attribute firstVisible
1835 * @description The index to start the Carousel from (indexes begin
1840 carousel.setAttributeConfig("firstVisible", {
1841 method : carousel._setFirstVisible,
1842 validator : carousel._validateFirstVisible,
1844 attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
1848 * @attribute selectOnScroll
1849 * @description Set this to true to automatically set focus to
1850 * follow scrolling in the Carousel.
1854 carousel.setAttributeConfig("selectOnScroll", {
1855 validator : JS.isBoolean,
1856 value : attrs.selectOnScroll || true
1860 * @attribute numVisible
1861 * @description The number of visible items in the Carousel's
1866 carousel.setAttributeConfig("numVisible", {
1867 setter : carousel._numVisibleSetter,
1868 method : carousel._setNumVisible,
1869 validator : carousel._validateNumVisible,
1870 value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
1874 * @attribute numItems
1875 * @description The number of items in the Carousel.
1878 carousel.setAttributeConfig("numItems", {
1879 method : carousel._setNumItems,
1880 validator : carousel._validateNumItems,
1881 value : carousel._itemsTable.numItems
1885 * @attribute scrollIncrement
1886 * @description The number of items to scroll by for arrow keys.
1890 carousel.setAttributeConfig("scrollIncrement", {
1891 validator : carousel._validateScrollIncrement,
1892 value : attrs.scrollIncrement || 1
1896 * @attribute selectedItem
1897 * @description The index of the selected item.
1900 carousel.setAttributeConfig("selectedItem", {
1901 setter : carousel._selectedItemSetter,
1902 method : carousel._setSelectedItem,
1903 validator : JS.isNumber,
1908 * @attribute revealAmount
1909 * @description The percentage of the item to be revealed on each
1910 * side of the Carousel (before and after the first and last item
1911 * in the Carousel's viewport.)
1915 carousel.setAttributeConfig("revealAmount", {
1916 method : carousel._setRevealAmount,
1917 validator : carousel._validateRevealAmount,
1918 value : attrs.revealAmount || 0
1922 * @attribute isCircular
1923 * @description Set this to true to wrap scrolling of the contents
1928 carousel.setAttributeConfig("isCircular", {
1929 validator : JS.isBoolean,
1930 value : attrs.isCircular || false
1934 * @attribute isVertical
1935 * @description True if the orientation of the Carousel is vertical
1939 carousel.setAttributeConfig("isVertical", {
1940 method : carousel._setOrientation,
1941 validator : JS.isBoolean,
1942 value : attrs.isVertical || false
1946 * @attribute navigation
1947 * @description The set of navigation controls for Carousel
1949 * { prev: null, // the previous navigation element<br>
1950 * next: null } // the next navigation element
1953 carousel.setAttributeConfig("navigation", {
1954 method : carousel._setNavigation,
1955 validator : carousel._validateNavigation,
1957 attrs.navigation || {prev: null,next: null,page: null}
1961 * @attribute animation
1962 * @description The optional animation attributes for the Carousel.
1964 * { speed: 0, // the animation speed (in seconds)<br>
1965 * effect: null } // the animation effect (like
1966 * YAHOO.util.Easing.easeOut)
1969 carousel.setAttributeConfig("animation", {
1970 validator : carousel._validateAnimation,
1971 value : attrs.animation || { speed: 0, effect: null }
1975 * @attribute autoPlay
1976 * @description Set this to time in milli-seconds to have the
1977 * Carousel automatically scroll the contents.
1979 * @deprecated Use autoPlayInterval instead.
1981 carousel.setAttributeConfig("autoPlay", {
1982 validator : JS.isNumber,
1983 value : attrs.autoPlay || 0
1987 * @attribute autoPlayInterval
1988 * @description The delay in milli-seconds for scrolling the
1989 * Carousel during auto-play.
1990 * Note: The startAutoPlay() method needs to be invoked to trigger
1991 * automatic scrolling of Carousel.
1994 carousel.setAttributeConfig("autoPlayInterval", {
1995 validator : JS.isNumber,
1996 value : attrs.autoPlayInterval || 0
2000 * @attribute numPages
2001 * @description The number of pages in the carousel.
2004 carousel.setAttributeConfig("numPages", {
2006 getter : carousel._getNumPages
2010 * @attribute lastVisible
2011 * @description The last item visible in the carousel.
2014 carousel.setAttributeConfig("lastVisible", {
2016 getter : carousel._getLastVisible
2021 * Initialize and bind the event handlers.
2023 * @method initEvents
2026 initEvents: function () {
2027 var carousel = this,
2028 cssClass = carousel.CLASSES,
2031 carousel.on("keydown", carousel._keyboardEventHandler);
2033 carousel.on(afterScrollEvent, syncNavigation);
2035 carousel.on(itemAddedEvent, syncUi);
2037 carousel.on(itemRemovedEvent, syncUi);
2039 carousel.on(itemReplacedEvent, syncUi);
2041 carousel.on(itemSelectedEvent, carousel._focusHandler);
2043 carousel.on(loadItemsEvent, syncUi);
2045 carousel.on(allItemsRemovedEvent, function (ev) {
2046 carousel.scrollTo(0);
2047 syncNavigation.call(carousel);
2048 syncPagerUi.call(carousel);
2051 carousel.on(pageChangeEvent, syncPagerUi, carousel);
2053 carousel.on(renderEvent, function (ev) {
2054 if (carousel.get("selectedItem") === null ||
2055 carousel.get("selectedItem") <= 0) { //in either case
2056 carousel.set("selectedItem", carousel.get("firstVisible"));
2058 syncNavigation.call(carousel, ev);
2059 syncPagerUi.call(carousel, ev);
2060 carousel._setClipContainerSize();
2064 carousel.on("selectedItemChange", function (ev) {
2065 setItemSelection.call(carousel, ev.newValue, ev.prevValue);
2066 if (ev.newValue >= 0) {
2067 carousel._updateTabIndex(
2068 carousel.getElementForItem(ev.newValue));
2070 carousel.fireEvent(itemSelectedEvent, ev.newValue);
2073 carousel.on(uiUpdateEvent, function (ev) {
2074 syncNavigation.call(carousel, ev);
2075 syncPagerUi.call(carousel, ev);
2078 carousel.on("firstVisibleChange", function (ev) {
2079 if (!carousel.get("selectOnScroll")) {
2080 if (ev.newValue >= 0) {
2081 carousel._updateTabIndex(
2082 carousel.getElementForItem(ev.newValue));
2087 // Handle item selection on mouse click
2088 carousel.on("click", function (ev) {
2089 if (carousel.isAutoPlayOn()) {
2090 carousel.stopAutoPlay();
2092 carousel._itemClickHandler(ev);
2093 carousel._pagerClickHandler(ev);
2096 // Restore the focus on the navigation buttons
2098 Event.onFocus(carousel.get("element"), function (ev, obj) {
2099 var target = Event.getTarget(ev);
2101 if (target && target.nodeName.toUpperCase() == "A" &&
2102 Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
2104 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
2106 focussedLi = target.parentNode;
2107 Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
2110 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
2114 obj._hasFocus = true;
2115 obj._updateNavButtons(Event.getTarget(ev), true);
2118 Event.onBlur(carousel.get("element"), function (ev, obj) {
2119 obj._hasFocus = false;
2120 obj._updateNavButtons(Event.getTarget(ev), false);
2125 * Return true if the Carousel is still animating, or false otherwise.
2127 * @method isAnimating
2128 * @return {Boolean} Return true if animation is still in progress, or
2132 isAnimating: function () {
2133 return this._isAnimationInProgress;
2137 * Return true if the auto-scrolling of Carousel is "on", or false
2140 * @method isAutoPlayOn
2141 * @return {Boolean} Return true if autoPlay is "on", or false
2145 isAutoPlayOn: function () {
2146 return this._isAutoPlayInProgress;
2150 * Return the carouselItemEl at index or null if the index is not
2153 * @method getElementForItem
2154 * @param index {Number} The index of the item to be returned
2155 * @return {Element} Return the item at index or null if not found
2158 getElementForItem: function (index) {
2159 var carousel = this;
2161 if (index < 0 || index >= carousel.get("numItems")) {
2165 if (carousel._itemsTable.items[index]) {
2166 return Dom.get(carousel._itemsTable.items[index].id);
2173 * Return the carouselItemEl for all items in the Carousel.
2175 * @method getElementForItems
2176 * @return {Array} Return all the items
2179 getElementForItems: function () {
2180 var carousel = this, els = [], i;
2182 for (i = 0; i < carousel._itemsTable.numItems; i++) {
2183 els.push(carousel.getElementForItem(i));
2190 * Return the item at index or null if the index is not found.
2193 * @param index {Number} The index of the item to be returned
2194 * @return {Object} Return the item at index or null if not found
2197 getItem: function (index) {
2198 var carousel = this;
2200 if (index < 0 || index >= carousel.get("numItems")) {
2204 if (carousel._itemsTable.items.length > index) {
2205 if (!JS.isUndefined(carousel._itemsTable.items[index])) {
2206 return carousel._itemsTable.items[index];
2214 * Return all items as an array.
2217 * @return {Array} Return all items in the Carousel
2220 getItems: function () {
2221 return this._itemsTable.items;
2225 * Return all loading items as an array.
2227 * @method getLoadingItems
2228 * @return {Array} Return all items that are loading in the Carousel.
2231 getLoadingItems: function () {
2232 return this._itemsTable.loading;
2236 * For a multirow carousel, return the number of rows specified by user.
2239 * @return {Number} Number of rows
2242 getRows: function () {
2247 * For a multirow carousel, return the number of cols specified by user.
2250 * @return {Array} Return all items in the Carousel
2253 getCols: function () {
2258 * Return the position of the Carousel item that has the id "id", or -1
2259 * if the id is not found.
2261 * @method getItemPositionById
2262 * @param index {Number} The index of the item to be returned
2265 getItemPositionById: function (id) {
2266 var carousel = this,
2267 n = carousel.get("numItems"),
2269 items = carousel._itemsTable.items,
2273 item = items[i] || {};
2284 * Return all visible items as an array.
2286 * @method getVisibleItems
2287 * @return {Array} The array of visible items
2290 getVisibleItems: function () {
2291 var carousel = this,
2292 i = carousel.get("firstVisible"),
2293 n = i + carousel.get("numVisible"),
2297 r.push(carousel.getElementForItem(i));
2305 * Remove an item at index from the Carousel.
2307 * @method removeItem
2309 * @param index {Number} The position to where in the list (starts from
2311 * @return {Boolean} Return true on success, false otherwise
2313 removeItem: function (index) {
2314 var carousel = this,
2315 itemsTable = carousel._itemsTable,
2317 num = carousel.get("numItems");
2319 if (index < 0 || index >= num) {
2323 item = itemsTable.items.splice(index, 1);
2324 if (item && item.length == 1) {
2325 if(itemsTable.numItems){
2326 itemsTable.numItems--;
2329 carousel.set("numItems", num - 1);
2331 carousel.fireEvent(itemRemovedEvent,
2332 { item: item[0], pos: index, ev: itemRemovedEvent });
2340 * Replace an item at index witin Carousel.
2342 * @method replaceItem
2344 * @param item {HTML | Object | HTMLElement} The item to be appended
2345 * to the Carousel. If the parameter is a string, it is assumed to be
2346 * the HTML content of the newly created item. If the parameter is an
2347 * object, it is assumed to supply the content and an optional class
2348 * and an optional id of the newly created item.
2349 * @param index {Number} The position to where in the list (starts from
2351 * @return {Boolean} Return true on success, false otherwise
2353 replaceItem: function (item, index) {
2354 var carousel = this,
2358 numItems = carousel.get("numItems"),
2366 if (JS.isString(item) || item.nodeName) {
2367 content = item.nodeName ? item.innerHTML : item;
2368 } else if (JS.isObject(item)) {
2369 content = item.content;
2374 if (JS.isUndefined(index)) {
2377 if (index < 0 || index >= numItems) {
2381 oel = carousel._itemsTable.items[index];
2383 oel = carousel._itemsTable.loading[index];
2384 carousel._itemsTable.items[index] = undefined;
2387 elId = oel.id || Dom.generateId();
2388 carousel._itemsTable.items.splice(index, 1, {
2390 className : carousel.CLASSES.ITEM + (item.className ? " " + item.className : ""),
2394 el = carousel._itemsTable.items[index];
2396 carousel.fireEvent(itemReplacedEvent,
2397 { newItem: el, oldItem: oel, pos: index, ev: itemReplacedEvent });
2403 * Replace multiple items at specified indexes.
2404 * NOTE: item at index must already exist.
2406 * @method replaceItems
2408 * @param items {Array} An array containing an array of replacement items each linked to the
2409 * index where the substitution should take place.
2410 * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]]
2411 * @return {Boolean} Return true on success, false otherwise
2413 replaceItems: function (items) {
2414 var i, n, rv = true;
2416 if (!JS.isArray(items)) {
2420 syncUiOnItemInsert = false;
2421 for (i = 0, n = items.length; i < n; i++) {
2422 if (this.replaceItem(items[i][0], items[i][1]) === false) {
2426 syncUiOnItemInsert = true;
2428 this._syncUiItems();
2434 * Render the Carousel.
2438 * @param appendTo {HTMLElement | String} The element to which the
2439 * Carousel should be appended prior to rendering.
2440 * @return {Boolean} Status of the operation
2442 render: function (appendTo) {
2443 var carousel = this,
2444 cssClass = carousel.CLASSES,
2445 rows = carousel._rows;
2447 carousel.addClass(cssClass.CAROUSEL);
2449 if (!carousel._clipEl) {
2450 carousel._clipEl = carousel._createCarouselClip();
2451 carousel._clipEl.appendChild(carousel._carouselEl);
2455 carousel.appendChild(carousel._clipEl);
2456 carousel.appendTo(appendTo);
2458 if (!Dom.inDocument(carousel.get("element"))) {
2461 carousel.appendChild(carousel._clipEl);
2465 Dom.addClass(carousel._clipEl, cssClass.MULTI_ROW);
2468 if (carousel.get("isVertical")) {
2469 carousel.addClass(cssClass.VERTICAL);
2471 carousel.addClass(cssClass.HORIZONTAL);
2474 if (carousel.get("numItems") < 1) {
2478 carousel._refreshUi();
2484 * Scroll the Carousel by an item backward.
2486 * @method scrollBackward
2489 scrollBackward: function () {
2490 var carousel = this;
2491 carousel.scrollTo(carousel._firstItem -
2492 carousel.get("scrollIncrement"));
2496 * Scroll the Carousel by an item forward.
2498 * @method scrollForward
2501 scrollForward: function () {
2502 var carousel = this;
2503 carousel.scrollTo(carousel._firstItem +
2504 carousel.get("scrollIncrement"));
2508 * Scroll the Carousel by a page backward.
2510 * @method scrollPageBackward
2513 scrollPageBackward: function () {
2514 var carousel = this,
2515 isVertical = carousel.get("isVertical"),
2516 cols = carousel._cols,
2517 firstVisible = carousel.get("firstVisible"),
2518 item = firstVisible - carousel.get("numVisible");
2521 // Only account for multi-row when scrolling backwards from
2524 item = firstVisible - cols;
2528 carousel.scrollTo(item);
2532 * Scroll the Carousel by a page forward.
2534 * @method scrollPageForward
2537 scrollPageForward: function () {
2538 var carousel = this,
2539 item = carousel._firstItem + carousel.get("numVisible");
2541 if (item > carousel.get("numItems")) {
2545 if (carousel.get("selectOnScroll")) {
2546 carousel._selectedItem = carousel._getSelectedItem(item);
2549 carousel.scrollTo(item);
2553 * Scroll the Carousel to make the item the first visible item.
2557 * @param item Number The index of the element to position at.
2558 * @param dontSelect Boolean True if select should be avoided
2560 scrollTo: function (item, dontSelect) {
2561 var carousel = this, animate, animCfg, isCircular, isVertical,
2562 delta, direction, firstItem, lastItem, itemsPerRow,
2563 itemsPerCol, numItems, numPerPage, offset, page, rv, sentinel,
2564 index, stopAutoScroll,
2565 itemsTable = carousel._itemsTable;
2567 if (itemsTable.numItems === 0 || item == carousel._firstItem ||
2568 carousel.isAnimating()) {
2569 return; // nothing to do!
2572 animCfg = carousel.get("animation");
2573 isCircular = carousel.get("isCircular");
2574 isVertical = carousel.get("isVertical");
2575 itemsPerRow = carousel._cols;
2576 itemsPerCol = carousel._rows;
2577 firstItem = carousel._firstItem;
2578 numItems = carousel.get("numItems");
2579 numPerPage = carousel.get("numVisible");
2580 page = carousel.get("currentPage");
2582 stopAutoScroll = function () {
2583 if (carousel.isAutoPlayOn()) {
2584 carousel.stopAutoPlay();
2590 // Normalize the offset so that it doesn't scroll to a
2591 // different index when number of items is not a factor of
2592 // the number of visible items
2593 if (numItems % numPerPage !== 0) {
2594 item = numItems + (numItems%numPerPage) - numPerPage-1;
2596 item = numItems + item;
2599 stopAutoScroll.call(carousel);
2602 } else if (numItems > 0 && item > numItems - 1) {
2604 if (carousel.get("isCircular")) {
2605 item = numItems - item;
2607 stopAutoScroll.call(carousel);
2616 direction = (carousel._firstItem > item) ? "backward" : "forward";
2618 sentinel = firstItem + numPerPage;
2619 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2620 rv = carousel.fireEvent(beforeScrollEvent,
2621 { dir: direction, first: firstItem, last: sentinel });
2622 if (rv === false) { // scrolling is prevented
2626 carousel.fireEvent(beforePageChangeEvent, { page: page });
2628 // call loaditems to check if we have all the items to display
2629 lastItem = item + numPerPage - 1;
2630 carousel._loadItems(lastItem > numItems-1 ? numItems-1 : lastItem);
2632 // Calculate the delta relative to the first item, the delta is
2637 // offset calculations for multirow Carousel
2639 delta = parseInt(delta / itemsPerRow, 10);
2641 delta = parseInt(delta / itemsPerCol, 10);
2645 carousel._firstItem = item;
2646 carousel.set("firstVisible", item);
2648 if (!dontSelect && carousel.get("selectOnScroll")) {
2649 carousel._selectedItem = item;
2653 sentinel = item + numPerPage;
2654 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2656 offset = getScrollOffset.call(carousel, delta);
2658 animate = animCfg.speed > 0;
2661 carousel._animateAndSetCarouselOffset(offset, item, sentinel,
2664 carousel._setCarouselOffset(offset);
2665 updateStateAfterScroll.call(carousel, item, sentinel);
2670 * Get the page an item is on within carousel.
2672 * @method getPageForItem
2674 * @param index {Number} Index of item
2675 * @return {Number} Page item is on
2677 getPageForItem : function(item) {
2679 (item+1) / parseInt(this.get("numVisible"),10)
2684 * Get the first visible item's index on any given page.
2686 * @method getFirstVisibleOnpage
2688 * @param page {Number} Page
2689 * @return {Number} First item's index
2691 getFirstVisibleOnPage : function(page) {
2692 return (page - 1) * this.get("numVisible");
2696 * Select the previous item in the Carousel.
2698 * @method selectPreviousItem
2701 selectPreviousItem: function () {
2702 var carousel = this,
2704 selected = carousel.get("selectedItem");
2706 if (selected == carousel._firstItem) {
2707 newpos = selected - carousel.get("numVisible");
2708 carousel._selectedItem = carousel._getSelectedItem(selected-1);
2709 // since we have selected the item already
2710 carousel.scrollTo(newpos, true);
2712 newpos = carousel.get("selectedItem") -
2713 carousel.get("scrollIncrement");
2714 carousel.set("selectedItem",carousel._getSelectedItem(newpos));
2719 * Select the next item in the Carousel.
2721 * @method selectNextItem
2724 selectNextItem: function () {
2725 var carousel = this, newpos = 0;
2727 newpos = carousel.get("selectedItem") +
2728 carousel.get("scrollIncrement");
2729 carousel.set("selectedItem", carousel._getSelectedItem(newpos));
2733 * Display the Carousel.
2739 var carousel = this,
2740 cssClass = carousel.CLASSES;
2742 if (carousel.fireEvent(beforeShowEvent) !== false) {
2743 carousel.addClass(cssClass.VISIBLE);
2744 showNavigation.call(carousel);
2745 carousel.fireEvent(showEvent);
2750 * Start auto-playing the Carousel.
2752 * @method startAutoPlay
2755 startAutoPlay: function () {
2756 var carousel = this, timer;
2758 if (JS.isUndefined(carousel._autoPlayTimer)) {
2759 timer = carousel.get("autoPlayInterval");
2763 carousel._isAutoPlayInProgress = true;
2764 carousel.fireEvent(startAutoPlayEvent);
2765 carousel._autoPlayTimer = setTimeout(function () {
2766 carousel._autoScroll();
2772 * Stop auto-playing the Carousel.
2774 * @method stopAutoPlay
2777 stopAutoPlay: function () {
2778 var carousel = this;
2780 if (!JS.isUndefined(carousel._autoPlayTimer)) {
2781 clearTimeout(carousel._autoPlayTimer);
2782 delete carousel._autoPlayTimer;
2783 carousel._isAutoPlayInProgress = false;
2784 carousel.fireEvent(stopAutoPlayEvent);
2789 * Update interface's pagination data within a registered template.
2791 * @method updatePagination
2794 updatePagination: function () {
2795 var carousel = this,
2796 pagination = carousel._pagination;
2797 if(!pagination.el){ return false; }
2799 var numItems = carousel.get('numItems'),
2800 numVisible = carousel.get('numVisible'),
2801 firstVisible = carousel.get('firstVisible')+1,
2802 currentPage = carousel.get('currentPage')+1,
2803 numPages = carousel.get('numPages'),
2805 'numVisible' : numVisible,
2806 'numPages' : numPages,
2807 'numItems' : numItems,
2808 'selectedItem' : carousel.get('selectedItem')+1,
2809 'currentPage' : currentPage,
2810 'firstVisible' : firstVisible,
2811 'lastVisible' : carousel.get("lastVisible")+1
2813 cb = pagination.callback || {},
2814 scope = cb.scope && cb.obj ? cb.obj : carousel;
2816 pagination.el.innerHTML = JS.isFunction(cb.fn) ? cb.fn.apply(scope, [pagination.template, replacements]) : YAHOO.lang.substitute(pagination.template, replacements);
2820 * Register carousels pagination template, append to interface, and populate.
2822 * @method registerPagination
2823 * @param template {String} Pagination template as passed to lang.substitute
2826 registerPagination: function (tpl, pos, cb) {
2827 var carousel = this;
2829 carousel._pagination.template = tpl;
2830 carousel._pagination.callback = cb || {};
2832 if(!carousel._pagination.el){
2833 carousel._pagination.el = createElement('DIV', {className:carousel.CLASSES.PAGINATION});
2835 if(pos == "before"){
2836 carousel._navEl.insertBefore(carousel._pagination.el, carousel._navEl.firstChild);
2838 carousel._navEl.appendChild(carousel._pagination.el);
2841 carousel.on('itemSelected', carousel.updatePagination);
2842 carousel.on('pageChange', carousel.updatePagination);
2845 carousel.updatePagination();
2849 * Return the string representation of the Carousel.
2855 toString: function () {
2856 return WidgetName + (this.get ? " (#" + this.get("id") + ")" : "");
2860 * Protected methods of the Carousel component
2864 * Set the Carousel offset to the passed offset after animating.
2866 * @method _animateAndSetCarouselOffset
2867 * @param {Integer} offset The offset to which the Carousel has to be
2869 * @param {Integer} item The index to which the Carousel will scroll.
2870 * @param {Integer} sentinel The last element in the view port.
2873 _animateAndSetCarouselOffset: function (offset, item, sentinel) {
2874 var carousel = this,
2875 animCfg = carousel.get("animation"),
2878 if (carousel.get("isVertical")) {
2879 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2880 { top: { to: offset } },
2881 animCfg.speed, animCfg.effect);
2883 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2884 { left: { to: offset } },
2885 animCfg.speed, animCfg.effect);
2888 carousel._isAnimationInProgress = true;
2889 animObj.onComplete.subscribe(carousel._animationCompleteHandler,
2890 { scope: carousel, item: item,
2896 * Handle the animation complete event.
2898 * @method _animationCompleteHandler
2899 * @param {Event} ev The event.
2900 * @param {Array} p The event parameters.
2901 * @param {Object} o The object that has the state of the Carousel
2904 _animationCompleteHandler: function (ev, p, o) {
2905 o.scope._isAnimationInProgress = false;
2906 updateStateAfterScroll.call(o.scope, o.item, o.last);
2910 * Automatically scroll the contents of the Carousel.
2911 * @method _autoScroll
2914 _autoScroll: function() {
2915 var carousel = this,
2916 currIndex = carousel._firstItem,
2919 if (currIndex >= carousel.get("numItems") - 1) {
2920 if (carousel.get("isCircular")) {
2923 carousel.stopAutoPlay();
2926 index = currIndex + carousel.get("numVisible");
2929 carousel._selectedItem = carousel._getSelectedItem(index);
2930 carousel.scrollTo.call(carousel, index);
2934 * Create the Carousel.
2936 * @method createCarousel
2937 * @param elId {String} The id of the element to be created
2940 _createCarousel: function (elId) {
2941 var carousel = this,
2942 cssClass = carousel.CLASSES,
2946 el = createElement("DIV", {
2947 className : cssClass.CAROUSEL,
2952 if (!carousel._carouselEl) {
2953 carousel._carouselEl=createElement(carousel.get("carouselEl"),
2954 { className: cssClass.CAROUSEL_EL });
2961 * Create the Carousel clip container.
2963 * @method createCarouselClip
2966 _createCarouselClip: function () {
2967 return createElement("DIV", { className: this.CLASSES.CONTENT });
2971 * Create the Carousel item.
2973 * @method createCarouselItem
2974 * @param obj {Object} The attributes of the element to be created
2977 _createCarouselItem: function (obj) {
2978 var attr, carousel = this;
2980 return createElement(carousel.get("carouselItemEl"), {
2981 className : obj.className,
2983 content : obj.content,
2989 * Return a valid item for a possibly out of bounds index considering
2990 * the isCircular property.
2992 * @method _getValidIndex
2993 * @param index {Number} The index of the item to be returned
2994 * @return {Object} Return a valid item index
2997 _getValidIndex: function (index) {
2998 var carousel = this,
2999 isCircular = carousel.get("isCircular"),
3000 numItems = carousel.get("numItems"),
3001 numVisible = carousel.get("numVisible"),
3002 sentinel = numItems - 1;
3005 index = isCircular ?
3006 Math.ceil(numItems/numVisible)*numVisible + index : 0;
3007 } else if (index > sentinel) {
3008 index = isCircular ? 0 : sentinel;
3015 * Get the value for the selected item.
3017 * @method _getSelectedItem
3018 * @param val {Number} The new value for "selected" item
3019 * @return {Number} The new value that would be set
3022 _getSelectedItem: function (val) {
3023 var carousel = this,
3024 isCircular = carousel.get("isCircular"),
3025 numItems = carousel.get("numItems"),
3026 sentinel = numItems - 1;
3030 val = numItems + val;
3032 val = carousel.get("selectedItem");
3034 } else if (val > sentinel) {
3036 val = val - numItems;
3038 val = carousel.get("selectedItem");
3045 * The "focus" handler for a Carousel.
3047 * @method _focusHandler
3048 * @param {Event} ev The event object
3051 _focusHandler: function() {
3052 var carousel = this;
3053 if (carousel._hasFocus) {
3059 * The "click" handler for the item.
3061 * @method _itemClickHandler
3062 * @param {Event} ev The event object
3065 _itemClickHandler: function (ev) {
3066 var carousel = this,
3067 carouselItem = carousel.get("carouselItemEl"),
3068 container = carousel.get("element"),
3071 target = Event.getTarget(ev),
3072 tag = target.tagName.toUpperCase();
3074 if(tag === "INPUT" ||
3076 tag === "TEXTAREA") {
3080 while (target && target != container &&
3081 target.id != carousel._carouselEl) {
3082 el = target.nodeName;
3083 if (el.toUpperCase() == carouselItem) {
3086 target = target.parentNode;
3089 if ((item = carousel.getItemPositionById(target.id)) >= 0) {
3090 carousel.set("selectedItem", carousel._getSelectedItem(item));
3096 * The keyboard event handler for Carousel.
3098 * @method _keyboardEventHandler
3099 * @param ev {Event} The event that is being handled.
3102 _keyboardEventHandler: function (ev) {
3103 var carousel = this,
3104 key = Event.getCharCode(ev),
3105 target = Event.getTarget(ev),
3108 // do not mess while animation is in progress or naving via select
3109 if (carousel.isAnimating() || target.tagName.toUpperCase() === "SELECT") {
3114 case 0x25: // left arrow
3115 case 0x26: // up arrow
3116 carousel.selectPreviousItem();
3119 case 0x27: // right arrow
3120 case 0x28: // down arrow
3121 carousel.selectNextItem();
3124 case 0x21: // page-up
3125 carousel.scrollPageBackward();
3128 case 0x22: // page-down
3129 carousel.scrollPageForward();
3135 if (carousel.isAutoPlayOn()) {
3136 carousel.stopAutoPlay();
3138 Event.preventDefault(ev);
3143 * The load the required set of items that are needed for display.
3145 * @method _loadItems
3148 _loadItems: function(last) {
3149 var carousel = this,
3150 numItems = carousel.get("numItems"),
3151 numVisible = carousel.get("numVisible"),
3152 reveal = carousel.get("revealAmount"),
3153 first = carousel._itemsTable.items.length,
3154 lastVisible = carousel.get("lastVisible");
3156 // adjust if going backwards
3157 if(first > last && last+1 >= numVisible){
3158 // need to get first a bit differently for the last page
3159 first = last % numVisible || last == lastVisible ? last - last % numVisible : last - numVisible + 1;
3162 if(reveal && last < numItems - 1){ last++; }
3164 if (last >= first && (!carousel.getItem(first) || !carousel.getItem(last))) {
3165 carousel.fireEvent(loadItemsEvent, {
3166 ev: loadItemsEvent, first: first, last: last,
3167 num: last - first + 1
3174 * The "onchange" handler for select box pagination.
3176 * @method _pagerChangeHandler
3177 * @param {Event} ev The event object
3180 _pagerChangeHandler: function (ev) {
3181 var carousel = this,
3182 target = Event.getTarget(ev),
3183 page = target.value,
3187 item = carousel.getFirstVisibleOnPage(page);
3188 carousel._selectedItem = item;
3189 carousel.scrollTo(item);
3194 * The "click" handler for anchor pagination.
3196 * @method _pagerClickHandler
3197 * @param {Event} ev The event object
3200 _pagerClickHandler: function (ev) {
3201 var carousel = this,
3202 css = carousel.CLASSES,
3203 target = Event.getTarget(ev),
3204 elNode = target.nodeName.toUpperCase(),
3210 if (Dom.hasClass(target, css.PAGER_ITEM) || Dom.hasClass(target.parentNode, css.PAGER_ITEM)) {
3211 if (elNode == "EM") {
3212 target = target.parentNode;// item is an em and not an anchor (when text is visible)
3215 stringIndex = val.lastIndexOf("#");
3216 page = parseInt(val.substring(stringIndex+1), 10);
3218 item = carousel.getFirstVisibleOnPage(page);
3219 carousel._selectedItem = item;
3220 carousel.scrollTo(item);
3223 Event.preventDefault(ev);
3228 * Find the Carousel within a container. The Carousel is identified by
3229 * the first element that matches the carousel element tag or the
3230 * element that has the Carousel class.
3232 * @method parseCarousel
3233 * @param parent {HTMLElement} The parent element to look under
3234 * @return {Boolean} True if Carousel is found, false otherwise
3237 _parseCarousel: function (parent) {
3238 var carousel = this, child, cssClass, domEl, found, node;
3240 cssClass = carousel.CLASSES;
3241 domEl = carousel.get("carouselEl");
3244 for (child = parent.firstChild; child; child = child.nextSibling) {
3245 if (child.nodeType == 1) {
3246 node = child.nodeName;
3247 if (node.toUpperCase() == domEl) {
3248 carousel._carouselEl = child;
3249 Dom.addClass(carousel._carouselEl,
3250 carousel.CLASSES.CAROUSEL_EL);
3260 * Find the items within the Carousel and add them to the items table.
3261 * A Carousel item is identified by elements that matches the carousel
3264 * @method parseCarouselItems
3267 _parseCarouselItems: function () {
3268 var carousel = this,
3269 cssClass = carousel.CLASSES,
3276 index = carousel.get("firstVisible"),
3277 parent = carousel._carouselEl;
3279 rows = carousel._rows;
3280 domItemEl = carousel.get("carouselItemEl");
3282 for (child = parent.firstChild; child; child = child.nextSibling) {
3283 if (child.nodeType == 1) {
3284 node = child.nodeName;
3285 if (node.toUpperCase() == domItemEl) {
3289 elId = Dom.generateId();
3290 child.setAttribute("id", elId);
3291 Dom.addClass(child, carousel.CLASSES.ITEM);
3293 carousel.addItem(child,index);
3301 * Find the Carousel navigation within a container. The navigation
3302 * elements need to match the carousel navigation class names.
3304 * @method parseCarouselNavigation
3305 * @param parent {HTMLElement} The parent element to look under
3306 * @return {Boolean} True if at least one is found, false otherwise
3309 _parseCarouselNavigation: function (parent) {
3310 var carousel = this,
3312 cssClass = carousel.CLASSES,
3319 nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent);
3320 if (nav.length > 0) {
3322 if (nav.hasOwnProperty(i)) {
3324 if (el.nodeName == "INPUT" ||
3325 el.nodeName == "BUTTON" ||
3326 el.nodeName == "A") {// Anchor support in Nav (for SEO)
3327 carousel._navBtns.prev.push(el);
3329 j = el.getElementsByTagName("INPUT");
3330 if (JS.isArray(j) && j.length > 0) {
3331 carousel._navBtns.prev.push(j[0]);
3333 j = el.getElementsByTagName("BUTTON");
3334 if (JS.isArray(j) && j.length > 0) {
3335 carousel._navBtns.prev.push(j[0]);
3341 cfg = { prev: nav };
3344 nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent);
3345 if (nav.length > 0) {
3347 if (nav.hasOwnProperty(i)) {
3349 if (el.nodeName == "INPUT" ||
3350 el.nodeName == "BUTTON" ||
3351 el.nodeName == "A") {// Anchor support in Nav (for SEO)
3352 carousel._navBtns.next.push(el);
3354 j = el.getElementsByTagName("INPUT");
3355 if (JS.isArray(j) && j.length > 0) {
3356 carousel._navBtns.next.push(j[0]);
3358 j = el.getElementsByTagName("BUTTON");
3359 if (JS.isArray(j) && j.length > 0) {
3360 carousel._navBtns.next.push(j[0]);
3369 cfg = { next: nav };
3374 carousel.set("navigation", cfg);
3382 * Refresh the widget UI if it is not already rendered, on first item
3385 * @method _refreshUi
3388 _refreshUi: function () {
3389 var carousel = this,
3390 isVertical = carousel.get("isVertical"),
3391 firstVisible = carousel.get("firstVisible"),
3392 i, item, n, rsz, sz;
3394 if (carousel._itemsTable.numItems < 1) {
3398 sz = getCarouselItemSize.call(carousel,
3399 isVertical ? "height" : "width");
3400 // This fixes the widget to auto-adjust height/width for absolute
3401 // positioned children.
3402 item = carousel._itemsTable.items[firstVisible].id;
3404 sz = isVertical ? getStyle(item, "width") :
3405 getStyle(item, "height");
3407 Dom.setStyle(carousel._carouselEl,
3408 isVertical ? "width" : "height", sz + "px");
3410 // Set the rendered state appropriately.
3411 carousel._hasRendered = true;
3412 carousel.fireEvent(renderEvent);
3416 * Set the Carousel offset to the passed offset.
3418 * @method _setCarouselOffset
3421 _setCarouselOffset: function (offset) {
3422 var carousel = this, which;
3424 which = carousel.get("isVertical") ? "top" : "left";
3425 Dom.setStyle(carousel._carouselEl, which, offset + "px");
3429 * Setup/Create the Carousel navigation element (if needed).
3431 * @method _setupCarouselNavigation
3434 _setupCarouselNavigation: function () {
3435 var carousel = this,
3436 btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
3438 cssClass = carousel.CLASSES;
3440 // TODO: can the _navBtns be tested against instead?
3441 navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
3442 "DIV", carousel.get("element"));
3444 if (navContainer.length === 0) {
3445 navContainer = createElement("DIV",
3446 { className: cssClass.NAVIGATION });
3447 carousel.insertBefore(navContainer,
3448 Dom.getFirstChild(carousel.get("element")));
3450 navContainer = navContainer[0];
3453 carousel._pages.el = createElement("UL");
3454 navContainer.appendChild(carousel._pages.el);
3456 nav = carousel.get("navigation");
3457 if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
3458 if (JS.isString(nav.prev)) {
3459 nav.prev = [nav.prev];
3461 for (btn in nav.prev) {
3462 if (nav.prev.hasOwnProperty(btn)) {
3463 carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
3467 // TODO: separate method for creating a navigation button
3468 prevButton = createElement("SPAN",
3469 { className: cssClass.BUTTON + cssClass.FIRST_NAV });
3471 Dom.setStyle(prevButton, "visibility", "visible");
3472 btn = Dom.generateId();
3473 prevButton.innerHTML = "<button type=\"button\" " +
3474 "id=\"" + btn + "\" name=\"" +
3475 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">" +
3476 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>";
3477 navContainer.appendChild(prevButton);
3479 carousel._navBtns.prev = [btn];
3480 cfg = { prev: [prevButton] };
3483 if (JS.isString(nav.next) || JS.isArray(nav.next)) {
3484 if (JS.isString(nav.next)) {
3485 nav.next = [nav.next];
3487 for (btn in nav.next) {
3488 if (nav.next.hasOwnProperty(btn)) {
3489 carousel._navBtns.next.push(Dom.get(nav.next[btn]));
3493 // TODO: separate method for creating a navigation button
3494 nextButton = createElement("SPAN",
3495 { className: cssClass.BUTTON + cssClass.NEXT_NAV });
3497 Dom.setStyle(nextButton, "visibility", "visible");
3498 btn = Dom.generateId();
3499 nextButton.innerHTML = "<button type=\"button\" " +
3500 "id=\"" + btn + "\" name=\"" +
3501 carousel.STRINGS.NEXT_BUTTON_TEXT + "\">" +
3502 carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>";
3503 navContainer.appendChild(nextButton);
3505 carousel._navBtns.next = [btn];
3507 cfg.next = [nextButton];
3509 cfg = { next: [nextButton] };
3514 carousel.set("navigation", cfg);
3517 return navContainer;
3521 * Set the clip container size (based on the new numVisible value).
3523 * @method _setClipContainerSize
3524 * @param clip {HTMLElement} The clip container element.
3525 * @param num {Number} optional The number of items per page.
3528 _setClipContainerSize: function (clip, num) {
3529 var carousel = this,
3530 isVertical = carousel.get("isVertical"),
3531 rows = carousel._rows,
3532 cols = carousel._cols,
3533 reveal = carousel.get("revealAmount"),
3534 itemHeight = getCarouselItemSize.call(carousel, "height"),
3535 itemWidth = getCarouselItemSize.call(carousel, "width"),
3539 carousel._recomputeSize = (containerHeight === 0); // bleh!
3540 if (carousel._recomputeSize) {
3541 carousel._hasRendered = false;
3542 return; // no use going further, bail out!
3545 clip = clip || carousel._clipEl;
3548 containerHeight = itemHeight * rows;
3549 containerWidth = itemWidth * cols;
3551 num = num || carousel.get("numVisible");
3553 containerHeight = itemHeight * num;
3555 containerWidth = itemWidth * num;
3559 reveal = getRevealSize.call(carousel);
3561 containerHeight += (reveal * 2);
3563 containerWidth += (reveal * 2);
3567 containerHeight += getDimensions(carousel._carouselEl,"height");
3568 Dom.setStyle(clip, "height", containerHeight + "px");
3569 // For multi-row Carousel
3571 containerWidth += getDimensions(carousel._carouselEl,
3573 Dom.setStyle(clip, "width", containerWidth + (0) + "px");
3576 containerWidth += getDimensions(carousel._carouselEl, "width");
3577 Dom.setStyle(clip, "width", containerWidth + "px");
3578 // For multi-row Carousel
3580 containerHeight += getDimensions(carousel._carouselEl,
3582 Dom.setStyle(clip, "height", containerHeight + "px");
3587 carousel._setContainerSize(clip); // adjust the container size
3592 * Set the container size.
3594 * @method _setContainerSize
3595 * @param clip {HTMLElement} The clip container element.
3596 * @param attr {String} Either set the height or width.
3599 _setContainerSize: function (clip, attr) {
3600 var carousel = this,
3601 config = carousel.CONFIG,
3602 cssClass = carousel.CLASSES,
3608 isVertical = carousel.get("isVertical");
3609 rows = carousel._rows;
3610 cols = carousel._cols;
3611 clip = clip || carousel._clipEl;
3612 attr = attr || (isVertical ? "height" : "width");
3613 size = parseFloat(Dom.getStyle(clip, attr), 10);
3615 size = JS.isNumber(size) ? size : 0;
3618 size += getDimensions(carousel._carouselEl, "height") +
3619 getStyle(carousel._navEl, "height");
3621 size += getDimensions(carousel._carouselEl, "width");
3625 if (size < config.HORZ_MIN_WIDTH) {
3626 size = config.HORZ_MIN_WIDTH;
3627 carousel.addClass(cssClass.MIN_WIDTH);
3630 carousel.setStyle(attr, size + "px");
3632 // Additionally the width of the container should be set for
3633 // the vertical Carousel
3635 size = getCarouselItemSize.call(carousel, "width");
3639 // Bug fix for vertical carousel (goes in conjunction with
3640 // .yui-carousel-element {... 3200px removed from styles), and
3641 // allows for multirows in IEs).
3642 Dom.setStyle(carousel._carouselEl, "width", size + "px");
3643 if (size < config.VERT_MIN_WIDTH) {
3644 size = config.VERT_MIN_WIDTH;
3645 // set a min width on vertical carousel, don't see why this
3646 // shouldn't always be set...
3647 carousel.addClass(cssClass.MIN_WIDTH);
3649 carousel.setStyle("width", size + "px");
3652 * Fix for automatically computing the height and width in IE.
3653 * Many thanks to ErisDS for the fix.
3654 * For more information visit,
3655 * http://erisds.co.uk/code/yui2-javascript-carousel-an-update-about-version-2-8
3657 size = getCarouselItemSize.call(carousel, "height");
3661 Dom.setStyle(carousel._carouselEl, "height", size + "px");
3666 * Set the value for the Carousel's first visible item.
3668 * @method _setFirstVisible
3669 * @param val {Number} The new value for firstVisible
3670 * @return {Number} The new value that would be set
3673 _setFirstVisible: function (val) {
3674 var carousel = this;
3676 if (val >= 0 && val < carousel.get("numItems")) {
3677 carousel.scrollTo(val);
3679 val = carousel.get("firstVisible");
3685 * Set the value for the Carousel's navigation.
3687 * @method _setNavigation
3688 * @param cfg {Object} The navigation configuration
3689 * @return {Object} The new value that would be set
3692 _setNavigation: function (cfg) {
3693 var carousel = this;
3696 Event.on(cfg.prev, "click", scrollPageBackward, carousel);
3699 Event.on(cfg.next, "click", scrollPageForward, carousel);
3704 * Clip the container size every time numVisible is set.
3706 * @method _setNumVisible
3707 * @param val {Number} The new value for numVisible
3708 * @return {Number} The new value that would be set
3711 _setNumVisible: function (val) { // TODO: _setNumVisible should just be reserved for setting numVisible.
3712 var carousel = this;
3714 carousel._setClipContainerSize(carousel._clipEl, val);
3718 * Set the value for the number of visible items in the Carousel.
3720 * @method _numVisibleSetter
3721 * @param val {Number} The new value for numVisible
3722 * @return {Number} The new value that would be set
3725 _numVisibleSetter: function (val) {
3726 var carousel = this,
3729 if(JS.isArray(val)) {
3730 carousel._cols = val[0];
3731 carousel._rows = val[1];
3732 numVisible = val[0] * val[1];
3738 * Set the value for selectedItem.
3740 * @method _selectedItemSetter
3741 * @param val {Number} The new value for selectedItem
3742 * @return {Number} The new value that would be set
3745 _selectedItemSetter: function (val) {
3746 var carousel = this;
3747 return (val < carousel.get("numItems")) ? val : 0;
3751 * Set the number of items in the Carousel.
3752 * Warning: Setting this to a lower number than the current removes
3753 * items from the end.
3755 * @method _setNumItems
3756 * @param val {Number} The new value for numItems
3757 * @return {Number} The new value that would be set
3760 _setNumItems: function (val) {
3761 var carousel = this,
3762 num = carousel._itemsTable.numItems;
3764 if (JS.isArray(carousel._itemsTable.items)) {
3765 if (carousel._itemsTable.items.length != num) { // out of sync
3766 num = carousel._itemsTable.items.length;
3767 carousel._itemsTable.numItems = num;
3773 carousel.removeItem(num - 1);
3782 * Set the orientation of the Carousel.
3784 * @method _setOrientation
3785 * @param val {Boolean} The new value for isVertical
3786 * @return {Boolean} The new value that would be set
3789 _setOrientation: function (val) {
3790 var carousel = this,
3791 cssClass = carousel.CLASSES;
3794 carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
3796 carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
3799 The _itemAttrCache need not be emptied since the cache is for
3800 DOM attributes that do not change; not the Carousel dimensions.
3807 * Set the value for the reveal amount percentage in the Carousel.
3809 * @method _setRevealAmount
3810 * @param val {Number} The new value for revealAmount
3811 * @return {Number} The new value that would be set
3814 _setRevealAmount: function (val) {
3815 var carousel = this;
3817 if (val >= 0 && val <= 100) {
3818 val = parseInt(val, 10);
3819 val = JS.isNumber(val) ? val : 0;
3820 carousel._setClipContainerSize();
3822 val = carousel.get("revealAmount");
3828 * Set the value for the selected item.
3830 * @method _setSelectedItem
3831 * @param val {Number} The new value for "selected" item
3834 _setSelectedItem: function (val) {
3835 this._selectedItem = val;
3839 * Get the total number of pages.
3841 * @method _getNumPages
3844 _getNumPages: function () {
3846 parseInt(this.get("numItems"),10) / parseInt(this.get("numVisible"),10)
3851 * Get the last visible item.
3853 * @method _getLastVisible
3856 _getLastVisible: function () {
3857 var carousel = this;
3858 return carousel.get("currentPage") + 1 == carousel.get("numPages") ?
3859 carousel.get("numItems") - 1:
3860 carousel.get("firstVisible") + carousel.get("numVisible") - 1;
3864 * Synchronize and redraw the UI after an item is added.
3866 * @method _syncUiForItemAdd
3869 _syncUiForItemAdd: function (obj) {
3872 carouselEl = carousel._carouselEl,
3875 itemsTable = carousel._itemsTable,
3881 pos = JS.isUndefined(obj.pos) ?
3882 obj.newPos || itemsTable.numItems - 1 : obj.pos;
3885 item = itemsTable.items[pos] || {};
3886 el = carousel._createCarouselItem({
3887 className : item.className,
3888 styles : item.styles,
3889 content : item.item,
3893 if (JS.isUndefined(obj.pos)) {
3894 if (!JS.isUndefined(itemsTable.loading[pos])) {
3895 oel = itemsTable.loading[pos];
3896 // if oel is null, it is a problem ...
3900 carouselEl.replaceChild(el, oel);
3901 // ... and remove the item from the data structure
3902 delete itemsTable.loading[pos];
3904 carouselEl.appendChild(el);
3907 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3908 sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
3911 carouselEl.insertBefore(el, sibling);
3916 if (JS.isUndefined(obj.pos)) {
3917 if (!Dom.isAncestor(carousel._carouselEl, oel)) {
3918 carouselEl.appendChild(oel);
3921 if (!Dom.isAncestor(carouselEl, oel)) {
3922 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3923 carouselEl.insertBefore(oel,
3924 Dom.get(itemsTable.items[obj.pos + 1].id));
3930 if (!carousel._hasRendered) {
3931 carousel._refreshUi();
3934 if (carousel.get("selectedItem") < 0) {
3935 carousel.set("selectedItem", carousel.get("firstVisible"));
3938 carousel._syncUiItems();
3942 * Synchronize and redraw the UI after an item is replaced.
3944 * @method _syncUiForItemReplace
3947 _syncUiForItemReplace: function (o) {
3948 var carousel = this,
3949 carouselEl = carousel._carouselEl,
3950 itemsTable = carousel._itemsTable,
3956 el = carousel._createCarouselItem({
3957 className : item.className,
3958 styles : item.styles,
3959 content : item.item,
3963 // replace the current item's attributes
3964 if ((oel = Dom.get(oel.id))) { // testing assignment
3965 oel.className = item.className;
3966 oel.styles = item.styles;
3967 oel.innerHTML = item.item;
3969 itemsTable.items[pos] = el;
3971 if (itemsTable.loading[pos]) {
3972 itemsTable.numItems++;
3973 delete itemsTable.loading[pos];
3976 // TODO: should we add the item if oel is undefined?
3978 // sync shouldn't be necessary since we're replacing items that are already positioned
3979 //carousel._syncUiItems();
3983 * Synchronize and redraw the UI after an item is removed.
3985 * @method _syncUiForItemRemove
3988 _syncUiForItemRemove: function (obj) {
3989 var carousel = this,
3990 carouselEl = carousel._carouselEl,
3993 num = carousel.get("numItems");
3997 if (item && (el = Dom.get(item.id))) {
3998 if (el && Dom.isAncestor(carouselEl, el)) {
3999 Event.purgeElement(el, true);
4000 carouselEl.removeChild(el);
4003 // nothing is done w/ pos after this, should we remove it?
4004 if (carousel.get("selectedItem") == pos) {
4005 pos = pos >= num ? num - 1 : pos;
4010 carousel._syncUiItems();
4014 * Find the closest sibling to insert before
4016 * @method _findClosestSibling
4019 _findClosestSibling: function (pos) {
4020 var carousel = this,
4021 itemsTable = carousel._itemsTable,
4022 len = itemsTable.items.length,
4026 // attempt to find the next closest sibling
4027 while (j<len && !sibling) {
4028 sibling = itemsTable.items[++j];
4035 * Synchronize the items table for lazy loading.
4037 * @method _syncUiForLazyLoading
4040 _syncUiForLazyLoading: function (obj) {
4041 var carousel = this,
4042 carouselEl = carousel._carouselEl,
4043 itemsTable = carousel._itemsTable,
4044 len = itemsTable.items.length,
4045 sibling = carousel._findClosestSibling(obj.last),
4047 // only add DOM nodes for the currently visible items
4048 // this eliminates uneccessary performance overhead
4049 // but still allows loading styles to be applied to the items
4050 first = last - carousel.get("numVisible") + 1,
4054 for (var i = first; i <= last; i++) {
4055 if(!itemsTable.loading[i] && !itemsTable.items[i]){
4056 el = carousel._createCarouselItem({
4057 className : carousel.CLASSES.ITEM + " " + carousel.CLASSES.ITEM_LOADING,
4058 content : carousel.STRINGS.ITEM_LOADING_CONTENT,
4059 id : Dom.generateId()
4063 sibling = Dom.get(sibling.id);
4065 carouselEl.insertBefore(el, sibling);
4069 carouselEl.appendChild(el);
4072 itemsTable.loading[i] = el;
4076 carousel._syncUiItems();
4080 * Redraw the UI for item positioning.
4082 * @method _syncUiItems
4085 _syncUiItems: function () {
4087 if(!syncUiOnItemInsert) {
4093 numItems = carousel.get("numItems"),
4095 itemsTable = carousel._itemsTable,
4096 items = itemsTable.items,
4097 loading = itemsTable.loading,
4100 updateStyles = false;
4102 for (i = 0; i < numItems; i++) {
4103 item = items[i] || loading[i];
4105 if (item && item.id) {
4106 styles = getCarouselItemPosition.call(carousel, i);
4107 item.styles = item.styles || {};
4109 for (attr in styles) {
4110 if(item.styles[attr] !== styles[attr])
4112 updateStyles = true;
4113 item.styles[attr] = styles[attr];
4118 setStyles(Dom.get(item.id), styles);
4120 updateStyles = false;
4126 * Set the correct class for the navigation buttons.
4128 * @method _updateNavButtons
4129 * @param el {Object} The target button
4130 * @param setFocus {Boolean} True to set focus ring, false otherwise.
4133 _updateNavButtons: function (el, setFocus) {
4135 cssClass = this.CLASSES,
4137 parent = el.parentNode;
4142 grandParent = parent.parentNode;
4144 if (el.nodeName.toUpperCase() == "BUTTON" &&
4145 Dom.hasClass(parent, cssClass.BUTTON)) {
4148 children = Dom.getChildren(grandParent);
4150 Dom.removeClass(children, cssClass.FOCUSSED_BUTTON);
4153 Dom.addClass(parent, cssClass.FOCUSSED_BUTTON);
4155 Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON);
4161 * Update the UI for the pager buttons based on the current page and
4162 * the number of pages.
4164 * @method _updatePagerButtons
4167 _updatePagerButtons: function () {
4169 if(!syncUiOnItemInsert) {
4173 var carousel = this,
4174 css = carousel.CLASSES,
4175 cur = carousel._pages.cur, // current page
4180 n = carousel.get("numVisible"),
4181 num = carousel._pages.num, // total pages
4182 pager = carousel._pages.el; // the pager container element
4184 if (num === 0 || !pager) {
4185 return; // don't do anything if number of pages is 0
4188 // Hide the pager before redrawing it
4189 Dom.setStyle(pager, "visibility", "hidden");
4191 // Remove all nodes from the pager
4192 while (pager.firstChild) {
4193 pager.removeChild(pager.firstChild);
4196 for (i = 0; i < num; i++) {
4198 el = document.createElement("LI");
4201 Dom.addClass(el, css.FIRST_PAGE);
4204 Dom.addClass(el, css.SELECTED_NAV);
4207 html = "<a class=" + css.PAGER_ITEM + " href=\"#" + (i+1) + "\" tabindex=\"0\"><em>" +
4208 carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
4210 el.innerHTML = html;
4212 pager.appendChild(el);
4215 // Show the pager now
4216 Dom.setStyle(pager, "visibility", "visible");
4220 * Update the UI for the pager menu based on the current page and
4221 * the number of pages. If the number of pages is greater than
4222 * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
4223 * down menu instead of a set of buttons.
4225 * @method _updatePagerMenu
4228 _updatePagerMenu: function () {
4229 var carousel = this,
4230 css = carousel.CLASSES,
4231 cur = carousel._pages.cur, // current page
4235 n = carousel.get("numVisible"),
4236 num = carousel._pages.num, // total pages
4237 pager = carousel._pages.el, // the pager container element
4240 if (num === 0 || !pager) {
4241 return;// don't do anything if number of pages is 0
4244 sel = document.createElement("SELECT");
4251 // Hide the pager before redrawing it
4252 Dom.setStyle(pager, "visibility", "hidden");
4254 // Remove all nodes from the pager
4255 while (pager.firstChild) {
4256 pager.removeChild(pager.firstChild);
4259 for (i = 0; i < num; i++) {
4261 el = document.createElement("OPTION");
4263 el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
4266 el.setAttribute("selected", "selected");
4269 sel.appendChild(el);
4272 el = document.createElement("FORM");
4275 el.appendChild(sel);
4276 pager.appendChild(el);
4279 // Show the pager now
4280 Event.addListener(sel, "change", carousel._pagerChangeHandler, this, true);
4281 Dom.setStyle(pager, "visibility", "visible");
4285 * Set the correct tab index for the Carousel items.
4287 * @method _updateTabIndex
4288 * @param el {Object} The element to be focussed
4291 _updateTabIndex: function (el) {
4292 var carousel = this;
4295 if (carousel._focusableItemEl) {
4296 carousel._focusableItemEl.tabIndex = -1;
4298 carousel._focusableItemEl = el;
4304 * Validate animation parameters.
4306 * @method _validateAnimation
4307 * @param cfg {Object} The animation configuration
4308 * @return {Boolean} The status of the validation
4311 _validateAnimation: function (cfg) {
4314 if (JS.isObject(cfg)) {
4316 rv = rv && JS.isNumber(cfg.speed);
4319 rv = rv && JS.isFunction(cfg.effect);
4320 } else if (!JS.isUndefined(YAHOO.util.Easing)) {
4321 cfg.effect = YAHOO.util.Easing.easeOut;
4331 * Validate the firstVisible value.
4333 * @method _validateFirstVisible
4334 * @param val {Number} The first visible value
4335 * @return {Boolean} The status of the validation
4338 _validateFirstVisible: function (val) {
4339 var carousel = this, numItems = carousel.get("numItems");
4341 if (JS.isNumber(val)) {
4342 if (numItems === 0 && val == numItems) {
4345 return (val >= 0 && val < numItems);
4353 * Validate and navigation parameters.
4355 * @method _validateNavigation
4356 * @param cfg {Object} The navigation configuration
4357 * @return {Boolean} The status of the validation
4360 _validateNavigation : function (cfg) {
4363 if (!JS.isObject(cfg)) {
4368 if (!JS.isArray(cfg.prev)) {
4371 for (i in cfg.prev) {
4372 if (cfg.prev.hasOwnProperty(i)) {
4373 if (!JS.isString(cfg.prev[i].nodeName)) {
4381 if (!JS.isArray(cfg.next)) {
4384 for (i in cfg.next) {
4385 if (cfg.next.hasOwnProperty(i)) {
4386 if (!JS.isString(cfg.next[i].nodeName)) {
4397 * Validate the numItems value.
4399 * @method _validateNumItems
4400 * @param val {Number} The numItems value
4401 * @return {Boolean} The status of the validation
4404 _validateNumItems: function (val) {
4405 return JS.isNumber(val) && (val >= 0);
4409 * Validate the numVisible value.
4411 * @method _validateNumVisible
4412 * @param val {Number} The numVisible value
4413 * @return {Boolean} The status of the validation
4416 _validateNumVisible: function (val) {
4419 if (JS.isNumber(val)) {
4420 rv = val > 0 && val <= this.get("numItems");
4421 } else if (JS.isArray(val)) {
4422 if (JS.isNumber(val[0]) && JS.isNumber(val[1])) {
4423 rv = val[0] * val[1] > 0 && val.length == 2;
4431 * Validate the revealAmount value.
4433 * @method _validateRevealAmount
4434 * @param val {Number} The revealAmount value
4435 * @return {Boolean} The status of the validation
4438 _validateRevealAmount: function (val) {
4441 if (JS.isNumber(val)) {
4442 rv = val >= 0 && val < 100;
4449 * Validate the scrollIncrement value.
4451 * @method _validateScrollIncrement
4452 * @param val {Number} The scrollIncrement value
4453 * @return {Boolean} The status of the validation
4456 _validateScrollIncrement: function (val) {
4459 if (JS.isNumber(val)) {
4460 rv = (val > 0 && val < this.get("numItems"));
4470 ;; Local variables: **
4472 ;; indent-tabs-mode: nil **
4475 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"});
4476 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"});