2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
8 * The Carousel module provides a widget for browsing among a set of like
9 * objects represented pictorially.
12 * @requires yahoo, dom, event, element
14 * @namespace YAHOO.widget
15 * @title Carousel Widget
20 var WidgetName; // forward declaration
23 * The Carousel widget.
26 * @extends YAHOO.util.Element
28 * @param el {HTMLElement | String} The HTML element that represents the
29 * the container that houses the Carousel.
30 * @param cfg {Object} (optional) The configuration values
32 YAHOO.widget.Carousel = function (el, cfg) {
33 YAHOO.log("Component creation", WidgetName);
35 YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg);
39 * Private variables of the Carousel component
42 /* Some abbreviations to avoid lengthy typing and lookups. */
43 var Carousel = YAHOO.widget.Carousel,
45 Event = YAHOO.util.Event,
53 WidgetName = "Carousel";
56 * The internal table of Carousel instances.
63 * Custom events of the Carousel component
68 * @description Fires when the Carousel has scrolled to the previous or
69 * next page. Passes back the index of the first and last visible items in
71 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
72 * for more information on listening for this event.
73 * @type YAHOO.util.CustomEvent
75 afterScrollEvent = "afterScroll",
78 * @event allItemsRemovedEvent
79 * @description Fires when all items have been removed from the Carousel.
81 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
82 * for more information on listening for this event.
83 * @type YAHOO.util.CustomEvent
85 allItemsRemovedEvent = "allItemsRemoved",
89 * @description Fires before the Carousel is hidden. See
90 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
91 * for more information on listening for this event.
92 * @type YAHOO.util.CustomEvent
94 beforeHideEvent = "beforeHide",
97 * @event beforePageChange
98 * @description Fires when the Carousel is about to scroll to the previous
99 * or next page. Passes back the page number of the current page. Note
100 * that the first page number is zero. See
101 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
102 * for more information on listening for this event.
103 * @type YAHOO.util.CustomEvent
105 beforePageChangeEvent = "beforePageChange",
108 * @event beforeScroll
109 * @description Fires when the Carousel is about to scroll to the previous
110 * or next page. Passes back the index of the first and last visible items
111 * in the Carousel and the direction (backward/forward) of the scroll. See
112 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
113 * for more information on listening for this event.
114 * @type YAHOO.util.CustomEvent
116 beforeScrollEvent = "beforeScroll",
120 * @description Fires when the Carousel is about to be shown. See
121 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
122 * for more information on listening for this event.
123 * @type YAHOO.util.CustomEvent
125 beforeShowEvent = "beforeShow",
129 * @description Fires when the Carousel loses focus. See
130 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
131 * for more information on listening for this event.
132 * @type YAHOO.util.CustomEvent
138 * @description Fires when the Carousel gains focus. See
139 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
140 * for more information on listening for this event.
141 * @type YAHOO.util.CustomEvent
143 focusEvent = "focus",
147 * @description Fires when the Carousel is hidden. See
148 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
149 * for more information on listening for this event.
150 * @type YAHOO.util.CustomEvent
156 * @description Fires when an item has been added to the Carousel. Passes
157 * back the content of the item that would be added, the index at which the
158 * item would be added, and the event itself. See
159 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
160 * for more information on listening for this event.
161 * @type YAHOO.util.CustomEvent
163 itemAddedEvent = "itemAdded",
167 * @description Fires when an item has been removed from the Carousel.
168 * Passes back the content of the item that would be removed, the index
169 * from which the item would be removed, and the event itself. See
170 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
171 * for more information on listening for this event.
172 * @type YAHOO.util.CustomEvent
174 itemRemovedEvent = "itemRemoved",
177 * @event itemSelected
178 * @description Fires when an item has been selected in the Carousel.
179 * Passes back the index of the selected item in the Carousel. Note, that
180 * the index begins from zero. See
181 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
182 * for more information on listening for this event.
183 * @type YAHOO.util.CustomEvent
185 itemSelectedEvent = "itemSelected",
189 * @description Fires when the Carousel needs more items to be loaded for
190 * displaying them. Passes back the first and last visible items in the
191 * Carousel, and the number of items needed to be loaded. See
192 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
193 * for more information on listening for this event.
194 * @type YAHOO.util.CustomEvent
196 loadItemsEvent = "loadItems",
199 * @event navigationStateChange
200 * @description Fires when the state of either one of the navigation
201 * buttons are changed from enabled to disabled or vice versa. Passes back
202 * the state (true/false) of the previous and next buttons. The value true
203 * signifies the button is enabled, false signifies disabled. See
204 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
205 * for more information on listening for this event.
206 * @type YAHOO.util.CustomEvent
208 navigationStateChangeEvent = "navigationStateChange",
212 * @description Fires after the Carousel has scrolled to the previous or
213 * next page. Passes back the page number of the current page. Note
214 * that the first page number is zero. See
215 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
216 * for more information on listening for this event.
217 * @type YAHOO.util.CustomEvent
219 pageChangeEvent = "pageChange",
224 * @description Fires when the Carousel is rendered. See
225 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
226 * for more information on listening for this event.
227 * @type YAHOO.util.CustomEvent
229 renderEvent = "render",
233 * @description Fires when the Carousel is shown. See
234 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
235 * for more information on listening for this event.
236 * @type YAHOO.util.CustomEvent
241 * @event startAutoPlay
242 * @description Fires when the auto play has started in the Carousel. See
243 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
244 * for more information on listening for this event.
245 * @type YAHOO.util.CustomEvent
247 startAutoPlayEvent = "startAutoPlay",
250 * @event stopAutoPlay
251 * @description Fires when the auto play has been stopped in the Carousel.
253 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
254 * for more information on listening for this event.
255 * @type YAHOO.util.CustomEvent
257 stopAutoPlayEvent = "stopAutoPlay",
261 * @event uiUpdateEvent
262 * @description Fires when the UI has been updated.
264 * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
265 * for more information on listening for this event.
266 * @type YAHOO.util.CustomEvent
268 uiUpdateEvent = "uiUpdate";
271 * Private helper functions used by the Carousel component
275 * Create an element, set its class name and optionally install the element
277 * @method createElement
278 * @param el {String} The element to be created
279 * @param attrs {Object} Configuration of parent, class and id attributes.
280 * If the content is specified, it is inserted after creation of the
281 * element. The content can also be an HTML element in which case it would
282 * be appended as a child node of the created element.
285 function createElement(el, attrs) {
286 var newEl = document.createElement(el);
289 if (attrs.className) {
290 Dom.addClass(newEl, attrs.className);
294 attrs.parent.appendChild(newEl);
298 newEl.setAttribute("id", attrs.id);
302 if (attrs.content.nodeName) {
303 newEl.appendChild(attrs.content);
305 newEl.innerHTML = attrs.content;
313 * Get the computed style of an element.
316 * @param el {HTMLElement} The element for which the style needs to be
318 * @param style {String} The style attribute
319 * @param type {String} "int", "float", etc. (defaults to int)
322 function getStyle(el, style, type) {
329 function getStyleIntVal(el, style) {
333 * XXX: Safari calculates incorrect marginRight for an element
334 * which has its parent element style set to overflow: hidden
335 * https://bugs.webkit.org/show_bug.cgi?id=13343
336 * Let us assume marginLeft == marginRight
338 if (style == "marginRight" && YAHOO.env.ua.webkit) {
339 val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
341 val = parseInt(Dom.getStyle(el, style), 10);
344 return JS.isNumber(val) ? val : 0;
347 function getStyleFloatVal(el, style) {
351 * XXX: Safari calculates incorrect marginRight for an element
352 * which has its parent element style set to overflow: hidden
353 * https://bugs.webkit.org/show_bug.cgi?id=13343
354 * Let us assume marginLeft == marginRight
356 if (style == "marginRight" && YAHOO.env.ua.webkit) {
357 val = parseFloat(Dom.getStyle(el, "marginLeft"));
359 val = parseFloat(Dom.getStyle(el, style));
362 return JS.isNumber(val) ? val : 0;
365 if (typeof type == "undefined") {
371 value = el.offsetHeight;
373 value += getStyleIntVal(el, "marginTop") +
374 getStyleIntVal(el, "marginBottom");
376 value = getStyleFloatVal(el, "height") +
377 getStyleIntVal(el, "marginTop") +
378 getStyleIntVal(el, "marginBottom") +
379 getStyleIntVal(el, "borderTopWidth") +
380 getStyleIntVal(el, "borderBottomWidth") +
381 getStyleIntVal(el, "paddingTop") +
382 getStyleIntVal(el, "paddingBottom");
386 value = el.offsetWidth;
388 value += getStyleIntVal(el, "marginLeft") +
389 getStyleIntVal(el, "marginRight");
391 value = getStyleFloatVal(el, "width") +
392 getStyleIntVal(el, "marginLeft") +
393 getStyleIntVal(el, "marginRight") +
394 getStyleIntVal(el, "borderLeftWidth") +
395 getStyleIntVal(el, "borderRightWidth") +
396 getStyleIntVal(el, "paddingLeft") +
397 getStyleIntVal(el, "paddingRight");
402 value = getStyleIntVal(el, style);
403 } else if (type == "float") {
404 value = getStyleFloatVal(el, style);
406 value = Dom.getStyle(el, style);
415 * Compute and return the height or width of a single Carousel item
416 * depending upon the orientation.
418 * @method getCarouselItemSize
419 * @param which {String} "height" or "width" to be returned. If this is
420 * passed explicitly, the calculated size is not cached.
423 function getCarouselItemSize(which) {
429 if (carousel._itemsTable.numItems === 0) {
433 if (typeof which == "undefined") {
434 if (carousel._itemsTable.size > 0) {
435 return carousel._itemsTable.size;
439 if (JS.isUndefined(carousel._itemsTable.items[0])) {
443 child = Dom.get(carousel._itemsTable.items[0].id);
445 if (typeof which == "undefined") {
446 vertical = carousel.get("isVertical");
448 vertical = which == "height";
452 size = getStyle(child, "height");
454 size = getStyle(child, "width");
457 if (typeof which == "undefined") {
458 carousel._itemsTable.size = size; // save the size for later
465 * Return the index of the first item in the view port for displaying item
468 * @method getFirstVisibleForPosition
469 * @param pos {Number} The position of the item to be displayed
472 function getFirstVisibleForPosition(pos) {
473 var num = this.get("numVisible");
475 return Math.floor(pos / num) * num;
479 * Return the scrolling offset size given the number of elements to
482 * @method getScrollOffset
483 * @param delta {Number} The delta number of elements to scroll by.
486 function getScrollOffset(delta) {
490 itemSize = getCarouselItemSize.call(this);
491 size = itemSize * delta;
493 // XXX: really, when the orientation is vertical, the scrolling
494 // is not exactly the number of elements into element size.
495 if (this.get("isVertical")) {
503 * Scroll the Carousel by a page backward.
505 * @method scrollPageBackward
506 * @param {Event} ev The event object
507 * @param {Object} obj The context object
510 function scrollPageBackward(ev, obj) {
511 obj.scrollPageBackward();
512 Event.preventDefault(ev);
516 * Scroll the Carousel by a page forward.
518 * @method scrollPageForward
519 * @param {Event} ev The event object
520 * @param {Object} obj The context object
523 function scrollPageForward(ev, obj) {
524 obj.scrollPageForward();
525 Event.preventDefault(ev);
529 * Set the selected item.
531 * @method setItemSelection
532 * @param {Number} newpos The index of the new position
533 * @param {Number} oldpos The index of the previous position
536 function setItemSelection(newpos, oldpos) {
538 cssClass = carousel.CLASSES,
540 firstItem = carousel._firstItem,
541 isCircular = carousel.get("isCircular"),
542 numItems = carousel.get("numItems"),
543 numVisible = carousel.get("numVisible"),
545 sentinel = firstItem + numVisible - 1;
547 if (position >= 0 && position < numItems) {
548 if (!JS.isUndefined(carousel._itemsTable.items[position])) {
549 el = Dom.get(carousel._itemsTable.items[position].id);
551 Dom.removeClass(el, cssClass.SELECTED_ITEM);
556 if (JS.isNumber(newpos)) {
557 newpos = parseInt(newpos, 10);
558 newpos = JS.isNumber(newpos) ? newpos : 0;
563 if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
564 newpos = getFirstVisibleForPosition.call(carousel, newpos);
565 carousel.scrollTo(newpos); // still loading the item
568 if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
569 el = Dom.get(carousel._itemsTable.items[newpos].id);
571 Dom.addClass(el, cssClass.SELECTED_ITEM);
575 if (newpos < firstItem || newpos > sentinel) { // out of focus
576 newpos = getFirstVisibleForPosition.call(carousel, newpos);
577 carousel.scrollTo(newpos);
582 * Fire custom events for enabling/disabling navigation elements.
584 * @method syncNavigation
587 function syncNavigation() {
590 cssClass = carousel.CLASSES,
595 // Don't do anything if the Carousel is not rendered
596 if (!carousel._hasRendered) {
600 navigation = carousel.get("navigation");
601 sentinel = carousel._firstItem + carousel.get("numVisible");
603 if (navigation.prev) {
604 if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
605 if (carousel.get("numItems") === 0 ||
606 !carousel.get("isCircular")) {
607 Event.removeListener(navigation.prev, "click",
609 Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
610 for (i = 0; i < carousel._navBtns.prev.length; i++) {
611 carousel._navBtns.prev[i].setAttribute("disabled",
614 carousel._prevEnabled = false;
616 attach = !carousel._prevEnabled;
619 attach = !carousel._prevEnabled;
623 Event.on(navigation.prev, "click", scrollPageBackward,
625 Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
626 for (i = 0; i < carousel._navBtns.prev.length; i++) {
627 carousel._navBtns.prev[i].removeAttribute("disabled");
629 carousel._prevEnabled = true;
634 if (navigation.next) {
635 if (sentinel >= carousel.get("numItems")) {
636 if (!carousel.get("isCircular")) {
637 Event.removeListener(navigation.next, "click",
639 Dom.addClass(navigation.next, cssClass.DISABLED);
640 for (i = 0; i < carousel._navBtns.next.length; i++) {
641 carousel._navBtns.next[i].setAttribute("disabled",
644 carousel._nextEnabled = false;
646 attach = !carousel._nextEnabled;
649 attach = !carousel._nextEnabled;
653 Event.on(navigation.next, "click", scrollPageForward,
655 Dom.removeClass(navigation.next, cssClass.DISABLED);
656 for (i = 0; i < carousel._navBtns.next.length; i++) {
657 carousel._navBtns.next[i].removeAttribute("disabled");
659 carousel._nextEnabled = true;
663 carousel.fireEvent(navigationStateChangeEvent,
664 { next: carousel._nextEnabled, prev: carousel._prevEnabled });
668 * Synchronize and redraw the Pager UI if necessary.
670 * @method syncPagerUi
673 function syncPagerUi(page) {
674 var carousel = this, numPages, numVisible;
676 // Don't do anything if the Carousel is not rendered
677 if (!carousel._hasRendered) {
681 numVisible = carousel.get("numVisible");
683 if (!JS.isNumber(page)) {
684 page = Math.ceil(carousel.get("selectedItem") / numVisible);
686 numPages = Math.ceil(carousel.get("numItems") / numVisible);
688 carousel._pages.num = numPages;
689 carousel._pages.cur = page;
691 if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
692 carousel._updatePagerMenu();
694 carousel._updatePagerButtons();
700 * Call the appropriate methods on events fired when an item is added, or
701 * removed for synchronizing the DOM.
704 * @param {Object} o The item that needs to be added or removed
710 if (!JS.isObject(o)) {
716 carousel._syncUiForItemAdd(o);
718 case itemRemovedEvent:
719 carousel._syncUiForItemRemove(o);
722 carousel._syncUiForLazyLoading(o);
726 carousel.fireEvent(uiUpdateEvent);
730 * Update the state variables after scrolling the Carousel view port.
732 * @method updateStateAfterScroll
733 * @param {Integer} item The index to which the Carousel has scrolled to.
734 * @param {Integer} sentinel The last element in the view port.
737 function updateStateAfterScroll(item, sentinel) {
739 page = carousel.get("currentPage"),
741 numPerPage = carousel.get("numVisible");
743 newPage = parseInt(carousel._firstItem / numPerPage, 10);
744 if (newPage != page) {
745 carousel.setAttributeConfig("currentPage", { value: newPage });
746 carousel.fireEvent(pageChangeEvent, newPage);
749 if (carousel.get("selectOnScroll")) {
750 if (carousel.get("selectedItem") != carousel._selectedItem) {
751 carousel.set("selectedItem", carousel._selectedItem);
755 clearTimeout(carousel._autoPlayTimer);
756 delete carousel._autoPlayTimer;
757 if (carousel.isAutoPlayOn()) {
758 carousel.startAutoPlay();
761 carousel.fireEvent(afterScrollEvent,
762 { first: carousel._firstItem,
768 * Static members and methods of the Carousel component
772 * Return the appropriate Carousel object based on the id associated with
773 * the Carousel element or false if none match.
778 Carousel.getById = function (id) {
779 return instances[id] ? instances[id].object : false;
782 YAHOO.extend(Carousel, YAHOO.util.Element, {
785 * Internal variables used within the Carousel component
789 * The Animation object.
797 * The Carousel element.
799 * @property _carouselEl
805 * The Carousel clipping container element.
813 * The current first index of the Carousel.
815 * @property _firstItem
821 * Does the Carousel element have focus?
823 * @property _hasFocus
829 * Is the Carousel rendered already?
831 * @property _hasRendered
837 * Is the animation still in progress?
839 * @property _isAnimationInProgress
842 _isAnimationInProgress: false,
845 * Is the auto-scrolling of Carousel in progress?
847 * @property _isAutoPlayInProgress
850 _isAutoPlayInProgress: false,
853 * The table of items in the Carousel.
854 * The numItems is the number of items in the Carousel, items being the
855 * array of items in the Carousel. The size is the size of a single
856 * item in the Carousel. It is cached here for efficiency (to avoid
857 * computing the size multiple times).
859 * @property _itemsTable
865 * The Carousel navigation buttons.
873 * The Carousel navigation.
881 * Status of the next navigation item.
883 * @property _nextEnabled
889 * The Carousel pages structure.
890 * This is an object of the total number of pages and the current page.
898 * Status of the previous navigation item.
900 * @property _prevEnabled
906 * Whether the Carousel size needs to be recomputed or not?
908 * @property _recomputeSize
911 _recomputeSize: true,
914 * CSS classes used by the Carousel component
920 * The class name of the Carousel navigation buttons.
923 * @default "yui-carousel-button"
925 BUTTON: "yui-carousel-button",
928 * The class name of the Carousel element.
931 * @default "yui-carousel"
933 CAROUSEL: "yui-carousel",
936 * The class name of the container of the items in the Carousel.
938 * @property CAROUSEL_EL
939 * @default "yui-carousel-element"
941 CAROUSEL_EL: "yui-carousel-element",
944 * The class name of the Carousel's container element.
946 * @property CONTAINER
947 * @default "yui-carousel-container"
949 CONTAINER: "yui-carousel-container",
952 * The class name of the Carousel's container element.
955 * @default "yui-carousel-content"
957 CONTENT: "yui-carousel-content",
960 * The class name of a disabled navigation button.
963 * @default "yui-carousel-button-disabled"
965 DISABLED: "yui-carousel-button-disabled",
968 * The class name of the first Carousel navigation button.
970 * @property FIRST_NAV
971 * @default " yui-carousel-first-button"
973 FIRST_NAV: " yui-carousel-first-button",
976 * The class name of a first disabled navigation button.
978 * @property FIRST_NAV_DISABLED
979 * @default "yui-carousel-first-button-disabled"
981 FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
984 * The class name of a first page element.
986 * @property FIRST_PAGE
987 * @default "yui-carousel-nav-first-page"
989 FIRST_PAGE: "yui-carousel-nav-first-page",
992 * The class name of the Carousel navigation button that has focus.
994 * @property FOCUSSED_BUTTON
995 * @default "yui-carousel-button-focus"
997 FOCUSSED_BUTTON: "yui-carousel-button-focus",
1000 * The class name of a horizontally oriented Carousel.
1002 * @property HORIZONTAL
1003 * @default "yui-carousel-horizontal"
1005 HORIZONTAL: "yui-carousel-horizontal",
1008 * The element to be used as the progress indicator when the item
1009 * is still being loaded.
1011 * @property ITEM_LOADING
1012 * @default The progress indicator (spinner) image CSS class
1014 ITEM_LOADING: "yui-carousel-item-loading",
1017 * The class name that will be set if the Carousel adjusts itself
1018 * for a minimum width.
1020 * @property MIN_WIDTH
1021 * @default "yui-carousel-min-width"
1023 MIN_WIDTH: "yui-carousel-min-width",
1026 * The navigation element container class name.
1028 * @property NAVIGATION
1029 * @default "yui-carousel-nav"
1031 NAVIGATION: "yui-carousel-nav",
1034 * The class name of the next Carousel navigation button.
1036 * @property NEXT_NAV
1037 * @default " yui-carousel-next-button"
1039 NEXT_NAV: " yui-carousel-next-button",
1042 * The class name of the next navigation link. This variable is
1043 * not only used for styling, but also for identifying the link
1044 * within the Carousel container.
1046 * @property NEXT_PAGE
1047 * @default "yui-carousel-next"
1049 NEXT_PAGE: "yui-carousel-next",
1052 * The class name for the navigation container for prev/next.
1054 * @property NAV_CONTAINER
1055 * @default "yui-carousel-buttons"
1057 NAV_CONTAINER: "yui-carousel-buttons",
1060 * The class name of the focussed page navigation. This class is
1061 * specifically used for the ugly focus handling in Opera.
1063 * @property PAGE_FOCUS
1064 * @default "yui-carousel-nav-page-focus"
1066 PAGE_FOCUS: "yui-carousel-nav-page-focus",
1069 * The class name of the previous navigation link. This variable
1070 * is not only used for styling, but also for identifying the link
1071 * within the Carousel container.
1073 * @property PREV_PAGE
1074 * @default "yui-carousel-prev"
1076 PREV_PAGE: "yui-carousel-prev",
1079 * The class name of the selected item.
1081 * @property SELECTED_ITEM
1082 * @default "yui-carousel-item-selected"
1084 SELECTED_ITEM: "yui-carousel-item-selected",
1087 * The class name of the selected paging navigation.
1089 * @property SELECTED_NAV
1090 * @default "yui-carousel-nav-page-selected"
1092 SELECTED_NAV: "yui-carousel-nav-page-selected",
1095 * The class name of a vertically oriented Carousel.
1097 * @property VERTICAL
1098 * @default "yui-carousel-vertical"
1100 VERTICAL: "yui-carousel-vertical",
1103 * The class name of the (vertical) Carousel's container element.
1105 * @property VERTICAL_CONTAINER
1106 * @default "yui-carousel-vertical-container"
1108 VERTICAL_CONTAINER: "yui-carousel-vertical-container",
1111 * The class name of a visible Carousel.
1114 * @default "yui-carousel-visible"
1116 VISIBLE: "yui-carousel-visible"
1121 * Configuration attributes for configuring the Carousel component
1127 * The offset of the first visible item in the Carousel.
1129 * @property FIRST_VISIBLE
1135 * The minimum width of the horizontal Carousel container to support
1136 * the navigation buttons.
1138 * @property HORZ_MIN_WIDTH
1141 HORZ_MIN_WIDTH: 180,
1144 * The maximum number of pager buttons allowed beyond which the UI
1145 * of the pager would be a drop-down of pages instead of buttons.
1147 * @property MAX_PAGER_BUTTONS
1150 MAX_PAGER_BUTTONS: 5,
1153 * The minimum width of the vertical Carousel container to support
1154 * the navigation buttons.
1156 * @property VERT_MIN_WIDTH
1162 * The number of visible items in the Carousel.
1164 * @property NUM_VISIBLE
1172 * Internationalizable strings in the Carousel component
1178 * The content to be used as the progress indicator when the item
1179 * is still being loaded.
1181 * @property ITEM_LOADING_CONTENT
1182 * @default "Loading"
1184 ITEM_LOADING_CONTENT: "Loading",
1187 * The next navigation button name/text.
1189 * @property NEXT_BUTTON_TEXT
1190 * @default "Next Page"
1192 NEXT_BUTTON_TEXT: "Next Page",
1195 * The prefix text for the pager in case the UI is a drop-down.
1197 * @property PAGER_PREFIX_TEXT
1198 * @default "Go to page "
1200 PAGER_PREFIX_TEXT: "Go to page ",
1203 * The previous navigation button name/text.
1205 * @property PREVIOUS_BUTTON_TEXT
1206 * @default "Previous Page"
1208 PREVIOUS_BUTTON_TEXT: "Previous Page"
1213 * Public methods of the Carousel component
1217 * Insert or append an item to the Carousel.
1221 * @param item {String | Object | HTMLElement} The item to be appended
1222 * to the Carousel. If the parameter is a string, it is assumed to be
1223 * the content of the newly created item. If the parameter is an
1224 * object, it is assumed to supply the content and an optional class
1225 * and an optional id of the newly created item.
1226 * @param index {Number} optional The position to where in the list
1227 * (starts from zero).
1228 * @return {Boolean} Return true on success, false otherwise
1230 addItem: function (item, index) {
1231 var carousel = this,
1235 numItems = carousel.get("numItems");
1241 if (JS.isString(item) || item.nodeName) {
1242 content = item.nodeName ? item.innerHTML : item;
1243 } else if (JS.isObject(item)) {
1244 content = item.content;
1246 YAHOO.log("Invalid argument to addItem", "error", WidgetName);
1250 className = item.className || "";
1251 elId = item.id ? item.id : Dom.generateId();
1253 if (JS.isUndefined(index)) {
1254 carousel._itemsTable.items.push({
1256 className : className,
1260 if (index < 0 || index >= numItems) {
1261 YAHOO.log("Index out of bounds", "error", WidgetName);
1264 carousel._itemsTable.items.splice(index, 0, {
1266 className : className,
1270 carousel._itemsTable.numItems++;
1272 if (numItems < carousel._itemsTable.items.length) {
1273 carousel.set("numItems", carousel._itemsTable.items.length);
1276 carousel.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent });
1282 * Insert or append multiple items to the Carousel.
1286 * @param items {Array} An array of items to be added with each item
1287 * representing an item, index pair [{item, index}, ...]
1288 * @return {Boolean} Return true on success, false otherwise
1290 addItems: function (items) {
1291 var i, n, rv = true;
1293 if (!JS.isArray(items)) {
1297 for (i = 0, n = items.length; i < n; i++) {
1298 if (this.addItem(items[i][0], items[i][1]) === false) {
1307 * Remove focus from the Carousel.
1313 this._carouselEl.blur();
1314 this.fireEvent(blurEvent);
1318 * Clears the items from Carousel.
1320 * @method clearItems
1323 clearItems: function () {
1324 var carousel = this, n = carousel.get("numItems");
1327 if (!carousel.removeItem(0)) {
1328 YAHOO.log("Item could not be removed - missing?",
1329 "warn", WidgetName);
1332 For dynamic loading, the numItems may be much larger than
1333 the actual number of items in the table. So, set the
1334 numItems to zero, and break out of the loop if the table
1337 if (carousel._itemsTable.numItems === 0) {
1338 carousel.set("numItems", 0);
1344 carousel.fireEvent(allItemsRemovedEvent);
1348 * Set focus on the Carousel.
1353 focus: function () {
1354 var carousel = this,
1357 isSelectionInvisible,
1365 // Don't do anything if the Carousel is not rendered
1366 if (!carousel._hasRendered) {
1370 if (carousel.isAnimating()) {
1371 // this messes up real bad!
1375 selItem = carousel.get("selectedItem");
1376 numVisible = carousel.get("numVisible");
1377 selectOnScroll = carousel.get("selectOnScroll");
1378 selected = (selItem >= 0) ?
1379 carousel.getItem(selItem) : null;
1380 first = carousel.get("firstVisible");
1381 last = first + numVisible - 1;
1382 isSelectionInvisible = (selItem < first || selItem > last);
1383 focusEl = (selected && selected.id) ?
1384 Dom.get(selected.id) : null;
1385 itemsTable = carousel._itemsTable;
1387 if (!selectOnScroll && isSelectionInvisible) {
1388 focusEl = (itemsTable && itemsTable.items &&
1389 itemsTable.items[first]) ?
1390 Dom.get(itemsTable.items[first].id) : null;
1397 // ignore focus errors
1401 carousel.fireEvent(focusEvent);
1405 * Hide the Carousel.
1411 var carousel = this;
1413 if (carousel.fireEvent(beforeHideEvent) !== false) {
1414 carousel.removeClass(carousel.CLASSES.VISIBLE);
1415 carousel.fireEvent(hideEvent);
1420 * Initialize the Carousel.
1424 * @param el {HTMLElement | String} The html element that represents
1425 * the Carousel container.
1426 * @param attrs {Object} The set of configuration attributes for
1427 * creating the Carousel.
1429 init: function (el, attrs) {
1430 var carousel = this,
1431 elId = el, // save for a rainy day
1435 YAHOO.log(el + " is neither an HTML element, nor a string",
1436 "error", WidgetName);
1440 carousel._hasRendered = false;
1441 carousel._navBtns = { prev: [], next: [] };
1442 carousel._pages = { el: null, num: 0, cur: 0 };
1443 carousel._itemsTable = { loading: {}, numItems: 0,
1444 items: [], size: 0 };
1446 YAHOO.log("Component initialization", WidgetName);
1448 if (JS.isString(el)) {
1450 } else if (!el.nodeName) {
1451 YAHOO.log(el + " is neither an HTML element, nor a string",
1452 "error", WidgetName);
1456 Carousel.superclass.init.call(carousel, el, attrs);
1459 if (!el.id) { // in case the HTML element is passed
1460 el.setAttribute("id", Dom.generateId());
1462 parse = carousel._parseCarousel(el);
1464 carousel._createCarousel(elId);
1467 el = carousel._createCarousel(elId);
1471 carousel.initEvents();
1474 carousel._parseCarouselItems();
1477 if (!attrs || typeof attrs.isVertical == "undefined") {
1478 carousel.set("isVertical", false);
1481 carousel._parseCarouselNavigation(el);
1482 carousel._navEl = carousel._setupCarouselNavigation();
1484 instances[elId] = { object: carousel };
1486 carousel._loadItems();
1490 * Initialize the configuration attributes used to create the Carousel.
1492 * @method initAttributes
1494 * @param attrs {Object} The set of configuration attributes for
1495 * creating the Carousel.
1497 initAttributes: function (attrs) {
1498 var carousel = this;
1500 attrs = attrs || {};
1501 Carousel.superclass.initAttributes.call(carousel, attrs);
1504 * @attribute carouselEl
1505 * @description The type of the Carousel element.
1509 carousel.setAttributeConfig("carouselEl", {
1510 validator : JS.isString,
1511 value : attrs.carouselEl || "OL"
1515 * @attribute carouselItemEl
1516 * @description The type of the list of items within the Carousel.
1520 carousel.setAttributeConfig("carouselItemEl", {
1521 validator : JS.isString,
1522 value : attrs.carouselItemEl || "LI"
1526 * @attribute currentPage
1527 * @description The current page number (read-only.)
1530 carousel.setAttributeConfig("currentPage", {
1536 * @attribute firstVisible
1537 * @description The index to start the Carousel from (indexes begin
1542 carousel.setAttributeConfig("firstVisible", {
1543 method : carousel._setFirstVisible,
1544 validator : carousel._validateFirstVisible,
1546 attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
1550 * @attribute selectOnScroll
1551 * @description Set this to true to automatically set focus to
1552 * follow scrolling in the Carousel.
1556 carousel.setAttributeConfig("selectOnScroll", {
1557 validator : JS.isBoolean,
1558 value : attrs.selectOnScroll || true
1562 * @attribute numVisible
1563 * @description The number of visible items in the Carousel's
1568 carousel.setAttributeConfig("numVisible", {
1569 method : carousel._setNumVisible,
1570 validator : carousel._validateNumVisible,
1571 value : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
1575 * @attribute numItems
1576 * @description The number of items in the Carousel.
1579 carousel.setAttributeConfig("numItems", {
1580 method : carousel._setNumItems,
1581 validator : carousel._validateNumItems,
1582 value : carousel._itemsTable.numItems
1586 * @attribute scrollIncrement
1587 * @description The number of items to scroll by for arrow keys.
1591 carousel.setAttributeConfig("scrollIncrement", {
1592 validator : carousel._validateScrollIncrement,
1593 value : attrs.scrollIncrement || 1
1597 * @attribute selectedItem
1598 * @description The index of the selected item.
1601 carousel.setAttributeConfig("selectedItem", {
1602 method : carousel._setSelectedItem,
1603 validator : JS.isNumber,
1608 * @attribute revealAmount
1609 * @description The percentage of the item to be revealed on each
1610 * side of the Carousel (before and after the first and last item
1611 * in the Carousel's viewport.)
1615 carousel.setAttributeConfig("revealAmount", {
1616 method : carousel._setRevealAmount,
1617 validator : carousel._validateRevealAmount,
1618 value : attrs.revealAmount || 0
1622 * @attribute isCircular
1623 * @description Set this to true to wrap scrolling of the contents
1628 carousel.setAttributeConfig("isCircular", {
1629 validator : JS.isBoolean,
1630 value : attrs.isCircular || false
1634 * @attribute isVertical
1635 * @description True if the orientation of the Carousel is vertical
1639 carousel.setAttributeConfig("isVertical", {
1640 method : carousel._setOrientation,
1641 validator : JS.isBoolean,
1642 value : attrs.isVertical || false
1646 * @attribute navigation
1647 * @description The set of navigation controls for Carousel
1649 * { prev: null, // the previous navigation element<br>
1650 * next: null } // the next navigation element
1653 carousel.setAttributeConfig("navigation", {
1654 method : carousel._setNavigation,
1655 validator : carousel._validateNavigation,
1657 attrs.navigation || {prev: null,next: null,page: null}
1661 * @attribute animation
1662 * @description The optional animation attributes for the Carousel.
1664 * { speed: 0, // the animation speed (in seconds)<br>
1665 * effect: null } // the animation effect (like
1666 * YAHOO.util.Easing.easeOut)
1669 carousel.setAttributeConfig("animation", {
1670 validator : carousel._validateAnimation,
1671 value : attrs.animation || { speed: 0, effect: null }
1675 * @attribute autoPlay
1676 * @description Set this to time in milli-seconds to have the
1677 * Carousel automatically scroll the contents.
1679 * @deprecated Use autoPlayInterval instead.
1681 carousel.setAttributeConfig("autoPlay", {
1682 validator : JS.isNumber,
1683 value : attrs.autoPlay || 0
1687 * @attribute autoPlayInterval
1688 * @description The delay in milli-seconds for scrolling the
1689 * Carousel during auto-play.
1690 * Note: The startAutoPlay() method needs to be invoked to trigger
1691 * automatic scrolling of Carousel.
1694 carousel.setAttributeConfig("autoPlayInterval", {
1695 validator : JS.isNumber,
1696 value : attrs.autoPlayInterval || 0
1701 * Initialize and bind the event handlers.
1703 * @method initEvents
1706 initEvents: function () {
1707 var carousel = this,
1708 cssClass = carousel.CLASSES,
1711 carousel.on("keydown", carousel._keyboardEventHandler);
1713 carousel.on(afterScrollEvent, syncNavigation);
1715 carousel.on(itemAddedEvent, syncUi);
1717 carousel.on(itemRemovedEvent, syncUi);
1719 carousel.on(itemSelectedEvent, function () {
1720 if (carousel._hasFocus) {
1725 carousel.on(loadItemsEvent, syncUi);
1727 carousel.on(allItemsRemovedEvent, function (ev) {
1728 carousel.scrollTo(0);
1729 syncNavigation.call(carousel);
1730 syncPagerUi.call(carousel);
1733 carousel.on(pageChangeEvent, syncPagerUi, carousel);
1735 carousel.on(renderEvent, function (ev) {
1736 carousel.set("selectedItem", carousel.get("firstVisible"));
1737 syncNavigation.call(carousel, ev);
1738 syncPagerUi.call(carousel, ev);
1739 carousel._setClipContainerSize();
1742 carousel.on("selectedItemChange", function (ev) {
1743 setItemSelection.call(carousel, ev.newValue, ev.prevValue);
1744 if (ev.newValue >= 0) {
1745 carousel._updateTabIndex(
1746 carousel.getElementForItem(ev.newValue));
1748 carousel.fireEvent(itemSelectedEvent, ev.newValue);
1751 carousel.on(uiUpdateEvent, function (ev) {
1752 syncNavigation.call(carousel, ev);
1753 syncPagerUi.call(carousel, ev);
1756 carousel.on("firstVisibleChange", function (ev) {
1757 if (!carousel.get("selectOnScroll")) {
1758 if (ev.newValue >= 0) {
1759 carousel._updateTabIndex(
1760 carousel.getElementForItem(ev.newValue));
1765 // Handle item selection on mouse click
1766 carousel.on("click", function (ev) {
1767 if (carousel.isAutoPlayOn()) {
1768 carousel.stopAutoPlay();
1770 carousel._itemClickHandler(ev);
1771 carousel._pagerClickHandler(ev);
1774 // Restore the focus on the navigation buttons
1776 Event.onFocus(carousel.get("element"), function (ev, obj) {
1777 var target = Event.getTarget(ev);
1779 if (target && target.nodeName.toUpperCase() == "A" &&
1780 Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
1782 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
1784 focussedLi = target.parentNode;
1785 Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
1788 Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
1792 obj._hasFocus = true;
1793 obj._updateNavButtons(Event.getTarget(ev), true);
1796 Event.onBlur(carousel.get("element"), function (ev, obj) {
1797 obj._hasFocus = false;
1798 obj._updateNavButtons(Event.getTarget(ev), false);
1803 * Return true if the Carousel is still animating, or false otherwise.
1805 * @method isAnimating
1806 * @return {Boolean} Return true if animation is still in progress, or
1810 isAnimating: function () {
1811 return this._isAnimationInProgress;
1815 * Return true if the auto-scrolling of Carousel is "on", or false
1818 * @method isAutoPlayOn
1819 * @return {Boolean} Return true if autoPlay is "on", or false
1823 isAutoPlayOn: function () {
1824 return this._isAutoPlayInProgress;
1828 * Return the carouselItemEl at index or null if the index is not
1831 * @method getElementForItem
1832 * @param index {Number} The index of the item to be returned
1833 * @return {Element} Return the item at index or null if not found
1836 getElementForItem: function (index) {
1837 var carousel = this;
1839 if (index < 0 || index >= carousel.get("numItems")) {
1840 YAHOO.log("Index out of bounds", "error", WidgetName);
1844 // TODO: may be cache the item
1845 if (carousel._itemsTable.numItems > index) {
1846 if (!JS.isUndefined(carousel._itemsTable.items[index])) {
1847 return Dom.get(carousel._itemsTable.items[index].id);
1855 * Return the carouselItemEl for all items in the Carousel.
1857 * @method getElementForItems
1858 * @return {Array} Return all the items
1861 getElementForItems: function () {
1862 var carousel = this, els = [], i;
1864 for (i = 0; i < carousel._itemsTable.numItems; i++) {
1865 els.push(carousel.getElementForItem(i));
1872 * Return the item at index or null if the index is not found.
1875 * @param index {Number} The index of the item to be returned
1876 * @return {Object} Return the item at index or null if not found
1879 getItem: function (index) {
1880 var carousel = this;
1882 if (index < 0 || index >= carousel.get("numItems")) {
1883 YAHOO.log("Index out of bounds", "error", WidgetName);
1887 if (carousel._itemsTable.numItems > index) {
1888 if (!JS.isUndefined(carousel._itemsTable.items[index])) {
1889 return carousel._itemsTable.items[index];
1897 * Return all items as an array.
1900 * @return {Array} Return all items in the Carousel
1903 getItems: function (index) {
1904 return this._itemsTable.items;
1908 * Return the position of the Carousel item that has the id "id", or -1
1909 * if the id is not found.
1911 * @method getItemPositionById
1912 * @param index {Number} The index of the item to be returned
1915 getItemPositionById: function (id) {
1916 var carousel = this, i = 0, n = carousel._itemsTable.numItems;
1919 if (!JS.isUndefined(carousel._itemsTable.items[i])) {
1920 if (carousel._itemsTable.items[i].id == id) {
1931 * Return all visible items as an array.
1933 * @method getVisibleItems
1934 * @return {Array} The array of visible items
1937 getVisibleItems: function () {
1938 var carousel = this,
1939 i = carousel.get("firstVisible"),
1940 n = i + carousel.get("numVisible"),
1944 r.push(carousel.getElementForItem(i));
1952 * Remove an item at index from the Carousel.
1954 * @method removeItem
1956 * @param index {Number} The position to where in the list (starts from
1958 * @return {Boolean} Return true on success, false otherwise
1960 removeItem: function (index) {
1961 var carousel = this,
1963 num = carousel.get("numItems");
1965 if (index < 0 || index >= num) {
1966 YAHOO.log("Index out of bounds", "error", WidgetName);
1970 item = carousel._itemsTable.items.splice(index, 1);
1971 if (item && item.length == 1) {
1972 carousel._itemsTable.numItems--;
1973 carousel.set("numItems", num - 1);
1975 carousel.fireEvent(itemRemovedEvent,
1976 { item: item[0], pos: index, ev: itemRemovedEvent });
1984 * Render the Carousel.
1988 * @param appendTo {HTMLElement | String} The element to which the
1989 * Carousel should be appended prior to rendering.
1990 * @return {Boolean} Status of the operation
1992 render: function (appendTo) {
1993 var carousel = this,
1994 cssClass = carousel.CLASSES;
1996 carousel.addClass(cssClass.CAROUSEL);
1998 if (!carousel._clipEl) {
1999 carousel._clipEl = carousel._createCarouselClip();
2000 carousel._clipEl.appendChild(carousel._carouselEl);
2004 carousel.appendChild(carousel._clipEl);
2005 carousel.appendTo(appendTo);
2007 if (!Dom.inDocument(carousel.get("element"))) {
2008 YAHOO.log("Nothing to render. The container should be " +
2009 "within the document if appendTo is not " +
2010 "specified", "error", WidgetName);
2013 carousel.appendChild(carousel._clipEl);
2016 if (carousel.get("isVertical")) {
2017 carousel.addClass(cssClass.VERTICAL);
2019 carousel.addClass(cssClass.HORIZONTAL);
2022 if (carousel.get("numItems") < 1) {
2023 YAHOO.log("No items in the Carousel to render", "warn",
2028 carousel._refreshUi();
2034 * Scroll the Carousel by an item backward.
2036 * @method scrollBackward
2039 scrollBackward: function () {
2040 var carousel = this;
2042 carousel.scrollTo(carousel._firstItem -
2043 carousel.get("scrollIncrement"));
2047 * Scroll the Carousel by an item forward.
2049 * @method scrollForward
2052 scrollForward: function () {
2053 var carousel = this;
2055 carousel.scrollTo(carousel._firstItem +
2056 carousel.get("scrollIncrement"));
2060 * Scroll the Carousel by a page backward.
2062 * @method scrollPageBackward
2065 scrollPageBackward: function () {
2066 var carousel = this,
2067 item = carousel._firstItem - carousel.get("numVisible");
2069 if (carousel.get("selectOnScroll")) {
2070 carousel._selectedItem = carousel._getSelectedItem(item);
2072 item = carousel._getValidIndex(item);
2074 carousel.scrollTo(item);
2078 * Scroll the Carousel by a page forward.
2080 * @method scrollPageForward
2083 scrollPageForward: function () {
2084 var carousel = this,
2085 item = carousel._firstItem + carousel.get("numVisible");
2087 if (carousel.get("selectOnScroll")) {
2088 carousel._selectedItem = carousel._getSelectedItem(item);
2090 item = carousel._getValidIndex(item);
2092 carousel.scrollTo(item);
2096 * Scroll the Carousel to make the item the first visible item.
2100 * @param item Number The index of the element to position at.
2101 * @param dontSelect Boolean True if select should be avoided
2103 scrollTo: function (item, dontSelect) {
2104 var carousel = this,
2105 animate, animCfg, isCircular, delta, direction, firstItem,
2106 numItems, numPerPage, offset, page, rv, sentinel,
2109 if (JS.isUndefined(item) || item == carousel._firstItem ||
2110 carousel.isAnimating()) {
2111 return; // nothing to do!
2114 animCfg = carousel.get("animation");
2115 isCircular = carousel.get("isCircular");
2116 firstItem = carousel._firstItem;
2117 numItems = carousel.get("numItems");
2118 numPerPage = carousel.get("numVisible");
2119 page = carousel.get("currentPage");
2120 stopAutoScroll = function () {
2121 if (carousel.isAutoPlayOn()) {
2122 carousel.stopAutoPlay();
2128 item = numItems + item;
2130 stopAutoScroll.call(carousel);
2133 } else if (numItems > 0 && item > numItems - 1) {
2134 if (carousel.get("isCircular")) {
2135 item = numItems - item;
2137 stopAutoScroll.call(carousel);
2142 direction = (carousel._firstItem > item) ? "backward" : "forward";
2144 sentinel = firstItem + numPerPage;
2145 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2146 rv = carousel.fireEvent(beforeScrollEvent,
2147 { dir: direction, first: firstItem, last: sentinel });
2148 if (rv === false) { // scrolling is prevented
2152 carousel.fireEvent(beforePageChangeEvent, { page: page });
2154 delta = firstItem - item; // yes, the delta is reverse
2155 carousel._firstItem = item;
2156 carousel.set("firstVisible", item);
2158 YAHOO.log("Scrolling to " + item + " delta = " + delta,WidgetName);
2160 carousel._loadItems(); // do we have all the items to display?
2162 sentinel = item + numPerPage;
2163 sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2165 offset = getScrollOffset.call(carousel, delta);
2166 YAHOO.log("Scroll offset = " + offset, WidgetName);
2168 animate = animCfg.speed > 0;
2171 carousel._animateAndSetCarouselOffset(offset, item, sentinel,
2174 carousel._setCarouselOffset(offset);
2175 updateStateAfterScroll.call(carousel, item, sentinel);
2180 * Select the previous item in the Carousel.
2182 * @method selectPreviousItem
2185 selectPreviousItem: function () {
2186 var carousel = this,
2188 selected = carousel.get("selectedItem");
2190 if (selected == this._firstItem) {
2191 newpos = selected - carousel.get("numVisible");
2192 carousel._selectedItem = carousel._getSelectedItem(selected-1);
2193 carousel.scrollTo(newpos);
2195 newpos = carousel.get("selectedItem") -
2196 carousel.get("scrollIncrement");
2197 carousel.set("selectedItem",carousel._getSelectedItem(newpos));
2202 * Select the next item in the Carousel.
2204 * @method selectNextItem
2207 selectNextItem: function () {
2208 var carousel = this, newpos = 0;
2210 newpos = carousel.get("selectedItem") +
2211 carousel.get("scrollIncrement");
2212 carousel.set("selectedItem", carousel._getSelectedItem(newpos));
2216 * Display the Carousel.
2222 var carousel = this,
2223 cssClass = carousel.CLASSES;
2225 if (carousel.fireEvent(beforeShowEvent) !== false) {
2226 carousel.addClass(cssClass.VISIBLE);
2227 carousel.fireEvent(showEvent);
2232 * Start auto-playing the Carousel.
2234 * @method startAutoPlay
2237 startAutoPlay: function () {
2238 var carousel = this, timer;
2240 if (JS.isUndefined(carousel._autoPlayTimer)) {
2241 timer = carousel.get("autoPlayInterval");
2245 carousel._isAutoPlayInProgress = true;
2246 carousel.fireEvent(startAutoPlayEvent);
2247 carousel._autoPlayTimer = setTimeout(function () {
2248 carousel._autoScroll();
2254 * Stop auto-playing the Carousel.
2256 * @method stopAutoPlay
2259 stopAutoPlay: function () {
2260 var carousel = this;
2262 if (!JS.isUndefined(carousel._autoPlayTimer)) {
2263 clearTimeout(carousel._autoPlayTimer);
2264 delete carousel._autoPlayTimer;
2265 carousel._isAutoPlayInProgress = false;
2266 carousel.fireEvent(stopAutoPlayEvent);
2271 * Return the string representation of the Carousel.
2277 toString: function () {
2278 return WidgetName + (this.get ? " (#" + this.get("id") + ")" : "");
2282 * Protected methods of the Carousel component
2286 * Set the Carousel offset to the passed offset after animating.
2288 * @method _animateAndSetCarouselOffset
2289 * @param {Integer} offset The offset to which the Carousel has to be
2291 * @param {Integer} item The index to which the Carousel will scroll.
2292 * @param {Integer} sentinel The last element in the view port.
2295 _animateAndSetCarouselOffset: function (offset, item, sentinel) {
2296 var carousel = this,
2297 animCfg = carousel.get("animation"),
2300 if (carousel.get("isVertical")) {
2301 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2302 { points: { by: [0, offset] } },
2303 animCfg.speed, animCfg.effect);
2305 animObj = new YAHOO.util.Motion(carousel._carouselEl,
2306 { points: { by: [offset, 0] } },
2307 animCfg.speed, animCfg.effect);
2310 carousel._isAnimationInProgress = true;
2311 animObj.onComplete.subscribe(carousel._animationCompleteHandler,
2312 { scope: carousel, item: item,
2318 * Handle the animation complete event.
2320 * @method _animationCompleteHandler
2321 * @param {Event} ev The event.
2322 * @param {Array} p The event parameters.
2323 * @param {Object} o The object that has the state of the Carousel
2326 _animationCompleteHandler: function (ev, p, o) {
2327 o.scope._isAnimationInProgress = false;
2328 updateStateAfterScroll.call(o.scope, o.item, o.last);
2332 * Automatically scroll the contents of the Carousel.
2333 * @method _autoScroll
2336 _autoScroll: function() {
2337 var carousel = this,
2338 currIndex = carousel._firstItem,
2341 if (currIndex >= carousel.get("numItems") - 1) {
2342 if (carousel.get("isCircular")) {
2345 carousel.stopAutoPlay();
2348 index = currIndex + carousel.get("numVisible");
2351 carousel._selectedItem = carousel._getSelectedItem(index);
2352 carousel.scrollTo.call(carousel, index);
2356 * Create the Carousel.
2358 * @method createCarousel
2359 * @param elId {String} The id of the element to be created
2362 _createCarousel: function (elId) {
2363 var carousel = this,
2364 cssClass = carousel.CLASSES,
2368 el = createElement("DIV", {
2369 className : cssClass.CAROUSEL,
2374 if (!carousel._carouselEl) {
2375 carousel._carouselEl=createElement(carousel.get("carouselEl"),
2376 { className: cssClass.CAROUSEL_EL });
2383 * Create the Carousel clip container.
2385 * @method createCarouselClip
2388 _createCarouselClip: function () {
2389 return createElement("DIV", { className: this.CLASSES.CONTENT });
2393 * Create the Carousel item.
2395 * @method createCarouselItem
2396 * @param obj {Object} The attributes of the element to be created
2399 _createCarouselItem: function (obj) {
2400 return createElement(this.get("carouselItemEl"), {
2401 className : obj.className,
2402 content : obj.content,
2408 * Return a valid item for a possibly out of bounds index considering
2409 * the isCircular property.
2411 * @method _getValidIndex
2412 * @param index {Number} The index of the item to be returned
2413 * @return {Object} Return a valid item index
2416 _getValidIndex: function (index) {
2417 var carousel = this,
2418 isCircular = carousel.get("isCircular"),
2419 numItems = carousel.get("numItems"),
2420 sentinel = numItems - 1;
2423 index = isCircular ? numItems + index : 0;
2424 } else if (index > sentinel) {
2425 index = isCircular ? index - numItems : sentinel;
2432 * Get the value for the selected item.
2434 * @method _getSelectedItem
2435 * @param val {Number} The new value for "selected" item
2436 * @return {Number} The new value that would be set
2439 _getSelectedItem: function (val) {
2440 var carousel = this,
2441 isCircular = carousel.get("isCircular"),
2442 numItems = carousel.get("numItems"),
2443 sentinel = numItems - 1;
2447 val = numItems + val;
2449 val = carousel.get("selectedItem");
2451 } else if (val > sentinel) {
2453 val = val - numItems;
2455 val = carousel.get("selectedItem");
2463 * The "click" handler for the item.
2465 * @method _itemClickHandler
2466 * @param {Event} ev The event object
2469 _itemClickHandler: function (ev) {
2470 var carousel = this,
2471 container = carousel.get("element"),
2474 target = YAHOO.util.Event.getTarget(ev);
2476 while (target && target != container &&
2477 target.id != carousel._carouselEl) {
2478 el = target.nodeName;
2479 if (el.toUpperCase() == carousel.get("carouselItemEl")) {
2482 target = target.parentNode;
2485 if ((item = carousel.getItemPositionById(target.id)) >= 0) {
2486 YAHOO.log("Setting selection to " + item, WidgetName);
2487 carousel.set("selectedItem", carousel._getSelectedItem(item));
2493 * The keyboard event handler for Carousel.
2495 * @method _keyboardEventHandler
2496 * @param ev {Event} The event that is being handled.
2499 _keyboardEventHandler: function (ev) {
2500 var carousel = this,
2501 key = Event.getCharCode(ev),
2504 if (carousel.isAnimating()) {
2505 return; // do not mess while animation is in progress
2509 case 0x25: // left arrow
2510 case 0x26: // up arrow
2511 carousel.selectPreviousItem();
2514 case 0x27: // right arrow
2515 case 0x28: // down arrow
2516 carousel.selectNextItem();
2519 case 0x21: // page-up
2520 carousel.scrollPageBackward();
2523 case 0x22: // page-down
2524 carousel.scrollPageForward();
2530 if (carousel.isAutoPlayOn()) {
2531 carousel.stopAutoPlay();
2533 Event.preventDefault(ev);
2538 * The load the required set of items that are needed for display.
2540 * @method _loadItems
2543 _loadItems: function() {
2544 var carousel = this,
2545 first = carousel.get("firstVisible"),
2547 numItems = carousel.get("numItems"),
2548 numVisible = carousel.get("numVisible"),
2549 reveal = carousel.get("revealAmount");
2551 last = first + numVisible - 1 + (reveal ? 1 : 0);
2552 last = last > numItems - 1 ? numItems - 1 : last;
2554 if (!carousel.getItem(first) || !carousel.getItem(last)) {
2555 carousel.fireEvent(loadItemsEvent, {
2556 ev: loadItemsEvent, first: first, last: last,
2563 * The "click" handler for the pager navigation.
2565 * @method _pagerClickHandler
2566 * @param {Event} ev The event object
2569 _pagerClickHandler: function (ev) {
2570 var carousel = this,
2572 target = Event.getTarget(ev),
2575 function getPagerNode(el) {
2576 var itemEl = carousel.get("carouselItemEl");
2578 if (el.nodeName.toUpperCase() == itemEl.toUpperCase()) {
2579 el = Dom.getChildrenBy(el, function (node) {
2580 // either an anchor or select at least
2581 return node.href || node.value;
2586 } else if (el.href || el.value) {
2594 target = getPagerNode(target);
2598 val = target.href || target.value;
2599 if (JS.isString(val) && val) {
2600 pos = val.lastIndexOf("#");
2602 val = carousel.getItemPositionById(
2603 val.substring(pos + 1));
2604 carousel._selectedItem = val;
2605 carousel.scrollTo(val);
2606 if (!target.value) { // not a select element
2609 Event.preventDefault(ev);
2616 * Find the Carousel within a container. The Carousel is identified by
2617 * the first element that matches the carousel element tag or the
2618 * element that has the Carousel class.
2620 * @method parseCarousel
2621 * @param parent {HTMLElement} The parent element to look under
2622 * @return {Boolean} True if Carousel is found, false otherwise
2625 _parseCarousel: function (parent) {
2626 var carousel = this, child, cssClass, domEl, found, node;
2628 cssClass = carousel.CLASSES;
2629 domEl = carousel.get("carouselEl");
2632 for (child = parent.firstChild; child; child = child.nextSibling) {
2633 if (child.nodeType == 1) {
2634 node = child.nodeName;
2635 if (node.toUpperCase() == domEl) {
2636 carousel._carouselEl = child;
2637 Dom.addClass(carousel._carouselEl,
2638 carousel.CLASSES.CAROUSEL_EL);
2639 YAHOO.log("Found Carousel - " + node +
2640 (child.id ? " (#" + child.id + ")" : ""),
2651 * Find the items within the Carousel and add them to the items table.
2652 * A Carousel item is identified by elements that matches the carousel
2655 * @method parseCarouselItems
2658 _parseCarouselItems: function () {
2659 var carousel = this,
2664 parent = carousel._carouselEl;
2666 domItemEl = carousel.get("carouselItemEl");
2668 for (child = parent.firstChild; child; child = child.nextSibling) {
2669 if (child.nodeType == 1) {
2670 node = child.nodeName;
2671 if (node.toUpperCase() == domItemEl) {
2675 elId = Dom.generateId();
2676 child.setAttribute("id", elId);
2678 carousel.addItem(child);
2685 * Find the Carousel navigation within a container. The navigation
2686 * elements need to match the carousel navigation class names.
2688 * @method parseCarouselNavigation
2689 * @param parent {HTMLElement} The parent element to look under
2690 * @return {Boolean} True if at least one is found, false otherwise
2693 _parseCarouselNavigation: function (parent) {
2694 var carousel = this,
2696 cssClass = carousel.CLASSES,
2703 nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent);
2704 if (nav.length > 0) {
2706 if (nav.hasOwnProperty(i)) {
2708 YAHOO.log("Found Carousel previous page navigation - " +
2709 el + (el.id ? " (#" + el.id + ")" : ""),
2711 if (el.nodeName == "INPUT" ||
2712 el.nodeName == "BUTTON") {
2713 carousel._navBtns.prev.push(el);
2715 j = el.getElementsByTagName("INPUT");
2716 if (JS.isArray(j) && j.length > 0) {
2717 carousel._navBtns.prev.push(j[0]);
2719 j = el.getElementsByTagName("BUTTON");
2720 if (JS.isArray(j) && j.length > 0) {
2721 carousel._navBtns.prev.push(j[0]);
2727 cfg = { prev: nav };
2730 nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent);
2731 if (nav.length > 0) {
2733 if (nav.hasOwnProperty(i)) {
2735 YAHOO.log("Found Carousel next page navigation - " +
2736 el + (el.id ? " (#" + el.id + ")" : ""),
2738 if (el.nodeName == "INPUT" ||
2739 el.nodeName == "BUTTON") {
2740 carousel._navBtns.next.push(el);
2742 j = el.getElementsByTagName("INPUT");
2743 if (JS.isArray(j) && j.length > 0) {
2744 carousel._navBtns.next.push(j[0]);
2746 j = el.getElementsByTagName("BUTTON");
2747 if (JS.isArray(j) && j.length > 0) {
2748 carousel._navBtns.next.push(j[0]);
2757 cfg = { next: nav };
2762 carousel.set("navigation", cfg);
2770 * Refresh the widget UI if it is not already rendered, on first item
2773 * @method _refreshUi
2776 _refreshUi: function () {
2777 var carousel = this;
2779 // Set the rendered state appropriately.
2780 carousel._hasRendered = true;
2781 carousel.fireEvent(renderEvent);
2785 * Set the Carousel offset to the passed offset.
2787 * @method _setCarouselOffset
2790 _setCarouselOffset: function (offset) {
2791 var carousel = this, which;
2793 which = carousel.get("isVertical") ? "top" : "left";
2794 offset += offset !== 0 ? getStyle(carousel._carouselEl, which) : 0;
2795 Dom.setStyle(carousel._carouselEl, which, offset + "px");
2799 * Setup/Create the Carousel navigation element (if needed).
2801 * @method _setupCarouselNavigation
2804 _setupCarouselNavigation: function () {
2805 var carousel = this,
2806 btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
2808 cssClass = carousel.CLASSES;
2810 // TODO: can the _navBtns be tested against instead?
2811 navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
2812 "DIV", carousel.get("element"));
2814 if (navContainer.length === 0) {
2815 navContainer = createElement("DIV",
2816 { className: cssClass.NAVIGATION });
2817 carousel.insertBefore(navContainer,
2818 Dom.getFirstChild(carousel.get("element")));
2820 navContainer = navContainer[0];
2823 carousel._pages.el = createElement("UL");
2824 navContainer.appendChild(carousel._pages.el);
2826 nav = carousel.get("navigation");
2827 if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
2828 if (JS.isString(nav.prev)) {
2829 nav.prev = [nav.prev];
2831 for (btn in nav.prev) {
2832 if (nav.prev.hasOwnProperty(btn)) {
2833 carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
2837 // TODO: separate method for creating a navigation button
2838 prevButton = createElement("SPAN",
2839 { className: cssClass.BUTTON + cssClass.FIRST_NAV });
2841 Dom.setStyle(prevButton, "visibility", "visible");
2842 btn = Dom.generateId();
2843 prevButton.innerHTML = "<button type=\"button\" " +
2844 "id=\"" + btn + "\" name=\"" +
2845 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">" +
2846 carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>";
2847 navContainer.appendChild(prevButton);
2849 carousel._navBtns.prev = [btn];
2850 cfg = { prev: [prevButton] };
2853 if (JS.isString(nav.next) || JS.isArray(nav.next)) {
2854 if (JS.isString(nav.next)) {
2855 nav.next = [nav.next];
2857 for (btn in nav.next) {
2858 if (nav.next.hasOwnProperty(btn)) {
2859 carousel._navBtns.next.push(Dom.get(nav.next[btn]));
2863 // TODO: separate method for creating a navigation button
2864 nextButton = createElement("SPAN",
2865 { className: cssClass.BUTTON + cssClass.NEXT_NAV });
2867 Dom.setStyle(nextButton, "visibility", "visible");
2868 btn = Dom.generateId();
2869 nextButton.innerHTML = "<button type=\"button\" " +
2870 "id=\"" + btn + "\" name=\"" +
2871 carousel.STRINGS.NEXT_BUTTON_TEXT + "\">" +
2872 carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>";
2873 navContainer.appendChild(nextButton);
2875 carousel._navBtns.next = [btn];
2877 cfg.next = [nextButton];
2879 cfg = { next: [nextButton] };
2884 carousel.set("navigation", cfg);
2887 return navContainer;
2891 * Set the clip container size (based on the new numVisible value).
2893 * @method _setClipContainerSize
2894 * @param clip {HTMLElement} The clip container element.
2895 * @param num {Number} optional The number of items per page.
2898 _setClipContainerSize: function (clip, num) {
2899 var carousel = this,
2900 attr, currVal, isVertical, itemSize, reveal, size, which;
2902 isVertical = carousel.get("isVertical");
2903 reveal = carousel.get("revealAmount");
2904 which = isVertical ? "height" : "width";
2905 attr = isVertical ? "top" : "left";
2907 clip = clip || carousel._clipEl;
2912 num = num || carousel.get("numVisible");
2913 itemSize = getCarouselItemSize.call(carousel, which);
2914 size = itemSize * num;
2916 // TODO: try to re-use the _hasRendered indicator
2917 carousel._recomputeSize = (size === 0); // bleh!
2918 if (carousel._recomputeSize) {
2919 carousel._hasRendered = false;
2920 return; // no use going further, bail out!
2924 reveal = itemSize * (reveal / 100) * 2;
2926 // TODO: set the Carousel's initial offset somwehere
2927 currVal = parseFloat(Dom.getStyle(carousel._carouselEl, attr));
2928 currVal = JS.isNumber(currVal) ? currVal : 0;
2929 Dom.setStyle(carousel._carouselEl,
2930 attr, currVal + (reveal / 2) + "px");
2934 size += getStyle(carousel._carouselEl, "marginTop") +
2935 getStyle(carousel._carouselEl, "marginBottom") +
2936 getStyle(carousel._carouselEl, "paddingTop") +
2937 getStyle(carousel._carouselEl, "paddingBottom") +
2938 getStyle(carousel._carouselEl, "borderTopWidth") +
2939 getStyle(carousel._carouselEl, "borderBottomWidth");
2940 // XXX: for vertical Carousel
2941 Dom.setStyle(clip, which, (size - (num - 1)) + "px");
2943 size += getStyle(carousel._carouselEl, "marginLeft") +
2944 getStyle(carousel._carouselEl, "marginRight") +
2945 getStyle(carousel._carouselEl, "paddingLeft") +
2946 getStyle(carousel._carouselEl, "paddingRight") +
2947 getStyle(carousel._carouselEl, "borderLeftWidth") +
2948 getStyle(carousel._carouselEl, "borderRightWidth");
2949 Dom.setStyle(clip, which, size + "px");
2952 carousel._setContainerSize(clip); // adjust the container size too
2956 * Set the container size.
2958 * @method _setContainerSize
2959 * @param clip {HTMLElement} The clip container element.
2960 * @param attr {String} Either set the height or width.
2963 _setContainerSize: function (clip, attr) {
2964 var carousel = this,
2965 config = carousel.CONFIG,
2966 cssClass = carousel.CLASSES,
2970 isVertical = carousel.get("isVertical");
2971 clip = clip || carousel._clipEl;
2972 attr = attr || (isVertical ? "height" : "width");
2973 size = parseFloat(Dom.getStyle(clip, attr), 10);
2975 size = JS.isNumber(size) ? size : 0;
2978 size += getStyle(carousel._carouselEl, "marginTop") +
2979 getStyle(carousel._carouselEl, "marginBottom") +
2980 getStyle(carousel._carouselEl, "paddingTop") +
2981 getStyle(carousel._carouselEl, "paddingBottom") +
2982 getStyle(carousel._carouselEl, "borderTopWidth") +
2983 getStyle(carousel._carouselEl, "borderBottomWidth") +
2984 getStyle(carousel._navEl, "height");
2986 size += getStyle(clip, "marginLeft") +
2987 getStyle(clip, "marginRight") +
2988 getStyle(clip, "paddingLeft") +
2989 getStyle(clip, "paddingRight") +
2990 getStyle(clip, "borderLeftWidth") +
2991 getStyle(clip, "borderRightWidth");
2995 if (size < config.HORZ_MIN_WIDTH) {
2996 size = config.HORZ_MIN_WIDTH;
2997 carousel.addClass(cssClass.MIN_WIDTH);
3000 carousel.setStyle(attr, size + "px");
3002 // Additionally the width of the container should be set for
3003 // the vertical Carousel
3005 size = getCarouselItemSize.call(carousel, "width");
3006 if (size < config.VERT_MIN_WIDTH) {
3007 size = config.VERT_MIN_WIDTH;
3008 carousel.addClass(cssClass.MIN_WIDTH);
3010 carousel.setStyle("width", size + "px");
3015 * Set the value for the Carousel's first visible item.
3017 * @method _setFirstVisible
3018 * @param val {Number} The new value for firstVisible
3019 * @return {Number} The new value that would be set
3022 _setFirstVisible: function (val) {
3023 var carousel = this;
3025 if (val >= 0 && val < carousel.get("numItems")) {
3026 carousel.scrollTo(val);
3028 val = carousel.get("firstVisible");
3034 * Set the value for the Carousel's navigation.
3036 * @method _setNavigation
3037 * @param cfg {Object} The navigation configuration
3038 * @return {Object} The new value that would be set
3041 _setNavigation: function (cfg) {
3042 var carousel = this;
3045 Event.on(cfg.prev, "click", scrollPageBackward, carousel);
3048 Event.on(cfg.next, "click", scrollPageForward, carousel);
3053 * Set the value for the number of visible items in the Carousel.
3055 * @method _setNumVisible
3056 * @param val {Number} The new value for numVisible
3057 * @return {Number} The new value that would be set
3060 _setNumVisible: function (val) {
3061 var carousel = this;
3063 carousel._setClipContainerSize(carousel._clipEl, val);
3067 * Set the number of items in the Carousel.
3068 * Warning: Setting this to a lower number than the current removes
3069 * items from the end.
3071 * @method _setNumItems
3072 * @param val {Number} The new value for numItems
3073 * @return {Number} The new value that would be set
3076 _setNumItems: function (val) {
3077 var carousel = this,
3078 num = carousel._itemsTable.numItems;
3080 if (JS.isArray(carousel._itemsTable.items)) {
3081 if (carousel._itemsTable.items.length != num) { // out of sync
3082 num = carousel._itemsTable.items.length;
3083 carousel._itemsTable.numItems = num;
3089 carousel.removeItem(num - 1);
3098 * Set the orientation of the Carousel.
3100 * @method _setOrientation
3101 * @param val {Boolean} The new value for isVertical
3102 * @return {Boolean} The new value that would be set
3105 _setOrientation: function (val) {
3106 var carousel = this,
3107 cssClass = carousel.CLASSES;
3110 carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
3112 carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
3114 carousel._itemsTable.size = 0; // force recalculation next time
3119 * Set the value for the reveal amount percentage in the Carousel.
3121 * @method _setRevealAmount
3122 * @param val {Number} The new value for revealAmount
3123 * @return {Number} The new value that would be set
3126 _setRevealAmount: function (val) {
3127 var carousel = this;
3129 if (val >= 0 && val <= 100) {
3130 val = parseInt(val, 10);
3131 val = JS.isNumber(val) ? val : 0;
3132 carousel._setClipContainerSize();
3134 val = carousel.get("revealAmount");
3140 * Set the value for the selected item.
3142 * @method _setSelectedItem
3143 * @param val {Number} The new value for "selected" item
3146 _setSelectedItem: function (val) {
3147 this._selectedItem = val;
3151 * Synchronize and redraw the UI after an item is added.
3153 * @method _syncUiForItemAdd
3156 _syncUiForItemAdd: function (obj) {
3157 var carousel = this,
3158 carouselEl = carousel._carouselEl,
3161 itemsTable = carousel._itemsTable,
3166 pos = JS.isUndefined(obj.pos) ? itemsTable.numItems - 1 : obj.pos;
3167 if (!JS.isUndefined(itemsTable.items[pos])) {
3168 item = itemsTable.items[pos];
3169 if (item && !JS.isUndefined(item.id)) {
3170 oel = Dom.get(item.id);
3174 el = carousel._createCarouselItem({
3175 className : item.className,
3176 content : item.item,
3179 if (JS.isUndefined(obj.pos)) {
3180 if (!JS.isUndefined(itemsTable.loading[pos])) {
3181 oel = itemsTable.loading[pos];
3182 // if oel is null, it is a problem ...
3186 carouselEl.replaceChild(el, oel);
3187 // ... and remove the item from the data structure
3188 delete itemsTable.loading[pos];
3190 carouselEl.appendChild(el);
3193 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3194 sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
3197 carouselEl.insertBefore(el, sibling);
3199 YAHOO.log("Unable to find sibling","error",WidgetName);
3203 if (JS.isUndefined(obj.pos)) {
3204 if (!Dom.isAncestor(carousel._carouselEl, oel)) {
3205 carouselEl.appendChild(oel);
3208 if (!Dom.isAncestor(carouselEl, oel)) {
3209 if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3210 carouselEl.insertBefore(oel,
3211 Dom.get(itemsTable.items[obj.pos + 1].id));
3217 if (!carousel._hasRendered) {
3218 carousel._refreshUi();
3221 if (carousel.get("selectedItem") < 0) {
3222 carousel.set("selectedItem", carousel.get("firstVisible"));
3227 * Synchronize and redraw the UI after an item is removed.
3229 * @method _syncUiForItemAdd
3232 _syncUiForItemRemove: function (obj) {
3233 var carousel = this,
3234 carouselEl = carousel._carouselEl,
3237 num = carousel.get("numItems");
3241 if (item && (el = Dom.get(item.id))) {
3242 if (el && Dom.isAncestor(carouselEl, el)) {
3243 Event.purgeElement(el, true);
3244 carouselEl.removeChild(el);
3247 if (carousel.get("selectedItem") == pos) {
3248 pos = pos >= num ? num - 1 : pos;
3249 carousel.set("selectedItem", pos);
3252 YAHOO.log("Unable to find item", "warn", WidgetName);
3257 * Synchronize and redraw the UI for lazy loading.
3259 * @method _syncUiForLazyLoading
3262 _syncUiForLazyLoading: function (obj) {
3263 var carousel = this,
3264 carouselEl = carousel._carouselEl,
3267 itemsTable = carousel._itemsTable,
3270 for (i = obj.first; i <= obj.last; i++) {
3271 el = carousel._createCarouselItem({
3272 className : carousel.CLASSES.ITEM_LOADING,
3273 content : carousel.STRINGS.ITEM_LOADING_CONTENT,
3274 id : Dom.generateId()
3277 if (!JS.isUndefined(itemsTable.items[obj.last + 1])) {
3278 sibling = Dom.get(itemsTable.items[obj.last + 1].id);
3280 carouselEl.insertBefore(el, sibling);
3282 YAHOO.log("Unable to find sibling", "error",
3286 carouselEl.appendChild(el);
3289 itemsTable.loading[i] = el;
3294 * Set the correct class for the navigation buttons.
3296 * @method _updateNavButtons
3297 * @param el {Object} The target button
3298 * @param setFocus {Boolean} True to set focus ring, false otherwise.
3301 _updateNavButtons: function (el, setFocus) {
3303 cssClass = this.CLASSES,
3305 parent = el.parentNode;
3310 grandParent = parent.parentNode;
3312 if (el.nodeName.toUpperCase() == "BUTTON" &&
3313 Dom.hasClass(parent, cssClass.BUTTON)) {
3316 children = Dom.getChildren(grandParent);
3318 Dom.removeClass(children, cssClass.FOCUSSED_BUTTON);
3321 Dom.addClass(parent, cssClass.FOCUSSED_BUTTON);
3323 Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON);
3329 * Update the UI for the pager buttons based on the current page and
3330 * the number of pages.
3332 * @method _updatePagerButtons
3335 _updatePagerButtons: function () {
3336 var carousel = this,
3337 css = carousel.CLASSES,
3338 cur = carousel._pages.cur, // current page
3343 n = carousel.get("numVisible"),
3344 num = carousel._pages.num, // total pages
3345 pager = carousel._pages.el; // the pager container element
3347 if (num === 0 || !pager) {
3348 return; // don't do anything if number of pages is 0
3351 // Hide the pager before redrawing it
3352 Dom.setStyle(pager, "visibility", "hidden");
3354 // Remove all nodes from the pager
3355 while (pager.firstChild) {
3356 pager.removeChild(pager.firstChild);
3359 for (i = 0; i < num; i++) {
3360 if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
3361 Dom.setStyle(pager, "visibility", "visible");
3364 item = carousel._itemsTable.items[i * n].id;
3366 el = document.createElement("LI");
3368 YAHOO.log("Unable to create an LI pager button", "error",
3370 Dom.setStyle(pager, "visibility", "visible");
3375 Dom.addClass(el, css.FIRST_PAGE);
3378 Dom.addClass(el, css.SELECTED_NAV);
3381 // TODO: use a template string for i18N compliance
3382 html = "<a href=\"#" + item + "\" tabindex=\"0\"><em>" +
3383 carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
3385 el.innerHTML = html;
3387 pager.appendChild(el);
3390 // Show the pager now
3391 Dom.setStyle(pager, "visibility", "visible");
3395 * Update the UI for the pager menu based on the current page and
3396 * the number of pages. If the number of pages is greater than
3397 * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
3398 * down menu instead of a set of buttons.
3400 * @method _updatePagerMenu
3403 _updatePagerMenu: function () {
3404 var carousel = this,
3405 cur = carousel._pages.cur, // current page
3409 n = carousel.get("numVisible"),
3410 num = carousel._pages.num, // total pages
3411 pager = carousel._pages.el, // the pager container element
3415 return; // don't do anything if number of pages is 0
3418 sel = document.createElement("SELECT");
3420 YAHOO.log("Unable to create the pager menu", "error",
3425 // Hide the pager before redrawing it
3426 Dom.setStyle(pager, "visibility", "hidden");
3428 // Remove all nodes from the pager
3429 while (pager.firstChild) {
3430 pager.removeChild(pager.firstChild);
3433 for (i = 0; i < num; i++) {
3434 if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
3435 Dom.setStyle(pager, "visibility", "visible");
3438 item = carousel._itemsTable.items[i * n].id;
3440 el = document.createElement("OPTION");
3442 YAHOO.log("Unable to create an OPTION pager menu", "error",
3444 Dom.setStyle(pager, "visibility", "visible");
3447 el.value = "#" + item;
3448 // TODO: use a template string for i18N compliance
3449 el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
3452 el.setAttribute("selected", "selected");
3455 sel.appendChild(el);
3458 el = document.createElement("FORM");
3460 YAHOO.log("Unable to create the pager menu", "error",
3463 el.appendChild(sel);
3464 pager.appendChild(el);
3467 // Show the pager now
3468 Dom.setStyle(pager, "visibility", "visible");
3472 * Set the correct tab index for the Carousel items.
3474 * @method _updateTabIndex
3475 * @param el {Object} The element to be focussed
3478 _updateTabIndex: function (el) {
3479 var carousel = this;
3482 if (carousel._focusableItemEl) {
3483 carousel._focusableItemEl.tabIndex = -1;
3485 carousel._focusableItemEl = el;
3491 * Validate animation parameters.
3493 * @method _validateAnimation
3494 * @param cfg {Object} The animation configuration
3495 * @return {Boolean} The status of the validation
3498 _validateAnimation: function (cfg) {
3501 if (JS.isObject(cfg)) {
3503 rv = rv && JS.isNumber(cfg.speed);
3506 rv = rv && JS.isFunction(cfg.effect);
3507 } else if (!JS.isUndefined(YAHOO.util.Easing)) {
3508 cfg.effect = YAHOO.util.Easing.easeOut;
3518 * Validate the firstVisible value.
3520 * @method _validateFirstVisible
3521 * @param val {Number} The first visible value
3522 * @return {Boolean} The status of the validation
3525 _validateFirstVisible: function (val) {
3526 var carousel = this, numItems = carousel.get("numItems");
3528 if (JS.isNumber(val)) {
3529 if (numItems === 0 && val == numItems) {
3532 return (val >= 0 && val < numItems);
3540 * Validate and navigation parameters.
3542 * @method _validateNavigation
3543 * @param cfg {Object} The navigation configuration
3544 * @return {Boolean} The status of the validation
3547 _validateNavigation : function (cfg) {
3550 if (!JS.isObject(cfg)) {
3555 if (!JS.isArray(cfg.prev)) {
3558 for (i in cfg.prev) {
3559 if (cfg.prev.hasOwnProperty(i)) {
3560 if (!JS.isString(cfg.prev[i].nodeName)) {
3568 if (!JS.isArray(cfg.next)) {
3571 for (i in cfg.next) {
3572 if (cfg.next.hasOwnProperty(i)) {
3573 if (!JS.isString(cfg.next[i].nodeName)) {
3584 * Validate the numItems value.
3586 * @method _validateNumItems
3587 * @param val {Number} The numItems value
3588 * @return {Boolean} The status of the validation
3591 _validateNumItems: function (val) {
3592 return JS.isNumber(val) && (val >= 0);
3596 * Validate the numVisible value.
3598 * @method _validateNumVisible
3599 * @param val {Number} The numVisible value
3600 * @return {Boolean} The status of the validation
3603 _validateNumVisible: function (val) {
3606 if (JS.isNumber(val)) {
3607 rv = val > 0 && val <= this.get("numItems");
3614 * Validate the revealAmount value.
3616 * @method _validateRevealAmount
3617 * @param val {Number} The revealAmount value
3618 * @return {Boolean} The status of the validation
3621 _validateRevealAmount: function (val) {
3624 if (JS.isNumber(val)) {
3625 rv = val >= 0 && val < 100;
3632 * Validate the scrollIncrement value.
3634 * @method _validateScrollIncrement
3635 * @param val {Number} The scrollIncrement value
3636 * @return {Boolean} The status of the validation
3639 _validateScrollIncrement: function (val) {
3642 if (JS.isNumber(val)) {
3643 rv = (val > 0 && val < this.get("numItems"));
3653 ;; Local variables: **
3655 ;; indent-tabs-mode: nil **
3658 YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.7.0", build: "1799"});