[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/2in3/2.9.0/build/yui2-carousel/ -> yui2-carousel-debug.js (source)

   1  YUI.add('yui2-carousel', function(Y) {
   2      var YAHOO    = Y.YUI2;
   3      /*
   4  Copyright (c) 2011, Yahoo! Inc. All rights reserved.
   5  Code licensed under the BSD License:
   6  http://developer.yahoo.com/yui/license.html
   7  version: 2.9.0
   8  */
   9  /**
  10   * The Carousel module provides a widget for browsing among a set of like
  11   * objects represented pictorially.
  12   *
  13   * @module carousel
  14   * @requires yahoo, dom, event, element
  15   * @optional animation
  16   * @namespace YAHOO.widget
  17   * @title Carousel Widget
  18   */
  19  (function () {
  20  
  21      var WidgetName = "Carousel"; // forward declaration
  22  
  23      /**
  24       * The Carousel widget.
  25       *
  26       * @class Carousel
  27       * @extends YAHOO.util.Element
  28       * @constructor
  29       * @param el {HTMLElement | String} The HTML element that represents the
  30       * the container that houses the Carousel.
  31       * @param cfg {Object} (optional) The configuration values
  32       */
  33      YAHOO.widget.Carousel = function (el, cfg) {
  34          YAHOO.log("Component creation", WidgetName);
  35  
  36          YAHOO.widget.Carousel.superclass.constructor.call(this, el, cfg);
  37      };
  38  
  39      /*
  40       * Private variables of the Carousel component
  41       */
  42  
  43      /* Some abbreviations to avoid lengthy typing and lookups. */
  44      var
  45      Carousel    = YAHOO.widget.Carousel,
  46      Dom         = YAHOO.util.Dom,
  47      Event       = YAHOO.util.Event,
  48      JS          = YAHOO.lang,
  49  
  50      /**
  51       * The internal table of Carousel instances.
  52       * @private
  53       * @static
  54       */
  55      instances = {},
  56      syncUiOnItemInsert = true,
  57  
  58      /*
  59       * Custom events of the Carousel component
  60       */
  61  
  62      /**
  63       * @event afterScroll
  64       * @description Fires when the Carousel has scrolled to the previous or
  65       * next page.  Passes back the index of the first and last visible items in
  66       * the Carousel.  See
  67       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  68       * for more information on listening for this event.
  69       * @type YAHOO.util.CustomEvent
  70       */
  71      afterScrollEvent = "afterScroll",
  72  
  73      /**
  74       * @event allItemsRemovedEvent
  75       * @description Fires when all items have been removed from the Carousel.
  76       * See
  77       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  78       * for more information on listening for this event.
  79       * @type YAHOO.util.CustomEvent
  80       */
  81      allItemsRemovedEvent = "allItemsRemoved",
  82  
  83      /**
  84       * @event beforeHide
  85       * @description Fires before the Carousel is hidden.  See
  86       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  87       * for more information on listening for this event.
  88       * @type YAHOO.util.CustomEvent
  89       */
  90      beforeHideEvent = "beforeHide",
  91  
  92      /**
  93       * @event beforePageChange
  94       * @description Fires when the Carousel is about to scroll to the previous
  95       * or next page.  Passes back the page number of the current page.  Note
  96       * that the first page number is zero.  See
  97       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
  98       * for more information on listening for this event.
  99       * @type YAHOO.util.CustomEvent
 100       */
 101      beforePageChangeEvent = "beforePageChange",
 102  
 103      /**
 104       * @event beforeScroll
 105       * @description Fires when the Carousel is about to scroll to the previous
 106       * or next page.  Passes back the index of the first and last visible items
 107       * in the Carousel and the direction (backward/forward) of the scroll.  See
 108       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 109       * for more information on listening for this event.
 110       * @type YAHOO.util.CustomEvent
 111       */
 112      beforeScrollEvent = "beforeScroll",
 113  
 114      /**
 115       * @event beforeShow
 116       * @description Fires when the Carousel is about to be shown.  See
 117       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 118       * for more information on listening for this event.
 119       * @type YAHOO.util.CustomEvent
 120       */
 121      beforeShowEvent = "beforeShow",
 122  
 123      /**
 124       * @event blur
 125       * @description Fires when the Carousel loses focus.  See
 126       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 127       * for more information on listening for this event.
 128       * @type YAHOO.util.CustomEvent
 129       */
 130      blurEvent = "blur",
 131  
 132      /**
 133       * @event focus
 134       * @description Fires when the Carousel gains focus.  See
 135       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 136       * for more information on listening for this event.
 137       * @type YAHOO.util.CustomEvent
 138       */
 139      focusEvent = "focus",
 140  
 141      /**
 142       * @event hide
 143       * @description Fires when the Carousel is hidden.  See
 144       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 145       * for more information on listening for this event.
 146       * @type YAHOO.util.CustomEvent
 147       */
 148      hideEvent = "hide",
 149  
 150      /**
 151       * @event itemAdded
 152       * @description Fires when an item has been added to the Carousel.  Passes
 153       * back the content of the item that would be added, the index at which the
 154       * item would be added, and the event itself.  See
 155       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 156       * for more information on listening for this event.
 157       * @type YAHOO.util.CustomEvent
 158       */
 159      itemAddedEvent = "itemAdded",
 160  
 161      /**
 162       * @event itemRemoved
 163       * @description Fires when an item has been removed from the Carousel.
 164       * Passes back the content of the item that would be removed, the index
 165       * from which the item would be removed, and the event itself.  See
 166       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 167       * for more information on listening for this event.
 168       * @type YAHOO.util.CustomEvent
 169       */
 170      itemRemovedEvent = "itemRemoved",
 171  
 172      /**
 173       * @event itemReplaced
 174       * @description Fires when an item has been replaced in the Carousel.
 175       * Passes back the content of the item that was replaced, the content
 176       * of the new item, the index where the replacement occurred, and the event
 177       * itself.  See
 178       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 179       * for more information on listening for this event.
 180       * @type YAHOO.util.CustomEvent
 181       */
 182      itemReplacedEvent = "itemReplaced",
 183  
 184      /**
 185       * @event itemSelected
 186       * @description Fires when an item has been selected in the Carousel.
 187       * Passes back the index of the selected item in the Carousel.  Note, that
 188       * the index begins from zero.  See
 189       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 190       * for more information on listening for this event.
 191       * @type YAHOO.util.CustomEvent
 192       */
 193      itemSelectedEvent = "itemSelected",
 194  
 195      /**
 196       * @event loadItems
 197       * @description Fires when the Carousel needs more items to be loaded for
 198       * displaying them.  Passes back the first and last visible items in the
 199       * Carousel, and the number of items needed to be loaded.  See
 200       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 201       * for more information on listening for this event.
 202       * @type YAHOO.util.CustomEvent
 203       */
 204      loadItemsEvent = "loadItems",
 205  
 206      /**
 207       * @event navigationStateChange
 208       * @description Fires when the state of either one of the navigation
 209       * buttons are changed from enabled to disabled or vice versa.  Passes back
 210       * the state (true/false) of the previous and next buttons.  The value true
 211       * signifies the button is enabled, false signifies disabled.  See
 212       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 213       * for more information on listening for this event.
 214       * @type YAHOO.util.CustomEvent
 215       */
 216      navigationStateChangeEvent = "navigationStateChange",
 217  
 218      /**
 219       * @event pageChange
 220       * @description Fires after the Carousel has scrolled to the previous or
 221       * next page.  Passes back the page number of the current page.  Note
 222       * that the first page number is zero.  See
 223       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 224       * for more information on listening for this event.
 225       * @type YAHOO.util.CustomEvent
 226       */
 227      pageChangeEvent = "pageChange",
 228  
 229      /*
 230       * Internal event.
 231       * @event render
 232       * @description Fires when the Carousel is rendered.  See
 233       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 234       * for more information on listening for this event.
 235       * @type YAHOO.util.CustomEvent
 236       */
 237      renderEvent = "render",
 238  
 239      /**
 240       * @event show
 241       * @description Fires when the Carousel is shown.  See
 242       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 243       * for more information on listening for this event.
 244       * @type YAHOO.util.CustomEvent
 245       */
 246      showEvent = "show",
 247  
 248      /**
 249       * @event startAutoPlay
 250       * @description Fires when the auto play has started in the Carousel.  See
 251       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 252       * for more information on listening for this event.
 253       * @type YAHOO.util.CustomEvent
 254       */
 255      startAutoPlayEvent = "startAutoPlay",
 256  
 257      /**
 258       * @event stopAutoPlay
 259       * @description Fires when the auto play has been stopped in the Carousel.
 260       * See
 261       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 262       * for more information on listening for this event.
 263       * @type YAHOO.util.CustomEvent
 264       */
 265      stopAutoPlayEvent = "stopAutoPlay",
 266  
 267      /*
 268       * Internal event.
 269       * @event uiUpdateEvent
 270       * @description Fires when the UI has been updated.
 271       * See
 272       * <a href="YAHOO.util.Element.html#addListener">Element.addListener</a>
 273       * for more information on listening for this event.
 274       * @type YAHOO.util.CustomEvent
 275       */
 276      uiUpdateEvent = "uiUpdate";
 277  
 278      /*
 279       * Private helper functions used by the Carousel component
 280       */
 281  
 282     /**
 283       * Set multiple styles on one element.
 284       * @method setStyles
 285       * @param el {HTMLElement} The element to set styles on
 286       * @param style {Object} top:"10px", left:"0px", etc.
 287       * @private
 288       */
 289       function setStyles(el, styles) {
 290           var which;
 291  
 292           for (which in styles) {
 293               if (styles.hasOwnProperty(which)) {
 294                   Dom.setStyle(el, which, styles[which]);
 295               }
 296           }
 297       }
 298  
 299      /**
 300       * Create an element, set its class name and optionally install the element
 301       * to its parent.
 302       * @method createElement
 303       * @param el {String} The element to be created
 304       * @param attrs {Object} Configuration of parent, class and id attributes.
 305       * If the content is specified, it is inserted after creation of the
 306       * element. The content can also be an HTML element in which case it would
 307       * be appended as a child node of the created element.
 308       * @private
 309       */
 310      function createElement(el, attrs) {
 311          var newEl = document.createElement(el);
 312  
 313          attrs = attrs || {};
 314          if (attrs.className) {
 315              Dom.addClass(newEl, attrs.className);
 316          }
 317  
 318          if (attrs.styles) {
 319              setStyles(newEl, attrs.styles);
 320          }
 321  
 322          if (attrs.parent) {
 323              attrs.parent.appendChild(newEl);
 324          }
 325  
 326          if (attrs.id) {
 327              newEl.setAttribute("id", attrs.id);
 328          }
 329  
 330          if (attrs.content) {
 331              if (attrs.content.nodeName) {
 332                  newEl.appendChild(attrs.content);
 333              } else {
 334                  newEl.innerHTML = attrs.content;
 335              }
 336          }
 337  
 338          return newEl;
 339      }
 340  
 341      /**
 342       * Get the computed style of an element.
 343       *
 344       * @method getStyle
 345       * @param el {HTMLElement} The element for which the style needs to be
 346       * returned.
 347       * @param style {String} The style attribute
 348       * @param type {String} "int", "float", etc. (defaults to int)
 349       * @private
 350       */
 351      function getStyle(el, style, type) {
 352          var value;
 353  
 354          if (!el) {
 355              return 0;
 356          }
 357  
 358          function getStyleIntVal(el, style) {
 359              var val;
 360  
 361              /*
 362               * XXX: Safari calculates incorrect marginRight for an element
 363               * which has its parent element style set to overflow: hidden
 364               * https://bugs.webkit.org/show_bug.cgi?id=13343
 365               * Let us assume marginLeft == marginRight
 366               *
 367               * Seems like IE9 also has this issue!
 368               */
 369              if (style == "marginRight" && (YAHOO.env.ua.webkit ||
 370                      (YAHOO.env.ua.ie && YAHOO.env.ua.ie >= 9))) {
 371                  val = parseInt(Dom.getStyle(el, "marginLeft"), 10);
 372              } else {
 373                  val = parseInt(Dom.getStyle(el, style), 10);
 374              }
 375  
 376              return JS.isNumber(val) ? val : 0;
 377          }
 378  
 379          function getStyleFloatVal(el, style) {
 380              var val;
 381  
 382              /*
 383               * XXX: Safari calculates incorrect marginRight for an element
 384               * which has its parent element style set to overflow: hidden
 385               * https://bugs.webkit.org/show_bug.cgi?id=13343
 386               * Let us assume marginLeft == marginRight
 387               */
 388              if (style == "marginRight" && YAHOO.env.ua.webkit) {
 389                  val = parseFloat(Dom.getStyle(el, "marginLeft"));
 390              } else {
 391                  val = parseFloat(Dom.getStyle(el, style));
 392              }
 393  
 394              return JS.isNumber(val) ? val : 0;
 395          }
 396  
 397          if (typeof type == "undefined") {
 398              type = "int";
 399          }
 400  
 401          switch (style) {
 402          case "height":
 403              value = el.offsetHeight;
 404              if (value > 0) {
 405                  value += getStyleIntVal(el, "marginTop")        +
 406                          getStyleIntVal(el, "marginBottom");
 407              } else {
 408                  value = getStyleFloatVal(el, "height")          +
 409                          getStyleIntVal(el, "marginTop")         +
 410                          getStyleIntVal(el, "marginBottom")      +
 411                          getStyleIntVal(el, "borderTopWidth")    +
 412                          getStyleIntVal(el, "borderBottomWidth") +
 413                          getStyleIntVal(el, "paddingTop")        +
 414                          getStyleIntVal(el, "paddingBottom");
 415              }
 416              break;
 417          case "width":
 418              value = el.offsetWidth;
 419              if (value > 0) {
 420                  value += getStyleIntVal(el, "marginLeft")       +
 421                          getStyleIntVal(el, "marginRight");
 422              } else {
 423                  value = getStyleFloatVal(el, "width")           +
 424                          getStyleIntVal(el, "marginLeft")        +
 425                          getStyleIntVal(el, "marginRight")       +
 426                          getStyleIntVal(el, "borderLeftWidth")   +
 427                          getStyleIntVal(el, "borderRightWidth")  +
 428                          getStyleIntVal(el, "paddingLeft")       +
 429                          getStyleIntVal(el, "paddingRight");
 430              }
 431              break;
 432          default:
 433              if (type == "int") {
 434                  value = getStyleIntVal(el, style);
 435              } else if (type == "float") {
 436                  value = getStyleFloatVal(el, style);
 437              } else {
 438                  value = Dom.getStyle(el, style);
 439              }
 440              break;
 441          }
 442  
 443          return value;
 444      }
 445  
 446      /**
 447       * Compute and return the height or width of a single Carousel item
 448       * depending upon the orientation.
 449       *
 450       * @method getCarouselItemSize
 451       * @param which {String} "height" or "width" to be returned.  If this is
 452       * passed explicitly, the calculated size is not cached.
 453       * @private
 454       */
 455      function getCarouselItemSize(which) {
 456          var carousel = this,
 457              child,
 458              item,
 459              size     = 0,
 460              vertical = false;
 461  
 462          if (carousel._itemAttrCache[which]) {
 463              return carousel._itemAttrCache[which];
 464          }
 465  
 466          if (carousel._itemsTable.numItems === 0) {
 467              return 0;
 468          }
 469  
 470          // get first loaded item
 471          item = carousel._findClosestSibling(-1);
 472  
 473          if (JS.isUndefined(item)) {
 474              return 0;
 475          }
 476  
 477          child = Dom.get(item.id);
 478  
 479          if (typeof which == "undefined") {
 480              vertical = carousel.get("isVertical");
 481          } else {
 482              vertical = which == "height";
 483          }
 484  
 485          if (vertical) {
 486              size = getStyle(child, "height");
 487          } else {
 488              size = getStyle(child, "width");
 489          }
 490  
 491          if (size) {
 492              carousel._itemAttrCache[which] = size;
 493          }
 494  
 495          return size;
 496      }
 497  
 498      /**
 499       * Return the size of a part of the item (reveal).
 500       *
 501       * @method getRevealSize
 502       * @private
 503       */
 504      function getRevealSize() {
 505          var carousel = this, isVertical, sz;
 506  
 507          isVertical = carousel.get("isVertical");
 508          sz  = getCarouselItemSize.call(carousel,
 509                  isVertical ? "height" : "width");
 510          return (sz * carousel.get("revealAmount") / 100);
 511      }
 512  
 513      /**
 514       * Compute and return the position of a Carousel item based on its
 515       * position.
 516       *
 517       * @method getCarouselItemPosition
 518       * @param position {Number} The position of the Carousel item.
 519       * @private
 520       */
 521      function getCarouselItemPosition(pos) {
 522          var carousel    = this,
 523              itemsPerRow = carousel._cols,
 524              itemsPerCol = carousel._rows,
 525              page,
 526              sz,
 527              isVertical,
 528              itemsCol,
 529              itemsRow,
 530              sentinel,
 531              top,
 532              left,
 533              rsz,
 534              delta,
 535              styles = {},
 536              itemsTable = carousel._itemsTable;
 537  
 538          isVertical = carousel.get("isVertical");
 539          sz  = getCarouselItemSize.call(carousel,
 540                  isVertical ? "height" : "width");
 541          rsz = getRevealSize.call(carousel);
 542  
 543          if (itemsPerCol) {
 544              page = this.getPageForItem(pos);
 545              if (isVertical) {
 546                  itemsRow = Math.floor(pos/itemsPerRow);
 547                  delta = itemsRow;
 548                  top = delta * sz;
 549                  styles.top  = (top + rsz) + "px";
 550  
 551                  sz  = getCarouselItemSize.call(carousel, "width");
 552  
 553                  itemsCol = pos % itemsPerRow;
 554                  delta = itemsCol;
 555                  left = delta * sz;
 556                  styles.left = left + "px";
 557              } else {
 558                  itemsCol = pos % itemsPerRow;
 559                  sentinel = (page - 1) * itemsPerRow;
 560                  delta = itemsCol + sentinel;
 561                  left = delta * sz;
 562                  styles.left = (left + rsz) + "px";
 563  
 564                  sz  = getCarouselItemSize.call(carousel, "height");
 565  
 566                  itemsRow = Math.floor(pos/itemsPerRow);
 567                  sentinel = (page - 1) * itemsPerCol;
 568                  delta = itemsRow - sentinel;
 569                  top = delta * sz;
 570  
 571                  styles.top  = top + "px";
 572              }
 573          } else {
 574              if (isVertical) {
 575                  styles.left = 0;
 576                  styles.top  = ((pos * sz) + rsz) + "px";
 577              } else {
 578                  styles.top  = 0;
 579                  styles.left = ((pos * sz) + rsz) + "px";
 580              }
 581          }
 582  
 583          return styles;
 584      }
 585  
 586      /**
 587       * Return the index of the first item in the view port for displaying item
 588       * in "pos".
 589       *
 590       * @method getFirstVisibleForPosition
 591       * @param pos {Number} The position of the item to be displayed
 592       * @private
 593       */
 594      function getFirstVisibleForPosition(pos) {
 595          var num = this.get("numVisible");
 596          return Math.floor(pos / num) * num;
 597      }
 598  
 599      /**
 600       * Return the scrolling offset size given the number of elements to
 601       * scroll.
 602       *
 603       * @method getScrollOffset
 604       * @param delta {Number} The delta number of elements to scroll by.
 605       * @private
 606       */
 607      function getScrollOffset(delta) {
 608          var carousel = this,
 609              itemSize = 0,
 610              size     = 0,
 611              attr     = carousel.get("isVertical") ? "height" : "width";
 612  
 613          itemSize = getCarouselItemSize.call(carousel, attr);
 614  
 615          size = itemSize * delta;
 616  
 617          return size;
 618      }
 619  
 620      /**
 621       * Scroll the Carousel by a page backward.
 622       *
 623       * @method scrollPageBackward
 624       * @param {Event} ev The event object
 625       * @param {Object} obj The context object
 626       * @private
 627       */
 628      function scrollPageBackward(ev, obj) {
 629          obj.scrollPageBackward();
 630          Event.preventDefault(ev);
 631      }
 632  
 633      /**
 634       * Scroll the Carousel by a page forward.
 635       *
 636       * @method scrollPageForward
 637       * @param {Event} ev The event object
 638       * @param {Object} obj The context object
 639       * @private
 640       */
 641      function scrollPageForward(ev, obj) {
 642          obj.scrollPageForward();
 643          Event.preventDefault(ev);
 644      }
 645  
 646      /**
 647       * Set the selected item.
 648       *
 649       * @method setItemSelection
 650       * @param {Number} newpos The index of the new position
 651       * @param {Number} oldpos The index of the previous position
 652       * @private
 653       */
 654       function setItemSelection(newpos, oldpos) {
 655          var carousel = this,
 656              cssClass   = carousel.CLASSES,
 657              el,
 658              firstItem  = carousel._firstItem,
 659              numItems   = carousel.get("numItems"),
 660              numVisible = carousel.get("numVisible"),
 661              position   = oldpos,
 662              sentinel   = firstItem + numVisible - 1;
 663  
 664          if (position >= 0 && position < numItems) {
 665              if (!JS.isUndefined(carousel._itemsTable.items[position])) {
 666                  el = Dom.get(carousel._itemsTable.items[position].id);
 667                  if (el) {
 668                      Dom.removeClass(el, cssClass.SELECTED_ITEM);
 669                  }
 670              }
 671          }
 672  
 673          if (JS.isNumber(newpos)) {
 674              newpos = parseInt(newpos, 10);
 675              newpos = JS.isNumber(newpos) ? newpos : 0;
 676          } else {
 677              newpos = firstItem;
 678          }
 679  
 680          if (JS.isUndefined(carousel._itemsTable.items[newpos])) {
 681              newpos = getFirstVisibleForPosition.call(carousel, newpos);
 682              carousel.scrollTo(newpos); // still loading the item
 683          }
 684  
 685          if (!JS.isUndefined(carousel._itemsTable.items[newpos])) {
 686              el = Dom.get(carousel._itemsTable.items[newpos].id);
 687              if (el) {
 688                  Dom.addClass(el, cssClass.SELECTED_ITEM);
 689              }
 690          }
 691  
 692          if (newpos < firstItem || newpos > sentinel) { // out of focus
 693              newpos = getFirstVisibleForPosition.call(carousel, newpos);
 694              carousel.scrollTo(newpos);
 695          }
 696      }
 697  
 698      /**
 699       * Show or hide navigation.
 700       *
 701       * @method showNavigation
 702       * @private
 703       */
 704      function showNavigation(hide) {
 705          var carousel = this,
 706              cfg = carousel.get("navigation");
 707  
 708          if (JS.isUndefined(cfg)) {
 709              return; // can't do anything
 710          }
 711  
 712          if (JS.isUndefined(hide)) {
 713              // show the navigation
 714              if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) &&
 715                  !JS.isUndefined(cfg.prev[0])) {
 716                  Dom.setStyle(cfg.prev[0], "visibility", "visible");
 717              }
 718              if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) &&
 719                  !JS.isUndefined(cfg.next[0])) {
 720                  Dom.setStyle(cfg.next[0], "visibility", "visible");
 721              }
 722              if (!JS.isUndefined(carousel._pages) &&
 723                  !JS.isUndefined(carousel._pages.el)) {
 724                  Dom.setStyle(carousel._pages.el, "visibility", "visible");
 725              }
 726          } else {
 727              // hide the navigation
 728              if (!JS.isUndefined(cfg.prev) && JS.isArray(cfg.prev) &&
 729                  !JS.isUndefined(cfg.prev[0])) {
 730                  Dom.setStyle(cfg.prev[0], "visibility", "hidden");
 731              }
 732              if (!JS.isUndefined(cfg.next) && JS.isArray(cfg.next) &&
 733                  !JS.isUndefined(cfg.next[0])) {
 734                  Dom.setStyle(cfg.next[0], "visibility", "hidden");
 735              }
 736              if (!JS.isUndefined(carousel._pages) &&
 737                  !JS.isUndefined(carousel._pages.el)) {
 738                  Dom.setStyle(carousel._pages.el, "visibility", "hidden");
 739              }
 740          }
 741      }
 742  
 743      /**
 744       * Fire custom events for enabling/disabling navigation elements.
 745       *
 746       * @method syncNavigation
 747       * @private
 748       */
 749      function syncNavigation() {
 750          var attach   = false,
 751              carousel = this,
 752              cssClass = carousel.CLASSES,
 753              i,
 754              navigation,
 755              sentinel;
 756  
 757          // Don't do anything if the Carousel is not rendered
 758          if (!carousel._hasRendered) {
 759              return;
 760          }
 761  
 762          navigation = carousel.get("navigation");
 763          sentinel   = carousel._firstItem + carousel.get("numVisible");
 764  
 765          if (navigation.prev) {
 766              if (carousel.get("numItems") === 0 || carousel._firstItem === 0) {
 767                  if (carousel.get("numItems") === 0 ||
 768                     !carousel.get("isCircular")) {
 769                      Event.removeListener(navigation.prev, "click",
 770                              scrollPageBackward);
 771                      Dom.addClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
 772                      for (i = 0; i < carousel._navBtns.prev.length; i++) {
 773                          carousel._navBtns.prev[i].setAttribute("disabled",
 774                                  "true");
 775                      }
 776                      carousel._prevEnabled = false;
 777                  } else {
 778                      attach = !carousel._prevEnabled;
 779                  }
 780              } else {
 781                  attach = !carousel._prevEnabled;
 782              }
 783  
 784              if (attach) {
 785                  Event.on(navigation.prev, "click", scrollPageBackward,
 786                           carousel);
 787                  Dom.removeClass(navigation.prev, cssClass.FIRST_NAV_DISABLED);
 788                  for (i = 0; i < carousel._navBtns.prev.length; i++) {
 789                      carousel._navBtns.prev[i].removeAttribute("disabled");
 790                  }
 791                  carousel._prevEnabled = true;
 792              }
 793          }
 794  
 795          attach = false;
 796          if (navigation.next) {
 797              if (sentinel >= carousel.get("numItems")) {
 798                  if (!carousel.get("isCircular")) {
 799                      Event.removeListener(navigation.next, "click",
 800                              scrollPageForward);
 801                      Dom.addClass(navigation.next, cssClass.DISABLED);
 802                      for (i = 0; i < carousel._navBtns.next.length; i++) {
 803                          carousel._navBtns.next[i].setAttribute("disabled",
 804                                  "true");
 805                      }
 806                      carousel._nextEnabled = false;
 807                  } else {
 808                      attach = !carousel._nextEnabled;
 809                  }
 810              } else {
 811                  attach = !carousel._nextEnabled;
 812              }
 813  
 814              if (attach) {
 815                  Event.on(navigation.next, "click", scrollPageForward,
 816                           carousel);
 817                  Dom.removeClass(navigation.next, cssClass.DISABLED);
 818                  for (i = 0; i < carousel._navBtns.next.length; i++) {
 819                      carousel._navBtns.next[i].removeAttribute("disabled");
 820                  }
 821                  carousel._nextEnabled = true;
 822              }
 823          }
 824  
 825          carousel.fireEvent(navigationStateChangeEvent,
 826                  { next: carousel._nextEnabled, prev: carousel._prevEnabled });
 827      }
 828  
 829      /**
 830       * Synchronize and redraw the Pager UI if necessary.
 831       *
 832       * @method syncPagerUi
 833       * @private
 834       */
 835      function syncPagerUi(page) {
 836          var carousel = this, numPages, numVisible;
 837  
 838          // Don't do anything if the Carousel is not rendered
 839          if (!carousel._hasRendered) {
 840              return;
 841          }
 842  
 843          numVisible = carousel.get("numVisible");
 844  
 845          if (!JS.isNumber(page)) {
 846              page = Math.floor(carousel.get("selectedItem") / numVisible);
 847          }
 848  
 849          numPages = Math.ceil(carousel.get("numItems") / numVisible);
 850  
 851          carousel._pages.num = numPages;
 852          carousel._pages.cur = page;
 853  
 854          if (numPages > carousel.CONFIG.MAX_PAGER_BUTTONS) {
 855              carousel._updatePagerMenu();
 856          } else {
 857              carousel._updatePagerButtons();
 858          }
 859      }
 860  
 861      /**
 862       * Get full dimensions of an element.
 863       *
 864       * @method getDimensions
 865       * @param {Object} el The element to get the dimensions of
 866       * @param {String} which Get the height or width of an element
 867       * @private
 868       */
 869      function getDimensions(el, which) {
 870          switch (which) {
 871          case 'height':
 872              return  getStyle(el, "marginTop")        +
 873                      getStyle(el, "marginBottom")     +
 874                      getStyle(el, "paddingTop")       +
 875                      getStyle(el, "paddingBottom")    +
 876                      getStyle(el, "borderTopWidth")   +
 877                      getStyle(el, "borderBottomWidth");
 878          case 'width':
 879              return   getStyle(el, "marginLeft")      +
 880                       getStyle(el, "marginRight")     +
 881                       getStyle(el, "paddingLeft")     +
 882                       getStyle(el, "paddingRight")    +
 883                       getStyle(el, "borderLeftWidth") +
 884                       getStyle(el, "borderRightWidth");
 885          default:
 886              break;
 887          }
 888  
 889          return getStyle(el, which);
 890      }
 891  
 892      /**
 893       * Handle UI update.
 894       * Call the appropriate methods on events fired when an item is added, or
 895       * removed for synchronizing the DOM.
 896       *
 897       * @method syncUi
 898       * @param {Object} o The item that needs to be added or removed
 899       * @private
 900       */
 901      function syncUi(o) {
 902          var carousel = this;
 903  
 904          if (!JS.isObject(o)) {
 905              return;
 906          }
 907  
 908          switch (o.ev) {
 909          case itemAddedEvent:
 910              carousel._syncUiForItemAdd(o);
 911              break;
 912          case itemRemovedEvent:
 913              carousel._syncUiForItemRemove(o);
 914              break;
 915          case itemReplacedEvent:
 916              carousel._syncUiForItemReplace(o);
 917              break;
 918          case loadItemsEvent:
 919              carousel._syncUiForLazyLoading(o);
 920              break;
 921          }
 922  
 923          carousel.fireEvent(uiUpdateEvent);
 924      }
 925  
 926      /**
 927       * Update the state variables after scrolling the Carousel view port.
 928       *
 929       * @method updateStateAfterScroll
 930       * @param {Integer} item The index to which the Carousel has scrolled to.
 931       * @param {Integer} sentinel The last element in the view port.
 932       * @private
 933       */
 934      function updateStateAfterScroll(item, sentinel) {
 935          var carousel   = this,
 936              page       = carousel.get("currentPage"),
 937              newPage,
 938              numPerPage = carousel.get("numVisible");
 939  
 940          newPage = parseInt(carousel._firstItem / numPerPage, 10);
 941          if (newPage != page) {
 942              carousel.setAttributeConfig("currentPage", { value: newPage });
 943              carousel.fireEvent(pageChangeEvent, newPage);
 944          }
 945  
 946          if (carousel.get("selectOnScroll")) {
 947              if (carousel.get("selectedItem") != carousel._selectedItem) {
 948                  carousel.set("selectedItem", carousel._selectedItem);
 949              }
 950          }
 951  
 952          clearTimeout(carousel._autoPlayTimer);
 953          delete carousel._autoPlayTimer;
 954          if (carousel.isAutoPlayOn()) {
 955              carousel.startAutoPlay();
 956          }
 957  
 958          carousel.fireEvent(afterScrollEvent,
 959                             { first: carousel._firstItem,
 960                               last: sentinel },
 961                             carousel);
 962      }
 963  
 964      /*
 965       * Static members and methods of the Carousel component
 966       */
 967  
 968      /**
 969       * Return the appropriate Carousel object based on the id associated with
 970       * the Carousel element or false if none match.
 971       * @method getById
 972       * @public
 973       * @static
 974       */
 975      Carousel.getById = function (id) {
 976          return instances[id] ? instances[id].object : false;
 977      };
 978  
 979      YAHOO.extend(Carousel, YAHOO.util.Element, {
 980  
 981          /*
 982           * Internal variables used within the Carousel component
 983           */
 984  
 985           /**
 986           * Number of rows for a multirow carousel.
 987           *
 988           * @property _rows
 989           * @private
 990           */
 991          _rows: null,
 992  
 993          /**
 994           * Number of cols for a multirow carousel.
 995           *
 996           * @property _cols
 997           * @private
 998           */
 999          _cols: null,
1000  
1001          /**
1002           * The Animation object.
1003           *
1004           * @property _animObj
1005           * @private
1006           */
1007          _animObj: null,
1008  
1009          /**
1010           * The Carousel element.
1011           *
1012           * @property _carouselEl
1013           * @private
1014           */
1015          _carouselEl: null,
1016  
1017          /**
1018           * The Carousel clipping container element.
1019           *
1020           * @property _clipEl
1021           * @private
1022           */
1023          _clipEl: null,
1024  
1025          /**
1026           * The current first index of the Carousel.
1027           *
1028           * @property _firstItem
1029           * @private
1030           */
1031          _firstItem: 0,
1032  
1033          /**
1034           * Does the Carousel element have focus?
1035           *
1036           * @property _hasFocus
1037           * @private
1038           */
1039          _hasFocus: false,
1040  
1041          /**
1042           * Is the Carousel rendered already?
1043           *
1044           * @property _hasRendered
1045           * @private
1046           */
1047          _hasRendered: false,
1048  
1049          /**
1050           * Is the animation still in progress?
1051           *
1052           * @property _isAnimationInProgress
1053           * @private
1054           */
1055          _isAnimationInProgress: false,
1056  
1057          /**
1058           * Is the auto-scrolling of Carousel in progress?
1059           *
1060           * @property _isAutoPlayInProgress
1061           * @private
1062           */
1063          _isAutoPlayInProgress: false,
1064  
1065          /**
1066           * The table of items in the Carousel.
1067           * The numItems is the number of items in the Carousel, items being the
1068           * array of items in the Carousel.  The size is the size of a single
1069           * item in the Carousel.  It is cached here for efficiency (to avoid
1070           * computing the size multiple times).
1071           *
1072           * @property _itemsTable
1073           * @private
1074           */
1075          _itemsTable: null,
1076  
1077          /**
1078           * The Carousel navigation buttons.
1079           *
1080           * @property _navBtns
1081           * @private
1082           */
1083          _navBtns: null,
1084  
1085          /**
1086           * The Carousel navigation.
1087           *
1088           * @property _navEl
1089           * @private
1090           */
1091          _navEl: null,
1092  
1093          /**
1094           * Status of the next navigation item.
1095           *
1096           * @property _nextEnabled
1097           * @private
1098           */
1099          _nextEnabled: true,
1100  
1101          /**
1102           * The Carousel pages structure.
1103           * This is an object of the total number of pages and the current page.
1104           *
1105           * @property _pages
1106           * @private
1107           */
1108          _pages: null,
1109  
1110          /**
1111           * The Carousel pagination structure.
1112           *
1113           * @property _pagination
1114           * @private
1115           */
1116          _pagination: null,
1117  
1118          /**
1119           * Status of the previous navigation item.
1120           *
1121           * @property _prevEnabled
1122           * @private
1123           */
1124          _prevEnabled: true,
1125  
1126          /**
1127           * Whether the Carousel size needs to be recomputed or not?
1128           *
1129           * @property _recomputeSize
1130           * @private
1131           */
1132          _recomputeSize: true,
1133  
1134          /**
1135           * Cache the Carousel item attributes.
1136           *
1137           * @property _itemAttrCache
1138           * @private
1139           */
1140           _itemAttrCache: null,
1141  
1142          /*
1143           * CSS classes used by the Carousel component
1144           */
1145  
1146          CLASSES: {
1147  
1148              /**
1149               * The class name of the Carousel navigation buttons.
1150               *
1151               * @property BUTTON
1152               * @default "yui-carousel-button"
1153               */
1154              BUTTON: "yui-carousel-button",
1155  
1156              /**
1157               * The class name of the Carousel element.
1158               *
1159               * @property CAROUSEL
1160               * @default "yui-carousel"
1161               */
1162              CAROUSEL: "yui-carousel",
1163  
1164              /**
1165               * The class name of the container of the items in the Carousel.
1166               *
1167               * @property CAROUSEL_EL
1168               * @default "yui-carousel-element"
1169               */
1170              CAROUSEL_EL: "yui-carousel-element",
1171  
1172              /**
1173               * The class name of the Carousel's container element.
1174               *
1175               * @property CONTAINER
1176               * @default "yui-carousel-container"
1177               */
1178              CONTAINER: "yui-carousel-container",
1179  
1180              /**
1181               * The class name of the Carousel's container element.
1182               *
1183               * @property CONTENT
1184               * @default "yui-carousel-content"
1185               */
1186              CONTENT: "yui-carousel-content",
1187  
1188              /**
1189               * The class name of a disabled navigation button.
1190               *
1191               * @property DISABLED
1192               * @default "yui-carousel-button-disabled"
1193               */
1194              DISABLED: "yui-carousel-button-disabled",
1195  
1196              /**
1197               * The class name of the first Carousel navigation button.
1198               *
1199               * @property FIRST_NAV
1200               * @default " yui-carousel-first-button"
1201               */
1202              FIRST_NAV: " yui-carousel-first-button",
1203  
1204              /**
1205               * The class name of a first disabled navigation button.
1206               *
1207               * @property FIRST_NAV_DISABLED
1208               * @default "yui-carousel-first-button-disabled"
1209               */
1210              FIRST_NAV_DISABLED: "yui-carousel-first-button-disabled",
1211  
1212              /**
1213               * The class name of a first page element.
1214               *
1215               * @property FIRST_PAGE
1216               * @default "yui-carousel-nav-first-page"
1217               */
1218              FIRST_PAGE: "yui-carousel-nav-first-page",
1219  
1220              /**
1221               * The class name of the Carousel navigation button that has focus.
1222               *
1223               * @property FOCUSSED_BUTTON
1224               * @default "yui-carousel-button-focus"
1225               */
1226              FOCUSSED_BUTTON: "yui-carousel-button-focus",
1227  
1228              /**
1229               * The class name of a horizontally oriented Carousel.
1230               *
1231               * @property HORIZONTAL
1232               * @default "yui-carousel-horizontal"
1233               */
1234              HORIZONTAL: "yui-carousel-horizontal",
1235  
1236              /**
1237               * The element to be used as the progress indicator when the item
1238               * is still being loaded.
1239               *
1240               * @property ITEM_LOADING
1241               * @default The progress indicator (spinner) image CSS class
1242               */
1243              ITEM_LOADING: "yui-carousel-item-loading",
1244  
1245              /**
1246               * The class name that will be set if the Carousel adjusts itself
1247               * for a minimum width.
1248               *
1249               * @property MIN_WIDTH
1250               * @default "yui-carousel-min-width"
1251               */
1252              MIN_WIDTH: "yui-carousel-min-width",
1253  
1254              /**
1255               * The navigation element container class name.
1256               *
1257               * @property NAVIGATION
1258               * @default "yui-carousel-nav"
1259               */
1260              NAVIGATION: "yui-carousel-nav",
1261  
1262              /**
1263               * The class name of the next Carousel navigation button.
1264               *
1265               * @property NEXT_NAV
1266               * @default " yui-carousel-next-button"
1267               */
1268              NEXT_NAV: " yui-carousel-next-button",
1269  
1270              /**
1271               * The class name of the next navigation link. This variable is
1272               * not only used for styling, but also for identifying the link
1273               * within the Carousel container.
1274               *
1275               * @property NEXT_PAGE
1276               * @default "yui-carousel-next"
1277               */
1278              NEXT_PAGE: "yui-carousel-next",
1279  
1280              /**
1281               * The class name for the navigation container for prev/next.
1282               *
1283               * @property NAV_CONTAINER
1284               * @default "yui-carousel-buttons"
1285               */
1286              NAV_CONTAINER: "yui-carousel-buttons",
1287  
1288              /**
1289                * The class name for an item in the pager UL or dropdown menu.
1290                *
1291                * @property PAGER_ITEM
1292                * @default "yui-carousel-pager-item"
1293                */
1294              PAGER_ITEM: "yui-carousel-pager-item",
1295  
1296              /**
1297               * The class name for the pagination container
1298               *
1299               * @property PAGINATION
1300               * @default "yui-carousel-pagination"
1301               */
1302              PAGINATION: "yui-carousel-pagination",
1303  
1304              /**
1305               * The class name of the focussed page navigation.  This class is
1306               * specifically used for the ugly focus handling in Opera.
1307               *
1308               * @property PAGE_FOCUS
1309               * @default "yui-carousel-nav-page-focus"
1310               */
1311              PAGE_FOCUS: "yui-carousel-nav-page-focus",
1312  
1313              /**
1314               * The class name of the previous navigation link. This variable
1315               * is not only used for styling, but also for identifying the link
1316               * within the Carousel container.
1317               *
1318               * @property PREV_PAGE
1319               * @default "yui-carousel-prev"
1320               */
1321              PREV_PAGE: "yui-carousel-prev",
1322  
1323              /**
1324               * The class name of the item.
1325               *
1326               * @property ITEM
1327               * @default "yui-carousel-item"
1328               */
1329              ITEM: "yui-carousel-item",
1330  
1331              /**
1332               * The class name of the selected item.
1333               *
1334               * @property SELECTED_ITEM
1335               * @default "yui-carousel-item-selected"
1336               */
1337              SELECTED_ITEM: "yui-carousel-item-selected",
1338  
1339              /**
1340               * The class name of the selected paging navigation.
1341               *
1342               * @property SELECTED_NAV
1343               * @default "yui-carousel-nav-page-selected"
1344               */
1345              SELECTED_NAV: "yui-carousel-nav-page-selected",
1346  
1347              /**
1348               * The class name of a vertically oriented Carousel.
1349               *
1350               * @property VERTICAL
1351               * @default "yui-carousel-vertical"
1352               */
1353              VERTICAL: "yui-carousel-vertical",
1354  
1355              /**
1356               * The class name of a multirow Carousel.
1357               *
1358               * @property MULTI_ROW
1359               * @default "yui-carousel-multi-row"
1360               */
1361              MULTI_ROW: "yui-carousel-multi-row",
1362  
1363              /**
1364               * The class name of a row in a multirow Carousel.
1365               *
1366               * @property ROW
1367               * @default "yui-carousel-new-row"
1368               */
1369              ROW: "yui-carousel-row",
1370  
1371              /**
1372               * The class name of a vertical Carousel's container element.
1373               *
1374               * @property VERTICAL_CONTAINER
1375               * @default "yui-carousel-vertical-container"
1376               */
1377              VERTICAL_CONTAINER: "yui-carousel-vertical-container",
1378  
1379              /**
1380               * The class name of a visible Carousel.
1381               *
1382               * @property VISIBLE
1383               * @default "yui-carousel-visible"
1384               */
1385              VISIBLE: "yui-carousel-visible"
1386  
1387          },
1388  
1389          /*
1390           * Configuration attributes for configuring the Carousel component
1391           */
1392  
1393          CONFIG: {
1394  
1395              /**
1396               * The offset of the first visible item in the Carousel.
1397               *
1398               * @property FIRST_VISIBLE
1399               * @default 0
1400               */
1401              FIRST_VISIBLE: 0,
1402  
1403              /**
1404               * The minimum width of the horizontal Carousel container to support
1405               * the navigation buttons.
1406               *
1407               * @property HORZ_MIN_WIDTH
1408               * @default 180
1409               */
1410              HORZ_MIN_WIDTH: 180,
1411  
1412              /**
1413               * The maximum number of pager buttons allowed beyond which the UI
1414               * of the pager would be a drop-down of pages instead of buttons.
1415               *
1416               * @property MAX_PAGER_BUTTONS
1417               * @default 5
1418               */
1419              MAX_PAGER_BUTTONS: 5,
1420  
1421              /**
1422               * The minimum width of the vertical Carousel container to support
1423               * the navigation buttons.
1424               *
1425               * @property VERT_MIN_WIDTH
1426               * @default 155
1427               */
1428              VERT_MIN_WIDTH: 115,
1429  
1430              /**
1431               * The number of visible items in the Carousel.
1432               *
1433               * @property NUM_VISIBLE
1434               * @default 3
1435               */
1436              NUM_VISIBLE: 3
1437  
1438          },
1439  
1440          /*
1441           * Internationalizable strings in the Carousel component
1442           */
1443  
1444          STRINGS: {
1445  
1446              /**
1447               * The content to be used as the progress indicator when the item
1448               * is still being loaded. Inserted into DOM with innerHTML.
1449               *
1450               * @property ITEM_LOADING_CONTENT
1451               * @type HTML
1452               * @default "Loading"
1453               */
1454              ITEM_LOADING_CONTENT: "Loading",
1455  
1456              /**
1457               * The next navigation button name/text. Inserted into DOM with innerHTML.
1458               *
1459               * @property NEXT_BUTTON_TEXT
1460               * @type HTML
1461               * @default "Next Page"
1462               */
1463              NEXT_BUTTON_TEXT: "Next Page",
1464  
1465              /**
1466               * The prefix text for the pager in case the UI is a drop-down.
1467               * Inserted into DOM with innerHTML.
1468               *
1469               * @property PAGER_PREFIX_TEXT
1470               * @type HTML
1471               * @default "Go to page "
1472               */
1473              PAGER_PREFIX_TEXT: "Go to page ",
1474  
1475              /**
1476               * The previous navigation button name/text. Inserted into DOM with innerHTML.
1477               *
1478               * @property PREVIOUS_BUTTON_TEXT
1479               * @type HTML
1480               * @default "Previous Page"
1481               */
1482              PREVIOUS_BUTTON_TEXT: "Previous Page"
1483  
1484          },
1485  
1486          /*
1487           * Public methods of the Carousel component
1488           */
1489  
1490          /**
1491           * Insert or append an item to the Carousel.
1492           * E.g. if Object: ({content:"Your Content", id:"", className:""}, index)
1493           *
1494           * @method addItem
1495           * @public
1496           * @param item {HTML | Object | HTMLElement} The item to be appended
1497           * to the Carousel. If the parameter is a string, it is assumed to be
1498           * the HTML content of the newly created item. If the parameter is an
1499           * object, it is assumed to supply the content and an optional class
1500           * and an optional id of the newly created item.
1501           * @param index {Number} optional The position to where in the list
1502           * (starts from zero).
1503           * @return {Boolean} Return true on success, false otherwise
1504           */
1505          addItem: function (item, index) {
1506              var carousel = this,
1507                  className,
1508                  content,
1509                  elId,
1510                  replaceItems = 0,
1511                  newIndex, // Add newIndex as workaround for undefined pos
1512                  numItems = carousel.get("numItems");
1513  
1514              if (!item) {
1515                  return false;
1516              }
1517  
1518              if (JS.isString(item) || item.nodeName) {
1519                  content = item.nodeName ? item.innerHTML : item;
1520              } else if (JS.isObject(item)) {
1521                  content = item.content;
1522              } else {
1523                  YAHOO.log("Invalid argument to addItem", "error", WidgetName);
1524                  return false;
1525              }
1526  
1527              className = carousel.CLASSES.ITEM +
1528                      (item.className ? " " + item.className : "");
1529              elId = item.id ? item.id : Dom.generateId();
1530  
1531              if (JS.isUndefined(index)) {
1532                  carousel._itemsTable.items.push({
1533                          item      : content,
1534                          className : className,
1535                          id        : elId
1536                  });
1537                  // Add newIndex as workaround for undefined pos
1538                  newIndex = carousel._itemsTable.items.length - 1;
1539              } else {
1540                  if (index < 0 || index > numItems) {
1541                      YAHOO.log("Index out of bounds", "error", WidgetName);
1542                      return false;
1543                  }
1544  
1545                  // make sure we splice into the correct position
1546                  if (!carousel._itemsTable.items[index]) {
1547                      carousel._itemsTable.items[index] = undefined;
1548                      replaceItems = 1;
1549                  }
1550  
1551                  carousel._itemsTable.items.splice(index, replaceItems, {
1552                          item      : content,
1553                          className : className,
1554                          id        : elId
1555                  });
1556              }
1557              carousel._itemsTable.numItems++;
1558  
1559              if (numItems < carousel._itemsTable.items.length) {
1560                  carousel.set("numItems", carousel._itemsTable.items.length);
1561              }
1562  
1563              // Add newPos as workaround for undefined pos
1564              carousel.fireEvent(itemAddedEvent,
1565                      { pos: index, ev: itemAddedEvent, newPos: newIndex });
1566  
1567              return true;
1568          },
1569  
1570          /**
1571           * Insert or append multiple items to the Carousel.
1572           *
1573           * @method addItems
1574           * @public
1575           * @param items {Array} An array containing an array of new items each linked to the
1576           * index where the insertion should take place.
1577           * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]]
1578           * NOTE: An item at index must already exist.
1579           * @return {Boolean} Return true on success, false otherwise
1580           */
1581          addItems: function (items) {
1582              var i, n, rv = true;
1583  
1584              if (!JS.isArray(items)) {
1585                  return false;
1586              }
1587  
1588              syncUiOnItemInsert = false;
1589              for (i = 0, n = items.length; i < n; i++) {
1590                  if (this.addItem(items[i][0], items[i][1]) === false) {
1591                      rv = false;
1592                  }
1593              }
1594              syncUiOnItemInsert = true;
1595  
1596              this._syncUiItems();
1597  
1598              return rv;
1599          },
1600  
1601          /**
1602           * Remove focus from the Carousel.
1603           *
1604           * @method blur
1605           * @public
1606           */
1607          blur: function () {
1608              this._carouselEl.blur();
1609              this.fireEvent(blurEvent);
1610          },
1611  
1612          /**
1613           * Clears the items from Carousel.
1614           *
1615           * @method clearItems
1616           * @public
1617           */
1618          clearItems: function () {
1619              var carousel = this, n = carousel.get("numItems");
1620  
1621              while (n > 0) {
1622                  if (!carousel.removeItem(0)) {
1623                      YAHOO.log("Item could not be removed - missing?",
1624                                "warn", WidgetName);
1625                  }
1626                  /*
1627                      For dynamic loading, the numItems may be much larger than
1628                      the actual number of items in the table. So, set the
1629                      numItems to zero, and break out of the loop if the table
1630                      is already empty.
1631                   */
1632                  if (carousel._itemsTable.numItems === 0) {
1633                      carousel.set("numItems", 0);
1634                      break;
1635                  }
1636                  n--;
1637              }
1638  
1639              carousel.fireEvent(allItemsRemovedEvent);
1640          },
1641  
1642          /**
1643           * Set focus on the Carousel.
1644           *
1645           * @method focus
1646           * @public
1647           */
1648          focus: function () {
1649              var carousel = this,
1650                  first,
1651                  focusEl,
1652                  isSelectionInvisible,
1653                  itemsTable,
1654                  last,
1655                  numVisible,
1656                  selectOnScroll,
1657                  selected,
1658                  selItem;
1659  
1660              // Don't do anything if the Carousel is not rendered
1661              if (!carousel._hasRendered) {
1662                  return;
1663              }
1664  
1665              if (carousel.isAnimating()) {
1666                  // this messes up real bad!
1667                  return;
1668              }
1669  
1670              selItem              = carousel.get("selectedItem");
1671              numVisible           = carousel.get("numVisible");
1672              selectOnScroll       = carousel.get("selectOnScroll");
1673              selected             = (selItem >= 0) ?
1674                                     carousel.getItem(selItem) : null;
1675              first                = carousel.get("firstVisible");
1676              last                 = first + numVisible - 1;
1677              isSelectionInvisible = (selItem < first || selItem > last);
1678              focusEl              = (selected && selected.id) ?
1679                                     Dom.get(selected.id) : null;
1680              itemsTable           = carousel._itemsTable;
1681  
1682              if (!selectOnScroll && isSelectionInvisible) {
1683                  focusEl = (itemsTable && itemsTable.items &&
1684                             itemsTable.items[first]) ?
1685                          Dom.get(itemsTable.items[first].id) : null;
1686              }
1687  
1688              if (focusEl) {
1689                  try {
1690                      focusEl.focus();
1691                  } catch (ex) {
1692                      // ignore focus errors
1693                  }
1694              }
1695  
1696              carousel.fireEvent(focusEvent);
1697          },
1698  
1699          /**
1700           * Hide the Carousel.
1701           *
1702           * @method hide
1703           * @public
1704           */
1705          hide: function () {
1706              var carousel = this;
1707  
1708              if (carousel.fireEvent(beforeHideEvent) !== false) {
1709                  carousel.removeClass(carousel.CLASSES.VISIBLE);
1710                  showNavigation.call(carousel, false);
1711                  carousel.fireEvent(hideEvent);
1712              }
1713          },
1714  
1715          /**
1716           * Initialize the Carousel.
1717           *
1718           * @method init
1719           * @public
1720           * @param el {HTMLElement | String} The html element that represents
1721           * the Carousel container.
1722           * @param attrs {Object} The set of configuration attributes for
1723           * creating the Carousel.
1724           */
1725          init: function (el, attrs) {
1726              var carousel = this,
1727                  elId     = el,  // save for a rainy day
1728                  parse    = false,
1729                  selected;
1730  
1731              if (!el) {
1732                  YAHOO.log(el + " is neither an HTML element, nor a string",
1733                          "error", WidgetName);
1734                  return;
1735              }
1736  
1737              carousel._hasRendered = false;
1738              carousel._navBtns     = { prev: [], next: [] };
1739              carousel._pages       = { el: null, num: 0, cur: 0 };
1740              carousel._pagination  = {};
1741              carousel._itemAttrCache = {};
1742  
1743              carousel._itemsTable  = { loading: {}, numItems: 0,
1744                                        items: [], size: 0 };
1745  
1746              YAHOO.log("Component initialization", WidgetName);
1747  
1748              if (JS.isString(el)) {
1749                  el = Dom.get(el);
1750              } else if (!el.nodeName) {
1751                  YAHOO.log(el + " is neither an HTML element, nor a string",
1752                          "error", WidgetName);
1753                  return;
1754              }
1755  
1756              Carousel.superclass.init.call(carousel, el, attrs);
1757  
1758              // check if we're starting somewhere in the middle
1759              selected = carousel.get("selectedItem");
1760              if(selected > 0){
1761                  carousel.set("firstVisible",getFirstVisibleForPosition.call(carousel,selected));
1762              }
1763  
1764              if (el) {
1765                  if (!el.id) {   // in case the HTML element is passed
1766                      el.setAttribute("id", Dom.generateId());
1767                  }
1768                  parse = carousel._parseCarousel(el);
1769                  if (!parse) {
1770                      carousel._createCarousel(elId);
1771                  }
1772              } else {
1773                  el = carousel._createCarousel(elId);
1774              }
1775              elId = el.id;
1776  
1777              carousel.initEvents();
1778  
1779              if (parse) {
1780                  carousel._parseCarouselItems();
1781              }
1782  
1783              // add the selected class
1784              if(selected > 0){
1785                  setItemSelection.call(carousel,selected,0);
1786              }
1787  
1788              if (!attrs || typeof attrs.isVertical == "undefined") {
1789                  carousel.set("isVertical", false);
1790              }
1791  
1792              carousel._parseCarouselNavigation(el);
1793              carousel._navEl = carousel._setupCarouselNavigation();
1794  
1795              instances[elId] = { object: carousel };
1796              carousel._loadItems(Math.min(carousel.get("firstVisible")+carousel.get("numVisible"),carousel.get("numItems"))-1);
1797          },
1798  
1799          /**
1800           * Initialize the configuration attributes used to create the Carousel.
1801           *
1802           * @method initAttributes
1803           * @public
1804           * @param attrs {Object} The set of configuration attributes for
1805           * creating the Carousel.
1806           */
1807          initAttributes: function (attrs) {
1808              var carousel = this;
1809  
1810              attrs = attrs || {};
1811              Carousel.superclass.initAttributes.call(carousel, attrs);
1812  
1813              /**
1814               * @attribute carouselEl
1815               * @description The type of the Carousel element.
1816               * @default OL
1817               * @type Boolean
1818               */
1819              carousel.setAttributeConfig("carouselEl", {
1820                      validator : JS.isString,
1821                      value     : attrs.carouselEl || "OL"
1822              });
1823  
1824              /**
1825               * @attribute carouselItemEl
1826               * @description The type of the list of items within the Carousel.
1827               * @default LI
1828               * @type Boolean
1829               */
1830              carousel.setAttributeConfig("carouselItemEl", {
1831                      validator : JS.isString,
1832                      value     : attrs.carouselItemEl || "LI"
1833              });
1834  
1835              /**
1836               * @attribute currentPage
1837               * @description The current page number (read-only.)
1838               * @type Number
1839               */
1840              carousel.setAttributeConfig("currentPage", {
1841                      readOnly : true,
1842                      value    : 0
1843              });
1844  
1845              /**
1846               * @attribute firstVisible
1847               * @description The index to start the Carousel from (indexes begin
1848               * from zero)
1849               * @default 0
1850               * @type Number
1851               */
1852              carousel.setAttributeConfig("firstVisible", {
1853                      method    : carousel._setFirstVisible,
1854                      validator : carousel._validateFirstVisible,
1855                      value     :
1856                          attrs.firstVisible || carousel.CONFIG.FIRST_VISIBLE
1857              });
1858  
1859              /**
1860               * @attribute selectOnScroll
1861               * @description Set this to true to automatically set focus to
1862               * follow scrolling in the Carousel.
1863               * @default true
1864               * @type Boolean
1865               */
1866              carousel.setAttributeConfig("selectOnScroll", {
1867                      validator : JS.isBoolean,
1868                      value     : attrs.selectOnScroll || true
1869              });
1870  
1871              /**
1872               * @attribute numVisible
1873               * @description The number of visible items in the Carousel's
1874               * viewport.
1875               * @default 3
1876               * @type Number
1877               */
1878              carousel.setAttributeConfig("numVisible", {
1879                      setter    : carousel._numVisibleSetter,
1880                      method    : carousel._setNumVisible,
1881                      validator : carousel._validateNumVisible,
1882                      value     : attrs.numVisible || carousel.CONFIG.NUM_VISIBLE
1883              });
1884  
1885              /**
1886               * @attribute numItems
1887               * @description The number of items in the Carousel.
1888               * @type Number
1889               */
1890              carousel.setAttributeConfig("numItems", {
1891                      method    : carousel._setNumItems,
1892                      validator : carousel._validateNumItems,
1893                      value     : carousel._itemsTable.numItems
1894              });
1895  
1896              /**
1897               * @attribute scrollIncrement
1898               * @description The number of items to scroll by for arrow keys.
1899               * @default 1
1900               * @type Number
1901               */
1902              carousel.setAttributeConfig("scrollIncrement", {
1903                      validator : carousel._validateScrollIncrement,
1904                      value     : attrs.scrollIncrement || 1
1905              });
1906  
1907              /**
1908               * @attribute selectedItem
1909               * @description The index of the selected item.
1910               * @type Number
1911               */
1912              carousel.setAttributeConfig("selectedItem", {
1913                      setter    : carousel._selectedItemSetter,
1914                      method    : carousel._setSelectedItem,
1915                      validator : JS.isNumber,
1916                      value     : -1
1917              });
1918  
1919              /**
1920               * @attribute revealAmount
1921               * @description The percentage of the item to be revealed on each
1922               * side of the Carousel (before and after the first and last item
1923               * in the Carousel's viewport.)
1924               * @default 0
1925               * @type Number
1926               */
1927              carousel.setAttributeConfig("revealAmount", {
1928                      method    : carousel._setRevealAmount,
1929                      validator : carousel._validateRevealAmount,
1930                      value     : attrs.revealAmount || 0
1931              });
1932  
1933              /**
1934               * @attribute isCircular
1935               * @description Set this to true to wrap scrolling of the contents
1936               * in the Carousel.
1937               * @default false
1938               * @type Boolean
1939               */
1940              carousel.setAttributeConfig("isCircular", {
1941                      validator : JS.isBoolean,
1942                      value     : attrs.isCircular || false
1943              });
1944  
1945              /**
1946               * @attribute isVertical
1947               * @description True if the orientation of the Carousel is vertical
1948               * @default false
1949               * @type Boolean
1950               */
1951              carousel.setAttributeConfig("isVertical", {
1952                      method    : carousel._setOrientation,
1953                      validator : JS.isBoolean,
1954                      value     : attrs.isVertical || false
1955              });
1956  
1957              /**
1958               * @attribute navigation
1959               * @description The set of navigation controls for Carousel
1960               * @default <br>
1961               * { prev: null, // the previous navigation element<br>
1962               *   next: null } // the next navigation element
1963               * @type Object
1964               */
1965              carousel.setAttributeConfig("navigation", {
1966                      method    : carousel._setNavigation,
1967                      validator : carousel._validateNavigation,
1968                      value     :
1969                          attrs.navigation || {prev: null,next: null,page: null}
1970              });
1971  
1972              /**
1973               * @attribute animation
1974               * @description The optional animation attributes for the Carousel.
1975               * @default <br>
1976               * { speed: 0, // the animation speed (in seconds)<br>
1977               *   effect: null } // the animation effect (like
1978               *   YAHOO.util.Easing.easeOut)
1979               * @type Object
1980               */
1981              carousel.setAttributeConfig("animation", {
1982                      validator : carousel._validateAnimation,
1983                      value     : attrs.animation || { speed: 0, effect: null }
1984              });
1985  
1986              /**
1987               * @attribute autoPlay
1988               * @description Set this to time in milli-seconds to have the
1989               * Carousel automatically scroll the contents.
1990               * @type Number
1991               * @deprecated Use autoPlayInterval instead.
1992               */
1993              carousel.setAttributeConfig("autoPlay", {
1994                      validator : JS.isNumber,
1995                      value     : attrs.autoPlay || 0
1996              });
1997  
1998              /**
1999               * @attribute autoPlayInterval
2000               * @description The delay in milli-seconds for scrolling the
2001               * Carousel during auto-play.
2002               * Note: The startAutoPlay() method needs to be invoked to trigger
2003               * automatic scrolling of Carousel.
2004               * @type Number
2005               */
2006              carousel.setAttributeConfig("autoPlayInterval", {
2007                      validator : JS.isNumber,
2008                      value     : attrs.autoPlayInterval || 0
2009              });
2010  
2011              /**
2012               * @attribute numPages
2013               * @description The number of pages in the carousel.
2014               * @type Number
2015               */
2016              carousel.setAttributeConfig("numPages", {
2017                      readOnly  : true,
2018                      getter    : carousel._getNumPages
2019              });
2020  
2021              /**
2022               * @attribute lastVisible
2023               * @description The last item visible in the carousel.
2024               * @type Number
2025               */
2026              carousel.setAttributeConfig("lastVisible", {
2027                      readOnly  : true,
2028                      getter    : carousel._getLastVisible
2029              });
2030          },
2031  
2032          /**
2033           * Initialize and bind the event handlers.
2034           *
2035           * @method initEvents
2036           * @public
2037           */
2038          initEvents: function () {
2039              var carousel = this,
2040                  cssClass = carousel.CLASSES,
2041                  focussedLi;
2042  
2043              carousel.on("keydown", carousel._keyboardEventHandler);
2044  
2045              carousel.on(afterScrollEvent, syncNavigation);
2046  
2047              carousel.on(itemAddedEvent, syncUi);
2048  
2049              carousel.on(itemRemovedEvent, syncUi);
2050  
2051              carousel.on(itemReplacedEvent, syncUi);
2052  
2053              carousel.on(itemSelectedEvent, carousel._focusHandler);
2054  
2055              carousel.on(loadItemsEvent, syncUi);
2056  
2057              carousel.on(allItemsRemovedEvent, function (ev) {
2058                  carousel.scrollTo(0);
2059                  syncNavigation.call(carousel);
2060                  syncPagerUi.call(carousel);
2061              });
2062  
2063              carousel.on(pageChangeEvent, syncPagerUi, carousel);
2064  
2065              carousel.on(renderEvent, function (ev) {
2066                  if (carousel.get("selectedItem") === null ||
2067                      carousel.get("selectedItem") <= 0) { //in either case
2068                      carousel.set("selectedItem", carousel.get("firstVisible"));
2069                  }
2070                  syncNavigation.call(carousel, ev);
2071                  syncPagerUi.call(carousel, ev);
2072                  carousel._setClipContainerSize();
2073                  carousel.show();
2074              });
2075  
2076              carousel.on("selectedItemChange", function (ev) {
2077                  setItemSelection.call(carousel, ev.newValue, ev.prevValue);
2078                  if (ev.newValue >= 0) {
2079                      carousel._updateTabIndex(
2080                              carousel.getElementForItem(ev.newValue));
2081                  }
2082                  carousel.fireEvent(itemSelectedEvent, ev.newValue);
2083              });
2084  
2085              carousel.on(uiUpdateEvent, function (ev) {
2086                  syncNavigation.call(carousel, ev);
2087                  syncPagerUi.call(carousel, ev);
2088              });
2089  
2090              carousel.on("firstVisibleChange", function (ev) {
2091                  if (!carousel.get("selectOnScroll")) {
2092                      if (ev.newValue >= 0) {
2093                          carousel._updateTabIndex(
2094                                  carousel.getElementForItem(ev.newValue));
2095                      }
2096                  }
2097              });
2098  
2099              // Handle item selection on mouse click
2100              carousel.on("click", function (ev) {
2101                  if (carousel.isAutoPlayOn()) {
2102                      carousel.stopAutoPlay();
2103                  }
2104                  carousel._itemClickHandler(ev);
2105                  carousel._pagerClickHandler(ev);
2106              });
2107  
2108              // Restore the focus on the navigation buttons
2109  
2110              Event.onFocus(carousel.get("element"), function (ev, obj) {
2111                  var target = Event.getTarget(ev);
2112  
2113                  if (target && target.nodeName.toUpperCase() == "A" &&
2114                      Dom.getAncestorByClassName(target, cssClass.NAVIGATION)) {
2115                      if (focussedLi) {
2116                          Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
2117                      }
2118                      focussedLi = target.parentNode;
2119                      Dom.addClass(focussedLi, cssClass.PAGE_FOCUS);
2120                  } else {
2121                      if (focussedLi) {
2122                          Dom.removeClass(focussedLi, cssClass.PAGE_FOCUS);
2123                      }
2124                  }
2125  
2126                  obj._hasFocus = true;
2127                  obj._updateNavButtons(Event.getTarget(ev), true);
2128              }, carousel);
2129  
2130              Event.onBlur(carousel.get("element"), function (ev, obj) {
2131                  obj._hasFocus = false;
2132                  obj._updateNavButtons(Event.getTarget(ev), false);
2133              }, carousel);
2134          },
2135  
2136          /**
2137           * Return true if the Carousel is still animating, or false otherwise.
2138           *
2139           * @method isAnimating
2140           * @return {Boolean} Return true if animation is still in progress, or
2141           * false otherwise.
2142           * @public
2143           */
2144          isAnimating: function () {
2145              return this._isAnimationInProgress;
2146          },
2147  
2148          /**
2149           * Return true if the auto-scrolling of Carousel is "on", or false
2150           * otherwise.
2151           *
2152           * @method isAutoPlayOn
2153           * @return {Boolean} Return true if autoPlay is "on", or false
2154           * otherwise.
2155           * @public
2156           */
2157          isAutoPlayOn: function () {
2158              return this._isAutoPlayInProgress;
2159          },
2160  
2161          /**
2162           * Return the carouselItemEl at index or null if the index is not
2163           * found.
2164           *
2165           * @method getElementForItem
2166           * @param index {Number} The index of the item to be returned
2167           * @return {Element} Return the item at index or null if not found
2168           * @public
2169           */
2170          getElementForItem: function (index) {
2171              var carousel = this;
2172  
2173              if (index < 0 || index >= carousel.get("numItems")) {
2174                  YAHOO.log("Index out of bounds", "error", WidgetName);
2175                  return null;
2176              }
2177  
2178              if (carousel._itemsTable.items[index]) {
2179                  return Dom.get(carousel._itemsTable.items[index].id);
2180              }
2181  
2182              return null;
2183          },
2184  
2185          /**
2186           * Return the carouselItemEl for all items in the Carousel.
2187           *
2188           * @method getElementForItems
2189           * @return {Array} Return all the items
2190           * @public
2191           */
2192          getElementForItems: function () {
2193              var carousel = this, els = [], i;
2194  
2195              for (i = 0; i < carousel._itemsTable.numItems; i++) {
2196                  els.push(carousel.getElementForItem(i));
2197              }
2198  
2199              return els;
2200          },
2201  
2202          /**
2203           * Return the item at index or null if the index is not found.
2204           *
2205           * @method getItem
2206           * @param index {Number} The index of the item to be returned
2207           * @return {Object} Return the item at index or null if not found
2208           * @public
2209           */
2210          getItem: function (index) {
2211              var carousel = this;
2212  
2213              if (index < 0 || index >= carousel.get("numItems")) {
2214                  YAHOO.log("Index out of bounds", "error", WidgetName);
2215                  return null;
2216              }
2217  
2218              if (carousel._itemsTable.items.length > index) {
2219                  if (!JS.isUndefined(carousel._itemsTable.items[index])) {
2220                      return carousel._itemsTable.items[index];
2221                  }
2222              }
2223  
2224              return null;
2225          },
2226  
2227          /**
2228           * Return all items as an array.
2229           *
2230           * @method getItems
2231           * @return {Array} Return all items in the Carousel
2232           * @public
2233           */
2234          getItems: function () {
2235              return this._itemsTable.items;
2236          },
2237  
2238          /**
2239           * Return all loading items as an array.
2240           *
2241           * @method getLoadingItems
2242           * @return {Array} Return all items that are loading in the Carousel.
2243           * @public
2244           */
2245          getLoadingItems: function () {
2246              return this._itemsTable.loading;
2247          },
2248  
2249          /**
2250           * For a multirow carousel, return the number of rows specified by user.
2251           *
2252           * @method getItems
2253           * @return {Number} Number of rows
2254           * @public
2255           */
2256          getRows: function () {
2257              return this._rows;
2258          },
2259  
2260          /**
2261           * For a multirow carousel, return the number of cols specified by user.
2262           *
2263           * @method getItems
2264           * @return {Array} Return all items in the Carousel
2265           * @public
2266           */
2267          getCols: function () {
2268              return this._cols;
2269          },
2270  
2271          /**
2272           * Return the position of the Carousel item that has the id "id", or -1
2273           * if the id is not found.
2274           *
2275           * @method getItemPositionById
2276           * @param index {Number} The index of the item to be returned
2277           * @public
2278           */
2279          getItemPositionById: function (id) {
2280              var carousel = this,
2281                  n = carousel.get("numItems"),
2282                  i = 0,
2283                  items = carousel._itemsTable.items,
2284                  item;
2285  
2286              while (i < n) {
2287                  item = items[i] || {};
2288                  if(item.id == id) {
2289                      return i;
2290                  }
2291                  i++;
2292              }
2293  
2294              return -1;
2295          },
2296  
2297          /**
2298           * Return all visible items as an array.
2299           *
2300           * @method getVisibleItems
2301           * @return {Array} The array of visible items
2302           * @public
2303           */
2304          getVisibleItems: function () {
2305              var carousel = this,
2306                  i        = carousel.get("firstVisible"),
2307                  n        = i + carousel.get("numVisible"),
2308                  r        = [];
2309  
2310              while (i < n) {
2311                  r.push(carousel.getElementForItem(i));
2312                  i++;
2313              }
2314  
2315              return r;
2316          },
2317  
2318          /**
2319           * Remove an item at index from the Carousel.
2320           *
2321           * @method removeItem
2322           * @public
2323           * @param index {Number} The position to where in the list (starts from
2324           * zero).
2325           * @return {Boolean} Return true on success, false otherwise
2326           */
2327          removeItem: function (index) {
2328              var carousel = this,
2329                  itemsTable = carousel._itemsTable,
2330                  item,
2331                  num      = carousel.get("numItems");
2332  
2333              if (index < 0 || index >= num) {
2334                  YAHOO.log("Index out of bounds", "error", WidgetName);
2335                  return false;
2336              }
2337  
2338              item = itemsTable.items.splice(index, 1);
2339              if (item && item.length == 1) {
2340                  if(itemsTable.numItems){
2341                      itemsTable.numItems--;
2342                  }
2343  
2344                  carousel.set("numItems", num - 1);
2345  
2346                  carousel.fireEvent(itemRemovedEvent,
2347                          { item: item[0], pos: index, ev: itemRemovedEvent });
2348                  return true;
2349              }
2350  
2351              return false;
2352          },
2353  
2354          /**
2355           * Replace an item at index witin Carousel.
2356           *
2357           * @method replaceItem
2358           * @public
2359           * @param item {HTML | Object | HTMLElement} The item to be appended
2360           * to the Carousel. If the parameter is a string, it is assumed to be
2361           * the HTML content of the newly created item. If the parameter is an
2362           * object, it is assumed to supply the content and an optional class
2363           * and an optional id of the newly created item.
2364           * @param index {Number} The position to where in the list (starts from
2365           * zero).
2366           * @return {Boolean} Return true on success, false otherwise
2367           */
2368          replaceItem: function (item, index) {
2369              var carousel = this,
2370                  className,
2371                  content,
2372                  elId,
2373                  numItems = carousel.get("numItems"),
2374                  oel,
2375                  el = item;
2376  
2377              if (!item) {
2378                  return false;
2379              }
2380  
2381              if (JS.isString(item) || item.nodeName) {
2382                  content = item.nodeName ? item.innerHTML : item;
2383              } else if (JS.isObject(item)) {
2384                  content = item.content;
2385              } else {
2386                  YAHOO.log("Invalid argument to replaceItem", "error", WidgetName);
2387                  return false;
2388              }
2389  
2390              if (JS.isUndefined(index)) {
2391                  YAHOO.log("Index must be defined for replaceItem", "error", WidgetName);
2392                  return false;
2393              } else {
2394                  if (index < 0 || index >= numItems) {
2395                      YAHOO.log("Index out of bounds in replaceItem", "error", WidgetName);
2396                      return false;
2397                  }
2398  
2399                  oel = carousel._itemsTable.items[index];
2400                  if(!oel){
2401                      oel = carousel._itemsTable.loading[index];
2402                      carousel._itemsTable.items[index] = undefined;
2403                  }
2404  
2405                  elId = oel.id || Dom.generateId();
2406                  carousel._itemsTable.items.splice(index, 1, {
2407                      item      : content,
2408                      className : carousel.CLASSES.ITEM + (item.className ? " " + item.className : ""),
2409                      id        : elId
2410                  });
2411  
2412                  el = carousel._itemsTable.items[index];
2413              }
2414              carousel.fireEvent(itemReplacedEvent,
2415                      { newItem: el, oldItem: oel, pos: index, ev: itemReplacedEvent });
2416  
2417              return true;
2418          },
2419  
2420          /**
2421           * Replace multiple items at specified indexes.
2422           * NOTE: item at index must already exist.
2423           *
2424           * @method replaceItems
2425           * @public
2426           * @param items {Array} An array containing an array of replacement items each linked to the
2427           * index where the substitution should take place.
2428           * E.g. [[{content:'<img/>'}, index1], [{content:'<img/>'}, index2]]
2429           * @return {Boolean} Return true on success, false otherwise
2430           */
2431           replaceItems: function (items) {
2432               var i, n, rv = true;
2433  
2434               if (!JS.isArray(items)) {
2435                   return false;
2436               }
2437  
2438               syncUiOnItemInsert = false;
2439               for (i = 0, n = items.length; i < n; i++) {
2440                   if (this.replaceItem(items[i][0], items[i][1]) === false) {
2441                       rv = false;
2442                   }
2443               }
2444               syncUiOnItemInsert = true;
2445  
2446               this._syncUiItems();
2447  
2448               return rv;
2449           },
2450  
2451          /**
2452           * Render the Carousel.
2453           *
2454           * @method render
2455           * @public
2456           * @param appendTo {HTMLElement | String} The element to which the
2457           * Carousel should be appended prior to rendering.
2458           * @return {Boolean} Status of the operation
2459           */
2460          render: function (appendTo) {
2461              var carousel  = this,
2462                  cssClass  = carousel.CLASSES,
2463                  rows = carousel._rows;
2464  
2465              carousel.addClass(cssClass.CAROUSEL);
2466  
2467              if (!carousel._clipEl) {
2468                  carousel._clipEl = carousel._createCarouselClip();
2469                  carousel._clipEl.appendChild(carousel._carouselEl);
2470              }
2471  
2472              if (appendTo) {
2473                  carousel.appendChild(carousel._clipEl);
2474                  carousel.appendTo(appendTo);
2475              } else {
2476                  if (!Dom.inDocument(carousel.get("element"))) {
2477                      YAHOO.log("Nothing to render. The container should be " +
2478                              "within the document if appendTo is not "       +
2479                              "specified", "error", WidgetName);
2480                      return false;
2481                  }
2482                  carousel.appendChild(carousel._clipEl);
2483              }
2484  
2485              if (rows) {
2486                  Dom.addClass(carousel._clipEl, cssClass.MULTI_ROW);
2487              }
2488  
2489              if (carousel.get("isVertical")) {
2490                  carousel.addClass(cssClass.VERTICAL);
2491              } else {
2492                  carousel.addClass(cssClass.HORIZONTAL);
2493              }
2494  
2495              if (carousel.get("numItems") < 1) {
2496                  YAHOO.log("No items in the Carousel to render", "warn",
2497                          WidgetName);
2498                  return false;
2499              }
2500  
2501              carousel._refreshUi();
2502  
2503              return true;
2504          },
2505  
2506          /**
2507           * Scroll the Carousel by an item backward.
2508           *
2509           * @method scrollBackward
2510           * @public
2511           */
2512          scrollBackward: function () {
2513              var carousel = this;
2514              carousel.scrollTo(carousel._firstItem -
2515                                carousel.get("scrollIncrement"));
2516          },
2517  
2518          /**
2519           * Scroll the Carousel by an item forward.
2520           *
2521           * @method scrollForward
2522           * @public
2523           */
2524          scrollForward: function () {
2525              var carousel = this;
2526              carousel.scrollTo(carousel._firstItem +
2527                                carousel.get("scrollIncrement"));
2528          },
2529  
2530          /**
2531           * Scroll the Carousel by a page backward.
2532           *
2533           * @method scrollPageBackward
2534           * @public
2535           */
2536          scrollPageBackward: function () {
2537              var carousel     = this,
2538                  isVertical   = carousel.get("isVertical"),
2539                  cols         = carousel._cols,
2540                  firstVisible = carousel.get("firstVisible"),
2541                  item         = firstVisible - carousel.get("numVisible");
2542  
2543              if (item < 0) {
2544                  // Only account for multi-row when scrolling backwards from
2545                  // item 0
2546                  if (cols) {
2547                      item = firstVisible - cols;
2548                  }
2549              }
2550  
2551              carousel.scrollTo(item);
2552          },
2553  
2554          /**
2555           * Scroll the Carousel by a page forward.
2556           *
2557           * @method scrollPageForward
2558           * @public
2559           */
2560          scrollPageForward: function () {
2561              var carousel = this,
2562                  item     = carousel._firstItem + carousel.get("numVisible");
2563  
2564              if (item > carousel.get("numItems")) {
2565                  item = 0;
2566              }
2567  
2568              if (carousel.get("selectOnScroll")) {
2569                  carousel._selectedItem = carousel._getSelectedItem(item);
2570              }
2571  
2572              carousel.scrollTo(item);
2573          },
2574  
2575          /**
2576           * Scroll the Carousel to make the item the first visible item.
2577           *
2578           * @method scrollTo
2579           * @public
2580           * @param item Number The index of the element to position at.
2581           * @param dontSelect Boolean True if select should be avoided
2582           */
2583          scrollTo: function (item, dontSelect) {
2584              var carousel   = this, animate, animCfg, isCircular, isVertical,
2585                  delta, direction, firstItem, lastItem, itemsPerRow,
2586                  itemsPerCol, numItems, numPerPage, offset, page, rv, sentinel,
2587                  index, stopAutoScroll,
2588                  itemsTable = carousel._itemsTable;
2589  
2590              if (itemsTable.numItems === 0 || item == carousel._firstItem ||
2591                  carousel.isAnimating()) {
2592                  return; // nothing to do!
2593              }
2594  
2595              animCfg        = carousel.get("animation");
2596              isCircular     = carousel.get("isCircular");
2597              isVertical     = carousel.get("isVertical");
2598              itemsPerRow    = carousel._cols;
2599              itemsPerCol    = carousel._rows;
2600              firstItem      = carousel._firstItem;
2601              numItems       = carousel.get("numItems");
2602              numPerPage     = carousel.get("numVisible");
2603              page           = carousel.get("currentPage");
2604  
2605              stopAutoScroll = function () {
2606                  if (carousel.isAutoPlayOn()) {
2607                      carousel.stopAutoPlay();
2608                  }
2609              };
2610  
2611              if (item < 0) {
2612                  if (isCircular) {
2613                      // Normalize the offset so that it doesn't scroll to a
2614                      // different index when number of items is not a factor of
2615                      // the number of visible items
2616                      if (numItems % numPerPage !== 0) {
2617                          item = numItems + (numItems%numPerPage) - numPerPage-1;
2618                      } else {
2619                          item = numItems + item;
2620                      }
2621                  } else {
2622                      stopAutoScroll.call(carousel);
2623                      return;
2624                  }
2625              } else if (numItems > 0 && item > numItems - 1) {
2626  
2627                  if (carousel.get("isCircular")) {
2628                      item = numItems - item;
2629                  } else {
2630                      stopAutoScroll.call(carousel);
2631                      return;
2632                  }
2633              }
2634  
2635              if (isNaN(item)) {
2636                  return;
2637              }
2638  
2639              direction = (carousel._firstItem > item) ? "backward" : "forward";
2640  
2641              sentinel  = firstItem + numPerPage;
2642              sentinel  = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2643              rv = carousel.fireEvent(beforeScrollEvent,
2644                      { dir: direction, first: firstItem, last: sentinel });
2645              if (rv === false) { // scrolling is prevented
2646                  return;
2647              }
2648  
2649              carousel.fireEvent(beforePageChangeEvent, { page: page });
2650  
2651              // call loaditems to check if we have all the items to display
2652              lastItem = item + numPerPage - 1;
2653              carousel._loadItems(lastItem > numItems-1 ? numItems-1 : lastItem);
2654  
2655              // Calculate the delta relative to the first item, the delta is
2656              // always negative.
2657              delta = 0 - item;
2658  
2659              if (itemsPerCol) {
2660                  // offset calculations for multirow Carousel
2661                  if (isVertical) {
2662                      delta = parseInt(delta / itemsPerRow, 10);
2663                  } else {
2664                      delta = parseInt(delta / itemsPerCol, 10);
2665                  }
2666              }
2667  
2668              carousel._firstItem = item;
2669              carousel.set("firstVisible", item);
2670  
2671              if (!dontSelect && carousel.get("selectOnScroll")) {
2672                  carousel._selectedItem = item;
2673              }
2674  
2675              YAHOO.log("Scrolling to " + item + " delta = " + delta, WidgetName);
2676  
2677              sentinel  = item + numPerPage;
2678              sentinel  = (sentinel > numItems - 1) ? numItems - 1 : sentinel;
2679  
2680              offset    = getScrollOffset.call(carousel, delta);
2681              YAHOO.log("Scroll offset = " + offset, WidgetName);
2682  
2683              animate   = animCfg.speed > 0;
2684  
2685              if (animate) {
2686                  carousel._animateAndSetCarouselOffset(offset, item, sentinel,
2687                          dontSelect);
2688              } else {
2689                  carousel._setCarouselOffset(offset);
2690                  updateStateAfterScroll.call(carousel, item, sentinel);
2691              }
2692          },
2693  
2694          /**
2695           * Get the page an item is on within carousel.
2696           *
2697           * @method getPageForItem
2698           * @public
2699           * @param index {Number} Index of item
2700           * @return {Number} Page item is on
2701           */
2702          getPageForItem : function(item) {
2703              return Math.ceil(
2704                  (item+1) / parseInt(this.get("numVisible"),10)
2705              );
2706          },
2707  
2708          /**
2709           * Get the first visible item's index on any given page.
2710           *
2711           * @method getFirstVisibleOnpage
2712           * @public
2713           * @param page {Number} Page
2714           * @return {Number} First item's index
2715           */
2716          getFirstVisibleOnPage : function(page) {
2717              return (page - 1) * this.get("numVisible");
2718          },
2719  
2720          /**
2721           * Select the previous item in the Carousel.
2722           *
2723           * @method selectPreviousItem
2724           * @public
2725           */
2726          selectPreviousItem: function () {
2727              var carousel = this,
2728                  newpos   = 0,
2729                  selected = carousel.get("selectedItem");
2730  
2731              if (selected == carousel._firstItem) {
2732                  newpos = selected - carousel.get("numVisible");
2733                  carousel._selectedItem = carousel._getSelectedItem(selected-1);
2734                  // since we have selected the item already
2735                  carousel.scrollTo(newpos, true);
2736              } else {
2737                  newpos = carousel.get("selectedItem") -
2738                           carousel.get("scrollIncrement");
2739                  carousel.set("selectedItem",carousel._getSelectedItem(newpos));
2740              }
2741          },
2742  
2743          /**
2744           * Select the next item in the Carousel.
2745           *
2746           * @method selectNextItem
2747           * @public
2748           */
2749          selectNextItem: function () {
2750              var carousel = this, newpos = 0;
2751  
2752              newpos = carousel.get("selectedItem") +
2753                       carousel.get("scrollIncrement");
2754              carousel.set("selectedItem", carousel._getSelectedItem(newpos));
2755          },
2756  
2757          /**
2758           * Display the Carousel.
2759           *
2760           * @method show
2761           * @public
2762           */
2763          show: function () {
2764              var carousel = this,
2765                  cssClass = carousel.CLASSES;
2766  
2767              if (carousel.fireEvent(beforeShowEvent) !== false) {
2768                  carousel.addClass(cssClass.VISIBLE);
2769                  showNavigation.call(carousel);
2770                  carousel.fireEvent(showEvent);
2771              }
2772          },
2773  
2774          /**
2775           * Start auto-playing the Carousel.
2776           *
2777           * @method startAutoPlay
2778           * @public
2779           */
2780          startAutoPlay: function () {
2781              var carousel = this, timer;
2782  
2783              if (JS.isUndefined(carousel._autoPlayTimer)) {
2784                  timer = carousel.get("autoPlayInterval");
2785                  if (timer <= 0) {
2786                      return;
2787                  }
2788                  carousel._isAutoPlayInProgress = true;
2789                  carousel.fireEvent(startAutoPlayEvent);
2790                  carousel._autoPlayTimer = setTimeout(function () {
2791                      carousel._autoScroll();
2792                  }, timer);
2793              }
2794          },
2795  
2796          /**
2797           * Stop auto-playing the Carousel.
2798           *
2799           * @method stopAutoPlay
2800           * @public
2801           */
2802          stopAutoPlay: function () {
2803              var carousel = this;
2804  
2805              if (!JS.isUndefined(carousel._autoPlayTimer)) {
2806                  clearTimeout(carousel._autoPlayTimer);
2807                  delete carousel._autoPlayTimer;
2808                  carousel._isAutoPlayInProgress = false;
2809                  carousel.fireEvent(stopAutoPlayEvent);
2810              }
2811          },
2812  
2813          /**
2814           * Update interface's pagination data within a registered template.
2815           *
2816           * @method updatePagination
2817           * @public
2818           */
2819          updatePagination: function () {
2820              var carousel = this,
2821                  pagination = carousel._pagination;
2822              if(!pagination.el){ return false; }
2823  
2824              var numItems = carousel.get('numItems'),
2825                  numVisible = carousel.get('numVisible'),
2826                  firstVisible = carousel.get('firstVisible')+1,
2827                  currentPage = carousel.get('currentPage')+1,
2828                  numPages = carousel.get('numPages'),
2829                  replacements = {
2830                      'numVisible' : numVisible,
2831                      'numPages' : numPages,
2832                      'numItems' : numItems,
2833                      'selectedItem' : carousel.get('selectedItem')+1,
2834                      'currentPage' : currentPage,
2835                      'firstVisible' : firstVisible,
2836                      'lastVisible' : carousel.get("lastVisible")+1
2837                  },
2838                  cb = pagination.callback || {},
2839                  scope = cb.scope && cb.obj ? cb.obj : carousel;
2840  
2841              pagination.el.innerHTML = JS.isFunction(cb.fn) ? cb.fn.apply(scope, [pagination.template, replacements]) : YAHOO.lang.substitute(pagination.template, replacements);
2842          },
2843  
2844          /**
2845           * Register carousels pagination template, append to interface, and populate.
2846           *
2847           * @method registerPagination
2848           * @param template {String} Pagination template as passed to lang.substitute
2849           * @public
2850           */
2851          registerPagination: function (tpl, pos, cb) {
2852              var carousel = this;
2853  
2854              carousel._pagination.template = tpl;
2855              carousel._pagination.callback = cb || {};
2856  
2857              if(!carousel._pagination.el){
2858                  carousel._pagination.el = createElement('DIV', {className:carousel.CLASSES.PAGINATION});
2859  
2860                  if(pos == "before"){
2861                      carousel._navEl.insertBefore(carousel._pagination.el, carousel._navEl.firstChild);
2862                  } else {
2863                      carousel._navEl.appendChild(carousel._pagination.el);
2864                  }
2865  
2866                  carousel.on('itemSelected', carousel.updatePagination);
2867                  carousel.on('pageChange', carousel.updatePagination);
2868              }
2869  
2870              carousel.updatePagination();
2871          },
2872  
2873          /**
2874           * Return the string representation of the Carousel.
2875           *
2876           * @method toString
2877           * @public
2878           * @return {String}
2879           */
2880          toString: function () {
2881              return WidgetName + (this.get ? " (#" + this.get("id") + ")" : "");
2882          },
2883  
2884          /*
2885           * Protected methods of the Carousel component
2886           */
2887  
2888          /**
2889           * Set the Carousel offset to the passed offset after animating.
2890           *
2891           * @method _animateAndSetCarouselOffset
2892           * @param {Integer} offset The offset to which the Carousel has to be
2893           * scrolled to.
2894           * @param {Integer} item The index to which the Carousel will scroll.
2895           * @param {Integer} sentinel The last element in the view port.
2896           * @protected
2897           */
2898          _animateAndSetCarouselOffset: function (offset, item, sentinel) {
2899              var carousel = this,
2900                  animCfg  = carousel.get("animation"),
2901                  animObj  = null;
2902  
2903              if (carousel.get("isVertical")) {
2904                  animObj = new YAHOO.util.Motion(carousel._carouselEl,
2905                          { top: { to: offset } },
2906                          animCfg.speed, animCfg.effect);
2907              } else {
2908                  animObj = new YAHOO.util.Motion(carousel._carouselEl,
2909                          { left: { to: offset } },
2910                          animCfg.speed, animCfg.effect);
2911              }
2912  
2913              carousel._isAnimationInProgress = true;
2914              animObj.onComplete.subscribe(carousel._animationCompleteHandler,
2915                                           { scope: carousel, item: item,
2916                                             last: sentinel });
2917              animObj.animate();
2918          },
2919  
2920          /**
2921           * Handle the animation complete event.
2922           *
2923           * @method _animationCompleteHandler
2924           * @param {Event} ev The event.
2925           * @param {Array} p The event parameters.
2926           * @param {Object} o The object that has the state of the Carousel
2927           * @protected
2928           */
2929          _animationCompleteHandler: function (ev, p, o) {
2930              o.scope._isAnimationInProgress = false;
2931              updateStateAfterScroll.call(o.scope, o.item, o.last);
2932          },
2933  
2934          /**
2935           * Automatically scroll the contents of the Carousel.
2936           * @method _autoScroll
2937           * @protected
2938           */
2939          _autoScroll: function() {
2940              var carousel  = this,
2941                  currIndex = carousel._firstItem,
2942                  index;
2943  
2944              if (currIndex >= carousel.get("numItems") - 1) {
2945                  if (carousel.get("isCircular")) {
2946                      index = 0;
2947                  } else {
2948                      carousel.stopAutoPlay();
2949                  }
2950              } else {
2951                  index = currIndex + carousel.get("numVisible");
2952              }
2953  
2954              carousel._selectedItem = carousel._getSelectedItem(index);
2955              carousel.scrollTo.call(carousel, index);
2956          },
2957  
2958          /**
2959           * Create the Carousel.
2960           *
2961           * @method createCarousel
2962           * @param elId {String} The id of the element to be created
2963           * @protected
2964           */
2965          _createCarousel: function (elId) {
2966              var carousel = this,
2967                  cssClass = carousel.CLASSES,
2968                  el       = Dom.get(elId);
2969  
2970              if (!el) {
2971                  el = createElement("DIV", {
2972                          className : cssClass.CAROUSEL,
2973                          id        : elId
2974                  });
2975              }
2976  
2977              if (!carousel._carouselEl) {
2978                  carousel._carouselEl=createElement(carousel.get("carouselEl"),
2979                          { className: cssClass.CAROUSEL_EL });
2980              }
2981  
2982              return el;
2983          },
2984  
2985          /**
2986           * Create the Carousel clip container.
2987           *
2988           * @method createCarouselClip
2989           * @protected
2990           */
2991          _createCarouselClip: function () {
2992              return createElement("DIV", { className: this.CLASSES.CONTENT });
2993          },
2994  
2995          /**
2996           * Create the Carousel item.
2997           *
2998           * @method createCarouselItem
2999           * @param obj {Object} The attributes of the element to be created
3000           * @protected
3001           */
3002          _createCarouselItem: function (obj) {
3003              var attr, carousel = this;
3004  
3005              return createElement(carousel.get("carouselItemEl"), {
3006                      className : obj.className,
3007                      styles    : {},
3008                      content   : obj.content,
3009                      id        : obj.id
3010              });
3011          },
3012  
3013          /**
3014           * Return a valid item for a possibly out of bounds index considering
3015           * the isCircular property.
3016           *
3017           * @method _getValidIndex
3018           * @param index {Number} The index of the item to be returned
3019           * @return {Object} Return a valid item index
3020           * @protected
3021           */
3022          _getValidIndex: function (index) {
3023              var carousel   = this,
3024                  isCircular = carousel.get("isCircular"),
3025                  numItems   = carousel.get("numItems"),
3026                  numVisible = carousel.get("numVisible"),
3027                  sentinel   = numItems - 1;
3028  
3029              if (index < 0) {
3030                  index = isCircular ?
3031                          Math.ceil(numItems/numVisible)*numVisible + index : 0;
3032              } else if (index > sentinel) {
3033                  index = isCircular ? 0 : sentinel;
3034              }
3035  
3036              return index;
3037          },
3038  
3039          /**
3040           * Get the value for the selected item.
3041           *
3042           * @method _getSelectedItem
3043           * @param val {Number} The new value for "selected" item
3044           * @return {Number} The new value that would be set
3045           * @protected
3046           */
3047          _getSelectedItem: function (val) {
3048              var carousel   = this,
3049                  isCircular = carousel.get("isCircular"),
3050                  numItems   = carousel.get("numItems"),
3051                  sentinel   = numItems - 1;
3052  
3053              if (val < 0) {
3054                  if (isCircular) {
3055                      val = numItems + val;
3056                  } else {
3057                      val = carousel.get("selectedItem");
3058                  }
3059              } else if (val > sentinel) {
3060                  if (isCircular) {
3061                      val = val - numItems;
3062                  } else {
3063                      val = carousel.get("selectedItem");
3064                  }
3065              }
3066              return val;
3067          },
3068  
3069          /**
3070           * The "focus" handler for a Carousel.
3071           *
3072           * @method _focusHandler
3073           * @param {Event} ev The event object
3074           * @protected
3075           */
3076           _focusHandler: function() {
3077               var carousel = this;
3078               if (carousel._hasFocus) {
3079                   carousel.focus();
3080               }
3081           },
3082  
3083          /**
3084           * The "click" handler for the item.
3085           *
3086           * @method _itemClickHandler
3087           * @param {Event} ev The event object
3088           * @protected
3089           */
3090          _itemClickHandler: function (ev) {
3091              var carousel     = this,
3092                  carouselItem = carousel.get("carouselItemEl"),
3093                  container    = carousel.get("element"),
3094                  el,
3095                  item,
3096                  target       = Event.getTarget(ev),
3097                  tag          = target.tagName.toUpperCase();
3098  
3099              if(tag === "INPUT" ||
3100                 tag === "SELECT" ||
3101                 tag === "TEXTAREA") {
3102                  return;
3103              }
3104  
3105              while (target && target != container &&
3106                     target.id != carousel._carouselEl) {
3107                  el = target.nodeName;
3108                  if (el.toUpperCase() == carouselItem) {
3109                      break;
3110                  }
3111                  target = target.parentNode;
3112              }
3113  
3114              if ((item = carousel.getItemPositionById(target.id)) >= 0) {
3115                  YAHOO.log("Setting selection to " + item, WidgetName);
3116                  carousel.set("selectedItem", carousel._getSelectedItem(item));
3117                  carousel.focus();
3118              }
3119          },
3120  
3121          /**
3122           * The keyboard event handler for Carousel.
3123           *
3124           * @method _keyboardEventHandler
3125           * @param ev {Event} The event that is being handled.
3126           * @protected
3127           */
3128          _keyboardEventHandler: function (ev) {
3129              var carousel = this,
3130                  key      = Event.getCharCode(ev),
3131                  target   = Event.getTarget(ev),
3132                  prevent  = false;
3133  
3134              // do not mess while animation is in progress or naving via select
3135              if (carousel.isAnimating() || target.tagName.toUpperCase() === "SELECT") {
3136                  return;
3137              }
3138  
3139              switch (key) {
3140              case 0x25:          // left arrow
3141              case 0x26:          // up arrow
3142                  carousel.selectPreviousItem();
3143                  prevent = true;
3144                  break;
3145              case 0x27:          // right arrow
3146              case 0x28:          // down arrow
3147                  carousel.selectNextItem();
3148                  prevent = true;
3149                  break;
3150              case 0x21:          // page-up
3151                  carousel.scrollPageBackward();
3152                  prevent = true;
3153                  break;
3154              case 0x22:          // page-down
3155                  carousel.scrollPageForward();
3156                  prevent = true;
3157                  break;
3158              }
3159  
3160              if (prevent) {
3161                  if (carousel.isAutoPlayOn()) {
3162                      carousel.stopAutoPlay();
3163                  }
3164                  Event.preventDefault(ev);
3165              }
3166          },
3167  
3168          /**
3169           * The load the required set of items that are needed for display.
3170           *
3171           * @method _loadItems
3172           * @protected
3173           */
3174          _loadItems: function(last) {
3175              var carousel    = this,
3176                  numItems    = carousel.get("numItems"),
3177                  numVisible  = carousel.get("numVisible"),
3178                  reveal      = carousel.get("revealAmount"),
3179                  first       = carousel._itemsTable.items.length,
3180                  lastVisible = carousel.get("lastVisible");
3181  
3182              // adjust if going backwards
3183              if(first > last && last+1 >= numVisible){
3184                  // need to get first a bit differently for the last page
3185                  first = last % numVisible || last == lastVisible ? last - last % numVisible : last - numVisible + 1;
3186              }
3187  
3188              if(reveal && last < numItems - 1){ last++; }
3189  
3190              if (last >= first && (!carousel.getItem(first) || !carousel.getItem(last))) {
3191                  carousel.fireEvent(loadItemsEvent, {
3192                          ev: loadItemsEvent, first: first, last: last,
3193                          num: last - first + 1
3194                  });
3195              }
3196  
3197          },
3198  
3199          /**
3200           * The "onchange" handler for select box pagination.
3201           *
3202           * @method _pagerChangeHandler
3203           * @param {Event} ev The event object
3204           * @protected
3205           */
3206           _pagerChangeHandler: function (ev) {
3207              var carousel = this,
3208                  target = Event.getTarget(ev),
3209                   page = target.value,
3210                   item;
3211  
3212               if (page) {
3213                   item = carousel.getFirstVisibleOnPage(page);
3214                   carousel._selectedItem = item;
3215                   carousel.scrollTo(item);
3216                   carousel.focus();
3217              }
3218            },
3219          /**
3220           * The "click" handler for anchor pagination.
3221           *
3222           * @method _pagerClickHandler
3223           * @param {Event} ev The event object
3224           * @protected
3225           */
3226           _pagerClickHandler: function (ev) {
3227               var carousel = this,
3228                   css = carousel.CLASSES,
3229                   target = Event.getTarget(ev),
3230                   elNode = target.nodeName.toUpperCase(),
3231                   val,
3232                   stringIndex,
3233                   page,
3234                   item;
3235  
3236               if (Dom.hasClass(target, css.PAGER_ITEM) || Dom.hasClass(target.parentNode, css.PAGER_ITEM))  {
3237                   if (elNode == "EM") {
3238                       target = target.parentNode;// item is an em and not an anchor (when text is visible)
3239                   }
3240                   val = target.href;
3241                   stringIndex = val.lastIndexOf("#");
3242                   page =  parseInt(val.substring(stringIndex+1), 10);
3243                      if (page != -1) {
3244                       item = carousel.getFirstVisibleOnPage(page);
3245                       carousel._selectedItem = item;
3246                       carousel.scrollTo(item);
3247                              carousel.focus();
3248                          }
3249                          Event.preventDefault(ev);
3250                      }
3251          },
3252  
3253          /**
3254           * Find the Carousel within a container. The Carousel is identified by
3255           * the first element that matches the carousel element tag or the
3256           * element that has the Carousel class.
3257           *
3258           * @method parseCarousel
3259           * @param parent {HTMLElement} The parent element to look under
3260           * @return {Boolean} True if Carousel is found, false otherwise
3261           * @protected
3262           */
3263          _parseCarousel: function (parent) {
3264              var carousel = this, child, cssClass, domEl, found, node;
3265  
3266              cssClass  = carousel.CLASSES;
3267              domEl     = carousel.get("carouselEl");
3268              found     = false;
3269  
3270              for (child = parent.firstChild; child; child = child.nextSibling) {
3271                  if (child.nodeType == 1) {
3272                      node = child.nodeName;
3273                      if (node.toUpperCase() == domEl) {
3274                          carousel._carouselEl = child;
3275                          Dom.addClass(carousel._carouselEl,
3276                                       carousel.CLASSES.CAROUSEL_EL);
3277                          YAHOO.log("Found Carousel - " + node +
3278                                  (child.id ? " (#" + child.id + ")" : ""),
3279                                  WidgetName);
3280                          found = true;
3281                      }
3282                  }
3283              }
3284  
3285              return found;
3286          },
3287  
3288          /**
3289           * Find the items within the Carousel and add them to the items table.
3290           * A Carousel item is identified by elements that matches the carousel
3291           * item element tag.
3292           *
3293           * @method parseCarouselItems
3294           * @protected
3295           */
3296          _parseCarouselItems: function () {
3297              var carousel = this,
3298                  cssClass = carousel.CLASSES,
3299                  i=0,
3300                  rows,
3301                  child,
3302                  domItemEl,
3303                  elId,
3304                  node,
3305                  index = carousel.get("firstVisible"),
3306                  parent   = carousel._carouselEl;
3307  
3308              rows = carousel._rows;
3309              domItemEl = carousel.get("carouselItemEl");
3310  
3311              for (child = parent.firstChild; child; child = child.nextSibling) {
3312                  if (child.nodeType == 1) {
3313                      node = child.nodeName;
3314                      if (node.toUpperCase() == domItemEl) {
3315                          if (child.id) {
3316                              elId = child.id;
3317                          } else {
3318                              elId = Dom.generateId();
3319                              child.setAttribute("id", elId);
3320                              Dom.addClass(child, carousel.CLASSES.ITEM);
3321                          }
3322                          carousel.addItem(child,index);
3323                          index++;
3324                      }
3325                  }
3326              }
3327          },
3328  
3329          /**
3330           * Find the Carousel navigation within a container. The navigation
3331           * elements need to match the carousel navigation class names.
3332           *
3333           * @method parseCarouselNavigation
3334           * @param parent {HTMLElement} The parent element to look under
3335           * @return {Boolean} True if at least one is found, false otherwise
3336           * @protected
3337           */
3338          _parseCarouselNavigation: function (parent) {
3339              var carousel = this,
3340                  cfg,
3341                  cssClass = carousel.CLASSES,
3342                  el,
3343                  i,
3344                  j,
3345                  nav,
3346                  rv       = false;
3347  
3348              nav = Dom.getElementsByClassName(cssClass.PREV_PAGE, "*", parent);
3349              if (nav.length > 0) {
3350                  for (i in nav) {
3351                      if (nav.hasOwnProperty(i)) {
3352                          el = nav[i];
3353                          YAHOO.log("Found Carousel previous page navigation - " +
3354                                  el + (el.id ? " (#" + el.id + ")" : ""),
3355                                  WidgetName);
3356                          if (el.nodeName == "INPUT" ||
3357                              el.nodeName == "BUTTON" ||
3358                              el.nodeName == "A") {// Anchor support in Nav (for SEO)
3359                              carousel._navBtns.prev.push(el);
3360                          } else {
3361                              j = el.getElementsByTagName("INPUT");
3362                              if (JS.isArray(j) && j.length > 0) {
3363                                  carousel._navBtns.prev.push(j[0]);
3364                              } else {
3365                                  j = el.getElementsByTagName("BUTTON");
3366                                  if (JS.isArray(j) && j.length > 0) {
3367                                      carousel._navBtns.prev.push(j[0]);
3368                                  }
3369                              }
3370                          }
3371                      }
3372                  }
3373                  cfg = { prev: nav };
3374              }
3375  
3376              nav = Dom.getElementsByClassName(cssClass.NEXT_PAGE, "*", parent);
3377              if (nav.length > 0) {
3378                  for (i in nav) {
3379                      if (nav.hasOwnProperty(i)) {
3380                          el = nav[i];
3381                          YAHOO.log("Found Carousel next page navigation - " +
3382                                  el + (el.id ? " (#" + el.id + ")" : ""),
3383                                  WidgetName);
3384                          if (el.nodeName == "INPUT" ||
3385                              el.nodeName == "BUTTON" ||
3386                              el.nodeName == "A") {// Anchor support in Nav (for SEO)
3387                              carousel._navBtns.next.push(el);
3388                          } else {
3389                              j = el.getElementsByTagName("INPUT");
3390                              if (JS.isArray(j) && j.length > 0) {
3391                                  carousel._navBtns.next.push(j[0]);
3392                              } else {
3393                                  j = el.getElementsByTagName("BUTTON");
3394                                  if (JS.isArray(j) && j.length > 0) {
3395                                      carousel._navBtns.next.push(j[0]);
3396                                  }
3397                              }
3398                          }
3399                      }
3400                  }
3401                  if (cfg) {
3402                      cfg.next = nav;
3403                  } else {
3404                      cfg = { next: nav };
3405                  }
3406              }
3407  
3408              if (cfg) {
3409                  carousel.set("navigation", cfg);
3410                  rv = true;
3411              }
3412  
3413              return rv;
3414          },
3415  
3416          /**
3417           * Refresh the widget UI if it is not already rendered, on first item
3418           * addition.
3419           *
3420           * @method _refreshUi
3421           * @protected
3422           */
3423          _refreshUi: function () {
3424              var carousel = this,
3425                  isVertical = carousel.get("isVertical"),
3426                  firstVisible = carousel.get("firstVisible"),
3427                  i, item, n, rsz, sz;
3428  
3429              if (carousel._itemsTable.numItems < 1) {
3430                  return;
3431              }
3432  
3433              sz  = getCarouselItemSize.call(carousel,
3434                      isVertical ? "height" : "width");
3435              // This fixes the widget to auto-adjust height/width for absolute
3436              // positioned children.
3437              item = carousel._itemsTable.items[firstVisible].id;
3438  
3439              sz   = isVertical ? getStyle(item, "width") :
3440                      getStyle(item, "height");
3441  
3442              Dom.setStyle(carousel._carouselEl,
3443                           isVertical ? "width" : "height", sz + "px");
3444  
3445              // Set the rendered state appropriately.
3446              carousel._hasRendered = true;
3447              carousel.fireEvent(renderEvent);
3448          },
3449  
3450          /**
3451           * Set the Carousel offset to the passed offset.
3452           *
3453           * @method _setCarouselOffset
3454           * @protected
3455           */
3456          _setCarouselOffset: function (offset) {
3457              var carousel = this, which;
3458  
3459              which = carousel.get("isVertical") ? "top" : "left";
3460              Dom.setStyle(carousel._carouselEl, which, offset + "px");
3461          },
3462  
3463          /**
3464           * Setup/Create the Carousel navigation element (if needed).
3465           *
3466           * @method _setupCarouselNavigation
3467           * @protected
3468           */
3469          _setupCarouselNavigation: function () {
3470              var carousel = this,
3471                  btn, cfg, cssClass, nav, navContainer, nextButton, prevButton;
3472  
3473              cssClass = carousel.CLASSES;
3474  
3475              // TODO: can the _navBtns be tested against instead?
3476              navContainer = Dom.getElementsByClassName(cssClass.NAVIGATION,
3477                      "DIV", carousel.get("element"));
3478  
3479              if (navContainer.length === 0) {
3480                  navContainer = createElement("DIV",
3481                          { className: cssClass.NAVIGATION });
3482                  carousel.insertBefore(navContainer,
3483                          Dom.getFirstChild(carousel.get("element")));
3484              } else {
3485                  navContainer = navContainer[0];
3486              }
3487  
3488              carousel._pages.el = createElement("UL");
3489              navContainer.appendChild(carousel._pages.el);
3490  
3491              nav = carousel.get("navigation");
3492              if (JS.isString(nav.prev) || JS.isArray(nav.prev)) {
3493                  if (JS.isString(nav.prev)) {
3494                      nav.prev = [nav.prev];
3495                  }
3496                  for (btn in nav.prev) {
3497                      if (nav.prev.hasOwnProperty(btn)) {
3498                          carousel._navBtns.prev.push(Dom.get(nav.prev[btn]));
3499                      }
3500                  }
3501              } else {
3502                  // TODO: separate method for creating a navigation button
3503                  prevButton = createElement("SPAN",
3504                          { className: cssClass.BUTTON + cssClass.FIRST_NAV });
3505                  // XXX: for IE 6.x
3506                  Dom.setStyle(prevButton, "visibility", "visible");
3507                  btn = Dom.generateId();
3508                  prevButton.innerHTML = "<button type=\"button\" "      +
3509                          "id=\"" + btn + "\" name=\""                   +
3510                          carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "\">"  +
3511                          carousel.STRINGS.PREVIOUS_BUTTON_TEXT + "</button>";
3512                  navContainer.appendChild(prevButton);
3513                  btn = Dom.get(btn);
3514                  carousel._navBtns.prev = [btn];
3515                  cfg = { prev: [prevButton] };
3516              }
3517  
3518              if (JS.isString(nav.next) || JS.isArray(nav.next)) {
3519                  if (JS.isString(nav.next)) {
3520                      nav.next = [nav.next];
3521                  }
3522                  for (btn in nav.next) {
3523                      if (nav.next.hasOwnProperty(btn)) {
3524                          carousel._navBtns.next.push(Dom.get(nav.next[btn]));
3525                      }
3526                  }
3527              } else {
3528                  // TODO: separate method for creating a navigation button
3529                  nextButton = createElement("SPAN",
3530                          { className: cssClass.BUTTON + cssClass.NEXT_NAV });
3531                  // XXX: for IE 6.x
3532                  Dom.setStyle(nextButton, "visibility", "visible");
3533                  btn = Dom.generateId();
3534                  nextButton.innerHTML = "<button type=\"button\" "      +
3535                          "id=\"" + btn + "\" name=\""                   +
3536                          carousel.STRINGS.NEXT_BUTTON_TEXT + "\">"      +
3537                          carousel.STRINGS.NEXT_BUTTON_TEXT + "</button>";
3538                  navContainer.appendChild(nextButton);
3539                  btn = Dom.get(btn);
3540                  carousel._navBtns.next = [btn];
3541                  if (cfg) {
3542                      cfg.next = [nextButton];
3543                  } else {
3544                      cfg = { next: [nextButton] };
3545                  }
3546              }
3547  
3548              if (cfg) {
3549                  carousel.set("navigation", cfg);
3550              }
3551  
3552              return navContainer;
3553          },
3554  
3555          /**
3556           * Set the clip container size (based on the new numVisible value).
3557           *
3558           * @method _setClipContainerSize
3559           * @param clip {HTMLElement} The clip container element.
3560           * @param num {Number} optional The number of items per page.
3561           * @protected
3562           */
3563          _setClipContainerSize: function (clip, num) {
3564              var carousel   = this,
3565                  isVertical = carousel.get("isVertical"),
3566                  rows       = carousel._rows,
3567                  cols       = carousel._cols,
3568                  reveal     = carousel.get("revealAmount"),
3569                  itemHeight = getCarouselItemSize.call(carousel, "height"),
3570                  itemWidth  = getCarouselItemSize.call(carousel, "width"),
3571                  containerHeight,
3572                  containerWidth;
3573  
3574              carousel._recomputeSize = (containerHeight === 0); // bleh!
3575              if (carousel._recomputeSize) {
3576                  carousel._hasRendered = false;
3577                  return;             // no use going further, bail out!
3578              }
3579  
3580              clip = clip || carousel._clipEl;
3581  
3582              if (rows) {
3583                   containerHeight = itemHeight * rows;
3584                   containerWidth  = itemWidth  * cols;
3585              } else {
3586                  num = num || carousel.get("numVisible");
3587                  if (isVertical) {
3588                      containerHeight = itemHeight * num;
3589                  } else {
3590                      containerWidth  = itemWidth  * num;
3591                  }
3592              }
3593  
3594              reveal = getRevealSize.call(carousel);
3595              if (isVertical) {
3596                  containerHeight += (reveal * 2);
3597              } else {
3598                  containerWidth  += (reveal * 2);
3599              }
3600  
3601              if (isVertical) {
3602                  containerHeight += getDimensions(carousel._carouselEl,"height");
3603                  Dom.setStyle(clip, "height", containerHeight + "px");
3604                  // For multi-row Carousel
3605                  if (cols) {
3606                      containerWidth += getDimensions(carousel._carouselEl,
3607                              "width");
3608                      Dom.setStyle(clip, "width", containerWidth + (0) + "px");
3609                  }
3610              } else {
3611                  containerWidth += getDimensions(carousel._carouselEl, "width");
3612                  Dom.setStyle(clip, "width", containerWidth + "px");
3613                  // For multi-row Carousel
3614                  if (rows) {
3615                      containerHeight += getDimensions(carousel._carouselEl,
3616                              "height");
3617                      Dom.setStyle(clip, "height", containerHeight + "px");
3618                  }
3619              }
3620  
3621              if (clip) {
3622                  carousel._setContainerSize(clip); // adjust the container size
3623              }
3624          },
3625  
3626          /**
3627           * Set the container size.
3628           *
3629           * @method _setContainerSize
3630           * @param clip {HTMLElement} The clip container element.
3631           * @param attr {String} Either set the height or width.
3632           * @protected
3633           */
3634          _setContainerSize: function (clip, attr) {
3635              var carousel = this,
3636                  config   = carousel.CONFIG,
3637                  cssClass = carousel.CLASSES,
3638                  isVertical,
3639                  rows,
3640                  cols,
3641                  size;
3642  
3643              isVertical = carousel.get("isVertical");
3644              rows       = carousel._rows;
3645              cols       = carousel._cols;
3646              clip       = clip || carousel._clipEl;
3647              attr       = attr || (isVertical ? "height" : "width");
3648              size       = parseFloat(Dom.getStyle(clip, attr), 10);
3649  
3650              size = JS.isNumber(size) ? size : 0;
3651  
3652              if (isVertical) {
3653                  size += getDimensions(carousel._carouselEl, "height") +
3654                          getStyle(carousel._navEl, "height");
3655              } else {
3656                  size += getDimensions(carousel._carouselEl, "width");
3657              }
3658  
3659              if (!isVertical) {
3660                  if (size < config.HORZ_MIN_WIDTH) {
3661                      size = config.HORZ_MIN_WIDTH;
3662                      carousel.addClass(cssClass.MIN_WIDTH);
3663                  }
3664              }
3665              carousel.setStyle(attr,  size + "px");
3666  
3667              // Additionally the width of the container should be set for
3668              // the vertical Carousel
3669              if (isVertical) {
3670                  size = getCarouselItemSize.call(carousel, "width");
3671                  if(cols) {
3672                      size = size * cols;
3673                  }
3674                  // Bug fix for vertical carousel (goes in conjunction with
3675                  // .yui-carousel-element {... 3200px removed from styles), and
3676                  // allows for multirows in IEs).
3677                  Dom.setStyle(carousel._carouselEl, "width", size + "px");
3678                  if (size < config.VERT_MIN_WIDTH) {
3679                      size = config.VERT_MIN_WIDTH;
3680                      // set a min width on vertical carousel, don't see why this
3681                      // shouldn't always be set...
3682                      carousel.addClass(cssClass.MIN_WIDTH);
3683                  }
3684                  carousel.setStyle("width",  size + "px");
3685              } else {
3686                  /*
3687                   * Fix for automatically computing the height and width in IE.
3688                   * Many thanks to ErisDS for the fix.
3689                   * For more information visit,
3690                   * http://erisds.co.uk/code/yui2-javascript-carousel-an-update-about-version-2-8
3691                   */
3692                  size = getCarouselItemSize.call(carousel, "height");
3693                  if (rows) {
3694                      size = size * rows;
3695                  }
3696                  Dom.setStyle(carousel._carouselEl, "height", size + "px");
3697              }
3698          },
3699  
3700          /**
3701           * Set the value for the Carousel's first visible item.
3702           *
3703           * @method _setFirstVisible
3704           * @param val {Number} The new value for firstVisible
3705           * @return {Number} The new value that would be set
3706           * @protected
3707           */
3708          _setFirstVisible: function (val) {
3709              var carousel = this;
3710  
3711              if (val >= 0 && val < carousel.get("numItems")) {
3712                  carousel.scrollTo(val);
3713              } else {
3714                  val = carousel.get("firstVisible");
3715              }
3716              return val;
3717          },
3718  
3719          /**
3720           * Set the value for the Carousel's navigation.
3721           *
3722           * @method _setNavigation
3723           * @param cfg {Object} The navigation configuration
3724           * @return {Object} The new value that would be set
3725           * @protected
3726           */
3727          _setNavigation: function (cfg) {
3728              var carousel = this;
3729  
3730              if (cfg.prev) {
3731                  Event.on(cfg.prev, "click", scrollPageBackward, carousel);
3732              }
3733              if (cfg.next) {
3734                  Event.on(cfg.next, "click", scrollPageForward, carousel);
3735              }
3736          },
3737  
3738          /**
3739           * Clip the container size every time numVisible is set.
3740           *
3741           * @method _setNumVisible
3742           * @param val {Number} The new value for numVisible
3743           * @return {Number} The new value that would be set
3744           * @protected
3745           */
3746          _setNumVisible: function (val) { // TODO: _setNumVisible should just be reserved for setting numVisible.
3747              var carousel = this;
3748  
3749              carousel._setClipContainerSize(carousel._clipEl, val);
3750          },
3751  
3752          /**
3753           * Set the value for the number of visible items in the Carousel.
3754           *
3755           * @method _numVisibleSetter
3756           * @param val {Number} The new value for numVisible
3757           * @return {Number} The new value that would be set
3758           * @protected
3759           */
3760          _numVisibleSetter: function (val) {
3761              var carousel = this,
3762                  numVisible = val;
3763  
3764              if(JS.isArray(val)) {
3765                  carousel._cols = val[0];
3766                  carousel._rows = val[1];
3767                  numVisible = val[0] *  val[1];
3768              }
3769              return numVisible;
3770          },
3771  
3772          /**
3773           * Set the value for selectedItem.
3774           *
3775           * @method _selectedItemSetter
3776           * @param val {Number} The new value for selectedItem
3777           * @return {Number} The new value that would be set
3778           * @protected
3779           */
3780          _selectedItemSetter: function (val) {
3781              var carousel = this;
3782              return (val < carousel.get("numItems")) ? val : 0;
3783          },
3784  
3785          /**
3786           * Set the number of items in the Carousel.
3787           * Warning: Setting this to a lower number than the current removes
3788           * items from the end.
3789           *
3790           * @method _setNumItems
3791           * @param val {Number} The new value for numItems
3792           * @return {Number} The new value that would be set
3793           * @protected
3794           */
3795          _setNumItems: function (val) {
3796              var carousel = this,
3797                  num      = carousel._itemsTable.numItems;
3798  
3799              if (JS.isArray(carousel._itemsTable.items)) {
3800                  if (carousel._itemsTable.items.length != num) { // out of sync
3801                      num = carousel._itemsTable.items.length;
3802                      carousel._itemsTable.numItems = num;
3803                  }
3804              }
3805  
3806              if (val < num) {
3807                  while (num > val) {
3808                      carousel.removeItem(num - 1);
3809                      num--;
3810                  }
3811              }
3812  
3813              return val;
3814          },
3815  
3816          /**
3817           * Set the orientation of the Carousel.
3818           *
3819           * @method _setOrientation
3820           * @param val {Boolean} The new value for isVertical
3821           * @return {Boolean} The new value that would be set
3822           * @protected
3823           */
3824          _setOrientation: function (val) {
3825              var carousel = this,
3826                  cssClass = carousel.CLASSES;
3827  
3828              if (val) {
3829                  carousel.replaceClass(cssClass.HORIZONTAL, cssClass.VERTICAL);
3830              } else {
3831                  carousel.replaceClass(cssClass.VERTICAL, cssClass.HORIZONTAL);
3832              }
3833              /*
3834                  The _itemAttrCache need not be emptied since the cache is for
3835                  DOM attributes that do not change; not the Carousel dimensions.
3836              */
3837  
3838              return val;
3839          },
3840  
3841          /**
3842           * Set the value for the reveal amount percentage in the Carousel.
3843           *
3844           * @method _setRevealAmount
3845           * @param val {Number} The new value for revealAmount
3846           * @return {Number} The new value that would be set
3847           * @protected
3848           */
3849          _setRevealAmount: function (val) {
3850              var carousel = this;
3851  
3852              if (val >= 0 && val <= 100) {
3853                  val = parseInt(val, 10);
3854                  val = JS.isNumber(val) ? val : 0;
3855                  carousel._setClipContainerSize();
3856              } else {
3857                  val = carousel.get("revealAmount");
3858              }
3859              return val;
3860          },
3861  
3862          /**
3863           * Set the value for the selected item.
3864           *
3865           * @method _setSelectedItem
3866           * @param val {Number} The new value for "selected" item
3867           * @protected
3868           */
3869          _setSelectedItem: function (val) {
3870              this._selectedItem = val;
3871          },
3872  
3873          /**
3874           * Get the total number of pages.
3875           *
3876           * @method _getNumPages
3877           * @protected
3878           */
3879          _getNumPages: function () {
3880              return Math.ceil(
3881                  parseInt(this.get("numItems"),10) / parseInt(this.get("numVisible"),10)
3882              );
3883          },
3884  
3885          /**
3886           * Get the last visible item.
3887           *
3888           * @method _getLastVisible
3889           * @protected
3890           */
3891          _getLastVisible: function () {
3892              var carousel = this;
3893              return carousel.get("currentPage") + 1 == carousel.get("numPages") ?
3894                     carousel.get("numItems") - 1:
3895                     carousel.get("firstVisible") + carousel.get("numVisible") - 1;
3896          },
3897  
3898          /**
3899           * Synchronize and redraw the UI after an item is added.
3900           *
3901           * @method _syncUiForItemAdd
3902           * @protected
3903           */
3904          _syncUiForItemAdd: function (obj) {
3905              var attr,
3906                  carousel   = this,
3907                  carouselEl = carousel._carouselEl,
3908                  el,
3909                  item,
3910                  itemsTable = carousel._itemsTable,
3911                  oel,
3912                  pos,
3913                  sibling,
3914                  styles;
3915  
3916              pos  = JS.isUndefined(obj.pos) ?
3917                     obj.newPos || itemsTable.numItems - 1 : obj.pos;
3918  
3919              if (!oel) {
3920                  item = itemsTable.items[pos] || {};
3921                  el = carousel._createCarouselItem({
3922                          className : item.className,
3923                          styles    : item.styles,
3924                          content   : item.item,
3925                          id        : item.id,
3926                          pos       : pos
3927                  });
3928                  if (JS.isUndefined(obj.pos)) {
3929                      if (!JS.isUndefined(itemsTable.loading[pos])) {
3930                          oel = itemsTable.loading[pos];
3931                          // if oel is null, it is a problem ...
3932                      }
3933                      if (oel) {
3934                          // replace the node
3935                          carouselEl.replaceChild(el, oel);
3936                          // ... and remove the item from the data structure
3937                          delete itemsTable.loading[pos];
3938                      } else {
3939                          carouselEl.appendChild(el);
3940                      }
3941                  } else {
3942                      if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3943                          sibling = Dom.get(itemsTable.items[obj.pos + 1].id);
3944                      }
3945                      if (sibling) {
3946                          carouselEl.insertBefore(el, sibling);
3947                      } else {
3948                          YAHOO.log("Unable to find sibling","error",WidgetName);
3949                      }
3950                  }
3951              } else {
3952                  if (JS.isUndefined(obj.pos)) {
3953                      if (!Dom.isAncestor(carousel._carouselEl, oel)) {
3954                          carouselEl.appendChild(oel);
3955                      }
3956                  } else {
3957                      if (!Dom.isAncestor(carouselEl, oel)) {
3958                          if (!JS.isUndefined(itemsTable.items[obj.pos + 1])) {
3959                              carouselEl.insertBefore(oel,
3960                                      Dom.get(itemsTable.items[obj.pos + 1].id));
3961                          }
3962                      }
3963                  }
3964              }
3965  
3966              if (!carousel._hasRendered) {
3967                  carousel._refreshUi();
3968              }
3969  
3970              if (carousel.get("selectedItem") < 0) {
3971                  carousel.set("selectedItem", carousel.get("firstVisible"));
3972              }
3973  
3974              carousel._syncUiItems();
3975          },
3976  
3977          /**
3978           * Synchronize and redraw the UI after an item is replaced.
3979           *
3980           * @method _syncUiForItemReplace
3981           * @protected
3982           */
3983          _syncUiForItemReplace: function (o) {
3984              var carousel   = this,
3985                  carouselEl = carousel._carouselEl,
3986                  itemsTable = carousel._itemsTable,
3987                  pos        = o.pos,
3988                  item       = o.newItem,
3989                  oel        = o.oldItem,
3990                  el;
3991  
3992              el = carousel._createCarouselItem({
3993                  className : item.className,
3994                  styles    : item.styles,
3995                  content   : item.item,
3996                  id        : oel.id
3997              });
3998  
3999              // replace the current item's attributes
4000              if ((oel = Dom.get(oel.id))) { // testing assignment
4001                  oel.className = item.className;
4002                  oel.styles = item.styles;
4003                  oel.innerHTML = item.item;
4004  
4005                  itemsTable.items[pos] = el;
4006  
4007                  if (itemsTable.loading[pos]) {
4008                      itemsTable.numItems++;
4009                      delete itemsTable.loading[pos];
4010                  }
4011              }
4012              // TODO: should we add the item if oel is undefined?
4013  
4014              // sync shouldn't be necessary since we're replacing items that are already positioned
4015              //carousel._syncUiItems();
4016          },
4017  
4018          /**
4019           * Synchronize and redraw the UI after an item is removed.
4020           *
4021           * @method _syncUiForItemRemove
4022           * @protected
4023           */
4024          _syncUiForItemRemove: function (obj) {
4025              var carousel   = this,
4026                  carouselEl = carousel._carouselEl,
4027                  el, item, num, pos;
4028  
4029              num  = carousel.get("numItems");
4030              item = obj.item;
4031              pos  = obj.pos;
4032  
4033              if (item && (el = Dom.get(item.id))) {
4034                  if (el && Dom.isAncestor(carouselEl, el)) {
4035                      Event.purgeElement(el, true);
4036                      carouselEl.removeChild(el);
4037                  }
4038  
4039                  // nothing is done w/ pos after this, should we remove it?
4040                  if (carousel.get("selectedItem") == pos) {
4041                      pos = pos >= num ? num - 1 : pos;
4042                  }
4043              } else {
4044                  YAHOO.log("Unable to find item", "warn", WidgetName);
4045              }
4046  
4047              carousel._syncUiItems();
4048          },
4049  
4050          /**
4051           * Find the closest sibling to insert before
4052           *
4053           * @method _findClosestSibling
4054           * @protected
4055           */
4056          _findClosestSibling: function (pos) {
4057              var carousel   = this,
4058                  itemsTable = carousel._itemsTable,
4059                  len        = itemsTable.items.length,
4060                  j          = pos,
4061                  sibling;
4062  
4063              // attempt to find the next closest sibling
4064              while (j<len && !sibling) {
4065                  sibling = itemsTable.items[++j];
4066              }
4067  
4068              return sibling;
4069          },
4070  
4071          /**
4072           * Synchronize the items table for lazy loading.
4073           *
4074           * @method _syncUiForLazyLoading
4075           * @protected
4076           */
4077          _syncUiForLazyLoading: function (obj) {
4078              var carousel   = this,
4079                  carouselEl = carousel._carouselEl,
4080                  itemsTable = carousel._itemsTable,
4081                  len = itemsTable.items.length,
4082                  sibling = carousel._findClosestSibling(obj.last),
4083                  last = obj.last,
4084                  // only add DOM nodes for the currently visible items
4085                  // this eliminates uneccessary performance overhead
4086                  // but still allows loading styles to be applied to the items
4087                  first = last - carousel.get("numVisible") + 1,
4088                  el,
4089                  j;
4090  
4091              for (var i = first; i <= last; i++) {
4092                  if(!itemsTable.loading[i] && !itemsTable.items[i]){
4093                      el = carousel._createCarouselItem({
4094                              className : carousel.CLASSES.ITEM + " " + carousel.CLASSES.ITEM_LOADING,
4095                              content   : carousel.STRINGS.ITEM_LOADING_CONTENT,
4096                              id        : Dom.generateId()
4097                      });
4098                      if (el) {
4099                          if (sibling) {
4100                              sibling = Dom.get(sibling.id);
4101                              if (sibling) {
4102                                  carouselEl.insertBefore(el, sibling);
4103                              } else {
4104                                  YAHOO.log("Unable to find sibling", "error",
4105                                          WidgetName);
4106                              }
4107                          } else {
4108                              carouselEl.appendChild(el);
4109                          }
4110                      }
4111                      itemsTable.loading[i] = el;
4112                  }
4113              }
4114  
4115              carousel._syncUiItems();
4116          },
4117  
4118          /**
4119           * Redraw the UI for item positioning.
4120           *
4121           * @method _syncUiItems
4122           * @protected
4123           */
4124          _syncUiItems: function () {
4125  
4126              if(!syncUiOnItemInsert) {
4127                  return;
4128              }
4129  
4130              var attr,
4131                  carousel = this,
4132                  numItems = carousel.get("numItems"),
4133                  i,
4134                  itemsTable = carousel._itemsTable,
4135                  items = itemsTable.items,
4136                  loading = itemsTable.loading,
4137                  item,
4138                  styles,
4139                  updateStyles = false;
4140  
4141              for (i = 0; i < numItems; i++) {
4142                  item = items[i] || loading[i];
4143  
4144                  if (item && item.id) {
4145                      styles = getCarouselItemPosition.call(carousel, i);
4146                      item.styles = item.styles || {};
4147  
4148                      for (attr in styles) {
4149                          if(item.styles[attr] !== styles[attr])
4150                          {
4151                              updateStyles = true;
4152                              item.styles[attr] = styles[attr];
4153                          }
4154                      }
4155                      if(updateStyles)
4156                      {
4157                          setStyles(Dom.get(item.id), styles);
4158                      }
4159                      updateStyles = false;
4160                  }
4161              }
4162          },
4163  
4164          /**
4165           * Set the correct class for the navigation buttons.
4166           *
4167           * @method _updateNavButtons
4168           * @param el {Object} The target button
4169           * @param setFocus {Boolean} True to set focus ring, false otherwise.
4170           * @protected
4171           */
4172          _updateNavButtons: function (el, setFocus) {
4173              var children,
4174                  cssClass = this.CLASSES,
4175                  grandParent,
4176                  parent   = el.parentNode;
4177  
4178              if (!parent) {
4179                  return;
4180              }
4181              grandParent = parent.parentNode;
4182  
4183              if (el.nodeName.toUpperCase() == "BUTTON" &&
4184                  Dom.hasClass(parent, cssClass.BUTTON)) {
4185                  if (setFocus) {
4186                      if (grandParent) {
4187                          children = Dom.getChildren(grandParent);
4188                          if (children) {
4189                              Dom.removeClass(children, cssClass.FOCUSSED_BUTTON);
4190                          }
4191                      }
4192                      Dom.addClass(parent, cssClass.FOCUSSED_BUTTON);
4193                  } else {
4194                      Dom.removeClass(parent, cssClass.FOCUSSED_BUTTON);
4195                  }
4196              }
4197          },
4198  
4199          /**
4200           * Update the UI for the pager buttons based on the current page and
4201           * the number of pages.
4202           *
4203           * @method _updatePagerButtons
4204           * @protected
4205           */
4206           _updatePagerButtons: function () {
4207  
4208               if(!syncUiOnItemInsert) {
4209                  return;
4210               }
4211  
4212               var carousel = this,
4213                   css      = carousel.CLASSES,
4214                   cur      = carousel._pages.cur, // current page
4215                   el,
4216                   html,
4217                   i,
4218                   item,
4219                   n        = carousel.get("numVisible"),
4220                   num      = carousel._pages.num, // total pages
4221                   pager    = carousel._pages.el;  // the pager container element
4222  
4223               if (num === 0 || !pager) {
4224                   return;         // don't do anything if number of pages is 0
4225               }
4226  
4227               // Hide the pager before redrawing it
4228               Dom.setStyle(pager, "visibility", "hidden");
4229  
4230               // Remove all nodes from the pager
4231               while (pager.firstChild) {
4232                   pager.removeChild(pager.firstChild);
4233               }
4234  
4235               for (i = 0; i < num; i++) {
4236  
4237                   el   = document.createElement("LI");
4238  
4239                   if (i === 0) {
4240                       Dom.addClass(el, css.FIRST_PAGE);
4241                   }
4242                   if (i == cur) {
4243                       Dom.addClass(el, css.SELECTED_NAV);
4244                   }
4245  
4246                   html = "<a class=" + css.PAGER_ITEM + " href=\"#" + (i+1) + "\" tabindex=\"0\"><em>"   +
4247                           carousel.STRINGS.PAGER_PREFIX_TEXT + " " + (i+1) +
4248                           "</em></a>";
4249                   el.innerHTML = html;
4250  
4251                   pager.appendChild(el);
4252               }
4253  
4254               // Show the pager now
4255               Dom.setStyle(pager, "visibility", "visible");
4256           },
4257  
4258          /**
4259           * Update the UI for the pager menu based on the current page and
4260           * the number of pages.  If the number of pages is greater than
4261           * MAX_PAGER_BUTTONS, then the selection of pages is provided by a drop
4262           * down menu instead of a set of buttons.
4263           *
4264           * @method _updatePagerMenu
4265           * @protected
4266           */
4267          _updatePagerMenu: function () {
4268              var carousel = this,
4269                  css      = carousel.CLASSES,
4270                  cur      = carousel._pages.cur, // current page
4271                  el,
4272                  i,
4273                  item,
4274                  n        = carousel.get("numVisible"),
4275                  num      = carousel._pages.num, // total pages
4276                  pager    = carousel._pages.el,  // the pager container element
4277                  sel;
4278  
4279              if (num === 0 || !pager) {
4280                  return;// don't do anything if number of pages is 0
4281              }
4282  
4283              sel = document.createElement("SELECT");
4284  
4285  
4286              if (!sel) {
4287                  YAHOO.log("Unable to create the pager menu", "error",
4288                            WidgetName);
4289                  return;
4290              }
4291  
4292              // Hide the pager before redrawing it
4293              Dom.setStyle(pager, "visibility", "hidden");
4294  
4295              // Remove all nodes from the pager
4296              while (pager.firstChild) {
4297                  pager.removeChild(pager.firstChild);
4298              }
4299  
4300              for (i = 0; i < num; i++) {
4301  
4302                  el   = document.createElement("OPTION");
4303                  el.value     = i+1;
4304                  el.innerHTML = carousel.STRINGS.PAGER_PREFIX_TEXT+" "+(i+1);
4305  
4306                  if (i == cur) {
4307                      el.setAttribute("selected", "selected");
4308                  }
4309  
4310                  sel.appendChild(el);
4311              }
4312  
4313              el = document.createElement("FORM");
4314              if (!el) {
4315                  YAHOO.log("Unable to create the pager menu", "error",
4316                            WidgetName);
4317              } else {
4318                  el.appendChild(sel);
4319                  pager.appendChild(el);
4320              }
4321  
4322              // Show the pager now
4323              Event.addListener(sel, "change", carousel._pagerChangeHandler, this, true);
4324              Dom.setStyle(pager, "visibility", "visible");
4325          },
4326  
4327          /**
4328           * Set the correct tab index for the Carousel items.
4329           *
4330           * @method _updateTabIndex
4331           * @param el {Object} The element to be focussed
4332           * @protected
4333           */
4334          _updateTabIndex: function (el) {
4335              var carousel = this;
4336  
4337              if (el) {
4338                  if (carousel._focusableItemEl) {
4339                      carousel._focusableItemEl.tabIndex = -1;
4340                  }
4341                  carousel._focusableItemEl = el;
4342                  el.tabIndex = 0;
4343              }
4344          },
4345  
4346          /**
4347           * Validate animation parameters.
4348           *
4349           * @method _validateAnimation
4350           * @param cfg {Object} The animation configuration
4351           * @return {Boolean} The status of the validation
4352           * @protected
4353           */
4354          _validateAnimation: function (cfg) {
4355              var rv = true;
4356  
4357              if (JS.isObject(cfg)) {
4358                  if (cfg.speed) {
4359                      rv = rv && JS.isNumber(cfg.speed);
4360                  }
4361                  if (cfg.effect) {
4362                      rv = rv && JS.isFunction(cfg.effect);
4363                  } else if (!JS.isUndefined(YAHOO.util.Easing)) {
4364                      cfg.effect = YAHOO.util.Easing.easeOut;
4365                  }
4366              } else {
4367                  rv = false;
4368              }
4369  
4370              return rv;
4371          },
4372  
4373          /**
4374           * Validate the firstVisible value.
4375           *
4376           * @method _validateFirstVisible
4377           * @param val {Number} The first visible value
4378           * @return {Boolean} The status of the validation
4379           * @protected
4380           */
4381          _validateFirstVisible: function (val) {
4382              var carousel = this, numItems = carousel.get("numItems");
4383  
4384              if (JS.isNumber(val)) {
4385                  if (numItems === 0 && val == numItems) {
4386                      return true;
4387                  } else {
4388                      return (val >= 0 && val < numItems);
4389                  }
4390              }
4391  
4392              return false;
4393          },
4394  
4395          /**
4396           * Validate and navigation parameters.
4397           *
4398           * @method _validateNavigation
4399           * @param cfg {Object} The navigation configuration
4400           * @return {Boolean} The status of the validation
4401           * @protected
4402           */
4403          _validateNavigation : function (cfg) {
4404              var i;
4405  
4406              if (!JS.isObject(cfg)) {
4407                  return false;
4408              }
4409  
4410              if (cfg.prev) {
4411                  if (!JS.isArray(cfg.prev)) {
4412                      return false;
4413                  }
4414                  for (i in cfg.prev) {
4415                      if (cfg.prev.hasOwnProperty(i)) {
4416                          if (!JS.isString(cfg.prev[i].nodeName)) {
4417                              return false;
4418                          }
4419                      }
4420                  }
4421              }
4422  
4423              if (cfg.next) {
4424                  if (!JS.isArray(cfg.next)) {
4425                      return false;
4426                  }
4427                  for (i in cfg.next) {
4428                      if (cfg.next.hasOwnProperty(i)) {
4429                          if (!JS.isString(cfg.next[i].nodeName)) {
4430                              return false;
4431                          }
4432                      }
4433                  }
4434              }
4435  
4436              return true;
4437          },
4438  
4439          /**
4440           * Validate the numItems value.
4441           *
4442           * @method _validateNumItems
4443           * @param val {Number} The numItems value
4444           * @return {Boolean} The status of the validation
4445           * @protected
4446           */
4447          _validateNumItems: function (val) {
4448              return JS.isNumber(val) && (val >= 0);
4449          },
4450  
4451          /**
4452           * Validate the numVisible value.
4453           *
4454           * @method _validateNumVisible
4455           * @param val {Number} The numVisible value
4456           * @return {Boolean} The status of the validation
4457           * @protected
4458           */
4459          _validateNumVisible: function (val) {
4460              var rv = false;
4461  
4462              if (JS.isNumber(val)) {
4463                  rv = val > 0 && val <= this.get("numItems");
4464              } else if (JS.isArray(val)) {
4465                  if (JS.isNumber(val[0]) && JS.isNumber(val[1])) {
4466                      rv = val[0] * val[1] > 0 && val.length == 2;
4467                  }
4468              }
4469  
4470              return rv;
4471          },
4472  
4473          /**
4474           * Validate the revealAmount value.
4475           *
4476           * @method _validateRevealAmount
4477           * @param val {Number} The revealAmount value
4478           * @return {Boolean} The status of the validation
4479           * @protected
4480           */
4481          _validateRevealAmount: function (val) {
4482              var rv = false;
4483  
4484              if (JS.isNumber(val)) {
4485                  rv = val >= 0 && val < 100;
4486              }
4487  
4488              return rv;
4489          },
4490  
4491          /**
4492           * Validate the scrollIncrement value.
4493           *
4494           * @method _validateScrollIncrement
4495           * @param val {Number} The scrollIncrement value
4496           * @return {Boolean} The status of the validation
4497           * @protected
4498           */
4499          _validateScrollIncrement: function (val) {
4500              var rv = false;
4501  
4502              if (JS.isNumber(val)) {
4503                  rv = (val > 0 && val < this.get("numItems"));
4504              }
4505  
4506              return rv;
4507          }
4508  
4509      });
4510  
4511  })();
4512  /*
4513  ;;  Local variables: **
4514  ;;  mode: js2 **
4515  ;;  indent-tabs-mode: nil **
4516  ;;  End: **
4517  */
4518  YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"});
4519  YAHOO.register("carousel", YAHOO.widget.Carousel, {version: "2.9.0", build: "2800"});
4520  
4521  }, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-dom", "yui2-event", "yui2-element", "yui2-skin-sam-carousel"], "optional": ["yui2-animation"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1