Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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"]});