source: extensions/Slim/js/jquery.jcarousel.js @ 25997

Last change on this file since 25997 was 25997, checked in by Miklfe, 10 years ago
File size: 42.9 KB
Line 
1/*! jCarousel - v0.3.0 - 2013-11-22
2* http://sorgalla.com/jcarousel
3* Copyright (c) 2013 Jan Sorgalla; Licensed MIT */
4(function($) {
5    'use strict';
6
7    var jCarousel = $.jCarousel = {};
8
9    jCarousel.version = '0.3.0';
10
11    var rRelativeTarget = /^([+\-]=)?(.+)$/;
12
13    jCarousel.parseTarget = function(target) {
14        var relative = false,
15            parts    = typeof target !== 'object' ?
16                           rRelativeTarget.exec(target) :
17                           null;
18
19        if (parts) {
20            target = parseInt(parts[2], 10) || 0;
21
22            if (parts[1]) {
23                relative = true;
24                if (parts[1] === '-=') {
25                    target *= -1;
26                }
27            }
28        } else if (typeof target !== 'object') {
29            target = parseInt(target, 10) || 0;
30        }
31
32        return {
33            target: target,
34            relative: relative
35        };
36    };
37
38    jCarousel.detectCarousel = function(element) {
39        var carousel;
40
41        while (element.length > 0) {
42            carousel = element.filter('[data-jcarousel]');
43
44            if (carousel.length > 0) {
45                return carousel;
46            }
47
48            carousel = element.find('[data-jcarousel]');
49
50            if (carousel.length > 0) {
51                return carousel;
52            }
53
54            element = element.parent();
55        }
56
57        return null;
58    };
59
60    jCarousel.base = function(pluginName) {
61        return {
62            version:  jCarousel.version,
63            _options:  {},
64            _element:  null,
65            _carousel: null,
66            _init:     $.noop,
67            _create:   $.noop,
68            _destroy:  $.noop,
69            _reload:   $.noop,
70            create: function() {
71                this._element
72                    .attr('data-' + pluginName.toLowerCase(), true)
73                    .data(pluginName, this);
74
75                if (false === this._trigger('create')) {
76                    return this;
77                }
78
79                this._create();
80
81                this._trigger('createend');
82
83                return this;
84            },
85            destroy: function() {
86                if (false === this._trigger('destroy')) {
87                    return this;
88                }
89
90                this._destroy();
91
92                this._trigger('destroyend');
93
94                this._element
95                    .removeData(pluginName)
96                    .removeAttr('data-' + pluginName.toLowerCase());
97
98                return this;
99            },
100            reload: function(options) {
101                if (false === this._trigger('reload')) {
102                    return this;
103                }
104
105                if (options) {
106                    this.options(options);
107                }
108
109                this._reload();
110
111                this._trigger('reloadend');
112
113                return this;
114            },
115            element: function() {
116                return this._element;
117            },
118            options: function(key, value) {
119                if (arguments.length === 0) {
120                    return $.extend({}, this._options);
121                }
122
123                if (typeof key === 'string') {
124                    if (typeof value === 'undefined') {
125                        return typeof this._options[key] === 'undefined' ?
126                                null :
127                                this._options[key];
128                    }
129
130                    this._options[key] = value;
131                } else {
132                    this._options = $.extend({}, this._options, key);
133                }
134
135                return this;
136            },
137            carousel: function() {
138                if (!this._carousel) {
139                    this._carousel = jCarousel.detectCarousel(this.options('carousel') || this._element);
140
141                    if (!this._carousel) {
142                        $.error('Could not detect carousel for plugin "' + pluginName + '"');
143                    }
144                }
145
146                return this._carousel;
147            },
148            _trigger: function(type, element, data) {
149                var event,
150                    defaultPrevented = false;
151
152                data = [this].concat(data || []);
153
154                (element || this._element).each(function() {
155                    event = $.Event((pluginName + ':' + type).toLowerCase());
156
157                    $(this).trigger(event, data);
158
159                    if (event.isDefaultPrevented()) {
160                        defaultPrevented = true;
161                    }
162                });
163
164                return !defaultPrevented;
165            }
166        };
167    };
168
169    jCarousel.plugin = function(pluginName, pluginPrototype) {
170        var Plugin = $[pluginName] = function(element, options) {
171            this._element = $(element);
172            this.options(options);
173
174            this._init();
175            this.create();
176        };
177
178        Plugin.fn = Plugin.prototype = $.extend(
179            {},
180            jCarousel.base(pluginName),
181            pluginPrototype
182        );
183
184        $.fn[pluginName] = function(options) {
185            var args        = Array.prototype.slice.call(arguments, 1),
186                returnValue = this;
187
188            if (typeof options === 'string') {
189                this.each(function() {
190                    var instance = $(this).data(pluginName);
191
192                    if (!instance) {
193                        return $.error(
194                            'Cannot call methods on ' + pluginName + ' prior to initialization; ' +
195                            'attempted to call method "' + options + '"'
196                        );
197                    }
198
199                    if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
200                        return $.error(
201                            'No such method "' + options + '" for ' + pluginName + ' instance'
202                        );
203                    }
204
205                    var methodValue = instance[options].apply(instance, args);
206
207                    if (methodValue !== instance && typeof methodValue !== 'undefined') {
208                        returnValue = methodValue;
209                        return false;
210                    }
211                });
212            } else {
213                this.each(function() {
214                    var instance = $(this).data(pluginName);
215
216                    if (instance instanceof Plugin) {
217                        instance.reload(options);
218                    } else {
219                        new Plugin(this, options);
220                    }
221                });
222            }
223
224            return returnValue;
225        };
226
227        return Plugin;
228    };
229}(jQuery));
230
231(function($, window) {
232    'use strict';
233
234    var toFloat = function(val) {
235        return parseFloat(val) || 0;
236    };
237
238    $.jCarousel.plugin('jcarousel', {
239        animating:   false,
240        tail:        0,
241        inTail:      false,
242        resizeTimer: null,
243        lt:          null,
244        vertical:    false,
245        rtl:         false,
246        circular:    false,
247        underflow:   false,
248        relative:    false,
249
250        _options: {
251            list: function() {
252                return this.element().children().eq(0);
253            },
254            items: function() {
255                return this.list().children();
256            },
257            animation:   400,
258            transitions: false,
259            wrap:        null,
260            vertical:    null,
261            rtl:         null,
262            center:      false
263        },
264
265        // Protected, don't access directly
266        _list:         null,
267        _items:        null,
268        _target:       null,
269        _first:        null,
270        _last:         null,
271        _visible:      null,
272        _fullyvisible: null,
273        _init: function() {
274            var self = this;
275
276            this.onWindowResize = function() {
277                if (self.resizeTimer) {
278                    clearTimeout(self.resizeTimer);
279                }
280
281                self.resizeTimer = setTimeout(function() {
282                    self.reload();
283                }, 100);
284            };
285
286            return this;
287        },
288        _create: function() {
289            this._reload();
290
291            $(window).on('resize.jcarousel', this.onWindowResize);
292        },
293        _destroy: function() {
294            $(window).off('resize.jcarousel', this.onWindowResize);
295        },
296        _reload: function() {
297            this.vertical = this.options('vertical');
298
299            if (this.vertical == null) {
300                this.vertical = this.list().height() > this.list().width();
301            }
302
303            this.rtl = this.options('rtl');
304
305            if (this.rtl == null) {
306                this.rtl = (function(element) {
307                    if (('' + element.attr('dir')).toLowerCase() === 'rtl') {
308                        return true;
309                    }
310
311                    var found = false;
312
313                    element.parents('[dir]').each(function() {
314                        if ((/rtl/i).test($(this).attr('dir'))) {
315                            found = true;
316                            return false;
317                        }
318                    });
319
320                    return found;
321                }(this._element));
322            }
323
324            this.lt = this.vertical ? 'top' : 'left';
325
326            // Ensure before closest() call
327            this.relative = this.list().css('position') === 'relative';
328
329            // Force list and items reload
330            this._list  = null;
331            this._items = null;
332
333            var item = this._target && this.index(this._target) >= 0 ?
334                           this._target :
335                           this.closest();
336
337            // _prepare() needs this here
338            this.circular  = this.options('wrap') === 'circular';
339            this.underflow = false;
340
341            var props = {'left': 0, 'top': 0};
342
343            if (item.length > 0) {
344                this._prepare(item);
345                this.list().find('[data-jcarousel-clone]').remove();
346
347                // Force items reload
348                this._items = null;
349
350                this.underflow = this._fullyvisible.length >= this.items().length;
351                this.circular  = this.circular && !this.underflow;
352
353                props[this.lt] = this._position(item) + 'px';
354            }
355
356            this.move(props);
357
358            return this;
359        },
360        list: function() {
361            if (this._list === null) {
362                var option = this.options('list');
363                this._list = $.isFunction(option) ? option.call(this) : this._element.find(option);
364            }
365
366            return this._list;
367        },
368        items: function() {
369            if (this._items === null) {
370                var option = this.options('items');
371                this._items = ($.isFunction(option) ? option.call(this) : this.list().find(option)).not('[data-jcarousel-clone]');
372            }
373
374            return this._items;
375        },
376        index: function(item) {
377            return this.items().index(item);
378        },
379        closest: function() {
380            var self    = this,
381                pos     = this.list().position()[this.lt],
382                closest = $(), // Ensure we're returning a jQuery instance
383                stop    = false,
384                lrb     = this.vertical ? 'bottom' : (this.rtl && !this.relative ? 'left' : 'right'),
385                width;
386
387            if (this.rtl && this.relative && !this.vertical) {
388                pos += this.list().width() - this.clipping();
389            }
390
391            this.items().each(function() {
392                closest = $(this);
393
394                if (stop) {
395                    return false;
396                }
397
398                var dim = self.dimension(closest);
399
400                pos += dim;
401
402                if (pos >= 0) {
403                    width = dim - toFloat(closest.css('margin-' + lrb));
404
405                    if ((Math.abs(pos) - dim + (width / 2)) <= 0) {
406                        stop = true;
407                    } else {
408                        return false;
409                    }
410                }
411            });
412
413
414            return closest;
415        },
416        target: function() {
417            return this._target;
418        },
419        first: function() {
420            return this._first;
421        },
422        last: function() {
423            return this._last;
424        },
425        visible: function() {
426            return this._visible;
427        },
428        fullyvisible: function() {
429            return this._fullyvisible;
430        },
431        hasNext: function() {
432            if (false === this._trigger('hasnext')) {
433                return true;
434            }
435
436            var wrap = this.options('wrap'),
437                end = this.items().length - 1;
438
439            return end >= 0 &&
440                ((wrap && wrap !== 'first') ||
441                    (this.index(this._last) < end) ||
442                    (this.tail && !this.inTail)) ? true : false;
443        },
444        hasPrev: function() {
445            if (false === this._trigger('hasprev')) {
446                return true;
447            }
448
449            var wrap = this.options('wrap');
450
451            return this.items().length > 0 &&
452                ((wrap && wrap !== 'last') ||
453                    (this.index(this._first) > 0) ||
454                    (this.tail && this.inTail)) ? true : false;
455        },
456        clipping: function() {
457            return this._element['inner' + (this.vertical ? 'Height' : 'Width')]();
458        },
459        dimension: function(element) {
460            return element['outer' + (this.vertical ? 'Height' : 'Width')](true);
461        },
462        scroll: function(target, animate, callback) {
463            if (this.animating) {
464                return this;
465            }
466
467            if (false === this._trigger('scroll', null, [target, animate])) {
468                return this;
469            }
470
471            if ($.isFunction(animate)) {
472                callback = animate;
473                animate  = true;
474            }
475
476            var parsed = $.jCarousel.parseTarget(target);
477
478            if (parsed.relative) {
479                var end    = this.items().length - 1,
480                    scroll = Math.abs(parsed.target),
481                    wrap   = this.options('wrap'),
482                    current,
483                    first,
484                    index,
485                    start,
486                    curr,
487                    isVisible,
488                    props,
489                    i;
490
491                if (parsed.target > 0) {
492                    var last = this.index(this._last);
493
494                    if (last >= end && this.tail) {
495                        if (!this.inTail) {
496                            this._scrollTail(animate, callback);
497                        } else {
498                            if (wrap === 'both' || wrap === 'last') {
499                                this._scroll(0, animate, callback);
500                            } else {
501                                if ($.isFunction(callback)) {
502                                    callback.call(this, false);
503                                }
504                            }
505                        }
506                    } else {
507                        current = this.index(this._target);
508
509                        if ((this.underflow && current === end && (wrap === 'circular' || wrap === 'both' || wrap === 'last')) ||
510                            (!this.underflow && last === end && (wrap === 'both' || wrap === 'last'))) {
511                            this._scroll(0, animate, callback);
512                        } else {
513                            index = current + scroll;
514
515                            if (this.circular && index > end) {
516                                i = end;
517                                curr = this.items().get(-1);
518
519                                while (i++ < index) {
520                                    curr = this.items().eq(0);
521                                    isVisible = this._visible.index(curr) >= 0;
522
523                                    if (isVisible) {
524                                        curr.after(curr.clone(true).attr('data-jcarousel-clone', true));
525                                    }
526
527                                    this.list().append(curr);
528
529                                    if (!isVisible) {
530                                        props = {};
531                                        props[this.lt] = this.dimension(curr);
532                                        this.moveBy(props);
533                                    }
534
535                                    // Force items reload
536                                    this._items = null;
537                                }
538
539                                this._scroll(curr, animate, callback);
540                            } else {
541                                this._scroll(Math.min(index, end), animate, callback);
542                            }
543                        }
544                    }
545                } else {
546                    if (this.inTail) {
547                        this._scroll(Math.max((this.index(this._first) - scroll) + 1, 0), animate, callback);
548                    } else {
549                        first  = this.index(this._first);
550                        current = this.index(this._target);
551                        start  = this.underflow ? current : first;
552                        index  = start - scroll;
553
554                        if (start <= 0 && ((this.underflow && wrap === 'circular') || wrap === 'both' || wrap === 'first')) {
555                            this._scroll(end, animate, callback);
556                        } else {
557                            if (this.circular && index < 0) {
558                                i    = index;
559                                curr = this.items().get(0);
560
561                                while (i++ < 0) {
562                                    curr = this.items().eq(-1);
563                                    isVisible = this._visible.index(curr) >= 0;
564
565                                    if (isVisible) {
566                                        curr.after(curr.clone(true).attr('data-jcarousel-clone', true));
567                                    }
568
569                                    this.list().prepend(curr);
570
571                                    // Force items reload
572                                    this._items = null;
573
574                                    var dim = this.dimension(curr);
575
576                                    props = {};
577                                    props[this.lt] = -dim;
578                                    this.moveBy(props);
579
580                                }
581
582                                this._scroll(curr, animate, callback);
583                            } else {
584                                this._scroll(Math.max(index, 0), animate, callback);
585                            }
586                        }
587                    }
588                }
589            } else {
590                this._scroll(parsed.target, animate, callback);
591            }
592
593            this._trigger('scrollend');
594
595            return this;
596        },
597        moveBy: function(properties, opts) {
598            var position = this.list().position(),
599                multiplier = 1,
600                correction = 0;
601
602            if (this.rtl && !this.vertical) {
603                multiplier = -1;
604
605                if (this.relative) {
606                    correction = this.list().width() - this.clipping();
607                }
608            }
609
610            if (properties.left) {
611                properties.left = (position.left + correction + toFloat(properties.left) * multiplier) + 'px';
612            }
613
614            if (properties.top) {
615                properties.top = (position.top + correction + toFloat(properties.top) * multiplier) + 'px';
616            }
617
618            return this.move(properties, opts);
619        },
620        move: function(properties, opts) {
621            opts = opts || {};
622
623            var option       = this.options('transitions'),
624                transitions  = !!option,
625                transforms   = !!option.transforms,
626                transforms3d = !!option.transforms3d,
627                duration     = opts.duration || 0,
628                list         = this.list();
629
630            if (!transitions && duration > 0) {
631                list.animate(properties, opts);
632                return;
633            }
634
635            var complete = opts.complete || $.noop,
636                css = {};
637
638            if (transitions) {
639                var backup = list.css(['transitionDuration', 'transitionTimingFunction', 'transitionProperty']),
640                    oldComplete = complete;
641
642                complete = function() {
643                    $(this).css(backup);
644                    oldComplete.call(this);
645                };
646                css = {
647                    transitionDuration: (duration > 0 ? duration / 1000 : 0) + 's',
648                    transitionTimingFunction: option.easing || opts.easing,
649                    transitionProperty: duration > 0 ? (function() {
650                        if (transforms || transforms3d) {
651                            // We have to use 'all' because jQuery doesn't prefix
652                            // css values, like transition-property: transform;
653                            return 'all';
654                        }
655
656                        return properties.left ? 'left' : 'top';
657                    })() : 'none',
658                    transform: 'none'
659                };
660            }
661
662            if (transforms3d) {
663                css.transform = 'translate3d(' + (properties.left || 0) + ',' + (properties.top || 0) + ',0)';
664            } else if (transforms) {
665                css.transform = 'translate(' + (properties.left || 0) + ',' + (properties.top || 0) + ')';
666            } else {
667                $.extend(css, properties);
668            }
669
670            if (transitions && duration > 0) {
671                list.one('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', complete);
672            }
673
674            list.css(css);
675
676            if (duration <= 0) {
677                list.each(function() {
678                    complete.call(this);
679                });
680            }
681        },
682        _scroll: function(item, animate, callback) {
683            if (this.animating) {
684                if ($.isFunction(callback)) {
685                    callback.call(this, false);
686                }
687
688                return this;
689            }
690
691            if (typeof item !== 'object') {
692                item = this.items().eq(item);
693            } else if (typeof item.jquery === 'undefined') {
694                item = $(item);
695            }
696
697            if (item.length === 0) {
698                if ($.isFunction(callback)) {
699                    callback.call(this, false);
700                }
701
702                return this;
703            }
704
705            this.inTail = false;
706
707            this._prepare(item);
708
709            var pos     = this._position(item),
710                currPos = this.list().position()[this.lt];
711
712            if (pos === currPos) {
713                if ($.isFunction(callback)) {
714                    callback.call(this, false);
715                }
716
717                return this;
718            }
719
720            var properties = {};
721            properties[this.lt] = pos + 'px';
722
723            this._animate(properties, animate, callback);
724
725            return this;
726        },
727        _scrollTail: function(animate, callback) {
728            if (this.animating || !this.tail) {
729                if ($.isFunction(callback)) {
730                    callback.call(this, false);
731                }
732
733                return this;
734            }
735
736            var pos = this.list().position()[this.lt];
737
738            if (this.rtl && this.relative && !this.vertical) {
739                pos += this.list().width() - this.clipping();
740            }
741
742            if (this.rtl && !this.vertical) {
743                pos += this.tail;
744            } else {
745                pos -= this.tail;
746            }
747
748            this.inTail = true;
749
750            var properties = {};
751            properties[this.lt] = pos + 'px';
752
753            this._update({
754                target:       this._target.next(),
755                fullyvisible: this._fullyvisible.slice(1).add(this._visible.last())
756            });
757
758            this._animate(properties, animate, callback);
759
760            return this;
761        },
762        _animate: function(properties, animate, callback) {
763            callback = callback || $.noop;
764
765            if (false === this._trigger('animate')) {
766                callback.call(this, false);
767                return this;
768            }
769
770            this.animating = true;
771
772            var animation = this.options('animation'),
773                complete  = $.proxy(function() {
774                    this.animating = false;
775
776                    var c = this.list().find('[data-jcarousel-clone]');
777
778                    if (c.length > 0) {
779                        c.remove();
780                        this._reload();
781                    }
782
783                    this._trigger('animateend');
784
785                    callback.call(this, true);
786                }, this);
787
788            var opts = typeof animation === 'object' ?
789                           $.extend({}, animation) :
790                           {duration: animation},
791                oldComplete = opts.complete || $.noop;
792
793            if (animate === false) {
794                opts.duration = 0;
795            } else if (typeof $.fx.speeds[opts.duration] !== 'undefined') {
796                opts.duration = $.fx.speeds[opts.duration];
797            }
798
799            opts.complete = function() {
800                complete();
801                oldComplete.call(this);
802            };
803
804            this.move(properties, opts);
805
806            return this;
807        },
808        _prepare: function(item) {
809            var index  = this.index(item),
810                idx    = index,
811                wh     = this.dimension(item),
812                clip   = this.clipping(),
813                lrb    = this.vertical ? 'bottom' : (this.rtl ? 'left'  : 'right'),
814                center = this.options('center'),
815                update = {
816                    target:       item,
817                    first:        item,
818                    last:         item,
819                    visible:      item,
820                    fullyvisible: wh <= clip ? item : $()
821                },
822                curr,
823                isVisible,
824                margin,
825                dim;
826
827            if (center) {
828                wh /= 2;
829                clip /= 2;
830            }
831
832            if (wh < clip) {
833                while (true) {
834                    curr = this.items().eq(++idx);
835
836                    if (curr.length === 0) {
837                        if (!this.circular) {
838                            break;
839                        }
840
841                        curr = this.items().eq(0);
842
843                        if (item.get(0) === curr.get(0)) {
844                            break;
845                        }
846
847                        isVisible = this._visible.index(curr) >= 0;
848
849                        if (isVisible) {
850                            curr.after(curr.clone(true).attr('data-jcarousel-clone', true));
851                        }
852
853                        this.list().append(curr);
854
855                        if (!isVisible) {
856                            var props = {};
857                            props[this.lt] = this.dimension(curr);
858                            this.moveBy(props);
859                        }
860
861                        // Force items reload
862                        this._items = null;
863                    }
864
865                    dim = this.dimension(curr);
866
867                    if (dim === 0) {
868                        break;
869                    }
870
871                    wh += dim;
872
873                    update.last    = curr;
874                    update.visible = update.visible.add(curr);
875
876                    // Remove right/bottom margin from total width
877                    margin = toFloat(curr.css('margin-' + lrb));
878
879                    if ((wh - margin) <= clip) {
880                        update.fullyvisible = update.fullyvisible.add(curr);
881                    }
882
883                    if (wh >= clip) {
884                        break;
885                    }
886                }
887            }
888
889            if (!this.circular && !center && wh < clip) {
890                idx = index;
891
892                while (true) {
893                    if (--idx < 0) {
894                        break;
895                    }
896
897                    curr = this.items().eq(idx);
898
899                    if (curr.length === 0) {
900                        break;
901                    }
902
903                    dim = this.dimension(curr);
904
905                    if (dim === 0) {
906                        break;
907                    }
908
909                    wh += dim;
910
911                    update.first   = curr;
912                    update.visible = update.visible.add(curr);
913
914                    // Remove right/bottom margin from total width
915                    margin = toFloat(curr.css('margin-' + lrb));
916
917                    if ((wh - margin) <= clip) {
918                        update.fullyvisible = update.fullyvisible.add(curr);
919                    }
920
921                    if (wh >= clip) {
922                        break;
923                    }
924                }
925            }
926
927            this._update(update);
928
929            this.tail = 0;
930
931            if (!center &&
932                this.options('wrap') !== 'circular' &&
933                this.options('wrap') !== 'custom' &&
934                this.index(update.last) === (this.items().length - 1)) {
935
936                // Remove right/bottom margin from total width
937                wh -= toFloat(update.last.css('margin-' + lrb));
938
939                if (wh > clip) {
940                    this.tail = wh - clip;
941                }
942            }
943
944            return this;
945        },
946        _position: function(item) {
947            var first  = this._first,
948                pos    = first.position()[this.lt],
949                center = this.options('center'),
950                centerOffset = center ? (this.clipping() / 2) - (this.dimension(first) / 2) : 0;
951
952            if (this.rtl && !this.vertical) {
953                if (this.relative) {
954                    pos -= this.list().width() - this.dimension(first);
955                } else {
956                    pos -= this.clipping() - this.dimension(first);
957                }
958
959                pos += centerOffset;
960            } else {
961                pos -= centerOffset;
962            }
963
964            if (!center &&
965                (this.index(item) > this.index(first) || this.inTail) &&
966                this.tail) {
967                pos = this.rtl && !this.vertical ? pos - this.tail : pos + this.tail;
968                this.inTail = true;
969            } else {
970                this.inTail = false;
971            }
972
973            return -pos;
974        },
975        _update: function(update) {
976            var self = this,
977                current = {
978                    target:       this._target || $(),
979                    first:        this._first || $(),
980                    last:         this._last || $(),
981                    visible:      this._visible || $(),
982                    fullyvisible: this._fullyvisible || $()
983                },
984                back = this.index(update.first || current.first) < this.index(current.first),
985                key,
986                doUpdate = function(key) {
987                    var elIn  = [],
988                        elOut = [];
989
990                    update[key].each(function() {
991                        if (current[key].index(this) < 0) {
992                            elIn.push(this);
993                        }
994                    });
995
996                    current[key].each(function() {
997                        if (update[key].index(this) < 0) {
998                            elOut.push(this);
999                        }
1000                    });
1001
1002                    if (back) {
1003                        elIn = elIn.reverse();
1004                    } else {
1005                        elOut = elOut.reverse();
1006                    }
1007
1008                    self._trigger(key + 'in', $(elIn));
1009                    self._trigger(key + 'out', $(elOut));
1010
1011                    self['_' + key] = update[key];
1012                };
1013
1014            for (key in update) {
1015                doUpdate(key);
1016            }
1017
1018            return this;
1019        }
1020    });
1021}(jQuery, window));
1022
1023(function($) {
1024    'use strict';
1025
1026    $.jcarousel.fn.scrollIntoView = function(target, animate, callback) {
1027        var parsed = $.jCarousel.parseTarget(target),
1028            first  = this.index(this._fullyvisible.first()),
1029            last   = this.index(this._fullyvisible.last()),
1030            index;
1031
1032        if (parsed.relative) {
1033            index = parsed.target < 0 ? Math.max(0, first + parsed.target) : last + parsed.target;
1034        } else {
1035            index = typeof parsed.target !== 'object' ? parsed.target : this.index(parsed.target);
1036        }
1037
1038        if (index < first) {
1039            return this.scroll(index, animate, callback);
1040        }
1041
1042        if (index >= first && index <= last) {
1043            if ($.isFunction(callback)) {
1044                callback.call(this, false);
1045            }
1046
1047            return this;
1048        }
1049
1050        var items = this.items(),
1051            clip = this.clipping(),
1052            lrb  = this.vertical ? 'bottom' : (this.rtl ? 'left'  : 'right'),
1053            wh   = 0,
1054            curr;
1055
1056        while (true) {
1057            curr = items.eq(index);
1058
1059            if (curr.length === 0) {
1060                break;
1061            }
1062
1063            wh += this.dimension(curr);
1064
1065            if (wh >= clip) {
1066                var margin = parseFloat(curr.css('margin-' + lrb)) || 0;
1067                if ((wh - margin) !== clip) {
1068                    index++;
1069                }
1070                break;
1071            }
1072
1073            if (index <= 0) {
1074                break;
1075            }
1076
1077            index--;
1078        }
1079
1080        return this.scroll(index, animate, callback);
1081    };
1082}(jQuery));
1083
1084(function($) {
1085    'use strict';
1086
1087    $.jCarousel.plugin('jcarouselControl', {
1088        _options: {
1089            target: '+=1',
1090            event:  'click',
1091            method: 'scroll'
1092        },
1093        _active: null,
1094        _init: function() {
1095            this.onDestroy = $.proxy(function() {
1096                this._destroy();
1097                this.carousel()
1098                    .one('jcarousel:createend', $.proxy(this._create, this));
1099            }, this);
1100            this.onReload = $.proxy(this._reload, this);
1101            this.onEvent = $.proxy(function(e) {
1102                e.preventDefault();
1103
1104                var method = this.options('method');
1105
1106                if ($.isFunction(method)) {
1107                    method.call(this);
1108                } else {
1109                    this.carousel()
1110                        .jcarousel(this.options('method'), this.options('target'));
1111                }
1112            }, this);
1113        },
1114        _create: function() {
1115            this.carousel()
1116                .one('jcarousel:destroy', this.onDestroy)
1117                .on('jcarousel:reloadend jcarousel:scrollend', this.onReload);
1118
1119            this._element
1120                .on(this.options('event') + '.jcarouselcontrol', this.onEvent);
1121
1122            this._reload();
1123        },
1124        _destroy: function() {
1125            this._element
1126                .off('.jcarouselcontrol', this.onEvent);
1127
1128            this.carousel()
1129                .off('jcarousel:destroy', this.onDestroy)
1130                .off('jcarousel:reloadend jcarousel:scrollend', this.onReload);
1131        },
1132        _reload: function() {
1133            var parsed   = $.jCarousel.parseTarget(this.options('target')),
1134                carousel = this.carousel(),
1135                active;
1136
1137            if (parsed.relative) {
1138                active = carousel
1139                    .jcarousel(parsed.target > 0 ? 'hasNext' : 'hasPrev');
1140            } else {
1141                var target = typeof parsed.target !== 'object' ?
1142                                carousel.jcarousel('items').eq(parsed.target) :
1143                                parsed.target;
1144
1145                active = carousel.jcarousel('target').index(target) >= 0;
1146            }
1147
1148            if (this._active !== active) {
1149                this._trigger(active ? 'active' : 'inactive');
1150                this._active = active;
1151            }
1152
1153            return this;
1154        }
1155    });
1156}(jQuery));
1157
1158(function($) {
1159    'use strict';
1160
1161    $.jCarousel.plugin('jcarouselPagination', {
1162        _options: {
1163            perPage: null,
1164            item: function(page) {
1165                return '<a href="#' + page + '">' + page + '</a>';
1166            },
1167            event:  'click',
1168            method: 'scroll'
1169        },
1170        _pages: {},
1171        _items: {},
1172        _currentPage: null,
1173        _init: function() {
1174            this.onDestroy = $.proxy(function() {
1175                this._destroy();
1176                this.carousel()
1177                    .one('jcarousel:createend', $.proxy(this._create, this));
1178            }, this);
1179            this.onReload = $.proxy(this._reload, this);
1180            this.onScroll = $.proxy(this._update, this);
1181        },
1182        _create: function() {
1183            this.carousel()
1184                .one('jcarousel:destroy', this.onDestroy)
1185                .on('jcarousel:reloadend', this.onReload)
1186                .on('jcarousel:scrollend', this.onScroll);
1187
1188            this._reload();
1189        },
1190        _destroy: function() {
1191            this._clear();
1192
1193            this.carousel()
1194                .off('jcarousel:destroy', this.onDestroy)
1195                .off('jcarousel:reloadend', this.onReload)
1196                .off('jcarousel:scrollend', this.onScroll);
1197        },
1198        _reload: function() {
1199            var perPage = this.options('perPage');
1200
1201            this._pages = {};
1202            this._items = {};
1203
1204            // Calculate pages
1205            if ($.isFunction(perPage)) {
1206                perPage = perPage.call(this);
1207            }
1208
1209            if (perPage == null) {
1210                this._pages = this._calculatePages();
1211            } else {
1212                var pp    = parseInt(perPage, 10) || 0,
1213                    items = this.carousel().jcarousel('items'),
1214                    page  = 1,
1215                    i     = 0,
1216                    curr;
1217
1218                while (true) {
1219                    curr = items.eq(i++);
1220
1221                    if (curr.length === 0) {
1222                        break;
1223                    }
1224
1225                    if (!this._pages[page]) {
1226                        this._pages[page] = curr;
1227                    } else {
1228                        this._pages[page] = this._pages[page].add(curr);
1229                    }
1230
1231                    if (i % pp === 0) {
1232                        page++;
1233                    }
1234                }
1235            }
1236
1237            this._clear();
1238
1239            var self     = this,
1240                carousel = this.carousel().data('jcarousel'),
1241                element  = this._element,
1242                item     = this.options('item');
1243
1244            $.each(this._pages, function(page, carouselItems) {
1245                var currItem = self._items[page] = $(item.call(self, page, carouselItems));
1246
1247                currItem.on(self.options('event') + '.jcarouselpagination', $.proxy(function() {
1248                    var target = carouselItems.eq(0);
1249
1250                    // If circular wrapping enabled, ensure correct scrolling direction
1251                    if (carousel.circular) {
1252                        var currentIndex = carousel.index(carousel.target()),
1253                            newIndex     = carousel.index(target);
1254
1255                        if (parseFloat(page) > parseFloat(self._currentPage)) {
1256                            if (newIndex < currentIndex) {
1257                                target = '+=' + (carousel.items().length - currentIndex + newIndex);
1258                            }
1259                        } else {
1260                            if (newIndex > currentIndex) {
1261                                target = '-=' + (currentIndex + (carousel.items().length - newIndex));
1262                            }
1263                        }
1264                    }
1265
1266                    carousel[this.options('method')](target);
1267                }, self));
1268
1269                element.append(currItem);
1270            });
1271
1272            this._update();
1273        },
1274        _update: function() {
1275            var target = this.carousel().jcarousel('target'),
1276                currentPage;
1277
1278            $.each(this._pages, function(page, carouselItems) {
1279                carouselItems.each(function() {
1280                    if (target.is(this)) {
1281                        currentPage = page;
1282                        return false;
1283                    }
1284                });
1285
1286                if (currentPage) {
1287                    return false;
1288                }
1289            });
1290
1291            if (this._currentPage !== currentPage) {
1292                this._trigger('inactive', this._items[this._currentPage]);
1293                this._trigger('active', this._items[currentPage]);
1294            }
1295
1296            this._currentPage = currentPage;
1297        },
1298        items: function() {
1299            return this._items;
1300        },
1301        _clear: function() {
1302            this._element.empty();
1303            this._currentPage = null;
1304        },
1305        _calculatePages: function() {
1306            var carousel = this.carousel().data('jcarousel'),
1307                items    = carousel.items(),
1308                clip     = carousel.clipping(),
1309                wh       = 0,
1310                idx      = 0,
1311                page     = 1,
1312                pages    = {},
1313                curr;
1314
1315            while (true) {
1316                curr = items.eq(idx++);
1317
1318                if (curr.length === 0) {
1319                    break;
1320                }
1321
1322                if (!pages[page]) {
1323                    pages[page] = curr;
1324                } else {
1325                    pages[page] = pages[page].add(curr);
1326                }
1327
1328                wh += carousel.dimension(curr);
1329
1330                if (wh >= clip) {
1331                    page++;
1332                    wh = 0;
1333                }
1334            }
1335
1336            return pages;
1337        }
1338    });
1339}(jQuery));
1340
1341(function($) {
1342    'use strict';
1343
1344    $.jCarousel.plugin('jcarouselAutoscroll', {
1345        _options: {
1346            target:    '+=1',
1347            interval:  3000,
1348            autostart: true
1349        },
1350        _timer: null,
1351        _init: function () {
1352            this.onDestroy = $.proxy(function() {
1353                this._destroy();
1354                this.carousel()
1355                    .one('jcarousel:createend', $.proxy(this._create, this));
1356            }, this);
1357
1358            this.onAnimateEnd = $.proxy(this.start, this);
1359        },
1360        _create: function() {
1361            this.carousel()
1362                .one('jcarousel:destroy', this.onDestroy);
1363
1364            if (this.options('autostart')) {
1365                this.start();
1366            }
1367        },
1368        _destroy: function() {
1369            this.stop();
1370            this.carousel()
1371                .off('jcarousel:destroy', this.onDestroy);
1372        },
1373        start: function() {
1374            this.stop();
1375
1376            this.carousel()
1377                .one('jcarousel:animateend', this.onAnimateEnd);
1378
1379            this._timer = setTimeout($.proxy(function() {
1380                this.carousel().jcarousel('scroll', this.options('target'));
1381            }, this), this.options('interval'));
1382
1383            return this;
1384        },
1385        stop: function() {
1386            if (this._timer) {
1387                this._timer = clearTimeout(this._timer);
1388            }
1389
1390            this.carousel()
1391                .off('jcarousel:animateend', this.onAnimateEnd);
1392
1393            return this;
1394        }
1395    });
1396}(jQuery));
Note: See TracBrowser for help on using the repository browser.