source: sandbox/2.2.0.2/library/jcarousel/lib/jquery.jcarousel.js @ 4492

Revision 4492, 34.4 KB checked in by adriano, 13 years ago (diff)

Ticket #812 - Melhorias nas interfaces de leitura e composiacao de email do Expresso Mail

Line 
1/*!
2 * jCarousel - Riding carousels with jQuery
3 *   http://sorgalla.com/jcarousel/
4 *
5 * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
6 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
7 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
8 *
9 * Built on top of the jQuery library
10 *   http://jquery.com
11 *
12 * Inspired by the "Carousel Component" by Bill Scott
13 *   http://billwscott.com/carousel/
14 */
15
16/*global window, jQuery */
17(function($) {
18    // Default configuration properties.
19    var defaults = {
20        vertical: false,
21        rtl: false,
22        start: 1,
23        offset: 1,
24        size: null,
25        scroll: 3,
26        visible: null,
27        animation: 'normal',
28        easing: 'swing',
29        auto: 0,
30        wrap: null,
31        initCallback: null,
32        setupCallback: null,
33        reloadCallback: null,
34        itemLoadCallback: null,
35        itemFirstInCallback: null,
36        itemFirstOutCallback: null,
37        itemLastInCallback: null,
38        itemLastOutCallback: null,
39        itemVisibleInCallback: null,
40        itemVisibleOutCallback: null,
41        animationStepCallback: null,
42        buttonNextHTML: '<div></div>',
43        buttonPrevHTML: '<div></div>',
44        buttonNextEvent: 'click',
45        buttonPrevEvent: 'click',
46        buttonNextCallback: null,
47        buttonPrevCallback: null,
48        itemFallbackDimension: null
49    }, windowLoaded = false;
50
51    $(window).bind('load.jcarousel', function() { windowLoaded = true; });
52
53    /**
54     * The jCarousel object.
55     *
56     * @constructor
57     * @class jcarousel
58     * @param e {HTMLElement} The element to create the carousel for.
59     * @param o {Object} A set of key/value pairs to set as configuration properties.
60     * @cat Plugins/jCarousel
61     */
62    $.jcarousel = function(e, o) {
63        this.options    = $.extend({}, defaults, o || {});
64
65        this.locked          = false;
66        this.autoStopped     = false;
67
68        this.container       = null;
69        this.clip            = null;
70        this.list            = null;
71        this.buttonNext      = null;
72        this.buttonPrev      = null;
73        this.buttonNextState = null;
74        this.buttonPrevState = null;
75
76        // Only set if not explicitly passed as option
77        if (!o || o.rtl === undefined) {
78            this.options.rtl = ($(e).attr('dir') || $('html').attr('dir') || '').toLowerCase() == 'rtl';
79        }
80
81        this.wh = !this.options.vertical ? 'width' : 'height';
82        this.lt = !this.options.vertical ? (this.options.rtl ? 'right' : 'left') : 'top';
83
84        // Extract skin class
85        var skin = '', split = e.className.split(' ');
86
87        for (var i = 0; i < split.length; i++) {
88            if (split[i].indexOf('jcarousel-skin') != -1) {
89                $(e).removeClass(split[i]);
90                skin = split[i];
91                break;
92            }
93        }
94
95        if (e.nodeName.toUpperCase() == 'UL' || e.nodeName.toUpperCase() == 'OL') {
96            this.list      = $(e);
97            this.clip      = this.list.parents('.jcarousel-clip');
98            this.container = this.list.parents('.jcarousel-container');
99        } else {
100            this.container = $(e);
101            this.list      = this.container.find('ul,ol').eq(0);
102            this.clip      = this.container.find('.jcarousel-clip');
103        }
104
105        if (this.clip.size() === 0) {
106            this.clip = this.list.wrap('<div></div>').parent();
107        }
108
109        if (this.container.size() === 0) {
110            this.container = this.clip.wrap('<div></div>').parent();
111        }
112
113        if (skin !== '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1) {
114            this.container.wrap('<div class=" '+ skin + '"></div>');
115        }
116
117        this.buttonPrev = $('.jcarousel-prev', this.container);
118
119        if (this.buttonPrev.size() === 0 && this.options.buttonPrevHTML !== null) {
120            this.buttonPrev = $(this.options.buttonPrevHTML).appendTo(this.container);
121        }
122
123        this.buttonPrev.addClass(this.className('jcarousel-prev'));
124
125        this.buttonNext = $('.jcarousel-next', this.container);
126
127        if (this.buttonNext.size() === 0 && this.options.buttonNextHTML !== null) {
128            this.buttonNext = $(this.options.buttonNextHTML).appendTo(this.container);
129        }
130
131        this.buttonNext.addClass(this.className('jcarousel-next'));
132
133        this.clip.addClass(this.className('jcarousel-clip')).css({
134            position: 'relative'
135        });
136
137        this.list.addClass(this.className('jcarousel-list')).css({
138            overflow: 'hidden',
139            position: 'relative',
140            top: 0,
141            margin: 0,
142            padding: 0
143        }).css((this.options.rtl ? 'right' : 'left'), 0);
144
145        this.container.addClass(this.className('jcarousel-container')).css({
146            position: 'relative'
147        });
148
149        if (!this.options.vertical && this.options.rtl) {
150            this.container.addClass('jcarousel-direction-rtl').attr('dir', 'rtl');
151        }
152
153        var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null;
154        var li = this.list.children('li');
155
156        var self = this;
157
158        if (li.size() > 0) {
159            var wh = 0, j = this.options.offset;
160            li.each(function() {
161                self.format(this, j++);
162                wh += self.dimension(this, di);
163            });
164
165            this.list.css(this.wh, (wh + 100) + 'px');
166
167            // Only set if not explicitly passed as option
168            if (!o || o.size === undefined) {
169                this.options.size = li.size();
170            }
171        }
172
173        // For whatever reason, .show() does not work in Safari...
174        this.container.css('display', 'block');
175        this.buttonNext.css('display', 'block');
176        this.buttonPrev.css('display', 'block');
177
178        this.funcNext   = function() { self.next(); };
179        this.funcPrev   = function() { self.prev(); };
180        this.funcResize = function() {
181            if (self.resizeTimer) {
182                clearTimeout(self.resizeTimer);
183            }
184
185            self.resizeTimer = setTimeout(function() {
186                self.reload();
187            }, 100);
188        };
189
190        if (this.options.initCallback !== null) {
191            this.options.initCallback(this, 'init');
192        }
193
194        if (!windowLoaded && $.browser.safari) {
195            this.buttons(false, false);
196            $(window).bind('load.jcarousel', function() { self.setup(); });
197        } else {
198            this.setup();
199        }
200    };
201
202    // Create shortcut for internal use
203    var $jc = $.jcarousel;
204
205    $jc.fn = $jc.prototype = {
206        jcarousel: '0.2.8'
207    };
208
209    $jc.fn.extend = $jc.extend = $.extend;
210
211    $jc.fn.extend({
212        /**
213         * Setups the carousel.
214         *
215         * @method setup
216         * @return undefined
217         */
218        setup: function() {
219            this.first       = null;
220            this.last        = null;
221            this.prevFirst   = null;
222            this.prevLast    = null;
223            this.animating   = false;
224            this.timer       = null;
225            this.resizeTimer = null;
226            this.tail        = null;
227            this.inTail      = false;
228
229            if (this.locked) {
230                return;
231            }
232
233            this.list.css(this.lt, this.pos(this.options.offset) + 'px');
234            var p = this.pos(this.options.start, true);
235            this.prevFirst = this.prevLast = null;
236            this.animate(p, false);
237
238            $(window).unbind('resize.jcarousel', this.funcResize).bind('resize.jcarousel', this.funcResize);
239
240            if (this.options.setupCallback !== null) {
241                this.options.setupCallback(this);
242            }
243        },
244
245        /**
246         * Clears the list and resets the carousel.
247         *
248         * @method reset
249         * @return undefined
250         */
251        reset: function() {
252            this.list.empty();
253
254            this.list.css(this.lt, '0px');
255            this.list.css(this.wh, '10px');
256
257            if (this.options.initCallback !== null) {
258                this.options.initCallback(this, 'reset');
259            }
260
261            this.setup();
262        },
263
264        /**
265         * Reloads the carousel and adjusts positions.
266         *
267         * @method reload
268         * @return undefined
269         */
270        reload: function() {
271            if (this.tail !== null && this.inTail) {
272                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail);
273            }
274
275            this.tail   = null;
276            this.inTail = false;
277
278            if (this.options.reloadCallback !== null) {
279                this.options.reloadCallback(this);
280            }
281
282            if (this.options.visible !== null) {
283                var self = this;
284                var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0;
285                this.list.children('li').each(function(i) {
286                    wh += self.dimension(this, di);
287                    if (i + 1 < self.first) {
288                        lt = wh;
289                    }
290                });
291
292                this.list.css(this.wh, wh + 'px');
293                this.list.css(this.lt, -lt + 'px');
294            }
295
296            this.scroll(this.first, false);
297        },
298
299        /**
300         * Locks the carousel.
301         *
302         * @method lock
303         * @return undefined
304         */
305        lock: function() {
306            this.locked = true;
307            this.buttons();
308        },
309
310        /**
311         * Unlocks the carousel.
312         *
313         * @method unlock
314         * @return undefined
315         */
316        unlock: function() {
317            this.locked = false;
318            this.buttons();
319        },
320
321        /**
322         * Sets the size of the carousel.
323         *
324         * @method size
325         * @return undefined
326         * @param s {Number} The size of the carousel.
327         */
328        size: function(s) {
329            if (s !== undefined) {
330                this.options.size = s;
331                if (!this.locked) {
332                    this.buttons();
333                }
334            }
335
336            return this.options.size;
337        },
338
339        /**
340         * Checks whether a list element exists for the given index (or index range).
341         *
342         * @method get
343         * @return bool
344         * @param i {Number} The index of the (first) element.
345         * @param i2 {Number} The index of the last element.
346         */
347        has: function(i, i2) {
348            if (i2 === undefined || !i2) {
349                i2 = i;
350            }
351
352            if (this.options.size !== null && i2 > this.options.size) {
353                i2 = this.options.size;
354            }
355
356            for (var j = i; j <= i2; j++) {
357                var e = this.get(j);
358                if (!e.length || e.hasClass('jcarousel-item-placeholder')) {
359                    return false;
360                }
361            }
362
363            return true;
364        },
365
366        /**
367         * Returns a jQuery object with list element for the given index.
368         *
369         * @method get
370         * @return jQuery
371         * @param i {Number} The index of the element.
372         */
373        get: function(i) {
374            return $('>.jcarousel-item-' + i, this.list);
375        },
376
377        /**
378         * Adds an element for the given index to the list.
379         * If the element already exists, it updates the inner html.
380         * Returns the created element as jQuery object.
381         *
382         * @method add
383         * @return jQuery
384         * @param i {Number} The index of the element.
385         * @param s {String} The innerHTML of the element.
386         */
387        add: function(i, s) {
388            var e = this.get(i), old = 0, n = $(s);
389
390            if (e.length === 0) {
391                var c, j = $jc.intval(i);
392                e = this.create(i);
393                while (true) {
394                    c = this.get(--j);
395                    if (j <= 0 || c.length) {
396                        if (j <= 0) {
397                            this.list.prepend(e);
398                        } else {
399                            c.after(e);
400                        }
401                        break;
402                    }
403                }
404            } else {
405                old = this.dimension(e);
406            }
407
408            if (n.get(0).nodeName.toUpperCase() == 'LI') {
409                e.replaceWith(n);
410                e = n;
411            } else {
412                e.empty().append(s);
413            }
414
415            this.format(e.removeClass(this.className('jcarousel-item-placeholder')), i);
416
417            var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null;
418            var wh = this.dimension(e, di) - old;
419
420            if (i > 0 && i < this.first) {
421                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px');
422            }
423
424            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px');
425
426            return e;
427        },
428
429        /**
430         * Removes an element for the given index from the list.
431         *
432         * @method remove
433         * @return undefined
434         * @param i {Number} The index of the element.
435         */
436        remove: function(i) {
437            var e = this.get(i);
438
439            // Check if item exists and is not currently visible
440            if (!e.length || (i >= this.first && i <= this.last)) {
441                return;
442            }
443
444            var d = this.dimension(e);
445
446            if (i < this.first) {
447                this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px');
448            }
449
450            e.remove();
451
452            this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px');
453        },
454
455        /**
456         * Moves the carousel forwards.
457         *
458         * @method next
459         * @return undefined
460         */
461        next: function() {
462            if (this.tail !== null && !this.inTail) {
463                this.scrollTail(false);
464            } else {
465                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last') && this.options.size !== null && this.last == this.options.size) ? 1 : this.first + this.options.scroll);
466            }
467        },
468
469        /**
470         * Moves the carousel backwards.
471         *
472         * @method prev
473         * @return undefined
474         */
475        prev: function() {
476            if (this.tail !== null && this.inTail) {
477                this.scrollTail(true);
478            } else {
479                this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first') && this.options.size !== null && this.first == 1) ? this.options.size : this.first - this.options.scroll);
480            }
481        },
482
483        /**
484         * Scrolls the tail of the carousel.
485         *
486         * @method scrollTail
487         * @return undefined
488         * @param b {Boolean} Whether scroll the tail back or forward.
489         */
490        scrollTail: function(b) {
491            if (this.locked || this.animating || !this.tail) {
492                return;
493            }
494
495            this.pauseAuto();
496
497            var pos  = $jc.intval(this.list.css(this.lt));
498
499            pos = !b ? pos - this.tail : pos + this.tail;
500            this.inTail = !b;
501
502            // Save for callbacks
503            this.prevFirst = this.first;
504            this.prevLast  = this.last;
505
506            this.animate(pos);
507        },
508
509        /**
510         * Scrolls the carousel to a certain position.
511         *
512         * @method scroll
513         * @return undefined
514         * @param i {Number} The index of the element to scoll to.
515         * @param a {Boolean} Flag indicating whether to perform animation.
516         */
517        scroll: function(i, a) {
518            if (this.locked || this.animating) {
519                return;
520            }
521
522            this.pauseAuto();
523            this.animate(this.pos(i), a);
524        },
525
526        /**
527         * Prepares the carousel and return the position for a certian index.
528         *
529         * @method pos
530         * @return {Number}
531         * @param i {Number} The index of the element to scoll to.
532         * @param fv {Boolean} Whether to force last item to be visible.
533         */
534        pos: function(i, fv) {
535            var pos  = $jc.intval(this.list.css(this.lt));
536
537            if (this.locked || this.animating) {
538                return pos;
539            }
540
541            if (this.options.wrap != 'circular') {
542                i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i);
543            }
544
545            var back = this.first > i;
546
547            // Create placeholders, new list width/height
548            // and new list position
549            var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
550            var c = back ? this.get(f) : this.get(this.last);
551            var j = back ? f : f - 1;
552            var e = null, l = 0, p = false, d = 0, g;
553
554            while (back ? --j >= i : ++j < i) {
555                e = this.get(j);
556                p = !e.length;
557                if (e.length === 0) {
558                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
559                    c[back ? 'before' : 'after' ](e);
560
561                    if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) {
562                        g = this.get(this.index(j));
563                        if (g.length) {
564                            e = this.add(j, g.clone(true));
565                        }
566                    }
567                }
568
569                c = e;
570                d = this.dimension(e);
571
572                if (p) {
573                    l += d;
574                }
575
576                if (this.first !== null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size === null || j <= this.options.size)))) {
577                    pos = back ? pos + d : pos - d;
578                }
579            }
580
581            // Calculate visible items
582            var clipping = this.clipping(), cache = [], visible = 0, v = 0;
583            c = this.get(i - 1);
584            j = i;
585
586            while (++visible) {
587                e = this.get(j);
588                p = !e.length;
589                if (e.length === 0) {
590                    e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
591                    // This should only happen on a next scroll
592                    if (c.length === 0) {
593                        this.list.prepend(e);
594                    } else {
595                        c[back ? 'before' : 'after' ](e);
596                    }
597
598                    if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) {
599                        g = this.get(this.index(j));
600                        if (g.length) {
601                            e = this.add(j, g.clone(true));
602                        }
603                    }
604                }
605
606                c = e;
607                d = this.dimension(e);
608                if (d === 0) {
609                    throw new Error('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
610                }
611
612                if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size) {
613                    cache.push(e);
614                } else if (p) {
615                    l += d;
616                }
617
618                v += d;
619
620                if (v >= clipping) {
621                    break;
622                }
623
624                j++;
625            }
626
627             // Remove out-of-range placeholders
628            for (var x = 0; x < cache.length; x++) {
629                cache[x].remove();
630            }
631
632            // Resize list
633            if (l > 0) {
634                this.list.css(this.wh, this.dimension(this.list) + l + 'px');
635
636                if (back) {
637                    pos -= l;
638                    this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px');
639                }
640            }
641
642            // Calculate first and last item
643            var last = i + visible - 1;
644            if (this.options.wrap != 'circular' && this.options.size && last > this.options.size) {
645                last = this.options.size;
646            }
647
648            if (j > last) {
649                visible = 0;
650                j = last;
651                v = 0;
652                while (++visible) {
653                    e = this.get(j--);
654                    if (!e.length) {
655                        break;
656                    }
657                    v += this.dimension(e);
658                    if (v >= clipping) {
659                        break;
660                    }
661                }
662            }
663
664            var first = last - visible + 1;
665            if (this.options.wrap != 'circular' && first < 1) {
666                first = 1;
667            }
668
669            if (this.inTail && back) {
670                pos += this.tail;
671                this.inTail = false;
672            }
673
674            this.tail = null;
675            if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) {
676                var m = $jc.intval(this.get(last).css(!this.options.vertical ? 'marginRight' : 'marginBottom'));
677                if ((v - m) > clipping) {
678                    this.tail = v - clipping - m;
679                }
680            }
681
682            if (fv && i === this.options.size && this.tail) {
683                pos -= this.tail;
684                this.inTail = true;
685            }
686
687            // Adjust position
688            while (i-- > first) {
689                pos += this.dimension(this.get(i));
690            }
691
692            // Save visible item range
693            this.prevFirst = this.first;
694            this.prevLast  = this.last;
695            this.first     = first;
696            this.last      = last;
697
698            return pos;
699        },
700
701        /**
702         * Animates the carousel to a certain position.
703         *
704         * @method animate
705         * @return undefined
706         * @param p {Number} Position to scroll to.
707         * @param a {Boolean} Flag indicating whether to perform animation.
708         */
709        animate: function(p, a) {
710            if (this.locked || this.animating) {
711                return;
712            }
713
714            this.animating = true;
715
716            var self = this;
717            var scrolled = function() {
718                self.animating = false;
719
720                if (p === 0) {
721                    self.list.css(self.lt,  0);
722                }
723
724                if (!self.autoStopped && (self.options.wrap == 'circular' || self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size === null || self.last < self.options.size || (self.last == self.options.size && self.tail !== null && !self.inTail))) {
725                    self.startAuto();
726                }
727
728                self.buttons();
729                self.notify('onAfterAnimation');
730
731                // This function removes items which are appended automatically for circulation.
732                // This prevents the list from growing infinitely.
733                if (self.options.wrap == 'circular' && self.options.size !== null) {
734                    for (var i = self.prevFirst; i <= self.prevLast; i++) {
735                        if (i !== null && !(i >= self.first && i <= self.last) && (i < 1 || i > self.options.size)) {
736                            self.remove(i);
737                        }
738                    }
739                }
740            };
741
742            this.notify('onBeforeAnimation');
743
744            // Animate
745            if (!this.options.animation || a === false) {
746                this.list.css(this.lt, p + 'px');
747                scrolled();
748            } else {
749                var o = !this.options.vertical ? (this.options.rtl ? {'right': p} : {'left': p}) : {'top': p};
750                // Define animation settings.
751                var settings = {
752                    duration: this.options.animation,
753                    easing:   this.options.easing,
754                    complete: scrolled
755                };
756                // If we have a step callback, specify it as well.
757                if ($.isFunction(this.options.animationStepCallback)) {
758                    settings.step = this.options.animationStepCallback;
759                }
760                // Start the animation.
761                this.list.animate(o, settings);
762            }
763        },
764
765        /**
766         * Starts autoscrolling.
767         *
768         * @method auto
769         * @return undefined
770         * @param s {Number} Seconds to periodically autoscroll the content.
771         */
772        startAuto: function(s) {
773            if (s !== undefined) {
774                this.options.auto = s;
775            }
776
777            if (this.options.auto === 0) {
778                return this.stopAuto();
779            }
780
781            if (this.timer !== null) {
782                return;
783            }
784
785            this.autoStopped = false;
786
787            var self = this;
788            this.timer = window.setTimeout(function() { self.next(); }, this.options.auto * 1000);
789        },
790
791        /**
792         * Stops autoscrolling.
793         *
794         * @method stopAuto
795         * @return undefined
796         */
797        stopAuto: function() {
798            this.pauseAuto();
799            this.autoStopped = true;
800        },
801
802        /**
803         * Pauses autoscrolling.
804         *
805         * @method pauseAuto
806         * @return undefined
807         */
808        pauseAuto: function() {
809            if (this.timer === null) {
810                return;
811            }
812
813            window.clearTimeout(this.timer);
814            this.timer = null;
815        },
816
817        /**
818         * Sets the states of the prev/next buttons.
819         *
820         * @method buttons
821         * @return undefined
822         */
823        buttons: function(n, p) {
824            if (n == null) {
825                n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size === null || this.last < this.options.size);
826                if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size !== null && this.last >= this.options.size) {
827                    n = this.tail !== null && !this.inTail;
828                }
829            }
830
831            if (p == null) {
832                p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
833                if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size !== null && this.first == 1) {
834                    p = this.tail !== null && this.inTail;
835                }
836            }
837
838            var self = this;
839
840            if (this.buttonNext.size() > 0) {
841                this.buttonNext.unbind(this.options.buttonNextEvent + '.jcarousel', this.funcNext);
842
843                if (n) {
844                    this.buttonNext.bind(this.options.buttonNextEvent + '.jcarousel', this.funcNext);
845                }
846
847                this.buttonNext[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
848
849                if (this.options.buttonNextCallback !== null && this.buttonNext.data('jcarouselstate') != n) {
850                    this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); }).data('jcarouselstate', n);
851                }
852            } else {
853                if (this.options.buttonNextCallback !== null && this.buttonNextState != n) {
854                    this.options.buttonNextCallback(self, null, n);
855                }
856            }
857
858            if (this.buttonPrev.size() > 0) {
859                this.buttonPrev.unbind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev);
860
861                if (p) {
862                    this.buttonPrev.bind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev);
863                }
864
865                this.buttonPrev[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true);
866
867                if (this.options.buttonPrevCallback !== null && this.buttonPrev.data('jcarouselstate') != p) {
868                    this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); }).data('jcarouselstate', p);
869                }
870            } else {
871                if (this.options.buttonPrevCallback !== null && this.buttonPrevState != p) {
872                    this.options.buttonPrevCallback(self, null, p);
873                }
874            }
875
876            this.buttonNextState = n;
877            this.buttonPrevState = p;
878        },
879
880        /**
881         * Notify callback of a specified event.
882         *
883         * @method notify
884         * @return undefined
885         * @param evt {String} The event name
886         */
887        notify: function(evt) {
888            var state = this.prevFirst === null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');
889
890            // Load items
891            this.callback('itemLoadCallback', evt, state);
892
893            if (this.prevFirst !== this.first) {
894                this.callback('itemFirstInCallback', evt, state, this.first);
895                this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
896            }
897
898            if (this.prevLast !== this.last) {
899                this.callback('itemLastInCallback', evt, state, this.last);
900                this.callback('itemLastOutCallback', evt, state, this.prevLast);
901            }
902
903            this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
904            this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
905        },
906
907        callback: function(cb, evt, state, i1, i2, i3, i4) {
908            if (this.options[cb] == null || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation')) {
909                return;
910            }
911
912            var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];
913
914            if (!$.isFunction(callback)) {
915                return;
916            }
917
918            var self = this;
919
920            if (i1 === undefined) {
921                callback(self, state, evt);
922            } else if (i2 === undefined) {
923                this.get(i1).each(function() { callback(self, this, i1, state, evt); });
924            } else {
925                var call = function(i) {
926                    self.get(i).each(function() { callback(self, this, i, state, evt); });
927                };
928                for (var i = i1; i <= i2; i++) {
929                    if (i !== null && !(i >= i3 && i <= i4)) {
930                        call(i);
931                    }
932                }
933            }
934        },
935
936        create: function(i) {
937            return this.format('<li></li>', i);
938        },
939
940        format: function(e, i) {
941            e = $(e);
942            var split = e.get(0).className.split(' ');
943            for (var j = 0; j < split.length; j++) {
944                if (split[j].indexOf('jcarousel-') != -1) {
945                    e.removeClass(split[j]);
946                }
947            }
948            e.addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i)).css({
949                'float': (this.options.rtl ? 'right' : 'left'),
950                'list-style': 'none'
951            }).attr('jcarouselindex', i);
952            return e;
953        },
954
955        className: function(c) {
956            return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical');
957        },
958
959        dimension: function(e, d) {
960            var el = $(e);
961
962            if (d == null) {
963                return !this.options.vertical ?
964                       (el.outerWidth(true) || $jc.intval(this.options.itemFallbackDimension)) :
965                       (el.outerHeight(true) || $jc.intval(this.options.itemFallbackDimension));
966            } else {
967                var w = !this.options.vertical ?
968                    d - $jc.intval(el.css('marginLeft')) - $jc.intval(el.css('marginRight')) :
969                    d - $jc.intval(el.css('marginTop')) - $jc.intval(el.css('marginBottom'));
970
971                $(el).css(this.wh, w + 'px');
972
973                return this.dimension(el);
974            }
975        },
976
977        clipping: function() {
978            return !this.options.vertical ?
979                this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) :
980                this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth'));
981        },
982
983        index: function(i, s) {
984            if (s == null) {
985                s = this.options.size;
986            }
987
988            return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1;
989        }
990    });
991
992    $jc.extend({
993        /**
994         * Gets/Sets the global default configuration properties.
995         *
996         * @method defaults
997         * @return {Object}
998         * @param d {Object} A set of key/value pairs to set as configuration properties.
999         */
1000        defaults: function(d) {
1001            return $.extend(defaults, d || {});
1002        },
1003
1004        intval: function(v) {
1005            v = parseInt(v, 10);
1006            return isNaN(v) ? 0 : v;
1007        },
1008
1009        windowLoaded: function() {
1010            windowLoaded = true;
1011        }
1012    });
1013
1014    /**
1015     * Creates a carousel for all matched elements.
1016     *
1017     * @example $("#mycarousel").jcarousel();
1018     * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul>
1019     * @result
1020     *
1021     * <div class="jcarousel-skin-name">
1022     *   <div class="jcarousel-container">
1023     *     <div class="jcarousel-clip">
1024     *       <ul class="jcarousel-list">
1025     *         <li class="jcarousel-item-1">First item</li>
1026     *         <li class="jcarousel-item-2">Second item</li>
1027     *       </ul>
1028     *     </div>
1029     *     <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div>
1030     *     <div class="jcarousel-next"></div>
1031     *   </div>
1032     * </div>
1033     *
1034     * @method jcarousel
1035     * @return jQuery
1036     * @param o {Hash|String} A set of key/value pairs to set as configuration properties or a method name to call on a formerly created instance.
1037     */
1038    $.fn.jcarousel = function(o) {
1039        if (typeof o == 'string') {
1040            var instance = $(this).data('jcarousel'), args = Array.prototype.slice.call(arguments, 1);
1041            return instance[o].apply(instance, args);
1042        } else {
1043            return this.each(function() {
1044                var instance = $(this).data('jcarousel');
1045                if (instance) {
1046                    if (o) {
1047                        $.extend(instance.options, o);
1048                    }
1049                    instance.reload();
1050                } else {
1051                    $(this).data('jcarousel', new $jc(this, o));
1052                }
1053            });
1054        }
1055    };
1056
1057})(jQuery);
Note: See TracBrowser for help on using the repository browser.