]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blobdiff - wradmin/static/yui/carousel/carousel.js
Rename public directory to static.
[philipp/winterrodeln/wradmin.git] / wradmin / static / yui / carousel / carousel.js
diff --git a/wradmin/static/yui/carousel/carousel.js b/wradmin/static/yui/carousel/carousel.js
new file mode 100644 (file)
index 0000000..f957c1f
--- /dev/null
@@ -0,0 +1,3616 @@
+/*
+Copyright (c) 2009, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.net/yui/license.txt
+version: 2.7.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
+ * @beta
+ */
+(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.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
+     */
+    afterScrollEvent = "afterScroll",
+
+    /**
+     * @event allItemsRemovedEvent
+     * @description Fires when all items have been removed from 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
+     */
+    allItemsRemovedEvent = "allItemsRemoved",
+
+    /**
+     * @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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    pageChangeEvent = "pageChange",
+
+    /*
+     * Internal event.
+     * @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
+     */
+    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
+     */
+    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
+     */
+    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
+     */
+    stopAutoPlayEvent = "stopAutoPlay",
+
+    /*
+     * Internal event.
+     * @event uiUpdateEvent
+     * @description Fires when the UI has been updated.
+     * See
+     * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
+     * for more information on listening for this event.
+     * @type YAHOO.util.CustomEvent
+     */
+    uiUpdateEvent = "uiUpdate";
+
+    /*
+     * Private helper functions used by the Carousel component
+     */
+
+    /**
+     * 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;
+
+        if (!el) {
+            return 0;
+        }
+
+        function getStyleIntVal(el, style) {
+            var val;
+
+            /*
+             * 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) {
+                val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
+            } else {
+                val = parseInt(Dom.getStyle(el, style), 10);
+            }
+
+            return JS.isNumber(val) ? val : 0;
+        }
+
+        function getStyleFloatVal(el, style) {
+            var val;
+
+            /*
+             * 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) {
+                val = parseFloat(Dom.getStyle(el, "marginLeft"));
+            } else {
+                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);
+            } 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 carousel = this,
+            child,
+            size     = 0,
+            vertical = false;
+
+        if (carousel._itemsTable.numItems === 0) {
+            return 0;
+        }
+
+        if (typeof which == "undefined") {
+            if (carousel._itemsTable.size > 0) {
+                return carousel._itemsTable.size;
+            }
+        }
+
+        if (JS.isUndefined(carousel._itemsTable.items[0])) {
+            return 0;
+        }
+
+        child = Dom.get(carousel._itemsTable.items[0].id);
+
+        if (typeof which == "undefined") {
+            vertical = carousel.get("isVertical");
+        } else {
+            vertical = which == "height";
+        }
+
+        if (vertical) {
+            size = getStyle(child, "height");
+        } else {
+            size = getStyle(child, "width");
+        }
+
+        if (typeof which == "undefined") {
+            carousel._itemsTable.size = size; // save the size for later
+        }
+
+        return size;
+    }
+
+    /**
+     * Return the index of the first item in the view port for displaying item
+     * in "pos".
+     *
+     * @method getFirstVisibleForPosition
+     * @param pos {Number} The position of the item to be displayed
+     * @private
+     */
+    function getFirstVisibleForPosition(pos) {
+        var num = this.get("numVisible");
+
+        return Math.floor(pos / num) * num;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 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} newpos The index of the new position
+     * @param {Number} oldpos The index of the previous position
+     * @private
+     */
+     function setItemSelection(newpos, oldpos) {
+        var carousel = this,
+            cssClass   = carousel.CLASSES,
+            el,
+            firstItem  = carousel._firstItem,
+            isCircular = carousel.get("isCircular"),
+            numItems   = carousel.get("numItems"),
+            numVisible = carousel.get("numVisible"),
+            position   = oldpos,
+            sentinel   = firstItem + numVisible - 1;
+
+        if (position >= 0 && position < numItems) {
+            if (!JS.isUndefined(carousel._itemsTable.items[position])) {
+                el = Dom.get(carousel._itemsTable.items[position].id);
+                if (el) {
+                    Dom.removeClass(el, cssClass.SELECTED_ITEM);
+                }
+            }
+        }
+
+        if (JS.isNumber(newpos)) {
+            newpos = parseInt(newpos, 10);
+            newpos = JS.isNumber(newpos) ? newpos : 0;
+        } else {
+            newpos = firstItem;
+        }
+
+        if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
+            newpos = getFirstVisibleForPosition.call(carousel, newpos);
+            carousel.scrollTo(newpos); // still loading the item
+        }
+
+        if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
+            el = Dom.get(carousel._itemsTable.items[newpos].id);
+            if (el) {
+                Dom.addClass(el, cssClass.SELECTED_ITEM);
+            }
+        }
+
+        if (newpos < firstItem || newpos > sentinel) { // out of focus
+            newpos = getFirstVisibleForPosition.call(carousel, newpos);
+            carousel.scrollTo(newpos);
+        }
+    }
+
+    /**
+     * Fire custom events for enabling/disabling navigation elements.
+     *
+     * @method syncNavigation
+     * @private
+     */
+    function syncNavigation() {
+        var attach   = false,
+            carousel = this,
+            cssClass = carousel.CLASSES,
+            i,
+            navigation,
+            sentinel;
+
+        // Don't do anything if the Carousel is not rendered
+        if (!carousel._hasRendered) {
+            return;
+        }
+
+        navigation = carousel.get("navigation");
+        sentinel   = carousel._firstItem + carousel.get("numVisible");
+
+        if (navigation.prev) {
+            if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
+                if (carousel.get("numItems") === 0 ||
+                   !carousel.get("isCircular")) {
+                    Event.removeListener(navigation.prev, "click",
+                            scrollPageBackward);
+                    Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
+                    for (i = 0; i < carousel._navBtns.prev.length; i++) {
+                        carousel._navBtns.prev[i].setAttribute("disabled",
+                                "true");
+                    }
+                    carousel._prevEnabled = false;
+                } else {
+                    attach = !carousel._prevEnabled;
+                }
+            } else {
+                attach = !carousel._prevEnabled;
+            }
+
+            if (attach) {
+                Event.on(navigation.prev, "click", scrollPageBackward,
+                         carousel);
+                Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
+                for (i = 0; i < carousel._navBtns.prev.length; i++) {
+                    carousel._navBtns.prev[i].removeAttribute("disabled");
+                }
+                carousel._prevEnabled = true;
+            }
+        }
+
+        attach = false;
+        if (navigation.next) {
+            if (sentinel >= carousel.get("numItems")) {
+                if (!carousel.get("isCircular")) {
+                    Event.removeListener(navigation.next, "click",
+                            scrollPageForward);
+                    Dom.addClass(navigation.next, cssClass.DISABLED);
+                    for (i = 0; i < carousel._navBtns.next.length; i++) {
+                        carousel._navBtns.next[i].setAttribute("disabled",
+                                "true");
+                    }
+                    carousel._nextEnabled = false;
+                } else {
+                    attach = !carousel._nextEnabled;
+                }
+            } else {
+                attach = !carousel._nextEnabled;
+            }
+
+            if (attach) {
+                Event.on(navigation.next, "click", scrollPageForward,
+                         carousel);
+                Dom.removeClass(navigation.next, cssClass.DISABLED);
+                for (i = 0; i < carousel._navBtns.next.length; i++) {
+                    carousel._navBtns.next[i].removeAttribute("disabled");
+                }
+                carousel._nextEnabled = true;
+            }
+        }
+
+        carousel.fireEvent(navigationStateChangeEvent,
+                { next: carousel._nextEnabled, prev: carousel._prevEnabled });
+    }
+
+    /**
+     * Synchronize and redraw the Pager UI if necessary.
+     *
+     * @method syncPagerUi
+     * @private
+     */
+    function syncPagerUi(page) {
+        var carousel = this, numPages, numVisible;
+
+        // Don't do anything if the Carousel is not rendered
+        if (!carousel._hasRendered) {
+            return;
+        }
+
+        numVisible = carousel.get("numVisible");
+
+        if (!JS.isNumber(page)) {
+            page = Math.ceil(carousel.get("selectedItem") / numVisible);
+        }
+        numPages = Math.ceil(carousel.get("numItems") / numVisible);
+
+        carousel._pages.num = numPages;
+        carousel._pages.cur = page;
+
+        if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
+            carousel._updatePagerMenu();
+        } else {
+            carousel._updatePagerButtons();
+        }
+    }
+
+    /**
+     * Handle UI update.
+     * Call the appropriate methods on events fired when an item is added, or
+     * removed for synchronizing the DOM.
+     *
+     * @method syncUi
+     * @param {Object} o The item that needs to be added or removed
+     * @private
+     */
+    function syncUi(o) {
+        var carousel = this;
+
+        if (!JS.isObject(o)) {
+            return;
+        }
+
+        switch (o.ev) {
+        case itemAddedEvent:
+            carousel._syncUiForItemAdd(o);
+            break;
+        case itemRemovedEvent:
+            carousel._syncUiForItemRemove(o);
+            break;
+        case loadItemsEvent:
+            carousel._syncUiForLazyLoading(o);
+            break;
+        }
+
+        carousel.fireEvent(uiUpdateEvent);
+    }
+
+    /**
+     * Update the state variables after scrolling the Carousel view port.
+     *
+     * @method updateStateAfterScroll
+     * @param {Integer} item The index to which the Carousel has scrolled to.
+     * @param {Integer} sentinel The last element in the view port.
+     * @private
+     */
+    function updateStateAfterScroll(item, sentinel) {
+        var carousel   = this,
+            page       = carousel.get("currentPage"),
+            newPage,
+            numPerPage = carousel.get("numVisible");
+
+        newPage = parseInt(carousel._firstItem / numPerPage, 10);
+        if (newPage != page) {
+            carousel.setAttributeConfig("currentPage", { value: newPage });
+            carousel.fireEvent(pageChangeEvent, newPage);
+        }
+
+        if (carousel.get("selectOnScroll")) {
+            if (carousel.get("selectedItem") != carousel._selectedItem) {
+                carousel.set("selectedItem", carousel._selectedItem);
+            }
+        }
+
+        clearTimeout(carousel._autoPlayTimer);
+        delete carousel._autoPlayTimer;
+        if (carousel.isAutoPlayOn()) {
+            carousel.startAutoPlay();
+        }
+
+        carousel.fireEvent(afterScrollEvent,
+                           { first: carousel._firstItem,
+                             last: sentinel },
+                           carousel);
+    }
+
+    /*
+     * 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].object : false;
+    };
+
+    YAHOO.extend(Carousel, YAHOO.util.Element, {
+
+        /*
+         * Internal variables used within the Carousel component
+         */
+
+        /**
+         * The Animation object.
+         *
+         * @property _animObj
+         * @private
+         */
+        _animObj: null,
+
+        /**
+         * 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,
+
+        /**
+         * Does the Carousel element have focus?
+         *
+         * @property _hasFocus
+         * @private
+         */
+        _hasFocus: false,
+
+        /**
+         * Is the Carousel rendered already?
+         *
+         * @property _hasRendered
+         * @private
+         */
+        _hasRendered: false,
+
+        /**
+         * Is the animation still in progress?
+         *
+         * @property _isAnimationInProgress
+         * @private
+         */
+        _isAnimationInProgress: false,
+
+        /**
+         * Is the auto-scrolling of Carousel in progress?
+         *
+         * @property _isAutoPlayInProgress
+         * @private
+         */
+        _isAutoPlayInProgress: 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 element to be used as the progress indicator when the item
+             * is still being loaded.
+             *
+             * @property ITEM_LOADING
+             * @default The progress indicator (spinner) image CSS class
+             */
+            ITEM_LOADING: "yui-carousel-item-loading",
+
+            /**
+             * The class name that will be set if the Carousel adjusts itself
+             * for a minimum width.
+             *
+             * @property MIN_WIDTH
+             * @default "yui-carousel-min-width"
+             */
+            MIN_WIDTH: "yui-carousel-min-width",
+
+            /**
+             * The navigation element container class name.
+             *
+             * @property NAVIGATION
+             * @default "yui-carousel-nav"
+             */
+            NAVIGATION: "yui-carousel-nav",
+
+            /**
+             * The class name of the next Carousel navigation button.
+             *
+             * @property NEXT_NAV
+             * @default " yui-carousel-next-button"
+             */
+            NEXT_NAV: " yui-carousel-next-button",
+
+            /**
+             * 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 focussed page navigation.  This class is
+             * specifically used for the ugly focus handling in Opera.
+             *
+             * @property PAGE_FOCUS
+             * @default "yui-carousel-nav-page-focus"
+             */
+            PAGE_FOCUS: "yui-carousel-nav-page-focus",
+
+            /**
+             * 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 minimum width of the horizontal Carousel container to support
+             * the navigation buttons.
+             *
+             * @property HORZ_MIN_WIDTH
+             * @default 180
+             */
+            HORZ_MIN_WIDTH: 180,
+
+            /**
+             * 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 vertical Carousel container to support
+             * the navigation buttons.
+             *
+             * @property VERT_MIN_WIDTH
+             * @default 99
+             */
+            VERT_MIN_WIDTH: 99,
+
+            /**
+             * The number of visible items in the Carousel.
+             *
+             * @property NUM_VISIBLE
+             * @default 3
+             */
+            NUM_VISIBLE: 3
+
+        },
+
+        /*
+         * Internationalizable strings in the Carousel component
+         */
+
+        STRINGS: {
+
+            /**
+             * The content to be used as the progress indicator when the item
+             * is still being loaded.
+             *
+             * @property ITEM_LOADING_CONTENT
+             * @default "Loading"
+             */
+            ITEM_LOADING_CONTENT: "Loading",
+
+            /**
+             * 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 carousel = this,
+                className,
+                content,
+                elId,
+                numItems = carousel.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 {
+                return false;
+            }
+
+            className = item.className || "";
+            elId      = item.id ? item.id : Dom.generateId();
+
+            if (JS.isUndefined(index)) {
+                carousel._itemsTable.items.push({
+                        item      : content,
+                        className : className,
+                        id        : elId
+                });
+            } else {
+                if (index < 0 || index >= numItems) {
+                    return false;
+                }
+                carousel._itemsTable.items.splice(index, 0, {
+                        item      : content,
+                        className : className,
+                        id        : elId
+                });
+            }
+            carousel._itemsTable.numItems++;
+
+            if (numItems < carousel._itemsTable.items.length) {
+                carousel.set("numItems", carousel._itemsTable.items.length);
+            }
+
+            carousel.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 carousel = this, n = carousel.get("numItems");
+
+            while (n > 0) {
+                if (!carousel.removeItem(0)) {
+                }
+                /*
+                    For dynamic loading, the numItems may be much larger than
+                    the actual number of items in the table.  So, set the
+                    numItems to zero, and break out of the loop if the table
+                    is already empty.
+                 */
+                if (carousel._itemsTable.numItems === 0) {
+                    carousel.set("numItems", 0);
+                    break;
+                }
+                n--;
+            }
+
+            carousel.fireEvent(allItemsRemovedEvent);
+        },
+
+        /**
+         * Set focus on the Carousel.
+         *
+         * @method focus
+         * @public
+         */
+        focus: function () {
+            var carousel = this,
+                first,
+                focusEl,
+                isSelectionInvisible,
+                itemsTable,
+                last,
+                numVisible,
+                selectOnScroll,
+                selected,
+                selItem;
+
+            // Don't do anything if the Carousel is not rendered
+            if (!carousel._hasRendered) {
+                return;
+            }
+
+            if (carousel.isAnimating()) {
+                // this messes up real bad!
+                return;
+            }
+
+            selItem              = carousel.get("selectedItem");
+            numVisible           = carousel.get("numVisible");
+            selectOnScroll       = carousel.get("selectOnScroll");
+            selected             = (selItem >= 0) ?
+                                   carousel.getItem(selItem) : null;
+            first                = carousel.get("firstVisible");
+            last                 = first + numVisible - 1;
+            isSelectionInvisible = (selItem < first || selItem > last);
+            focusEl              = (selected && selected.id) ?
+                                   Dom.get(selected.id) : null;
+            itemsTable           = carousel._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
+                }
+            }
+
+            carousel.fireEvent(focusEvent);
+        },
+
+        /**
+         * Hide the Carousel.
+         *
+         * @method hide
+         * @public
+         */
+        hide: function () {
+            var carousel = this;
+
+            if (carousel.fireEvent(beforeHideEvent) !== false) {
+                carousel.removeClass(carousel.CLASSES.VISIBLE);
+                carousel.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 carousel = this,
+                elId     = el,  // save for a rainy day
+                parse    = false;
+
+            if (!el) {
+                return;
+            }
+
+            carousel._hasRendered = false;
+            carousel._navBtns     = { prev: [], next: [] };
+            carousel._pages       = { el: null, num: 0, cur: 0 };
+            carousel._itemsTable  = { loading: {}, numItems: 0,
+                                      items: [], size: 0 };
+
+
+            if (JS.isString(el)) {
+                el = Dom.get(el);
+            } else if (!el.nodeName) {
+                return;
+            }
+
+            Carousel.superclass.init.call(carousel, el, attrs);
+
+            if (el) {
+                if (!el.id) {   // in case the HTML element is passed
+                    el.setAttribute("id", Dom.generateId());
+                }
+                parse = carousel._parseCarousel(el);
+                if (!parse) {
+                    carousel._createCarousel(elId);
+                }
+            } else {
+                el = carousel._createCarousel(elId);
+            }
+            elId = el.id;
+
+            carousel.initEvents();
+
+            if (parse) {
+                carousel._parseCarouselItems();
+            }
+
+            if (!attrs || typeof attrs.isVertical == "undefined") {
+                carousel.set("isVertical", false);
+            }
+
+            carousel._parseCarouselNavigation(el);
+            carousel._navEl = carousel._setupCarouselNavigation();
+
+            instances[elId] = { object: carousel };
+
+            carousel._loadItems();
+        },
+
+        /**
+         * 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) {
+            var carousel = this;
+
+            attrs = attrs || {};
+            Carousel.superclass.initAttributes.call(carousel, attrs);
+
+            /**
+             * @attribute carouselEl
+             * @description The type of the Carousel element.
+             * @default OL
+             * @type Boolean
+             */
+            carousel.setAttributeConfig("carouselEl", {
+                    validator : JS.isString,
+                    value     : attrs.carouselEl || "OL"
+            });
+
+            /**
+             * @attribute carouselItemEl
+             * @description The type of the list of items within the Carousel.
+             * @default LI
+             * @type Boolean
+             */
+            carousel.setAttributeConfig("carouselItemEl", {
+                    validator : JS.isString,
+                    value     : attrs.carouselItemEl || "LI"
+            });
+
+            /**
+             * @attribute currentPage
+             * @description The current page number (read-only.)
+             * @type Number
+             */
+            carousel.setAttributeConfig("currentPage", {
+                    readOnly : true,
+                    value    : 0
+            });
+
+            /**
+             * @attribute firstVisible
+             * @description The index to start the Carousel from (indexes begin
+             * from zero)
+             * @default 0
+             * @type Number
+             */
+            carousel.setAttributeConfig("firstVisible", {
+                    method    : carousel._setFirstVisible,
+                    validator : carousel._validateFirstVisible,
+                    value     :
+                        attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
+            });
+
+            /**
+             * @attribute selectOnScroll
+             * @description Set this to true to automatically set focus to
+             * follow scrolling in the Carousel.
+             * @default true
+             * @type Boolean
+             */
+            carousel.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
+             */
+            carousel.setAttributeConfig("numVisible", {
+                    method    : carousel._setNumVisible,
+                    validator : carousel._validateNumVisible,
+                    value     : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
+            });
+
+            /**
+             * @attribute numItems
+             * @description The number of items in the Carousel.
+             * @type Number
+             */
+            carousel.setAttributeConfig("numItems", {
+                    method    : carousel._setNumItems,
+                    validator : carousel._validateNumItems,
+                    value     : carousel._itemsTable.numItems
+            });
+
+            /**
+             * @attribute scrollIncrement
+             * @description The number of items to scroll by for arrow keys.
+             * @default 1
+             * @type Number
+             */
+            carousel.setAttributeConfig("scrollIncrement", {
+                    validator : carousel._validateScrollIncrement,
+                    value     : attrs.scrollIncrement || 1
+            });
+
+            /**
+             * @attribute selectedItem
+             * @description The index of the selected item.
+             * @type Number
+             */
+            carousel.setAttributeConfig("selectedItem", {
+                    method    : carousel._setSelectedItem,
+                    validator : JS.isNumber,
+                    value     : -1
+            });
+
+            /**
+             * @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
+             */
+            carousel.setAttributeConfig("revealAmount", {
+                    method    : carousel._setRevealAmount,
+                    validator : carousel._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
+             */
+            carousel.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
+             */
+            carousel.setAttributeConfig("isVertical", {
+                    method    : carousel._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
+             */
+            carousel.setAttributeConfig("navigation", {
+                    method    : carousel._setNavigation,
+                    validator : carousel._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
+             */
+            carousel.setAttributeConfig("animation", {
+                    validator : carousel._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
+             * @deprecated Use autoPlayInterval instead.
+             */
+            carousel.setAttributeConfig("autoPlay", {
+                    validator : JS.isNumber,
+                    value     : attrs.autoPlay || 0
+            });
+
+            /**
+             * @attribute autoPlayInterval
+             * @description The delay in milli-seconds for scrolling the
+             * Carousel during auto-play.
+             * Note: The startAutoPlay() method needs to be invoked to trigger
+             * automatic scrolling of Carousel.
+             * @type Number
+             */
+            carousel.setAttributeConfig("autoPlayInterval", {
+                    validator : JS.isNumber,
+                    value     : attrs.autoPlayInterval || 0
+            });
+        },
+
+        /**
+         * Initialize and bind the event handlers.
+         *
+         * @method initEvents
+         * @public
+         */
+        initEvents: function () {
+            var carousel = this,
+                cssClass = carousel.CLASSES,
+                focussedLi;
+
+            carousel.on("keydown", carousel._keyboardEventHandler);
+
+            carousel.on(afterScrollEvent, syncNavigation);
+
+            carousel.on(itemAddedEvent, syncUi);
+
+            carousel.on(itemRemovedEvent, syncUi);
+
+            carousel.on(itemSelectedEvent, function () {
+                if (carousel._hasFocus) {
+                    carousel.focus();
+                }
+            });
+
+            carousel.on(loadItemsEvent, syncUi);
+
+            carousel.on(allItemsRemovedEvent, function (ev) {
+                carousel.scrollTo(0);
+                syncNavigation.call(carousel);
+                syncPagerUi.call(carousel);
+            });
+
+            carousel.on(pageChangeEvent, syncPagerUi, carousel);
+
+            carousel.on(renderEvent, function (ev) {
+                carousel.set("selectedItem", carousel.get("firstVisible"));
+                syncNavigation.call(carousel, ev);
+                syncPagerUi.call(carousel, ev);
+                carousel._setClipContainerSize();
+            });
+
+            carousel.on("selectedItemChange", function (ev) {
+                setItemSelection.call(carousel, ev.newValue, ev.prevValue);
+                if (ev.newValue >= 0) {
+                    carousel._updateTabIndex(
+                            carousel.getElementForItem(ev.newValue));
+                }
+                carousel.fireEvent(itemSelectedEvent, ev.newValue);
+            });
+
+            carousel.on(uiUpdateEvent, function (ev) {
+                syncNavigation.call(carousel, ev);
+                syncPagerUi.call(carousel, ev);
+            });
+
+            carousel.on("firstVisibleChange", function (ev) {
+                if (!carousel.get("selectOnScroll")) {
+                    if (ev.newValue >= 0) {
+                        carousel._updateTabIndex(
+                                carousel.getElementForItem(ev.newValue));
+                    }
+                }
+            });
+
+            // Handle item selection on mouse click
+            carousel.on("click", function (ev) {
+                if (carousel.isAutoPlayOn()) {
+                    carousel.stopAutoPlay();
+                }
+                carousel._itemClickHandler(ev);
+                carousel._pagerClickHandler(ev);
+            });
+
+            // Restore the focus on the navigation buttons
+
+            Event.onFocus(carousel.get("element"), function (ev, obj) {
+                var target = Event.getTarget(ev);
+
+                if (target && target.nodeName.toUpperCase() == "A" &&
+                    Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
+                    if (focussedLi) {
+                        Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
+                    }
+                    focussedLi = target.parentNode;
+                    Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
+                } else {
+                    if (focussedLi) {
+                        Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
+                    }
+                }
+
+                obj._hasFocus = true;
+                obj._updateNavButtons(Event.getTarget(ev), true);
+            }, carousel);
+
+            Event.onBlur(carousel.get("element"), function (ev, obj) {
+                obj._hasFocus = false;
+                obj._updateNavButtons(Event.getTarget(ev), false);
+            }, carousel);
+        },
+
+        /**
+         * Return true if the Carousel is still animating, or false otherwise.
+         *
+         * @method isAnimating
+         * @return {Boolean} Return true if animation is still in progress, or
+         * false otherwise.
+         * @public
+         */
+        isAnimating: function () {
+            return this._isAnimationInProgress;
+        },
+
+        /**
+         * Return true if the auto-scrolling of Carousel is "on", or false
+         * otherwise.
+         *
+         * @method isAutoPlayOn
+         * @return {Boolean} Return true if autoPlay is "on", or false
+         * otherwise.
+         * @public
+         */
+        isAutoPlayOn: function () {
+            return this._isAutoPlayInProgress;
+        },
+
+        /**
+         * Return the carouselItemEl 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) {
+            var carousel = this;
+
+            if (index < 0 || index >= carousel.get("numItems")) {
+                return null;
+            }
+
+            // TODO: may be cache the item
+            if (carousel._itemsTable.numItems > index) {
+                if (!JS.isUndefined(carousel._itemsTable.items[index])) {
+                    return Dom.get(carousel._itemsTable.items[index].id);
+                }
+            }
+
+            return null;
+        },
+
+        /**
+         * Return the carouselItemEl for all items in the Carousel.
+         *
+         * @method getElementForItems
+         * @return {Array} Return all the items
+         * @public
+         */
+        getElementForItems: function () {
+            var carousel = this, els = [], i;
+
+            for (i = 0; i < carousel._itemsTable.numItems; i++) {
+                els.push(carousel.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) {
+            var carousel = this;
+
+            if (index < 0 || index >= carousel.get("numItems")) {
+                return null;
+            }
+
+            if (carousel._itemsTable.numItems > index) {
+                if (!JS.isUndefined(carousel._itemsTable.items[index])) {
+                    return carousel._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 carousel = this, i = 0, n = carousel._itemsTable.numItems;
+
+            while (i < n) {
+                if (!JS.isUndefined(carousel._itemsTable.items[i])) {
+                    if (carousel._itemsTable.items[i].id == id) {
+                        return i;
+                    }
+                }
+                i++;
+            }
+
+            return -1;
+        },
+
+        /**
+         * Return all visible items as an array.
+         *
+         * @method getVisibleItems
+         * @return {Array} The array of visible items
+         * @public
+         */
+        getVisibleItems: function () {
+            var carousel = this,
+                i        = carousel.get("firstVisible"),
+                n        = i + carousel.get("numVisible"),
+                r        = [];
+
+            while (i < n) {
+                r.push(carousel.getElementForItem(i));
+                i++;
+            }
+
+            return r;
+        },
+
+        /**
+         * 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 carousel = this,
+                item,
+                num      = carousel.get("numItems");
+
+            if (index < 0 || index >= num) {
+                return false;
+            }
+
+            item = carousel._itemsTable.items.splice(index, 1);
+            if (item && item.length == 1) {
+                carousel._itemsTable.numItems--;
+                carousel.set("numItems", num - 1);
+
+                carousel.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 carousel = this,
+                cssClass = carousel.CLASSES;
+
+            carousel.addClass(cssClass.CAROUSEL);
+
+            if (!carousel._clipEl) {
+                carousel._clipEl = carousel._createCarouselClip();
+                carousel._clipEl.appendChild(carousel._carouselEl);
+            }
+
+            if (appendTo) {
+                carousel.appendChild(carousel._clipEl);
+                carousel.appendTo(appendTo);
+            } else {
+                if (!Dom.inDocument(carousel.get("element"))) {
+                    return false;
+                }
+                carousel.appendChild(carousel._clipEl);
+            }
+
+            if (carousel.get("isVertical")) {
+                carousel.addClass(cssClass.VERTICAL);
+            } else {
+                carousel.addClass(cssClass.HORIZONTAL);
+            }
+
+            if (carousel.get("numItems") < 1) {
+                return false;
+            }
+
+            carousel._refreshUi();
+
+            return true;
+        },
+
+        /**
+         * Scroll the Carousel by an item backward.
+         *
+         * @method scrollBackward
+         * @public
+         */
+        scrollBackward: function () {
+            var carousel = this;
+
+            carousel.scrollTo(carousel._firstItem -
+                              carousel.get("scrollIncrement"));
+        },
+
+        /**
+         * Scroll the Carousel by an item forward.
+         *
+         * @method scrollForward
+         * @public
+         */
+        scrollForward: function () {
+            var carousel = this;
+
+            carousel.scrollTo(carousel._firstItem +
+                              carousel.get("scrollIncrement"));
+        },
+
+        /**
+         * Scroll the Carousel by a page backward.
+         *
+         * @method scrollPageBackward
+         * @public
+         */
+        scrollPageBackward: function () {
+            var carousel = this,
+                item     = carousel._firstItem - carousel.get("numVisible");
+
+            if (carousel.get("selectOnScroll")) {
+                carousel._selectedItem = carousel._getSelectedItem(item);
+            } else {
+                item = carousel._getValidIndex(item);
+            }
+            carousel.scrollTo(item);
+        },
+
+        /**
+         * Scroll the Carousel by a page forward.
+         *
+         * @method scrollPageForward
+         * @public
+         */
+        scrollPageForward: function () {
+            var carousel = this,
+                item     = carousel._firstItem + carousel.get("numVisible");
+
+            if (carousel.get("selectOnScroll")) {
+                carousel._selectedItem = carousel._getSelectedItem(item);
+            } else {
+                item = carousel._getValidIndex(item);
+            }
+            carousel.scrollTo(item);
+        },
+
+        /**
+         * 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 carousel   = this,
+                animate, animCfg, isCircular, delta, direction, firstItem,
+                numItems, numPerPage, offset, page, rv, sentinel,
+                stopAutoScroll;
+
+            if (JS.isUndefined(item) || item == carousel._firstItem ||
+                carousel.isAnimating()) {
+                return;         // nothing to do!
+            }
+
+            animCfg        = carousel.get("animation");
+            isCircular     = carousel.get("isCircular");
+            firstItem      = carousel._firstItem;
+            numItems       = carousel.get("numItems");
+            numPerPage     = carousel.get("numVisible");
+            page           = carousel.get("currentPage");
+            stopAutoScroll = function () {
+                if (carousel.isAutoPlayOn()) {
+                    carousel.stopAutoPlay();
+                }
+            };
+
+            if (item < 0) {
+                if (isCircular) {
+                    item = numItems + item;
+                } else {
+                    stopAutoScroll.call(carousel);
+                    return;
+                }
+            } else if (numItems > 0 && item > numItems - 1) {
+                if (carousel.get("isCircular")) {
+                    item = numItems - item;
+                } else {
+                    stopAutoScroll.call(carousel);
+                    return;
+                }
+            }
+
+            direction = (carousel._firstItem > item) ? "backward" : "forward";
+
+            sentinel  = firstItem + numPerPage;
+            sentinel  = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
+            rv = carousel.fireEvent(beforeScrollEvent,
+                    { dir: direction, first: firstItem, last: sentinel });
+            if (rv === false) { // scrolling is prevented
+                return;
+            }
+
+            carousel.fireEvent(beforePageChangeEvent, { page: page });
+
+            delta = firstItem - item; // yes, the delta is reverse
+            carousel._firstItem = item;
+            carousel.set("firstVisible", item);
+
+
+            carousel._loadItems(); // do we have all the items to display?
+
+            sentinel  = item + numPerPage;
+            sentinel  = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
+
+            offset    = getScrollOffset.call(carousel, delta);
+
+            animate   = animCfg.speed > 0;
+
+            if (animate) {
+                carousel._animateAndSetCarouselOffset(offset, item, sentinel,
+                        dontSelect);
+            } else {
+                carousel._setCarouselOffset(offset);
+                updateStateAfterScroll.call(carousel, item, sentinel);
+            }
+        },
+
+        /**
+         * Select the previous item in the Carousel.
+         *
+         * @method selectPreviousItem
+         * @public
+         */
+        selectPreviousItem: function () {
+            var carousel = this,
+                newpos   = 0,
+                selected = carousel.get("selectedItem");
+
+            if (selected == this._firstItem) {
+                newpos = selected - carousel.get("numVisible");
+                carousel._selectedItem = carousel._getSelectedItem(selected-1);
+                carousel.scrollTo(newpos);
+            } else {
+                newpos = carousel.get("selectedItem") -
+                         carousel.get("scrollIncrement");
+                carousel.set("selectedItem",carousel._getSelectedItem(newpos));
+            }
+        },
+
+        /**
+         * Select the next item in the Carousel.
+         *
+         * @method selectNextItem
+         * @public
+         */
+        selectNextItem: function () {
+            var carousel = this, newpos = 0;
+
+            newpos = carousel.get("selectedItem") +
+                     carousel.get("scrollIncrement");
+            carousel.set("selectedItem", carousel._getSelectedItem(newpos));
+        },
+
+        /**
+         * Display the Carousel.
+         *
+         * @method show
+         * @public
+         */
+        show: function () {
+            var carousel = this,
+                cssClass = carousel.CLASSES;
+
+            if (carousel.fireEvent(beforeShowEvent) !== false) {
+                carousel.addClass(cssClass.VISIBLE);
+                carousel.fireEvent(showEvent);
+            }
+        },
+
+        /**
+         * Start auto-playing the Carousel.
+         *
+         * @method startAutoPlay
+         * @public
+         */
+        startAutoPlay: function () {
+            var carousel = this, timer;
+
+            if (JS.isUndefined(carousel._autoPlayTimer)) {
+                timer = carousel.get("autoPlayInterval");
+                if (timer <= 0) {
+                    return;
+                }
+                carousel._isAutoPlayInProgress = true;
+                carousel.fireEvent(startAutoPlayEvent);
+                carousel._autoPlayTimer = setTimeout(function () {
+                    carousel._autoScroll();
+                }, timer);
+            }
+        },
+
+        /**
+         * Stop auto-playing the Carousel.
+         *
+         * @method stopAutoPlay
+         * @public
+         */
+        stopAutoPlay: function () {
+            var carousel = this;
+
+            if (!JS.isUndefined(carousel._autoPlayTimer)) {
+                clearTimeout(carousel._autoPlayTimer);
+                delete carousel._autoPlayTimer;
+                carousel._isAutoPlayInProgress = false;
+                carousel.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
+         */
+
+        /**
+         * Set the Carousel offset to the passed offset after animating.
+         *
+         * @method _animateAndSetCarouselOffset
+         * @param {Integer} offset The offset to which the Carousel has to be
+         * scrolled to.
+         * @param {Integer} item The index to which the Carousel will scroll.
+         * @param {Integer} sentinel The last element in the view port.
+         * @protected
+         */
+        _animateAndSetCarouselOffset: function (offset, item, sentinel) {
+            var carousel = this,
+                animCfg  = carousel.get("animation"),
+                animObj  = null;
+
+            if (carousel.get("isVertical")) {
+                animObj = new YAHOO.util.Motion(carousel._carouselEl,
+                        { points: { by: [0, offset] } },
+                        animCfg.speed, animCfg.effect);
+            } else {
+                animObj = new YAHOO.util.Motion(carousel._carouselEl,
+                        { points: { by: [offset, 0] } },
+                        animCfg.speed, animCfg.effect);
+            }
+
+            carousel._isAnimationInProgress = true;
+            animObj.onComplete.subscribe(carousel._animationCompleteHandler,
+                                         { scope: carousel, item: item,
+                                           last: sentinel });
+            animObj.animate();
+        },
+
+        /**
+         * Handle the animation complete event.
+         *
+         * @method _animationCompleteHandler
+         * @param {Event} ev The event.
+         * @param {Array} p The event parameters.
+         * @param {Object} o The object that has the state of the Carousel
+         * @protected
+         */
+        _animationCompleteHandler: function (ev, p, o) {
+            o.scope._isAnimationInProgress = false;
+            updateStateAfterScroll.call(o.scope, o.item, o.last);
+        },
+
+        /**
+         * Automatically scroll the contents of the Carousel.
+         * @method _autoScroll
+         * @protected
+         */
+        _autoScroll: function() {
+            var carousel  = this,
+                currIndex = carousel._firstItem,
+                index;
+
+            if (currIndex >= carousel.get("numItems") - 1) {
+                if (carousel.get("isCircular")) {
+                    index = 0;
+                } else {
+                    carousel.stopAutoPlay();
+                }
+            } else {
+                index = currIndex + carousel.get("numVisible");
+            }
+
+            carousel._selectedItem = carousel._getSelectedItem(index);
+            carousel.scrollTo.call(carousel, index);
+        },
+
+        /**
+         * Create the Carousel.
+         *
+         * @method createCarousel
+         * @param elId {String} The id of the element to be created
+         * @protected
+         */
+        _createCarousel: function (elId) {
+            var carousel = this,
+                cssClass = carousel.CLASSES,
+                el       = Dom.get(elId);
+
+            if (!el) {
+                el = createElement("DIV", {
+                        className : cssClass.CAROUSEL,
+                        id        : elId
+                });
+            }
+
+            if (!carousel._carouselEl) {
+                carousel._carouselEl=createElement(carousel.get("carouselEl"),
+                        { className: cssClass.CAROUSEL_EL });
+            }
+
+            return el;
+        },
+
+        /**
+         * Create the Carousel clip container.
+         *
+         * @method createCarouselClip
+         * @protected
+         */
+        _createCarouselClip: function () {
+            return createElement("DIV", { className: this.CLASSES.CONTENT });
+        },
+
+        /**
+         * Create the Carousel item.
+         *
+         * @method createCarouselItem
+         * @param obj {Object} The attributes of the element to be created
+         * @protected
+         */
+        _createCarouselItem: function (obj) {
+            return createElement(this.get("carouselItemEl"), {
+                    className : obj.className,
+                    content   : obj.content,
+                    id        : obj.id
+            });
+        },
+
+        /**
+         * Return a valid item for a possibly out of bounds index considering
+         * the isCircular property.
+         *
+         * @method _getValidIndex
+         * @param index {Number} The index of the item to be returned
+         * @return {Object} Return a valid item index
+         * @protected
+         */
+        _getValidIndex: function (index) {
+            var carousel   = this,
+                isCircular = carousel.get("isCircular"),
+                numItems   = carousel.get("numItems"),
+                sentinel   = numItems - 1;
+
+            if (index < 0) {
+                index = isCircular ? numItems + index : 0;
+            } else if (index > sentinel) {
+                index = isCircular ? index - numItems : sentinel;
+            }
+
+            return index;
+        },
+
+        /**
+         * 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 carousel   = this,
+                isCircular = carousel.get("isCircular"),
+                numItems   = carousel.get("numItems"),
+                sentinel   = numItems - 1;
+
+            if (val < 0) {
+                if (isCircular) {
+                    val = numItems + val;
+                } else {
+                    val = carousel.get("selectedItem");
+                }
+            } else if (val > sentinel) {
+                if (isCircular) {
+                    val = val - numItems;
+                } else {
+                    val = carousel.get("selectedItem");
+                }
+            }
+
+            return val;
+        },
+
+        /**
+         * The "click" handler for the item.
+         *
+         * @method _itemClickHandler
+         * @param {Event} ev The event object
+         * @protected
+         */
+        _itemClickHandler: function (ev) {
+            var carousel  = this,
+                container = carousel.get("element"),
+                el,
+                item,
+                target    = YAHOO.util.Event.getTarget(ev);
+
+            while (target && target != container &&
+                   target.id != carousel._carouselEl) {
+                el = target.nodeName;
+                if (el.toUpperCase() == carousel.get("carouselItemEl")) {
+                    break;
+                }
+                target = target.parentNode;
+            }
+
+            if ((item = carousel.getItemPositionById(target.id)) >= 0) {
+                carousel.set("selectedItem", carousel._getSelectedItem(item));
+                carousel.focus();
+            }
+        },
+
+        /**
+         * The keyboard event handler for Carousel.
+         *
+         * @method _keyboardEventHandler
+         * @param ev {Event} The event that is being handled.
+         * @protected
+         */
+        _keyboardEventHandler: function (ev) {
+            var carousel = this,
+                key      = Event.getCharCode(ev),
+                prevent  = false;
+
+            if (carousel.isAnimating()) {
+                return;         // do not mess while animation is in progress
+            }
+
+            switch (key) {
+            case 0x25:          // left arrow
+            case 0x26:          // up arrow
+                carousel.selectPreviousItem();
+                prevent = true;
+                break;
+            case 0x27:          // right arrow
+            case 0x28:          // down arrow
+                carousel.selectNextItem();
+                prevent = true;
+                break;
+            case 0x21:          // page-up
+                carousel.scrollPageBackward();
+                prevent = true;
+                break;
+            case 0x22:          // page-down
+                carousel.scrollPageForward();
+                prevent = true;
+                break;
+            }
+
+            if (prevent) {
+                if (carousel.isAutoPlayOn()) {
+                    carousel.stopAutoPlay();
+                }
+                Event.preventDefault(ev);
+            }
+        },
+
+        /**
+         * The load the required set of items that are needed for display.
+         *
+         * @method _loadItems
+         * @protected
+         */
+        _loadItems: function() {
+            var carousel   = this,
+                first      = carousel.get("firstVisible"),
+                last       = 0,
+                numItems   = carousel.get("numItems"),
+                numVisible = carousel.get("numVisible"),
+                reveal     = carousel.get("revealAmount");
+
+            last = first + numVisible - 1 + (reveal ? 1 : 0);
+            last = last > numItems - 1 ? numItems - 1 : last;
+
+            if (!carousel.getItem(first) || !carousel.getItem(last)) {
+                carousel.fireEvent(loadItemsEvent, {
+                        ev: loadItemsEvent, first: first, last: last,
+                        num: last - first
+                });
+            }
+        },
+
+        /**
+         * The "click" handler for the pager navigation.
+         *
+         * @method _pagerClickHandler
+         * @param {Event} ev The event object
+         * @protected
+         */
+        _pagerClickHandler: function (ev) {
+            var carousel = this,
+                pos,
+                target   = Event.getTarget(ev),
+                val;
+
+            function getPagerNode(el) {
+                var itemEl = carousel.get("carouselItemEl");
+
+                if (el.nodeName.toUpperCase() == itemEl.toUpperCase()) {
+                    el = Dom.getChildrenBy(el, function (node) {
+                        // either an anchor or select at least
+                        return node.href || node.value;
+                    });
+                    if (el && el[0]) {
+                        return el[0];
+                    }
+                } else if (el.href || el.value) {
+                    return el;
+                }
+
+                return null;
+            }
+
+            if (target) {
+                target = getPagerNode(target);
+                if (!target) {
+                    return;
+                }
+                val = target.href || target.value;
+                if (JS.isString(val) && val) {
+                    pos = val.lastIndexOf("#");
+                    if (pos != -1) {
+                        val = carousel.getItemPositionById(
+                                val.substring(pos + 1));
+                        carousel._selectedItem = val;
+                        carousel.scrollTo(val);
+                        if (!target.value) { // not a select element
+                            carousel.focus();
+                        }
+                        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 carousel = this, child, cssClass, domEl, found, node;
+
+            cssClass  = carousel.CLASSES;
+            domEl     = carousel.get("carouselEl");
+            found     = false;
+
+            for (child = parent.firstChild; child; child = child.nextSibling) {
+                if (child.nodeType == 1) {
+                    node = child.nodeName;
+                    if (node.toUpperCase() == domEl) {
+                        carousel._carouselEl = child;
+                        Dom.addClass(carousel._carouselEl,
+                                     carousel.CLASSES.CAROUSEL_EL);
+                        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 carousel = this,
+                child,
+                domItemEl,
+                elId,
+                node,
+                parent   = carousel._carouselEl;
+
+            domItemEl = carousel.get("carouselItemEl");
+
+            for (child = parent.firstChild; child; child = child.nextSibling) {
+                if (child.nodeType == 1) {
+                    node = child.nodeName;
+                    if (node.toUpperCase() == domItemEl) {
+                        if (child.id) {
+                            elId = child.id;
+                        } else {
+                            elId = Dom.generateId();
+                            child.setAttribute("id", elId);
+                        }
+                        carousel.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 carousel = this,
+                cfg,
+                cssClass = carousel.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];
+                        if (el.nodeName == "INPUT" ||
+                            el.nodeName == "BUTTON") {
+                            carousel._navBtns.prev.push(el);
+                        } else {
+                            j = el.getElementsByTagName("INPUT");
+                            if (JS.isArray(j) && j.length > 0) {
+                                carousel._navBtns.prev.push(j[0]);
+                            } else {
+                                j = el.getElementsByTagName("BUTTON");
+                                if (JS.isArray(j) && j.length > 0) {
+                                    carousel._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];
+                        if (el.nodeName == "INPUT" ||
+                            el.nodeName == "BUTTON") {
+                            carousel._navBtns.next.push(el);
+                        } else {
+                            j = el.getElementsByTagName("INPUT");
+                            if (JS.isArray(j) && j.length > 0) {
+                                carousel._navBtns.next.push(j[0]);
+                            } else {
+                                j = el.getElementsByTagName("BUTTON");
+                                if (JS.isArray(j) && j.length > 0) {
+                                    carousel._navBtns.next.push(j[0]);
+                                }
+                            }
+                        }
+                    }
+                }
+                if (cfg) {
+                    cfg.next = nav;
+                } else {
+                    cfg = { next: nav };
+                }
+            }
+
+            if (cfg) {
+                carousel.set("navigation", cfg);
+                rv = true;
+            }
+
+            return rv;
+        },
+
+        /**
+         * Refresh the widget UI if it is not already rendered, on first item
+         * addition.
+         *
+         * @method _refreshUi
+         * @protected
+         */
+        _refreshUi: function () {
+            var carousel = this;
+
+            // Set the rendered state appropriately.
+            carousel._hasRendered = true;
+            carousel.fireEvent(renderEvent);
+        },
+
+        /**
+         * Set the Carousel offset to the passed offset.
+         *
+         * @method _setCarouselOffset
+         * @protected
+         */
+        _setCarouselOffset: function (offset) {
+            var carousel = this, which;
+
+            which   = carousel.get("isVertical") ? "top" : "left";
+            offset += offset !== 0 ? getStyle(carousel._carouselEl, which) : 0;
+            Dom.setStyle(carousel._carouselEl, which, offset + "px");
+        },
+
+        /**
+         * Setup/Create the Carousel navigation element (if needed).
+         *
+         * @method _setupCarouselNavigation
+         * @protected
+         */
+        _setupCarouselNavigation: function () {
+            var carousel = this,
+                btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
+
+            cssClass = carousel.CLASSES;
+
+            // TODO: can the _navBtns be tested against instead?
+            navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
+                    "DIV", carousel.get("element"));
+
+            if (navContainer.length === 0) {
+                navContainer = createElement("DIV",
+                        { className: cssClass.NAVIGATION });
+                carousel.insertBefore(navContainer,
+                        Dom.getFirstChild(carousel.get("element")));
+            } else {
+                navContainer = navContainer[0];
+            }
+
+            carousel._pages.el = createElement("UL");
+            navContainer.appendChild(carousel._pages.el);
+
+            nav = carousel.get("navigation");
+            if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
+                if (JS.isString(nav.prev)) {
+                    nav.prev = [nav.prev];
+                }
+                for (btn in nav.prev) {
+                    if (nav.prev.hasOwnProperty(btn)) {
+                        carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
+                    }
+                }
+            } 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 = "<button type=\"button\" "      +
+                        "id=\"" + btn + "\" name=\""                   +
+                        carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">"  +
+                        carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>";
+                navContainer.appendChild(prevButton);
+                btn = Dom.get(btn);
+                carousel._navBtns.prev = [btn];
+                cfg = { prev: [prevButton] };
+            }
+
+            if (JS.isString(nav.next) || JS.isArray(nav.next)) {
+                if (JS.isString(nav.next)) {
+                    nav.next = [nav.next];
+                }
+                for (btn in nav.next) {
+                    if (nav.next.hasOwnProperty(btn)) {
+                        carousel._navBtns.next.push(Dom.get(nav.next[btn]));
+                    }
+                }
+            } else {
+                // TODO: separate method for creating a navigation button
+                nextButton = createElement("SPAN",
+                        { className: cssClass.BUTTON + cssClass.NEXT_NAV });
+                // XXX: for IE 6.x
+                Dom.setStyle(nextButton, "visibility", "visible");
+                btn = Dom.generateId();
+                nextButton.innerHTML = "<button type=\"button\" "      +
+                        "id=\"" + btn + "\" name=\""                   +
+                        carousel.STRINGS.NEXT_BUTTON_TEXT + "\">"      +
+                        carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>";
+                navContainer.appendChild(nextButton);
+                btn = Dom.get(btn);
+                carousel._navBtns.next = [btn];
+                if (cfg) {
+                    cfg.next = [nextButton];
+                } else {
+                    cfg = { next: [nextButton] };
+                }
+            }
+
+            if (cfg) {
+                carousel.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 carousel = this,
+                attr, currVal, isVertical, itemSize, reveal, size, which;
+
+            isVertical = carousel.get("isVertical");
+            reveal     = carousel.get("revealAmount");
+            which      = isVertical ? "height" : "width";
+            attr       = isVertical ? "top" : "left";
+
+            clip       = clip || carousel._clipEl;
+            if (!clip) {
+                return;
+            }
+
+            num        = num  || carousel.get("numVisible");
+            itemSize   = getCarouselItemSize.call(carousel, which);
+            size       = itemSize * num;
+
+            // TODO: try to re-use the _hasRendered indicator
+            carousel._recomputeSize = (size === 0); // bleh!
+            if (carousel._recomputeSize) {
+                carousel._hasRendered = false;
+                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(carousel._carouselEl, attr));
+                currVal = JS.isNumber(currVal) ? currVal : 0;
+                Dom.setStyle(carousel._carouselEl,
+                             attr, currVal + (reveal / 2) + "px");
+            }
+
+            if (isVertical) {
+                size += getStyle(carousel._carouselEl, "marginTop")        +
+                        getStyle(carousel._carouselEl, "marginBottom")     +
+                        getStyle(carousel._carouselEl, "paddingTop")       +
+                        getStyle(carousel._carouselEl, "paddingBottom")    +
+                        getStyle(carousel._carouselEl, "borderTopWidth")   +
+                        getStyle(carousel._carouselEl, "borderBottomWidth");
+                // XXX: for vertical Carousel
+                Dom.setStyle(clip, which, (size - (num - 1)) + "px");
+            } else {
+                size += getStyle(carousel._carouselEl, "marginLeft")      +
+                        getStyle(carousel._carouselEl, "marginRight")     +
+                        getStyle(carousel._carouselEl, "paddingLeft")     +
+                        getStyle(carousel._carouselEl, "paddingRight")    +
+                        getStyle(carousel._carouselEl, "borderLeftWidth") +
+                        getStyle(carousel._carouselEl, "borderRightWidth");
+                Dom.setStyle(clip, which, size + "px");
+            }
+
+            carousel._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 carousel = this,
+                config   = carousel.CONFIG,
+                cssClass = carousel.CLASSES,
+                isVertical,
+                size;
+
+            isVertical = carousel.get("isVertical");
+            clip       = clip || carousel._clipEl;
+            attr       = attr || (isVertical ? "height" : "width");
+            size       = parseFloat(Dom.getStyle(clip, attr), 10);
+
+            size = JS.isNumber(size) ? size : 0;
+
+            if (isVertical) {
+                size += getStyle(carousel._carouselEl, "marginTop")         +
+                        getStyle(carousel._carouselEl, "marginBottom")      +
+                        getStyle(carousel._carouselEl, "paddingTop")        +
+                        getStyle(carousel._carouselEl, "paddingBottom")     +
+                        getStyle(carousel._carouselEl, "borderTopWidth")    +
+                        getStyle(carousel._carouselEl, "borderBottomWidth") +
+                        getStyle(carousel._navEl, "height");
+            } else {
+                size += getStyle(clip, "marginLeft")                    +
+                        getStyle(clip, "marginRight")                   +
+                        getStyle(clip, "paddingLeft")                   +
+                        getStyle(clip, "paddingRight")                  +
+                        getStyle(clip, "borderLeftWidth")               +
+                        getStyle(clip, "borderRightWidth");
+            }
+
+            if (!isVertical) {
+                if (size < config.HORZ_MIN_WIDTH) {
+                    size = config.HORZ_MIN_WIDTH;
+                    carousel.addClass(cssClass.MIN_WIDTH);
+                }
+            }
+            carousel.setStyle(attr,  size + "px");
+
+            // Additionally the width of the container should be set for
+            // the vertical Carousel
+            if (isVertical) {
+                size = getCarouselItemSize.call(carousel, "width");
+                if (size < config.VERT_MIN_WIDTH) {
+                    size = config.VERT_MIN_WIDTH;
+                    carousel.addClass(cssClass.MIN_WIDTH);
+                }
+                carousel.setStyle("width",  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) {
+            var carousel = this;
+
+            if (val >= 0 && val < carousel.get("numItems")) {
+                carousel.scrollTo(val);
+            } else {
+                val = carousel.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) {
+            var carousel = this;
+
+            if (cfg.prev) {
+                Event.on(cfg.prev, "click", scrollPageBackward, carousel);
+            }
+            if (cfg.next) {
+                Event.on(cfg.next, "click", scrollPageForward, carousel);
+            }
+        },
+
+        /**
+         * 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) {
+            var carousel = this;
+
+            carousel._setClipContainerSize(carousel._clipEl, 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 carousel = this,
+                num      = carousel._itemsTable.numItems;
+
+            if (JS.isArray(carousel._itemsTable.items)) {
+                if (carousel._itemsTable.items.length != num) { // out of sync
+                    num = carousel._itemsTable.items.length;
+                    carousel._itemsTable.numItems = num;
+                }
+            }
+
+            if (val < num) {
+                while (num > val) {
+                    carousel.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 carousel = this,
+                cssClass = carousel.CLASSES;
+
+            if (val) {
+                carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
+            } else {
+                carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
+            }
+            carousel._itemsTable.size = 0; // force recalculation next time
+            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) {
+            var carousel = this;
+
+            if (val >= 0 && val <= 100) {
+                val = parseInt(val, 10);
+                val = JS.isNumber(val) ? val : 0;
+                carousel._setClipContainerSize();
+            } else {
+                val = carousel.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 UI after an item is added.
+         *
+         * @method _syncUiForItemAdd
+         * @protected
+         */
+        _syncUiForItemAdd: function (obj) {
+            var carousel   = this,
+                carouselEl = carousel._carouselEl,
+                el,
+                item,
+                itemsTable = carousel._itemsTable,
+                oel,
+                pos,
+                sibling;
+
+            pos  = JS.isUndefined(obj.pos) ? itemsTable.numItems - 1 : obj.pos;
+            if (!JS.isUndefined(itemsTable.items[pos])) {
+                item = itemsTable.items[pos];
+                if (item && !JS.isUndefined(item.id)) {
+                    oel  = Dom.get(item.id);
+                }
+            }
+            if (!oel) {
+                el = carousel._createCarouselItem({
+                        className : item.className,
+                        content   : item.item,
+                        id        : item.id
+                });
+                if (JS.isUndefined(obj.pos)) {
+                    if (!JS.isUndefined(itemsTable.loading[pos])) {
+                        oel = itemsTable.loading[pos];
+                        // if oel is null, it is a problem ...
+                    }
+                    if (oel) {
+                        // replace the node
+                        carouselEl.replaceChild(el, oel);
+                        // ... and remove the item from the data structure
+                        delete itemsTable.loading[pos];
+                    } else {
+                        carouselEl.appendChild(el);
+                    }
+                } else {
+                    if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
+                        sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
+                    }
+                    if (sibling) {
+                        carouselEl.insertBefore(el, sibling);
+                    } else {
+                    }
+                }
+            } else {
+                if (JS.isUndefined(obj.pos)) {
+                    if (!Dom.isAncestor(carousel._carouselEl, oel)) {
+                        carouselEl.appendChild(oel);
+                    }
+                } else {
+                    if (!Dom.isAncestor(carouselEl, oel)) {
+                        if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
+                            carouselEl.insertBefore(oel,
+                                    Dom.get(itemsTable.items[obj.pos + 1].id));
+                        }
+                    }
+                }
+            }
+
+            if (!carousel._hasRendered) {
+                carousel._refreshUi();
+            }
+
+            if (carousel.get("selectedItem") < 0) {
+                carousel.set("selectedItem", carousel.get("firstVisible"));
+            }
+        },
+
+        /**
+         * Synchronize and redraw the UI after an item is removed.
+         *
+         * @method _syncUiForItemAdd
+         * @protected
+         */
+        _syncUiForItemRemove: function (obj) {
+            var carousel   = this,
+                carouselEl = carousel._carouselEl,
+                el, item, num, pos;
+
+            num  = carousel.get("numItems");
+            item = obj.item;
+            pos  = obj.pos;
+
+            if (item && (el = Dom.get(item.id))) {
+                if (el && Dom.isAncestor(carouselEl, el)) {
+                    Event.purgeElement(el, true);
+                    carouselEl.removeChild(el);
+                }
+
+                if (carousel.get("selectedItem") == pos) {
+                    pos = pos >= num ? num - 1 : pos;
+                    carousel.set("selectedItem", pos);
+                }
+            } else {
+            }
+        },
+
+        /**
+         * Synchronize and redraw the UI for lazy loading.
+         *
+         * @method _syncUiForLazyLoading
+         * @protected
+         */
+        _syncUiForLazyLoading: function (obj) {
+            var carousel   = this,
+                carouselEl = carousel._carouselEl,
+                el,
+                i,
+                itemsTable = carousel._itemsTable,
+                sibling;
+
+            for (i = obj.first; i <= obj.last; i++) {
+                el = carousel._createCarouselItem({
+                        className : carousel.CLASSES.ITEM_LOADING,
+                        content   : carousel.STRINGS.ITEM_LOADING_CONTENT,
+                        id        : Dom.generateId()
+                });
+                if (el) {
+                    if (!JS.isUndefined(itemsTable.items[obj.last + 1])) {
+                        sibling = Dom.get(itemsTable.items[obj.last + 1].id);
+                        if (sibling) {
+                            carouselEl.insertBefore(el, sibling);
+                        } else {
+                        }
+                    } else {
+                        carouselEl.appendChild(el);
+                    }
+                }
+                itemsTable.loading[i] = el;
+            }
+        },
+
+        /**
+         * 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() == "BUTTON" &&
+                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);
+                }
+            }
+        },
+
+        /**
+         * Update the UI for the pager buttons based on the current page and
+         * the number of pages.
+         *
+         * @method _updatePagerButtons
+         * @protected
+         */
+        _updatePagerButtons: function () {
+            var carousel = this,
+                css      = carousel.CLASSES,
+                cur      = carousel._pages.cur, // current page
+                el,
+                html,
+                i,
+                item,
+                n        = carousel.get("numVisible"),
+                num      = carousel._pages.num, // total pages
+                pager    = carousel._pages.el;  // the pager container element
+
+            if (num === 0 || !pager) {
+                return;         // don't do anything if number of pages is 0
+            }
+
+            // Hide the pager before redrawing it
+            Dom.setStyle(pager, "visibility", "hidden");
+
+            // Remove all nodes from the pager
+            while (pager.firstChild) {
+                pager.removeChild(pager.firstChild);
+            }
+
+            for (i = 0; i < num; i++) {
+                if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
+                    Dom.setStyle(pager, "visibility", "visible");
+                    break;
+                }
+                item = carousel._itemsTable.items[i * n].id;
+
+                el   = document.createElement("LI");
+                if (!el) {
+                    Dom.setStyle(pager, "visibility", "visible");
+                    break;
+                }
+
+                if (i === 0) {
+                    Dom.addClass(el, css.FIRST_PAGE);
+                }
+                if (i == cur) {
+                    Dom.addClass(el, css.SELECTED_NAV);
+                }
+
+                // TODO: use a template string for i18N compliance
+                html = "<a href=\"#" + item + "\" tabindex=\"0\"><em>"   +
+                        carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
+                        "</em></a>";
+                el.innerHTML = html;
+
+                pager.appendChild(el);
+            }
+
+            // Show the pager now
+            Dom.setStyle(pager, "visibility", "visible");
+        },
+
+        /**
+         * Update the UI for the pager menu based on the current page and
+         * the number of pages.  If the number of pages is greater than
+         * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
+         * down menu instead of a set of buttons.
+         *
+         * @method _updatePagerMenu
+         * @protected
+         */
+        _updatePagerMenu: function () {
+            var carousel = this,
+                cur      = carousel._pages.cur, // current page
+                el,
+                i,
+                item,
+                n        = carousel.get("numVisible"),
+                num      = carousel._pages.num, // total pages
+                pager    = carousel._pages.el,  // the pager container element
+                sel;
+
+            if (num === 0) {
+                return;         // don't do anything if number of pages is 0
+            }
+
+            sel = document.createElement("SELECT");
+            if (!sel) {
+                return;
+            }
+
+            // Hide the pager before redrawing it
+            Dom.setStyle(pager, "visibility", "hidden");
+
+            // Remove all nodes from the pager
+            while (pager.firstChild) {
+                pager.removeChild(pager.firstChild);
+            }
+
+            for (i = 0; i < num; i++) {
+                if (JS.isUndefined(carousel._itemsTable.items[i * n])) {
+                    Dom.setStyle(pager, "visibility", "visible");
+                    break;
+                }
+                item = carousel._itemsTable.items[i * n].id;
+
+                el   = document.createElement("OPTION");
+                if (!el) {
+                    Dom.setStyle(pager, "visibility", "visible");
+                    break;
+                }
+                el.value     = "#" + item;
+                // TODO: use a template string for i18N compliance
+                el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
+
+                if (i == cur) {
+                    el.setAttribute("selected", "selected");
+                }
+
+                sel.appendChild(el);
+            }
+
+            el = document.createElement("FORM");
+            if (!el) {
+            } else {
+                el.appendChild(sel);
+                pager.appendChild(el);
+            }
+
+            // Show the pager now
+            Dom.setStyle(pager, "visibility", "visible");
+        },
+
+        /**
+         * Set the correct tab index for the Carousel items.
+         *
+         * @method _updateTabIndex
+         * @param el {Object} The element to be focussed
+         * @protected
+         */
+        _updateTabIndex: function (el) {
+            var carousel = this;
+
+            if (el) {
+                if (carousel._focusableItemEl) {
+                    carousel._focusableItemEl.tabIndex = -1;
+                }
+                carousel._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 carousel = this, numItems = carousel.get("numItems");
+
+            if (JS.isNumber(val)) {
+                if (numItems === 0 && val == numItems) {
+                    return true;
+                } else {
+                    return (val >= 0 && val < numItems);
+                }
+            }
+
+            return false;
+        },
+
+        /**
+         * 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) {
+            return JS.isNumber(val) && (val >= 0);
+        },
+
+        /**
+         * 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;
+        }
+
+    });
+
+})();
+/*
+;;  Local variables: **
+;;  mode: js2 **
+;;  indent-tabs-mode: nil **
+;;  End: **
+*/
+YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.7.0", build: "1799"});