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