[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

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