| /* |
| Copyright (c) 2008, Yahoo! Inc. All rights reserved. |
| Code licensed under the BSD License: |
| http://developer.yahoo.net/yui/license.txt |
| version: 2.6.0 |
| */ |
| /** |
| * The Carousel module provides a widget for browsing among a set of like |
| * objects represented pictorially. |
| * |
| * @module carousel |
| * @requires yahoo, dom, event, element |
| * @optional animation |
| * @namespace YAHOO.widget |
| * @title Carousel Widget |
| */ |
| (function () { |
| |
| var WidgetName; // forward declaration |
| |
| /** |
| * The Carousel widget. |
| * |
| * @class Carousel |
| * @extends YAHOO.util.Element |
| * @constructor |
| * @param el {HTMLElement | String} The HTML element that represents the |
| * the container that houses the Carousel. |
| * @param cfg {Object} (optional) The configuration values |
| */ |
| YAHOO.widget.Carousel = function (el, cfg) { |
| YAHOO.log("Component creation", WidgetName); |
| |
| this._navBtns = {}; |
| this._pages = {}; |
| |
| YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg); |
| }; |
| |
| /* |
| * Private variables of the Carousel component |
| */ |
| |
| /* Some abbreviations to avoid lengthy typing and lookups. */ |
| var Carousel = YAHOO.widget.Carousel, |
| Dom = YAHOO.util.Dom, |
| Event = YAHOO.util.Event, |
| JS = YAHOO.lang; |
| |
| /** |
| * The widget name. |
| * @private |
| * @static |
| */ |
| WidgetName = "Carousel"; |
| |
| /** |
| * The internal table of Carousel instances. |
| * @private |
| * @static |
| */ |
| var instances = {}; |
| |
| /* |
| * Custom events of the Carousel component |
| */ |
| |
| /** |
| * @event afterScroll |
| * @description Fires when the Carousel has scrolled to the previous or |
| * next page. Passes back the index of the first and last visible items in |
| * the Carousel. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var afterScrollEvent = "afterScroll"; |
| |
| /** |
| * @event beforeHide |
| * @description Fires before the Carousel is hidden. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var beforeHideEvent = "beforeHide"; |
| |
| /** |
| * @event beforePageChange |
| * @description Fires when the Carousel is about to scroll to the previous |
| * or next page. Passes back the page number of the current page. Note |
| * that the first page number is zero. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var beforePageChangeEvent = "beforePageChange"; |
| |
| /** |
| * @event beforeScroll |
| * @description Fires when the Carousel is about to scroll to the previous |
| * or next page. Passes back the index of the first and last visible items |
| * in the Carousel and the direction (backward/forward) of the scroll. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var beforeScrollEvent = "beforeScroll"; |
| |
| /** |
| * @event beforeShow |
| * @description Fires when the Carousel is about to be shown. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var beforeShowEvent = "beforeShow"; |
| |
| /** |
| * @event blur |
| * @description Fires when the Carousel loses focus. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var blurEvent = "blur"; |
| |
| /** |
| * @event focus |
| * @description Fires when the Carousel gains focus. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var focusEvent = "focus"; |
| |
| /** |
| * @event hide |
| * @description Fires when the Carousel is hidden. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var hideEvent = "hide"; |
| |
| /** |
| * @event itemAdded |
| * @description Fires when an item has been added to the Carousel. Passes |
| * back the content of the item that would be added, the index at which the |
| * item would be added, and the event itself. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var itemAddedEvent = "itemAdded"; |
| |
| /** |
| * @event itemRemoved |
| * @description Fires when an item has been removed from the Carousel. |
| * Passes back the content of the item that would be removed, the index |
| * from which the item would be removed, and the event itself. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var itemRemovedEvent = "itemRemoved"; |
| |
| /** |
| * @event itemSelected |
| * @description Fires when an item has been selected in the Carousel. |
| * Passes back the index of the selected item in the Carousel. Note, that |
| * the index begins from zero. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var itemSelectedEvent = "itemSelected"; |
| |
| /** |
| * @event loadItems |
| * @description Fires when the Carousel needs more items to be loaded for |
| * displaying them. Passes back the first and last visible items in the |
| * Carousel, and the number of items needed to be loaded. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var loadItemsEvent = "loadItems"; |
| |
| /** |
| * @event navigationStateChange |
| * @description Fires when the state of either one of the navigation |
| * buttons are changed from enabled to disabled or vice versa. Passes back |
| * the state (true/false) of the previous and next buttons. The value true |
| * signifies the button is enabled, false signifies disabled. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var navigationStateChangeEvent = "navigationStateChange"; |
| |
| /** |
| * @event pageChange |
| * @description Fires after the Carousel has scrolled to the previous or |
| * next page. Passes back the page number of the current page. Note |
| * that the first page number is zero. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var pageChangeEvent = "pageChange"; |
| |
| /** |
| * @event render |
| * @description Fires when the Carousel is rendered. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var renderEvent = "render"; |
| |
| /** |
| * @event show |
| * @description Fires when the Carousel is shown. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var showEvent = "show"; |
| |
| /** |
| * @event startAutoPlay |
| * @description Fires when the auto play has started in the Carousel. See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var startAutoPlayEvent = "startAutoPlay"; |
| |
| /** |
| * @event stopAutoPlay |
| * @description Fires when the auto play has been stopped in the Carousel. |
| * See |
| * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a> |
| * for more information on listening for this event. |
| * @type YAHOO.util.CustomEvent |
| */ |
| var stopAutoPlayEvent = "stopAutoPlay"; |
| |
| /* |
| * Private helper functions used by the Carousel component |
| */ |
| |
| /** |
| * Automatically scroll the contents of the Carousel. |
| * @method autoScroll |
| * @private |
| */ |
| function autoScroll() { |
| var currIndex = this._firstItem, |
| index; |
| |
| if (currIndex >= this.get("numItems") - 1) { |
| if (this.get("isCircular")) { |
| index = 0; |
| } else { |
| this.stopAutoPlay(); |
| } |
| } else { |
| index = currIndex + this.get("numVisible"); |
| } |
| this.scrollTo.call(this, index); |
| } |
| |
| /** |
| * Create an element, set its class name and optionally install the element |
| * to its parent. |
| * @method createElement |
| * @param el {String} The element to be created |
| * @param attrs {Object} Configuration of parent, class and id attributes. |
| * If the content is specified, it is inserted after creation of the |
| * element. The content can also be an HTML element in which case it would |
| * be appended as a child node of the created element. |
| * @private |
| */ |
| function createElement(el, attrs) { |
| var newEl = document.createElement(el); |
| |
| attrs = attrs || {}; |
| if (attrs.className) { |
| Dom.addClass(newEl, attrs.className); |
| } |
| |
| if (attrs.parent) { |
| attrs.parent.appendChild(newEl); |
| } |
| |
| if (attrs.id) { |
| newEl.setAttribute("id", attrs.id); |
| } |
| |
| if (attrs.content) { |
| if (attrs.content.nodeName) { |
| newEl.appendChild(attrs.content); |
| } else { |
| newEl.innerHTML = attrs.content; |
| } |
| } |
| |
| return newEl; |
| } |
| |
| /** |
| * Get the computed style of an element. |
| * |
| * @method getStyle |
| * @param el {HTMLElement} The element for which the style needs to be |
| * returned. |
| * @param style {String} The style attribute |
| * @param type {String} "int", "float", etc. (defaults to int) |
| * @private |
| */ |
| function getStyle(el, style, type) { |
| var value; |
| |
| function getStyleIntVal(el, style) { |
| var val; |
| |
| val = parseInt(Dom.getStyle(el, style), 10); |
| return JS.isNumber(val) ? val : 0; |
| } |
| |
| function getStyleFloatVal(el, style) { |
| var val; |
| |
| val = parseFloat(Dom.getStyle(el, style)); |
| return JS.isNumber(val) ? val : 0; |
| } |
| |
| if (typeof type == "undefined") { |
| type = "int"; |
| } |
| |
| switch (style) { |
| case "height": |
| value = el.offsetHeight; |
| if (value > 0) { |
| value += getStyleIntVal(el, "marginTop") + |
| getStyleIntVal(el, "marginBottom"); |
| } else { |
| value = getStyleFloatVal(el, "height") + |
| getStyleIntVal(el, "marginTop") + |
| getStyleIntVal(el, "marginBottom") + |
| getStyleIntVal(el, "borderTopWidth") + |
| getStyleIntVal(el, "borderBottomWidth") + |
| getStyleIntVal(el, "paddingTop") + |
| getStyleIntVal(el, "paddingBottom"); |
| } |
| break; |
| case "width": |
| value = el.offsetWidth; |
| if (value > 0) { |
| value += getStyleIntVal(el, "marginLeft") + |
| getStyleIntVal(el, "marginRight"); |
| } else { |
| value = getStyleFloatVal(el, "width") + |
| getStyleIntVal(el, "marginLeft") + |
| getStyleIntVal(el, "marginRight") + |
| getStyleIntVal(el, "borderLeftWidth") + |
| getStyleIntVal(el, "borderRightWidth") + |
| getStyleIntVal(el, "paddingLeft") + |
| getStyleIntVal(el, "paddingRight"); |
| } |
| break; |
| default: |
| if (type == "int") { |
| value = getStyleIntVal(el, style); |
| // XXX: Safari calculates incorrect marginRight for an element |
| // which has its parent element style set to overflow: hidden |
| // https://bugs.webkit.org/show_bug.cgi?id=13343 |
| // Let us assume marginLeft == marginRight |
| if (style == "marginRight" && YAHOO.env.ua.webkit) { |
| value = getStyleIntVal(el, "marginLeft"); |
| } |
| } else if (type == "float") { |
| value = getStyleFloatVal(el, style); |
| } else { |
| value = Dom.getStyle(el, style); |
| } |
| break; |
| } |
| |
| return value; |
| } |
| |
| /** |
| * Compute and return the height or width of a single Carousel item |
| * depending upon the orientation. |
| * |
| * @method getCarouselItemSize |
| * @param which {String} "height" or "width" to be returned. If this is |
| * passed explicitly, the calculated size is not cached. |
| * @private |
| */ |
| function getCarouselItemSize(which) { |
| var child, |
| size = 0, |
| vertical = false; |
| |
| if (this._itemsTable.numItems === 0) { |
| return 0; |
| } |
| |
| if (typeof which == "undefined") { |
| if (this._itemsTable.size > 0) { |
| return this._itemsTable.size; |
| } |
| } |
| |
| if (JS.isUndefined(this._itemsTable.items[0])) { |
| return 0; |
| } |
| |
| child = Dom.get(this._itemsTable.items[0].id); |
| |
| if (typeof which == "undefined") { |
| vertical = this.get("isVertical"); |
| } else { |
| vertical = which == "height"; |
| } |
| |
| if (vertical) { |
| size = getStyle(child, "height"); |
| } else { |
| size = getStyle(child, "width"); |
| } |
| |
| if (typeof which == "undefined") { |
| this._itemsTable.size = size; // save the size for later |
| } |
| |
| return size; |
| } |
| |
| /** |
| * Return the scrolling offset size given the number of elements to |
| * scroll. |
| * |
| * @method getScrollOffset |
| * @param delta {Number} The delta number of elements to scroll by. |
| * @private |
| */ |
| function getScrollOffset(delta) { |
| var itemSize = 0, |
| size = 0; |
| |
| itemSize = getCarouselItemSize.call(this); |
| size = itemSize * delta; |
| |
| // XXX: really, when the orientation is vertical, the scrolling |
| // is not exactly the number of elements into element size. |
| if (this.get("isVertical")) { |
| size -= delta; |
| } |
| |
| return size; |
| } |
| |
| /** |
| * The load the required set of items that are needed for display. |
| * |
| * @method loadItems |
| * @private |
| */ |
| function loadItems() { |
| var first = this.get("firstVisible"), |
| last = 0, |
| numItems = this.get("numItems"), |
| numVisible = this.get("numVisible"), |
| reveal = this.get("revealAmount"); |
| |
| last = first + numVisible - 1 + (reveal ? 1 : 0); |
| last = last > numItems - 1 ? numItems - 1 : last; |
| |
| if (!this.getItem(first) || !this.getItem(last)) { |
| this.fireEvent(loadItemsEvent, { |
| ev: loadItemsEvent, |
| first: first, last: last, |
| num: last - first |
| }); |
| } |
| } |
| |
| /** |
| * Scroll the Carousel by a page backward. |
| * |
| * @method scrollPageBackward |
| * @param {Event} ev The event object |
| * @param {Object} obj The context object |
| * @private |
| */ |
| function scrollPageBackward(ev, obj) { |
| obj.scrollPageBackward(); |
| Event.preventDefault(ev); |
| } |
| |
| /** |
| * Scroll the Carousel by a page forward. |
| * |
| * @method scrollPageForward |
| * @param {Event} ev The event object |
| * @param {Object} obj The context object |
| * @private |
| */ |
| function scrollPageForward(ev, obj) { |
| obj.scrollPageForward(); |
| Event.preventDefault(ev); |
| } |
| |
| /** |
| * Set the selected item. |
| * |
| * @method setItemSelection |
| * @param {Number} newposition The index of the new position |
| * @param {Number} oldposition The index of the previous position |
| * @private |
| */ |
| function setItemSelection(newposition, oldposition) { |
| var backwards, |
| cssClass = this.CLASSES, |
| el, |
| firstItem = this._firstItem, |
| isCircular = this.get("isCircular"), |
| numItems = this.get("numItems"), |
| numVisible = this.get("numVisible"), |
| position = oldposition, |
| sentinel = firstItem + numVisible - 1; |
| |
| backwards = numVisible > 1 && !isCircular && position > newposition; |
| |
| if (position >= 0 && position < numItems) { |
| if (!JS.isUndefined(this._itemsTable.items[position])) { |
| el = Dom.get(this._itemsTable.items[position].id); |
| if (el) { |
| Dom.removeClass(el, cssClass.SELECTED_ITEM); |
| } |
| } |
| } |
| |
| if (JS.isNumber(newposition)) { |
| newposition = parseInt(newposition, 10); |
| newposition = JS.isNumber(newposition) ? newposition : 0; |
| } else { |
| newposition = firstItem; |
| } |
| |
| if (JS.isUndefined(this._itemsTable.items[newposition])) { |
| this.scrollTo(newposition); // still loading the item |
| } |
| |
| if (!JS.isUndefined(this._itemsTable.items[newposition])) { |
| el = Dom.get(this._itemsTable.items[newposition].id); |
| if (el) { |
| Dom.addClass(el, cssClass.SELECTED_ITEM); |
| } |
| } |
| |
| if (newposition < firstItem || newposition > sentinel) { |
| // out of focus |
| if (backwards) { |
| this.scrollTo(firstItem - numVisible, true); |
| } else { |
| this.scrollTo(newposition); |
| } |
| } |
| } |
| |
| /** |
| * Fire custom events for enabling/disabling navigation elements. |
| * |
| * @method syncNavigation |
| * @private |
| */ |
| function syncNavigation() { |
| var attach = false, |
| cssClass = this.CLASSES, |
| i, |
| navigation, |
| sentinel; |
| |
| navigation = this.get("navigation"); |
| sentinel = this._firstItem + this.get("numVisible"); |
| |
| if (navigation.prev) { |
| if (this._firstItem === 0) { |
| if (!this.get("isCircular")) { |
| Event.removeListener(navigation.prev, "click", |
| scrollPageBackward); |
| Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); |
| for (i = 0; i < this._navBtns.prev.length; i++) { |
| this._navBtns.prev[i].setAttribute("disabled", "true"); |
| } |
| this._prevEnabled = false; |
| } else { |
| attach = !this._prevEnabled; |
| } |
| } else { |
| attach = !this._prevEnabled; |
| } |
| |
| if (attach) { |
| Event.on(navigation.prev, "click", scrollPageBackward, this); |
| Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED); |
| for (i = 0; i < this._navBtns.prev.length; i++) { |
| this._navBtns.prev[i].removeAttribute("disabled"); |
| } |
| this._prevEnabled = true; |
| } |
| } |
| |
| attach = false; |
| if (navigation.next) { |
| if (sentinel >= this.get("numItems")) { |
| if (!this.get("isCircular")) { |
| Event.removeListener(navigation.next, "click", |
| scrollPageForward); |
| Dom.addClass(navigation.next, cssClass.DISABLED); |
| for (i = 0; i < this._navBtns.next.length; i++) { |
| this._navBtns.next[i].setAttribute("disabled", "true"); |
| } |
| this._nextEnabled = false; |
| } else { |
| attach = !this._nextEnabled; |
| } |
| } else { |
| attach = !this._nextEnabled; |
| } |
| |
| if (attach) { |
| Event.on(navigation.next, "click", scrollPageForward, this); |
| Dom.removeClass(navigation.next, cssClass.DISABLED); |
| for (i = 0; i < this._navBtns.next.length; i++) { |
| this._navBtns.next[i].removeAttribute("disabled"); |
| } |
| this._nextEnabled = true; |
| } |
| } |
| |
| this.fireEvent(navigationStateChangeEvent, |
| { next: this._nextEnabled, prev: this._prevEnabled }); |
| } |
| |
| /** |
| * Fire custom events for synchronizing the DOM. |
| * |
| * @method syncUI |
| * @param {Object} o The item that needs to be added or removed |
| * @private |
| */ |
| function syncUI(o) { |
| var el, i, item, num, oel, pos, sibling; |
| |
| if (!JS.isObject(o)) { |
| return; |
| } |
| |
| switch (o.ev) { |
| case itemAddedEvent: |
| pos = JS.isUndefined(o.pos) ? this._itemsTable.numItems-1 : o.pos; |
| if (!JS.isUndefined(this._itemsTable.items[pos])) { |
| item = this._itemsTable.items[pos]; |
| if (item && !JS.isUndefined(item.id)) { |
| oel = Dom.get(item.id); |
| } |
| } |
| if (!oel) { |
| el = this._createCarouselItem({ |
| className : item.className, |
| content : item.item, |
| id : item.id |
| }); |
| if (JS.isUndefined(o.pos)) { |
| if (!JS.isUndefined(this._itemsTable.loading[pos])) { |
| oel = this._itemsTable.loading[pos]; |
| } |
| if (oel) { |
| this._carouselEl.replaceChild(el, oel); |
| } else { |
| this._carouselEl.appendChild(el); |
| } |
| } else { |
| if (!JS.isUndefined(this._itemsTable.items[o.pos + 1])) { |
| sibling = Dom.get(this._itemsTable.items[o.pos + 1].id); |
| } |
| if (sibling) { |
| this._carouselEl.insertBefore(el, sibling); |
| } else { |
| YAHOO.log("Unable to find sibling","error",WidgetName); |
| } |
| } |
| } else { |
| if (JS.isUndefined(o.pos)) { |
| if (!Dom.isAncestor(this._carouselEl, oel)) { |
| this._carouselEl.appendChild(oel); |
| } |
| } else { |
| if (!Dom.isAncestor(this._carouselEl, oel)) { |
| if (!JS.isUndefined(this._itemsTable.items[o.pos+1])) { |
| this._carouselEl.insertBefore(oel, Dom.get( |
| this._itemsTable.items[o.pos+1].id)); |
| } |
| } |
| } |
| } |
| |
| if (this._recomputeSize) { |
| this._setClipContainerSize(); |
| } |
| break; |
| case itemRemovedEvent: |
| num = this.get("numItems"); |
| item = o.item; |
| pos = o.pos; |
| |
| if (item && (el = Dom.get(item.id))) { |
| if (el && Dom.isAncestor(this._carouselEl, el)) { |
| Event.purgeElement(el, true); |
| this._carouselEl.removeChild(el); |
| } |
| |
| if (this.get("selectedItem") == pos) { |
| pos = pos >= num ? num - 1 : pos; |
| this.set("selectedItem", pos); |
| } |
| } else { |
| YAHOO.log("Unable to find item", "warn", WidgetName); |
| } |
| break; |
| case loadItemsEvent: |
| for (i = o.first; i <= o.last; i++) { |
| el = this._createCarouselItem({ |
| content : this.CONFIG.ITEM_LOADING, |
| id : Dom.generateId() |
| }); |
| if (el) { |
| if (!JS.isUndefined(this._itemsTable.items[o.last + 1])) { |
| sibling = Dom.get(this._itemsTable.items[o.last+1].id); |
| if (sibling) { |
| this._carouselEl.insertBefore(el, sibling); |
| } else { |
| YAHOO.log("Unable to find sibling", "error", |
| WidgetName); |
| } |
| } else { |
| this._carouselEl.appendChild(el); |
| } |
| } |
| this._itemsTable.loading[i] = el; |
| } |
| break; |
| } |
| } |
| |
| /* |
| * Static members and methods of the Carousel component |
| */ |
| |
| /** |
| * Return the appropriate Carousel object based on the id associated with |
| * the Carousel element or false if none match. |
| * @method getById |
| * @public |
| * @static |
| */ |
| Carousel.getById = function (id) { |
| return instances[id] ? instances[id] : false; |
| }; |
| |
| YAHOO.extend(Carousel, YAHOO.util.Element, { |
| |
| /* |
| * Internal variables used within the Carousel component |
| */ |
| |
| /** |
| * The Carousel element. |
| * |
| * @property _carouselEl |
| * @private |
| */ |
| _carouselEl: null, |
| |
| /** |
| * The Carousel clipping container element. |
| * |
| * @property _clipEl |
| * @private |
| */ |
| _clipEl: null, |
| |
| /** |
| * The current first index of the Carousel. |
| * |
| * @property _firstItem |
| * @private |
| */ |
| _firstItem: 0, |
| |
| /** |
| * Is the animation still in progress? |
| * |
| * @property _isAnimationInProgress |
| * @private |
| */ |
| _isAnimationInProgress: false, |
| |
| /** |
| * The table of items in the Carousel. |
| * The numItems is the number of items in the Carousel, items being the |
| * array of items in the Carousel. The size is the size of a single |
| * item in the Carousel. It is cached here for efficiency (to avoid |
| * computing the size multiple times). |
| * |
| * @property _itemsTable |
| * @private |
| */ |
| _itemsTable: null, |
| |
| /** |
| * The Carousel navigation buttons. |
| * |
| * @property _navBtns |
| * @private |
| */ |
| _navBtns: null, |
| |
| /** |
| * The Carousel navigation. |
| * |
| * @property _navEl |
| * @private |
| */ |
| _navEl: null, |
| |
| /** |
| * Status of the next navigation item. |
| * |
| * @property _nextEnabled |
| * @private |
| */ |
| _nextEnabled: true, |
| |
| /** |
| * The Carousel pages structure. |
| * This is an object of the total number of pages and the current page. |
| * |
| * @property _pages |
| * @private |
| */ |
| _pages: null, |
| |
| /** |
| * Status of the previous navigation item. |
| * |
| * @property _prevEnabled |
| * @private |
| */ |
| _prevEnabled: true, |
| |
| /** |
| * Whether the Carousel size needs to be recomputed or not? |
| * |
| * @property _recomputeSize |
| * @private |
| */ |
| _recomputeSize: true, |
| |
| /* |
| * CSS classes used by the Carousel component |
| */ |
| |
| CLASSES: { |
| |
| /** |
| * The class name of the Carousel navigation buttons. |
| * |
| * @property BUTTON |
| * @default "yui-carousel-button" |
| */ |
| BUTTON: "yui-carousel-button", |
| |
| /** |
| * The class name of the Carousel element. |
| * |
| * @property CAROUSEL |
| * @default "yui-carousel" |
| */ |
| CAROUSEL: "yui-carousel", |
| |
| /** |
| * The class name of the container of the items in the Carousel. |
| * |
| * @property CAROUSEL_EL |
| * @default "yui-carousel-element" |
| */ |
| CAROUSEL_EL: "yui-carousel-element", |
| |
| /** |
| * The class name of the Carousel's container element. |
| * |
| * @property CONTAINER |
| * @default "yui-carousel-container" |
| */ |
| CONTAINER: "yui-carousel-container", |
| |
| /** |
| * The class name of the Carousel's container element. |
| * |
| * @property CONTENT |
| * @default "yui-carousel-content" |
| */ |
| CONTENT: "yui-carousel-content", |
| |
| /** |
| * The class name of a disabled navigation button. |
| * |
| * @property DISABLED |
| * @default "yui-carousel-button-disabled" |
| */ |
| DISABLED: "yui-carousel-button-disabled", |
| |
| /** |
| * The class name of the first Carousel navigation button. |
| * |
| * @property FIRST_NAV |
| * @default " yui-carousel-first-button" |
| */ |
| FIRST_NAV: " yui-carousel-first-button", |
| |
| /** |
| * The class name of a first disabled navigation button. |
| * |
| * @property FIRST_NAV_DISABLED |
| * @default "yui-carousel-first-button-disabled" |
| */ |
| FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled", |
| |
| /** |
| * The class name of a first page element. |
| * |
| * @property FIRST_PAGE |
| * @default "yui-carousel-nav-first-page" |
| */ |
| FIRST_PAGE: "yui-carousel-nav-first-page", |
| |
| /** |
| * The class name of the Carousel navigation button that has focus. |
| * |
| * @property FOCUSSED_BUTTON |
| * @default "yui-carousel-button-focus" |
| */ |
| FOCUSSED_BUTTON: "yui-carousel-button-focus", |
| |
| /** |
| * The class name of a horizontally oriented Carousel. |
| * |
| * @property HORIZONTAL |
| * @default "yui-carousel-horizontal" |
| */ |
| HORIZONTAL: "yui-carousel-horizontal", |
| |
| /** |
| * The navigation element container class name. |
| * |
| * @property NAVIGATION |
| * @default "yui-carousel-nav" |
| */ |
| NAVIGATION: "yui-carousel-nav", |
| |
| /** |
| * The class name of the next navigation link. This variable is not |
| * only used for styling, but also for identifying the link within |
| * the Carousel container. |
| * |
| * @property NEXT_PAGE |
| * @default "yui-carousel-next" |
| */ |
| NEXT_PAGE: "yui-carousel-next", |
| |
| /** |
| * The class name for the navigation container for prev/next. |
| * |
| * @property NAV_CONTAINER |
| * @default "yui-carousel-buttons" |
| */ |
| NAV_CONTAINER: "yui-carousel-buttons", |
| |
| /** |
| * The class name of the previous navigation link. This variable |
| * is not only used for styling, but also for identifying the link |
| * within the Carousel container. |
| * |
| * @property PREV_PAGE |
| * @default "yui-carousel-prev" |
| */ |
| PREV_PAGE: "yui-carousel-prev", |
| |
| /** |
| * The class name of the selected item. |
| * |
| * @property SELECTED_ITEM |
| * @default "yui-carousel-item-selected" |
| */ |
| SELECTED_ITEM: "yui-carousel-item-selected", |
| |
| /** |
| * The class name of the selected paging navigation. |
| * |
| * @property SELECTED_NAV |
| * @default "yui-carousel-nav-page-selected" |
| */ |
| SELECTED_NAV: "yui-carousel-nav-page-selected", |
| |
| /** |
| * The class name of a vertically oriented Carousel. |
| * |
| * @property VERTICAL |
| * @default "yui-carousel-vertical" |
| */ |
| VERTICAL: "yui-carousel-vertical", |
| |
| /** |
| * The class name of the (vertical) Carousel's container element. |
| * |
| * @property VERTICAL_CONTAINER |
| * @default "yui-carousel-vertical-container" |
| */ |
| VERTICAL_CONTAINER: "yui-carousel-vertical-container", |
| |
| /** |
| * The class name of a visible Carousel. |
| * |
| * @property VISIBLE |
| * @default "yui-carousel-visible" |
| */ |
| VISIBLE: "yui-carousel-visible" |
| |
| }, |
| |
| /* |
| * Configuration attributes for configuring the Carousel component |
| */ |
| |
| CONFIG: { |
| |
| /** |
| * The offset of the first visible item in the Carousel. |
| * |
| * @property FIRST_VISIBLE |
| * @default 0 |
| */ |
| FIRST_VISIBLE: 0, |
| |
| /** |
| * The element to be used as the progress indicator when the item |
| * is still being loaded. |
| * |
| * @property ITEM_LOADING |
| * @default The progress indicator (spinner) image |
| */ |
| ITEM_LOADING: "<img " + |
| "src=\"../../build/carousel/assets/ajax-loader.gif\" " + |
| "alt=\"Loading\" " + |
| "style=\"margin-top:-32px;position:relative;top:50%;\">", |
| |
| /** |
| * The tag name of the Carousel item. |
| * |
| * @property ITEM_TAG_NAME |
| * @default "LI" |
| */ |
| ITEM_TAG_NAME: "LI", |
| |
| /** |
| * The maximum number of pager buttons allowed beyond which the UI |
| * of the pager would be a drop-down of pages instead of buttons. |
| * |
| * @property MAX_PAGER_BUTTONS |
| * @default 5 |
| */ |
| MAX_PAGER_BUTTONS: 5, |
| |
| /** |
| * The minimum width of the Carousel container to support the |
| * navigation buttons. |
| * |
| * @property MIN_WIDTH |
| * @default 99 |
| */ |
| MIN_WIDTH: 99, |
| |
| /** |
| * The number of visible items in the Carousel. |
| * |
| * @property NUM_VISIBLE |
| * @default 3 |
| */ |
| NUM_VISIBLE: 3, |
| |
| /** |
| * The tag name of the Carousel. |
| * |
| * @property TAG_NAME |
| * @default "OL" |
| */ |
| TAG_NAME: "OL" |
| |
| }, |
| |
| /* |
| * Internationalizable strings in the Carousel component |
| */ |
| |
| STRINGS: { |
| |
| /** |
| * The next navigation button name/text. |
| * |
| * @property NEXT_BUTTON_TEXT |
| * @default "Next Page" |
| */ |
| NEXT_BUTTON_TEXT: "Next Page", |
| |
| /** |
| * The prefix text for the pager in case the UI is a drop-down. |
| * |
| * @property PAGER_PREFIX_TEXT |
| * @default "Go to page " |
| */ |
| PAGER_PREFIX_TEXT: "Go to page ", |
| |
| /** |
| * The previous navigation button name/text. |
| * |
| * @property PREVIOUS_BUTTON_TEXT |
| * @default "Previous Page" |
| */ |
| PREVIOUS_BUTTON_TEXT: "Previous Page" |
| |
| }, |
| |
| /* |
| * Public methods of the Carousel component |
| */ |
| |
| /** |
| * Insert or append an item to the Carousel. |
| * |
| * @method addItem |
| * @public |
| * @param item {String | Object | HTMLElement} The item to be appended |
| * to the Carousel. If the parameter is a string, it is assumed to be |
| * the content of the newly created item. If the parameter is an |
| * object, it is assumed to supply the content and an optional class |
| * and an optional id of the newly created item. |
| * @param index {Number} optional The position to where in the list |
| * (starts from zero). |
| * @return {Boolean} Return true on success, false otherwise |
| */ |
| addItem: function (item, index) { |
| var className, content, el, elId, numItems = this.get("numItems"); |
| |
| if (!item) { |
| return false; |
| } |
| |
| if (JS.isString(item) || item.nodeName) { |
| content = item.nodeName ? item.innerHTML : item; |
| } else if (JS.isObject(item)) { |
| content = item.content; |
| } else { |
| YAHOO.log("Invalid argument to addItem", "error", WidgetName); |
| return false; |
| } |
| |
| className = item.className || ""; |
| elId = item.id ? item.id : Dom.generateId(); |
| |
| if (JS.isUndefined(index)) { |
| this._itemsTable.items.push({ |
| item : content, |
| className : className, |
| id : elId |
| }); |
| } else { |
| if (index < 0 || index >= numItems) { |
| YAHOO.log("Index out of bounds", "error", WidgetName); |
| return false; |
| } |
| this._itemsTable.items.splice(index, 0, { |
| item : content, |
| className : className, |
| id : elId |
| }); |
| } |
| this._itemsTable.numItems++; |
| |
| if (numItems < this._itemsTable.items.length) { |
| this.set("numItems", this._itemsTable.items.length); |
| } |
| |
| this.fireEvent(itemAddedEvent, { pos: index, ev: itemAddedEvent }); |
| |
| return true; |
| }, |
| |
| /** |
| * Insert or append multiple items to the Carousel. |
| * |
| * @method addItems |
| * @public |
| * @param items {Array} An array of items to be added with each item |
| * representing an item, index pair [{item, index}, ...] |
| * @return {Boolean} Return true on success, false otherwise |
| */ |
| addItems: function (items) { |
| var i, n, rv = true; |
| |
| if (!JS.isArray(items)) { |
| return false; |
| } |
| |
| for (i = 0, n = items.length; i < n; i++) { |
| if (this.addItem(items[i][0], items[i][1]) === false) { |
| rv = false; |
| } |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Remove focus from the Carousel. |
| * |
| * @method blur |
| * @public |
| */ |
| blur: function () { |
| this._carouselEl.blur(); |
| this.fireEvent(blurEvent); |
| }, |
| |
| /** |
| * Clears the items from Carousel. |
| * |
| * @method clearItems |
| * public |
| */ |
| clearItems: function () { |
| var n = this.get("numItems"); |
| |
| while (n > 0) { |
| this.removeItem(0); |
| n--; |
| } |
| }, |
| |
| /** |
| * Set focus on the Carousel. |
| * |
| * @method focus |
| * @public |
| */ |
| focus: function () { |
| var selItem, |
| numVisible, |
| selectOnScroll, |
| selected, |
| first, |
| last, |
| isSelectionInvisible, |
| focusEl, |
| itemsTable; |
| |
| if (this._isAnimationInProgress) { |
| // this messes up real bad! |
| return; |
| } |
| |
| selItem = this.get("selectedItem"); |
| numVisible = this.get("numVisible"); |
| selectOnScroll = this.get("selectOnScroll"); |
| selected = this.getItem(selItem); |
| first = this.get("firstVisible"); |
| last = first + numVisible - 1; |
| isSelectionInvisible = (selItem < first || selItem > last); |
| focusEl = (selected && selected.id) ? |
| Dom.get(selected.id) : null; |
| itemsTable = this._itemsTable; |
| |
| if (!selectOnScroll && isSelectionInvisible) { |
| focusEl = (itemsTable && itemsTable.items && |
| itemsTable.items[first]) ? |
| Dom.get(itemsTable.items[first].id) : null; |
| } |
| |
| if (focusEl) { |
| try { |
| focusEl.focus(); |
| } catch (ex) { |
| // ignore focus errors |
| } |
| } |
| |
| this.fireEvent(focusEvent); |
| }, |
| |
| /** |
| * Hide the Carousel. |
| * |
| * @method hide |
| * @public |
| */ |
| hide: function () { |
| if (this.fireEvent(beforeHideEvent) !== false) { |
| this.removeClass(this.CLASSES.VISIBLE); |
| this.fireEvent(hideEvent); |
| } |
| }, |
| |
| /** |
| * Initialize the Carousel. |
| * |
| * @method init |
| * @public |
| * @param el {HTMLElement | String} The html element that represents |
| * the Carousel container. |
| * @param attrs {Object} The set of configuration attributes for |
| * creating the Carousel. |
| */ |
| init: function (el, attrs) { |
| var elId = el, // save for a rainy day |
| parse = false; |
| |
| if (!el) { |
| YAHOO.log(el + " is neither an HTML element, nor a string", |
| "error", WidgetName); |
| return; |
| } |
| |
| this._itemsTable = { loading: {}, numItems: 0, items: [], size: 0 }; |
| YAHOO.log("Component initialization", WidgetName); |
| |
| if (JS.isString(el)) { |
| el = Dom.get(el); |
| } else if (!el.nodeName) { |
| YAHOO.log(el + " is neither an HTML element, nor a string", |
| "error", WidgetName); |
| return; |
| } |
| |
| if (el) { |
| if (!el.id) { // in case the HTML element is passed |
| el.setAttribute("id", Dom.generateId()); |
| } |
| this._parseCarousel(el); |
| parse = true; |
| } else { |
| el = this._createCarousel(elId); |
| } |
| elId = el.id; |
| |
| Carousel.superclass.init.call(this, el, attrs); |
| |
| this.initEvents(); |
| |
| if (parse) { |
| this._parseCarouselItems(); |
| } |
| |
| if (!attrs || typeof attrs.isVertical == "undefined") { |
| this.set("isVertical", false); |
| } |
| |
| this._parseCarouselNavigation(el); |
| this._navEl = this._setupCarouselNavigation(); |
| |
| instances[elId] = this; |
| |
| loadItems.call(this); |
| }, |
| |
| /** |
| * Initialize the configuration attributes used to create the Carousel. |
| * |
| * @method initAttributes |
| * @public |
| * @param attrs {Object} The set of configuration attributes for |
| * creating the Carousel. |
| */ |
| initAttributes: function (attrs) { |
| attrs = attrs || {}; |
| Carousel.superclass.initAttributes.call(this, attrs); |
| |
| /** |
| * @attribute currentPage |
| * @description The current page number (read-only.) |
| * @type Number |
| */ |
| this.setAttributeConfig("currentPage", { |
| readOnly : true, |
| value : 0 |
| }); |
| |
| /** |
| * @attribute firstVisible |
| * @description The index to start the Carousel from (indexes begin |
| * from zero) |
| * @default 0 |
| * @type Number |
| */ |
| this.setAttributeConfig("firstVisible", { |
| method : this._setFirstVisible, |
| validator : this._validateFirstVisible, |
| value : attrs.firstVisible || this.CONFIG.FIRST_VISIBLE |
| }); |
| |
| /** |
| * @attribute selectOnScroll |
| * @description Set this to true to automatically set focus to |
| * follow scrolling in the Carousel. |
| * @default true |
| * @type Boolean |
| */ |
| this.setAttributeConfig("selectOnScroll", { |
| validator : JS.isBoolean, |
| value : attrs.selectOnScroll || true |
| }); |
| |
| /** |
| * @attribute numVisible |
| * @description The number of visible items in the Carousel's |
| * viewport. |
| * @default 3 |
| * @type Number |
| */ |
| this.setAttributeConfig("numVisible", { |
| method : this._setNumVisible, |
| validator : this._validateNumVisible, |
| value : attrs.numVisible || this.CONFIG.NUM_VISIBLE |
| }); |
| |
| /** |
| * @attribute numItems |
| * @description The number of items in the Carousel. |
| * @type Number |
| */ |
| this.setAttributeConfig("numItems", { |
| method : this._setNumItems, |
| validator : this._validateNumItems, |
| value : this._itemsTable.numItems |
| }); |
| |
| /** |
| * @attribute scrollIncrement |
| * @description The number of items to scroll by for arrow keys. |
| * @default 1 |
| * @type Number |
| */ |
| this.setAttributeConfig("scrollIncrement", { |
| validator : this._validateScrollIncrement, |
| value : attrs.scrollIncrement || 1 |
| }); |
| |
| /** |
| * @attribute selectedItem |
| * @description The index of the selected item. |
| * @type Number |
| */ |
| this.setAttributeConfig("selectedItem", { |
| method : this._setSelectedItem, |
| validator : JS.isNumber, |
| value : 0 |
| }); |
| |
| /** |
| * @attribute revealAmount |
| * @description The percentage of the item to be revealed on each |
| * side of the Carousel (before and after the first and last item |
| * in the Carousel's viewport.) |
| * @default 0 |
| * @type Number |
| */ |
| this.setAttributeConfig("revealAmount", { |
| method : this._setRevealAmount, |
| validator : this._validateRevealAmount, |
| value : attrs.revealAmount || 0 |
| }); |
| |
| /** |
| * @attribute isCircular |
| * @description Set this to true to wrap scrolling of the contents |
| * in the Carousel. |
| * @default false |
| * @type Boolean |
| */ |
| this.setAttributeConfig("isCircular", { |
| validator : JS.isBoolean, |
| value : attrs.isCircular || false |
| }); |
| |
| /** |
| * @attribute isVertical |
| * @description True if the orientation of the Carousel is vertical |
| * @default false |
| * @type Boolean |
| */ |
| this.setAttributeConfig("isVertical", { |
| method : this._setOrientation, |
| validator : JS.isBoolean, |
| value : attrs.isVertical || false |
| }); |
| |
| /** |
| * @attribute navigation |
| * @description The set of navigation controls for Carousel |
| * @default <br> |
| * { prev: null, // the previous navigation element<br> |
| * next: null } // the next navigation element |
| * @type Object |
| */ |
| this.setAttributeConfig("navigation", { |
| method : this._setNavigation, |
| validator : this._validateNavigation, |
| value : attrs.navigation || { |
| prev: null, next: null, page: null } |
| }); |
| |
| /** |
| * @attribute animation |
| * @description The optional animation attributes for the Carousel. |
| * @default <br> |
| * { speed: 0, // the animation speed (in seconds)<br> |
| * effect: null } // the animation effect (like |
| * YAHOO.util.Easing.easeOut) |
| * @type Object |
| */ |
| this.setAttributeConfig("animation", { |
| validator : this._validateAnimation, |
| value : attrs.animation || { speed: 0, effect: null } |
| }); |
| |
| /** |
| * @attribute autoPlay |
| * @description Set this to time in milli-seconds to have the |
| * Carousel automatically scroll the contents. |
| * @type Number |
| */ |
| this.setAttributeConfig("autoPlay", { |
| validator : JS.isNumber, |
| value : attrs.autoPlay || 0 |
| }); |
| }, |
| |
| /** |
| * Initialize and bind the event handlers. |
| * |
| * @method initEvents |
| * @public |
| */ |
| initEvents: function () { |
| var cssClass = this.CLASSES; |
| |
| this.on("keydown", this._keyboardEventHandler); |
| |
| this.subscribe(afterScrollEvent, syncNavigation); |
| this.on(afterScrollEvent, this.focus); |
| |
| this.subscribe(itemAddedEvent, syncUI); |
| this.subscribe(itemAddedEvent, syncNavigation); |
| |
| this.subscribe(itemRemovedEvent, syncUI); |
| this.subscribe(itemRemovedEvent, syncNavigation); |
| |
| this.on(itemSelectedEvent, this.focus); |
| |
| this.subscribe(loadItemsEvent, syncUI); |
| |
| this.subscribe(pageChangeEvent, this._syncPagerUI); |
| |
| this.subscribe(renderEvent, syncNavigation); |
| this.subscribe(renderEvent, this._syncPagerUI); |
| |
| this.on("selectedItemChange", function (ev) { |
| setItemSelection.call(this, ev.newValue, ev.prevValue); |
| this._updateTabIndex(this.getElementForItem(ev.newValue)); |
| this.fireEvent(itemSelectedEvent, ev.newValue); |
| }); |
| |
| this.on("firstVisibleChange", function (ev) { |
| if (!this.get("selectOnScroll")) { |
| this._updateTabIndex(this.getElementForItem(ev.newValue)); |
| } |
| }); |
| |
| // Handle item selection on mouse click |
| this.on("click", this._itemClickHandler); |
| |
| // Handle page navigation |
| this.on("click", this._pagerClickHandler); |
| |
| // Restore the focus on the navigation buttons |
| Event.onFocus(this.get("element"), function (ev, obj) { |
| obj._updateNavButtons(Event.getTarget(ev), true); |
| }, this); |
| |
| Event.onBlur(this.get("element"), function (ev, obj) { |
| obj._updateNavButtons(Event.getTarget(ev), false); |
| }, this); |
| |
| }, |
| |
| /** |
| * Return the ITEM_TAG_NAME at index or null if the index is not found. |
| * |
| * @method getElementForItem |
| * @param index {Number} The index of the item to be returned |
| * @return {Element} Return the item at index or null if not found |
| * @public |
| */ |
| getElementForItem: function (index) { |
| if (index < 0 || index >= this.get("numItems")) { |
| YAHOO.log("Index out of bounds", "error", WidgetName); |
| return null; |
| } |
| |
| // TODO: may be cache the item |
| if (this._itemsTable.numItems > index) { |
| if (!JS.isUndefined(this._itemsTable.items[index])) { |
| return Dom.get(this._itemsTable.items[index].id); |
| } |
| } |
| |
| return null; |
| }, |
| |
| /** |
| * Return the ITEM_TAG_NAME for all items in the Carousel. |
| * |
| * @method getElementForItems |
| * @return {Array} Return all the items |
| * @public |
| */ |
| getElementForItems: function () { |
| var els = [], i; |
| |
| for (i = 0; i < this._itemsTable.numItems; i++) { |
| els.push(this.getElementForItem(i)); |
| } |
| |
| return els; |
| }, |
| |
| /** |
| * Return the item at index or null if the index is not found. |
| * |
| * @method getItem |
| * @param index {Number} The index of the item to be returned |
| * @return {Object} Return the item at index or null if not found |
| * @public |
| */ |
| getItem: function (index) { |
| if (index < 0 || index >= this.get("numItems")) { |
| YAHOO.log("Index out of bounds", "error", WidgetName); |
| return null; |
| } |
| |
| if (this._itemsTable.numItems > index) { |
| if (!JS.isUndefined(this._itemsTable.items[index])) { |
| return this._itemsTable.items[index]; |
| } |
| } |
| |
| return null; |
| }, |
| |
| /** |
| * Return all items as an array. |
| * |
| * @method getItems |
| * @return {Array} Return all items in the Carousel |
| * @public |
| */ |
| getItems: function (index) { |
| return this._itemsTable.items; |
| }, |
| |
| /** |
| * Return the position of the Carousel item that has the id "id", or -1 |
| * if the id is not found. |
| * |
| * @method getItemPositionById |
| * @param index {Number} The index of the item to be returned |
| * @public |
| */ |
| getItemPositionById: function (id) { |
| var i = 0, n = this._itemsTable.numItems; |
| |
| while (i < n) { |
| if (!JS.isUndefined(this._itemsTable.items[i])) { |
| if (this._itemsTable.items[i].id == id) { |
| return i; |
| } |
| } |
| i++; |
| } |
| |
| return -1; |
| }, |
| |
| /** |
| * Remove an item at index from the Carousel. |
| * |
| * @method removeItem |
| * @public |
| * @param index {Number} The position to where in the list (starts from |
| * zero). |
| * @return {Boolean} Return true on success, false otherwise |
| */ |
| removeItem: function (index) { |
| var item, num = this.get("numItems"); |
| |
| if (index < 0 || index >= num) { |
| YAHOO.log("Index out of bounds", "error", WidgetName); |
| return false; |
| } |
| |
| item = this._itemsTable.items.splice(index, 1); |
| if (item && item.length == 1) { |
| this.set("numItems", num - 1); |
| |
| this.fireEvent(itemRemovedEvent, |
| { item: item[0], pos: index, ev: itemRemovedEvent }); |
| return true; |
| } |
| |
| return false; |
| }, |
| |
| /** |
| * Render the Carousel. |
| * |
| * @method render |
| * @public |
| * @param appendTo {HTMLElement | String} The element to which the |
| * Carousel should be appended prior to rendering. |
| * @return {Boolean} Status of the operation |
| */ |
| render: function (appendTo) { |
| var config = this.CONFIG, |
| cssClass = this.CLASSES, |
| size; |
| |
| this.addClass(cssClass.CAROUSEL); |
| |
| if (!this._clipEl) { |
| this._clipEl = this._createCarouselClip(); |
| this._clipEl.appendChild(this._carouselEl); |
| } |
| |
| if (appendTo) { |
| this.appendChild(this._clipEl); |
| this.appendTo(appendTo); |
| this._setClipContainerSize(); |
| } else { |
| if (!Dom.inDocument(this.get("element"))) { |
| YAHOO.log("Nothing to render. The container should be " + |
| "within the document if appendTo is not " + |
| "specified", "error", WidgetName); |
| return false; |
| } |
| this.appendChild(this._clipEl); |
| } |
| |
| if (this.get("isVertical")) { |
| size = getCarouselItemSize.call(this); |
| size = size < config.MIN_WIDTH ? config.MIN_WIDTH : size; |
| this.setStyle("width", size + "px"); |
| this.addClass(cssClass.VERTICAL); |
| } else { |
| this.addClass(cssClass.HORIZONTAL); |
| } |
| |
| if (this.get("numItems") < 1) { |
| YAHOO.log("No items in the Carousel to render", "warn", |
| WidgetName); |
| return false; |
| } |
| |
| // Make sure at least one item is selected |
| this.set("selectedItem", this.get("firstVisible")); |
| |
| this.fireEvent(renderEvent); |
| |
| // By now, the navigation would have been rendered, so calculate |
| // the container height now. |
| this._setContainerSize(); |
| |
| return true; |
| }, |
| |
| /** |
| * Scroll the Carousel by an item backward. |
| * |
| * @method scrollBackward |
| * @public |
| */ |
| scrollBackward: function () { |
| this.scrollTo(this._firstItem - this.get("scrollIncrement")); |
| }, |
| |
| /** |
| * Scroll the Carousel by an item forward. |
| * |
| * @method scrollForward |
| * @public |
| */ |
| scrollForward: function () { |
| this.scrollTo(this._firstItem + this.get("scrollIncrement")); |
| }, |
| |
| /** |
| * Scroll the Carousel by a page backward. |
| * |
| * @method scrollPageBackward |
| * @public |
| */ |
| scrollPageBackward: function () { |
| this.scrollTo(this._firstItem - this.get("numVisible")); |
| }, |
| |
| /** |
| * Scroll the Carousel by a page forward. |
| * |
| * @method scrollPageForward |
| * @public |
| */ |
| scrollPageForward: function () { |
| this.scrollTo(this._firstItem + this.get("numVisible")); |
| }, |
| |
| /** |
| * Scroll the Carousel to make the item the first visible item. |
| * |
| * @method scrollTo |
| * @public |
| * @param item Number The index of the element to position at. |
| * @param dontSelect Boolean True if select should be avoided |
| */ |
| scrollTo: function (item, dontSelect) { |
| var anim, |
| animate, |
| animAttrs, |
| animCfg = this.get("animation"), |
| isCircular = this.get("isCircular"), |
| delta, |
| direction, |
| firstItem = this._firstItem, |
| newPage, |
| numItems = this.get("numItems"), |
| numPerPage = this.get("numVisible"), |
| offset, |
| page = this.get("currentPage"), |
| rv, |
| sentinel, |
| which; |
| |
| if (item == firstItem) { |
| return; // nothing to do! |
| } |
| |
| if (this._isAnimationInProgress) { |
| return; // let it take its own sweet time to complete |
| } |
| |
| if (item < 0) { |
| if (isCircular) { |
| item = numItems + item; |
| } else { |
| return; |
| } |
| } else if (item > numItems - 1) { |
| if (this.get("isCircular")) { |
| item = numItems - item; |
| } else { |
| return; |
| } |
| } |
| |
| direction = (this._firstItem > item) ? "backward" : "forward"; |
| |
| sentinel = firstItem + numPerPage; |
| sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel; |
| rv = this.fireEvent(beforeScrollEvent, |
| { dir: direction, first: firstItem, last: sentinel }); |
| if (rv === false) { // scrolling is prevented |
| return; |
| } |
| |
| this.fireEvent(beforePageChangeEvent, { page: page }); |
| |
| delta = firstItem - item; // yes, the delta is reverse |
| this._firstItem = item; |
| this.set("firstVisible", item); |
| |
| YAHOO.log("Scrolling to " + item + " delta = " + delta, WidgetName); |
| |
| loadItems.call(this); // do we have all the items to display? |
| |
| sentinel = item + numPerPage; |
| sentinel = (sentinel > numItems - 1) ? numItems - 1 : sentinel; |
| |
| which = this.get("isVertical") ? "top" : "left"; |
| offset = getScrollOffset.call(this, delta); |
| YAHOO.log("Scroll offset = " + offset, WidgetName); |
| |
| animate = animCfg.speed > 0; |
| |
| if (animate) { |
| this._isAnimationInProgress = true; |
| if (this.get("isVertical")) { |
| animAttrs = { points: { by: [0, offset] } }; |
| } else { |
| animAttrs = { points: { by: [offset, 0] } }; |
| } |
| anim = new YAHOO.util.Motion(this._carouselEl, animAttrs, |
| animCfg.speed, animCfg.effect); |
| anim.onComplete.subscribe(function (ev) { |
| var first = this.get("firstVisible"); |
| |
| this._isAnimationInProgress = false; |
| this.fireEvent(afterScrollEvent, |
| { first: first, last: sentinel }); |
| }, null, this); |
| anim.animate(); |
| anim = null; |
| } else { |
| offset += getStyle(this._carouselEl, which); |
| Dom.setStyle(this._carouselEl, which, offset + "px"); |
| } |
| |
| newPage = parseInt(this._firstItem / numPerPage, 10); |
| if (newPage != page) { |
| this.setAttributeConfig("currentPage", { value: newPage }); |
| this.fireEvent(pageChangeEvent, newPage); |
| } |
| |
| if (!dontSelect) { |
| if (this.get("selectOnScroll")) { |
| if (item != this._selectedItem) { // out of sync |
| this.set("selectedItem", this._getSelectedItem(item)); |
| } |
| } |
| } |
| |
| delete this._autoPlayTimer; |
| if (this.get("autoPlay") > 0) { |
| this.startAutoPlay(); |
| } |
| |
| if (!animate) { |
| this.fireEvent(afterScrollEvent, |
| { first: item, last: sentinel }); |
| } |
| }, |
| |
| /** |
| * Display the Carousel. |
| * |
| * @method show |
| * @public |
| */ |
| show: function () { |
| var cssClass = this.CLASSES; |
| |
| if (this.fireEvent(beforeShowEvent) !== false) { |
| this.addClass(cssClass.VISIBLE); |
| this.fireEvent(showEvent); |
| } |
| }, |
| |
| /** |
| * Start auto-playing the Carousel. |
| * |
| * @method startAutoPlay |
| * @public |
| */ |
| startAutoPlay: function () { |
| var self = this, |
| timer = this.get("autoPlay"); |
| |
| if (timer > 0) { |
| if (!JS.isUndefined(this._autoPlayTimer)) { |
| return; |
| } |
| this.fireEvent(startAutoPlayEvent); |
| this._autoPlayTimer = setTimeout(function () { |
| autoScroll.call(self); }, timer); |
| } |
| }, |
| |
| /** |
| * Stop auto-playing the Carousel. |
| * |
| * @method stopAutoPlay |
| * @public |
| */ |
| stopAutoPlay: function () { |
| if (!JS.isUndefined(this._autoPlayTimer)) { |
| clearTimeout(this._autoPlayTimer); |
| delete this._autoPlayTimer; |
| this.set("autoPlay", 0); |
| this.fireEvent(stopAutoPlayEvent); |
| } |
| }, |
| |
| /** |
| * Return the string representation of the Carousel. |
| * |
| * @method toString |
| * @public |
| * @return {String} |
| */ |
| toString: function () { |
| return WidgetName + (this.get ? " (#" + this.get("id") + ")" : ""); |
| }, |
| |
| /* |
| * Protected methods of the Carousel component |
| */ |
| |
| /** |
| * Create the Carousel. |
| * |
| * @method createCarousel |
| * @param elId {String} The id of the element to be created |
| * @protected |
| */ |
| _createCarousel: function (elId) { |
| var cssClass = this.CLASSES; |
| |
| var el = createElement("DIV", { |
| className : cssClass.CAROUSEL, |
| id : elId |
| }); |
| |
| if (!this._carouselEl) { |
| this._carouselEl = createElement(this.CONFIG.TAG_NAME, |
| { className: cssClass.CAROUSEL_EL }); |
| } |
| |
| return el; |
| }, |
| |
| /** |
| * Create the Carousel clip container. |
| * |
| * @method createCarouselClip |
| * @protected |
| */ |
| _createCarouselClip: function () { |
| var el = createElement("DIV", { className: this.CLASSES.CONTENT }); |
| this._setClipContainerSize(el); |
| |
| return el; |
| }, |
| |
| /** |
| * Create the Carousel item. |
| * |
| * @method createCarouselItem |
| * @param obj {Object} The attributes of the element to be created |
| * @protected |
| */ |
| _createCarouselItem: function (obj) { |
| return createElement(this.CONFIG.ITEM_TAG_NAME, { |
| className : obj.className, |
| content : obj.content, |
| id : obj.id |
| }); |
| }, |
| |
| /** |
| * Get the value for the selected item. |
| * |
| * @method _getSelectedItem |
| * @param val {Number} The new value for "selected" item |
| * @return {Number} The new value that would be set |
| * @protected |
| */ |
| _getSelectedItem: function (val) { |
| var isCircular = this.get("isCircular"), |
| numItems = this.get("numItems"), |
| sentinel = numItems - 1; |
| |
| if (val < 0) { |
| if (isCircular) { |
| val = numItems + val; |
| } else { |
| val = this.get("selectedItem"); |
| } |
| } else if (val > sentinel) { |
| if (isCircular) { |
| val = val - numItems; |
| } else { |
| val = this.get("selectedItem"); |
| } |
| } |
| |
| return val; |
| }, |
| |
| /** |
| * The "click" handler for the item. |
| * |
| * @method _itemClickHandler |
| * @param {Event} ev The event object |
| * @protected |
| */ |
| _itemClickHandler: function (ev) { |
| var container = this.get("element"), |
| el, |
| item, |
| target = YAHOO.util.Event.getTarget(ev); |
| |
| while (target && target != container && |
| target.id != this._carouselEl) { |
| el = target.nodeName; |
| if (el.toUpperCase() == this.CONFIG.ITEM_TAG_NAME) { |
| break; |
| } |
| target = target.parentNode; |
| } |
| |
| if ((item = this.getItemPositionById(target.id)) >= 0) { |
| YAHOO.log("Setting selection to " + item, WidgetName); |
| this.set("selectedItem", this._getSelectedItem(item)); |
| } |
| }, |
| |
| /** |
| * The keyboard event handler for Carousel. |
| * |
| * @method _keyboardEventHandler |
| * @param ev {Event} The event that is being handled. |
| * @protected |
| */ |
| _keyboardEventHandler: function (ev) { |
| var key = Event.getCharCode(ev), |
| prevent = false, |
| position = 0, |
| selItem; |
| |
| if (this._isAnimationInProgress) { |
| return; // do not mess while animation is in progress |
| } |
| |
| switch (key) { |
| case 0x25: // left arrow |
| case 0x26: // up arrow |
| selItem = this.get("selectedItem"); |
| if (selItem == this._firstItem) { |
| position = selItem - this.get("numVisible"); |
| this.scrollTo(position); |
| this.set("selectedItem", this._getSelectedItem(selItem-1)); |
| } else { |
| position = this.get("selectedItem") - |
| this.get("scrollIncrement"); |
| this.set("selectedItem", this._getSelectedItem(position)); |
| } |
| prevent = true; |
| break; |
| case 0x27: // right arrow |
| case 0x28: // down arrow |
| position = this.get("selectedItem")+this.get("scrollIncrement"); |
| this.set("selectedItem", this._getSelectedItem(position)); |
| prevent = true; |
| break; |
| case 0x21: // page-up |
| this.scrollPageBackward(); |
| prevent = true; |
| break; |
| case 0x22: // page-down |
| this.scrollPageForward(); |
| prevent = true; |
| break; |
| } |
| |
| if (prevent) { |
| Event.preventDefault(ev); |
| } |
| }, |
| |
| /** |
| * The "click" handler for the pager navigation. |
| * |
| * @method _pagerClickHandler |
| * @param {Event} ev The event object |
| * @protected |
| */ |
| _pagerClickHandler: function (ev) { |
| var pos, target, val; |
| |
| target = Event.getTarget(ev); |
| val = target.href || target.value; |
| if (JS.isString(val) && val) { |
| pos = val.lastIndexOf("#"); |
| if (pos != -1) { |
| val = this.getItemPositionById(val.substring(pos + 1)); |
| this.scrollTo(val); |
| Event.preventDefault(ev); |
| } |
| } |
| }, |
| |
| /** |
| * Find the Carousel within a container. The Carousel is identified by |
| * the first element that matches the carousel element tag or the |
| * element that has the Carousel class. |
| * |
| * @method parseCarousel |
| * @param parent {HTMLElement} The parent element to look under |
| * @return {Boolean} True if Carousel is found, false otherwise |
| * @protected |
| */ |
| _parseCarousel: function (parent) { |
| var child, cssClass, found, node; |
| |
| cssClass = this.CLASSES; |
| found = false; |
| |
| for (child = parent.firstChild; child; child = child.nextSibling) { |
| if (child.nodeType == 1) { |
| node = child.nodeName; |
| if (node.toUpperCase() == this.CONFIG.TAG_NAME) { |
| this._carouselEl = child; |
| Dom.addClass(this._carouselEl,this.CLASSES.CAROUSEL_EL); |
| YAHOO.log("Found Carousel - " + node + |
| (child.id ? " (#" + child.id + ")" : ""), |
| WidgetName); |
| found = true; |
| } |
| } |
| } |
| |
| return found; |
| }, |
| |
| /** |
| * Find the items within the Carousel and add them to the items table. |
| * A Carousel item is identified by elements that matches the carousel |
| * item element tag. |
| * |
| * @method parseCarouselItems |
| * @protected |
| */ |
| _parseCarouselItems: function () { |
| var child, |
| elId, |
| node, |
| parent = this._carouselEl; |
| |
| for (child = parent.firstChild; child; child = child.nextSibling) { |
| if (child.nodeType == 1) { |
| node = child.nodeName; |
| if (node.toUpperCase() == this.CONFIG.ITEM_TAG_NAME) { |
| if (child.id) { |
| elId = child.id; |
| } else { |
| elId = Dom.generateId(); |
| child.setAttribute("id", elId); |
| } |
| this.addItem(child); |
| } |
| } |
| } |
| }, |
| |
| /** |
| * Find the Carousel navigation within a container. The navigation |
| * elements need to match the carousel navigation class names. |
| * |
| * @method parseCarouselNavigation |
| * @param parent {HTMLElement} The parent element to look under |
| * @return {Boolean} True if at least one is found, false otherwise |
| * @protected |
| */ |
| _parseCarouselNavigation: function (parent) { |
| var cfg, cssClass = this.CLASSES, el, i, j, nav, rv = false; |
| |
| nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent); |
| if (nav.length > 0) { |
| for (i in nav) { |
| if (nav.hasOwnProperty(i)) { |
| el = nav[i]; |
| YAHOO.log("Found Carousel previous page navigation - " + |
| el + (el.id ? " (#" + el.id + ")" : ""), |
| WidgetName); |
| if (el.nodeName == "INPUT" || |
| el.nodeName == "BUTTON") { |
| if (typeof this._navBtns.prev == "undefined") { |
| this._navBtns.prev = []; |
| } |
| this._navBtns.prev.push(el); |
| } else { |
| j = el.getElementsByTagName("INPUT"); |
| if (JS.isArray(j) && j.length > 0) { |
| this._navBtns.prev.push(j[0]); |
| } else { |
| j = el.getElementsByTagName("BUTTON"); |
| if (JS.isArray(j) && j.length > 0) { |
| this._navBtns.prev.push(j[0]); |
| } |
| } |
| } |
| } |
| } |
| cfg = { prev: nav }; |
| } |
| |
| nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent); |
| if (nav.length > 0) { |
| for (i in nav) { |
| if (nav.hasOwnProperty(i)) { |
| el = nav[i]; |
| YAHOO.log("Found Carousel next page navigation - " + |
| el + (el.id ? " (#" + el.id + ")" : ""), |
| WidgetName); |
| if (el.nodeName == "INPUT" || |
| el.nodeName == "BUTTON") { |
| if (typeof this._navBtns.next == "undefined") { |
| this._navBtns.next = []; |
| } |
| this._navBtns.next.push(el); |
| } else { |
| j = el.getElementsByTagName("INPUT"); |
| if (JS.isArray(j) && j.length > 0) { |
| this._navBtns.next.push(j[0]); |
| } else { |
| j = el.getElementsByTagName("BUTTON"); |
| if (JS.isArray(j) && j.length > 0) { |
| this._navBtns.next.push(j[0]); |
| } |
| } |
| } |
| } |
| } |
| if (cfg) { |
| cfg.next = nav; |
| } else { |
| cfg = { next: nav }; |
| } |
| } |
| |
| if (cfg) { |
| this.set("navigation", cfg); |
| rv = true; |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Setup/Create the Carousel navigation element (if needed). |
| * |
| * @method _setupCarouselNavigation |
| * @protected |
| */ |
| _setupCarouselNavigation: function () { |
| var btn, cfg, cssClass, nav, navContainer, nextButton, pageEl, |
| prevButton; |
| |
| cssClass = this.CLASSES; |
| |
| navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION, |
| "DIV", this.get("element")); |
| |
| if (navContainer.length === 0) { |
| navContainer = createElement("DIV", |
| { className: cssClass.NAVIGATION }); |
| this.insertBefore(navContainer, |
| Dom.getFirstChild(this.get("element"))); |
| } else { |
| navContainer = navContainer[0]; |
| } |
| |
| this._pages.el = createElement("UL"); |
| navContainer.appendChild(this._pages.el); |
| |
| nav = this.get("navigation"); |
| if (nav.prev && nav.prev.length > 0) { |
| navContainer.appendChild(nav.prev[0]); |
| } else { |
| // TODO: separate method for creating a navigation button |
| prevButton = createElement("SPAN", |
| { className: cssClass.BUTTON + cssClass.FIRST_NAV }); |
| // XXX: for IE 6.x |
| Dom.setStyle(prevButton, "visibility", "visible"); |
| btn = Dom.generateId(); |
| prevButton.innerHTML = "<input type=\"button\" " + |
| "id=\"" + btn + "\" " + |
| "value=\"" + this.STRINGS.PREVIOUS_BUTTON_TEXT + "\" " + |
| "name=\"" + this.STRINGS.PREVIOUS_BUTTON_TEXT + "\">"; |
| navContainer.appendChild(prevButton); |
| btn = Dom.get(btn); |
| this._navBtns.prev = [btn]; |
| cfg = { prev: [prevButton] }; |
| } |
| |
| if (nav.next && nav.next.length > 0) { |
| navContainer.appendChild(nav.next[0]); |
| } else { |
| // TODO: separate method for creating a navigation button |
| nextButton = createElement("SPAN", |
| { className: cssClass.BUTTON }); |
| // XXX: for IE 6.x |
| Dom.setStyle(nextButton, "visibility", "visible"); |
| btn = Dom.generateId(); |
| nextButton.innerHTML = "<input type=\"button\" " + |
| "id=\"" + btn + "\" " + |
| "value=\"" + this.STRINGS.NEXT_BUTTON_TEXT + "\" " + |
| "name=\"" + this.STRINGS.NEXT_BUTTON_TEXT + "\">"; |
| navContainer.appendChild(nextButton); |
| btn = Dom.get(btn); |
| this._navBtns.next = [btn]; |
| if (cfg) { |
| cfg.next = [nextButton]; |
| } else { |
| cfg = { next: [nextButton] }; |
| } |
| } |
| |
| if (cfg) { |
| this.set("navigation", cfg); |
| } |
| |
| return navContainer; |
| }, |
| |
| /** |
| * Set the clip container size (based on the new numVisible value). |
| * |
| * @method _setClipContainerSize |
| * @param clip {HTMLElement} The clip container element. |
| * @param num {Number} optional The number of items per page. |
| * @protected |
| */ |
| _setClipContainerSize: function (clip, num) { |
| var attr, currVal, isVertical, itemSize, reveal, size, which; |
| |
| isVertical = this.get("isVertical"); |
| reveal = this.get("revealAmount"); |
| which = isVertical ? "height" : "width"; |
| attr = isVertical ? "top" : "left"; |
| |
| clip = clip || this._clipEl; |
| if (!clip) { |
| return; |
| } |
| |
| num = num || this.get("numVisible"); |
| itemSize = getCarouselItemSize.call(this, which); |
| size = itemSize * num; |
| |
| this._recomputeSize = (size === 0); // bleh! |
| if (this._recomputeSize) { |
| return; // no use going further, bail out! |
| } |
| |
| if (reveal > 0) { |
| reveal = itemSize * (reveal / 100) * 2; |
| size += reveal; |
| // TODO: set the Carousel's initial offset somwehere |
| currVal = parseFloat(Dom.getStyle(this._carouselEl, attr)); |
| currVal = JS.isNumber(currVal) ? currVal : 0; |
| Dom.setStyle(this._carouselEl, attr, currVal+(reveal/2)+"px"); |
| } |
| |
| if (isVertical) { |
| size += getStyle(this._carouselEl, "marginTop") + |
| getStyle(this._carouselEl, "marginBottom") + |
| getStyle(this._carouselEl, "paddingTop") + |
| getStyle(this._carouselEl, "paddingBottom") + |
| getStyle(this._carouselEl, "borderTop") + |
| getStyle(this._carouselEl, "borderBottom"); |
| // XXX: for vertical Carousel |
| Dom.setStyle(clip, which, (size - (num - 1)) + "px"); |
| } else { |
| size += getStyle(this._carouselEl, "marginLeft") + |
| getStyle(this._carouselEl, "marginRight") + |
| getStyle(this._carouselEl, "paddingLeft") + |
| getStyle(this._carouselEl, "paddingRight") + |
| getStyle(this._carouselEl, "borderLeft") + |
| getStyle(this._carouselEl, "borderRight"); |
| Dom.setStyle(clip, which, size + "px"); |
| } |
| |
| this._setContainerSize(clip); // adjust the container size too |
| }, |
| |
| /** |
| * Set the container size. |
| * |
| * @method _setContainerSize |
| * @param clip {HTMLElement} The clip container element. |
| * @param attr {String} Either set the height or width. |
| * @protected |
| */ |
| _setContainerSize: function (clip, attr) { |
| var isVertical, size; |
| |
| isVertical = this.get("isVertical"); |
| clip = clip || this._clipEl; |
| attr = attr || (isVertical ? "height" : "width"); |
| size = parseFloat(Dom.getStyle(clip, attr), 10); |
| |
| size = JS.isNumber(size) ? size : 0; |
| |
| size += getStyle(clip, "marginLeft") + |
| getStyle(clip, "marginRight") + |
| getStyle(clip, "paddingLeft") + |
| getStyle(clip, "paddingRight") + |
| getStyle(clip, "borderLeft") + |
| getStyle(clip, "borderRight"); |
| |
| if (isVertical) { |
| size += getStyle(this._navEl, "height"); |
| } |
| |
| this.setStyle(attr, size + "px"); |
| }, |
| |
| /** |
| * Set the value for the Carousel's first visible item. |
| * |
| * @method _setFirstVisible |
| * @param val {Number} The new value for firstVisible |
| * @return {Number} The new value that would be set |
| * @protected |
| */ |
| _setFirstVisible: function (val) { |
| if (val >= 0 && val < this.get("numItems")) { |
| this.scrollTo(val); |
| } else { |
| val = this.get("firstVisible"); |
| } |
| return val; |
| }, |
| |
| /** |
| * Set the value for the Carousel's navigation. |
| * |
| * @method _setNavigation |
| * @param cfg {Object} The navigation configuration |
| * @return {Object} The new value that would be set |
| * @protected |
| */ |
| _setNavigation: function (cfg) { |
| if (cfg.prev) { |
| Event.on(cfg.prev, "click", scrollPageBackward, this); |
| } |
| if (cfg.next) { |
| Event.on(cfg.next, "click", scrollPageForward, this); |
| } |
| }, |
| |
| /** |
| * Set the value for the number of visible items in the Carousel. |
| * |
| * @method _setNumVisible |
| * @param val {Number} The new value for numVisible |
| * @return {Number} The new value that would be set |
| * @protected |
| */ |
| _setNumVisible: function (val) { |
| if (val > 1 && val < this.get("numItems")) { |
| this._setClipContainerSize(this._clipEl, val); |
| } else { |
| val = this.get("numVisible"); |
| } |
| return val; |
| }, |
| |
| /** |
| * Set the number of items in the Carousel. |
| * Warning: Setting this to a lower number than the current removes |
| * items from the end. |
| * |
| * @method _setNumItems |
| * @param val {Number} The new value for numItems |
| * @return {Number} The new value that would be set |
| * @protected |
| */ |
| _setNumItems: function (val) { |
| var num = this._itemsTable.numItems; |
| |
| if (JS.isArray(this._itemsTable.items)) { |
| if (this._itemsTable.items.length != num) { // out of sync |
| num = this._itemsTable.items.length; |
| this._itemsTable.numItems = num; |
| } |
| } |
| |
| if (val < num) { |
| while (num > val) { |
| this.removeItem(num - 1); |
| num--; |
| } |
| } |
| |
| return val; |
| }, |
| |
| /** |
| * Set the orientation of the Carousel. |
| * |
| * @method _setOrientation |
| * @param val {Boolean} The new value for isVertical |
| * @return {Boolean} The new value that would be set |
| * @protected |
| */ |
| _setOrientation: function (val) { |
| var cssClass = this.CLASSES; |
| |
| if (val) { |
| this.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL); |
| } else { |
| this.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL); |
| } |
| this._itemsTable.size = 0; // invalidate our size computation cache |
| return val; |
| }, |
| |
| /** |
| * Set the value for the reveal amount percentage in the Carousel. |
| * |
| * @method _setRevealAmount |
| * @param val {Number} The new value for revealAmount |
| * @return {Number} The new value that would be set |
| * @protected |
| */ |
| _setRevealAmount: function (val) { |
| if (val >= 0 && val <= 100) { |
| val = parseInt(val, 10); |
| val = JS.isNumber(val) ? val : 0; |
| this._setClipContainerSize(); |
| } else { |
| val = this.get("revealAmount"); |
| } |
| return val; |
| }, |
| |
| /** |
| * Set the value for the selected item. |
| * |
| * @method _setSelectedItem |
| * @param val {Number} The new value for "selected" item |
| * @protected |
| */ |
| _setSelectedItem: function (val) { |
| this._selectedItem = val; |
| }, |
| |
| /** |
| * Synchronize and redraw the Pager UI if necessary. |
| * |
| * @method _syncPagerUI |
| * @protected |
| */ |
| _syncPagerUI: function (page) { |
| var a, |
| cssClass = this.CLASSES, |
| i, |
| markup = "", |
| numPages, |
| numVisible = this.get("numVisible"); |
| |
| page = page || 0; |
| numPages = Math.ceil(this.get("numItems") / numVisible); |
| |
| this._pages.num = numPages; |
| this._pages.cur = page; |
| |
| if (numPages > this.CONFIG.MAX_PAGER_BUTTONS) { |
| markup = "<form><select>"; |
| } else { |
| markup = ""; |
| } |
| |
| for (i = 0; i < numPages; i++) { |
| if (JS.isUndefined(this._itemsTable.items[i * numVisible])) { |
| break; |
| } |
| a = this._itemsTable.items[i * numVisible].id; |
| if (numPages > this.CONFIG.MAX_PAGER_BUTTONS) { |
| markup += "<option value=\"#" + a + "\" " + |
| (i == page ? " selected" : "") + ">" + |
| this.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) + |
| "</option>"; |
| } else { |
| markup += "<li class=\"" + |
| (i === 0 ? cssClass.FIRST_PAGE : "") + |
| (i == page ? " " + cssClass.SELECTED_NAV : "") + |
| "\"><a href=\"#" + a + "\" tabindex=\"0\"><em>" + |
| this.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) + |
| "</em></a></li>"; |
| } |
| } |
| |
| if (numPages > this.CONFIG.MAX_PAGER_BUTTONS) { |
| markup += "</select></form>"; |
| } |
| |
| this._pages.el.innerHTML = markup; |
| markup = null; |
| }, |
| |
| /** |
| * Set the correct class for the navigation buttons. |
| * |
| * @method _updateNavButtons |
| * @param el {Object} The target button |
| * @param setFocus {Boolean} True to set focus ring, false otherwise. |
| * @protected |
| */ |
| _updateNavButtons: function (el, setFocus) { |
| var children, |
| cssClass = this.CLASSES, |
| grandParent, |
| parent = el.parentNode; |
| |
| if (!parent) { |
| return; |
| } |
| grandParent = parent.parentNode; |
| |
| if (el.nodeName.toUpperCase() == "INPUT" && |
| Dom.hasClass(parent, cssClass.BUTTON)) { |
| if (setFocus) { |
| if (grandParent) { |
| children = Dom.getChildren(grandParent); |
| if (children) { |
| Dom.removeClass(children, cssClass.FOCUSSED_BUTTON); |
| } |
| } |
| Dom.addClass(parent, cssClass.FOCUSSED_BUTTON); |
| } else { |
| Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON); |
| } |
| } |
| }, |
| |
| /** |
| * Set the correct tab index for the Carousel items. |
| * |
| * @method _updateTabIndex |
| * @param el {Object} The element to be focussed |
| * @protected |
| */ |
| _updateTabIndex: function (el) { |
| if (el) { |
| if (this._focusableItemEl) { |
| this._focusableItemEl.tabIndex = -1; |
| } |
| this._focusableItemEl = el; |
| el.tabIndex = 0; |
| } |
| }, |
| |
| /** |
| * Validate animation parameters. |
| * |
| * @method _validateAnimation |
| * @param cfg {Object} The animation configuration |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateAnimation: function (cfg) { |
| var rv = true; |
| |
| if (JS.isObject(cfg)) { |
| if (cfg.speed) { |
| rv = rv && JS.isNumber(cfg.speed); |
| } |
| if (cfg.effect) { |
| rv = rv && JS.isFunction(cfg.effect); |
| } else if (!JS.isUndefined(YAHOO.util.Easing)) { |
| cfg.effect = YAHOO.util.Easing.easeOut; |
| } |
| } else { |
| rv = false; |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Validate the firstVisible value. |
| * |
| * @method _validateFirstVisible |
| * @param val {Number} The first visible value |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateFirstVisible: function (val) { |
| var rv = false; |
| |
| if (JS.isNumber(val)) { |
| rv = (val >= 0 && val < this.get("numItems")); |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Validate and navigation parameters. |
| * |
| * @method _validateNavigation |
| * @param cfg {Object} The navigation configuration |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateNavigation : function (cfg) { |
| var i; |
| |
| if (!JS.isObject(cfg)) { |
| return false; |
| } |
| |
| if (cfg.prev) { |
| if (!JS.isArray(cfg.prev)) { |
| return false; |
| } |
| for (i in cfg.prev) { |
| if (cfg.prev.hasOwnProperty(i)) { |
| if (!JS.isString(cfg.prev[i].nodeName)) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| if (cfg.next) { |
| if (!JS.isArray(cfg.next)) { |
| return false; |
| } |
| for (i in cfg.next) { |
| if (cfg.next.hasOwnProperty(i)) { |
| if (!JS.isString(cfg.next[i].nodeName)) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| }, |
| |
| /** |
| * Validate the numItems value. |
| * |
| * @method _validateNumItems |
| * @param val {Number} The numItems value |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateNumItems: function (val) { |
| var rv = false; |
| |
| if (JS.isNumber(val)) { |
| rv = val > 0; |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Validate the numVisible value. |
| * |
| * @method _validateNumVisible |
| * @param val {Number} The numVisible value |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateNumVisible: function (val) { |
| var rv = false; |
| |
| if (JS.isNumber(val)) { |
| rv = val > 0 && val < this.get("numItems"); |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Validate the revealAmount value. |
| * |
| * @method _validateRevealAmount |
| * @param val {Number} The revealAmount value |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateRevealAmount: function (val) { |
| var rv = false; |
| |
| if (JS.isNumber(val)) { |
| rv = val >= 0 && val < 100; |
| } |
| |
| return rv; |
| }, |
| |
| /** |
| * Validate the scrollIncrement value. |
| * |
| * @method _validateScrollIncrement |
| * @param val {Number} The scrollIncrement value |
| * @return {Boolean} The status of the validation |
| * @protected |
| */ |
| _validateScrollIncrement: function (val) { |
| var rv = false; |
| |
| if (JS.isNumber(val)) { |
| rv = (val > 0 && val < this.get("numItems")); |
| } |
| |
| return rv; |
| } |
| |
| }); |
| |
| })(); |
| |
| YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.6.0", build: "1321"}); |