(function($) { 'use strict'; var jCarousel = $.jCarousel = {}; jCarousel.version = '0.3.0-beta'; var rRelativeTarget = /^([+\-]=)?(.+)$/; jCarousel.parseTarget = function(target) { var relative = false, parts = typeof target !== 'object' ? rRelativeTarget.exec(target) : null; if (parts) { target = parseInt(parts[2], 10) || 0; if (parts[1]) { relative = true; if (parts[1] === '-=') { target *= -1; } } } else if (typeof target !== 'object') { target = parseInt(target, 10) || 0; } return { target: target, relative: relative }; }; jCarousel.detectCarousel = function(element) { var carousel; while (element.size() > 0) { carousel = element.filter('[data-jcarousel]'); if (carousel.size() > 0) { return carousel; } carousel = element.find('[data-jcarousel]'); if (carousel.size() > 0) { return carousel; } element = element.parent(); } return null; }; jCarousel.base = function(pluginName) { return { version: jCarousel.version, _options: {}, _element: null, _carousel: null, _init: $.noop, _create: $.noop, _destroy: $.noop, _reload: $.noop, create: function() { this._element .attr('data-' + pluginName.toLowerCase(), true) .data(pluginName, this); if (false === this._trigger('create')) { return this; } this._create(); this._trigger('createend'); return this; }, destroy: function() { if (false === this._trigger('destroy')) { return this; } this._destroy(); this._trigger('destroyend'); this._element .removeData(pluginName) .removeAttr('data-' + pluginName.toLowerCase()); return this; }, reload: function(options) { if (false === this._trigger('reload')) { return this; } if (options) { this.options(options); } this._reload(); this._trigger('reloadend'); return this; }, element: function() { return this._element; }, options: function(key, value) { if (arguments.length === 0) { return $.extend({}, this._options); } if (typeof key === 'string') { if (typeof value === 'undefined') { return typeof this._options[key] === 'undefined' ? null : this._options[key]; } this._options[key] = value; } else { this._options = $.extend({}, this._options, key); } return this; }, carousel: function() { if (!this._carousel) { this._carousel = jCarousel.detectCarousel(this.options('carousel') || this._element); if (!this._carousel) { $.error('Could not detect carousel for plugin "' + pluginName + '"'); } } return this._carousel; }, _trigger: function(type, element, data) { var event = $.Event((type + '.' + pluginName).toLowerCase()); (element || this._element) .trigger(event, [this].concat(data || [])); return !event.isDefaultPrevented(); } }; }; jCarousel.plugin = function(pluginName, pluginPrototype) { var Plugin = $[pluginName] = function(element, options) { this._element = $(element); this.options(options); this._init(); this.create(); }; Plugin.fn = Plugin.prototype = $.extend( {}, jCarousel.base(pluginName), pluginPrototype ); $.fn[pluginName] = function(options) { var args = Array.prototype.slice.call(arguments, 1), returnValue = this; if (typeof options === 'string') { this.each(function() { var instance = $(this).data(pluginName); if (!instance) { return $.error( 'Cannot call methods on ' + pluginName + ' prior to initialization; ' + 'attempted to call method "' + options + '"' ); } if (!$.isFunction(instance[options]) || options.charAt(0) === '_') { return $.error( 'No such method "' + options + '" for ' + pluginName + ' instance' ); } var methodValue = instance[options].apply(instance, args); if (methodValue !== instance && typeof methodValue !== 'undefined') { returnValue = methodValue; return false; } }); } else { this.each(function() { var instance = $(this).data(pluginName); if (instance) { instance.reload(options); } else { new Plugin(this, options); } }); } return returnValue; }; return Plugin; }; }(jQuery)); (function($, window) { 'use strict'; var toFloat = function(val) { return parseFloat(val) || 0; }; $.jCarousel.plugin('jcarousel', { animating: false, tail: 0, inTail: false, resizeTimer: null, lt: null, vertical: false, rtl: false, circular: false, _options: { list: function() { return this.element().children().eq(0); }, items: function() { return this.list().children(); }, animation: 400, wrap: null, vertical: null, rtl: null, center: false }, // Protected, don't access directly _list: null, _items: null, _target: null, _first: null, _last: null, _visible: null, _fullyvisible: null, _init: function() { var self = this; this.onWindowResize = function() { if (self.resizeTimer) { clearTimeout(self.resizeTimer); } self.resizeTimer = setTimeout(function() { self.reload(); }, 100); }; this.onAnimationComplete = function(callback) { self.animating = false; var c = self.list().find('[data-jcarousel-clone]'); if (c.size() > 0) { c.remove(); self._reload(); } self._trigger('animateend'); if ($.isFunction(callback)) { callback.call(self, true); } }; return this; }, _create: function() { this._reload(); $(window) .bind('resize.jcarousel', this.onWindowResize); }, _destroy: function() { $(window) .unbind('resize.jcarousel', this.onWindowResize); }, _reload: function() { this.vertical = this.options('vertical'); if (this.vertical == null) { this.vertical = this.list().height() > this.list().width(); } this.rtl = this.options('rtl'); if (this.rtl == null) { this.rtl = (function(element) { if (('' + element.attr('dir')).toLowerCase() === 'rtl') { return true; } var found = false; element.parents('[dir]').each(function() { if ((/rtl/i).test($(this).attr('dir'))) { found = true; return false; } }); return found; }(this._element)); } this.lt = this.vertical ? 'top' : 'left'; // Force items reload this._items = null; var item = this._target && this.index(this._target) >= 0 ? this._target : this.closest(); // _prepare() needs this here this.circular = this.options('wrap') === 'circular'; if (item.size() > 0) { this._prepare(item); this.list().find('[data-jcarousel-clone]').remove(); // Force items reload this._items = null; this.circular = this.options('wrap') === 'circular' && this._fullyvisible.size() < this.items().size(); this.list().css(this.lt, this._position(item) + 'px'); } else { this.list().css({'left': 0, 'top': 0}); } return this; }, list: function() { if (this._list === null) { var option = this.options('list'); this._list = $.isFunction(option) ? option.call(this) : this._element.find(option); } return this._list; }, items: function() { if (this._items === null) { var option = this.options('items'); this._items = ($.isFunction(option) ? option.call(this) : this.list().find(option)).not('[data-jcarousel-clone]'); } return this._items; }, index: function(item) { return this.items().index(item); }, closest: function() { var self = this, pos = this.list().position()[this.lt], closest = $(), // Ensure we're returning a jQuery instance stop = false, lrb = this.vertical ? 'bottom' : (this.rtl ? 'left' : 'right'), width; if (this.rtl && !this.vertical) { pos = (pos + this.list().width() - this.clipping()) * -1; } this.items().each(function() { closest = $(this); if (stop) { return false; } var dim = self.dimension(closest); pos += dim; if (pos >= 0) { width = dim - toFloat(closest.css('margin-' + lrb)); if ((Math.abs(pos) - dim + (width / 2)) <= 0) { stop = true; } else { return false; } } }); return closest; }, target: function() { return this._target; }, first: function() { return this._first; }, last: function() { return this._last; }, visible: function() { return this._visible; }, fullyvisible: function() { return this._fullyvisible; }, hasNext: function() { if (false === this._trigger('hasnext')) { return true; } var wrap = this.options('wrap'), end = this.items().size() - 1; return end >= 0 && ((wrap && wrap !== 'first') || (this.index(this._last) < end) || (this.tail && !this.inTail)) ? true : false; }, hasPrev: function() { if (false === this._trigger('hasprev')) { return true; } var wrap = this.options('wrap'); return this.items().size() > 0 && ((wrap && wrap !== 'last') || (this.index(this._first) > 0) || (this.tail && this.inTail)) ? true : false; }, clipping: function() { return this._element['inner' + (this.vertical ? 'Height' : 'Width')](); }, dimension: function(element) { return element['outer' + (this.vertical ? 'Height' : 'Width')](true); }, scroll: function(target, animate, callback) { if (this.animating) { return this; } if (false === this._trigger('scroll', null, [target, animate])) { return this; } if ($.isFunction(animate)) { callback = animate; animate = true; } var parsed = $.jCarousel.parseTarget(target); if (parsed.relative) { var end = this.items().size() - 1, scroll = Math.abs(parsed.target), wrap = this.options('wrap'), first, index, curr, i; if (parsed.target > 0) { var last = this.index(this._last); if (last >= end && this.tail) { if (!this.inTail) { this._scrollTail(animate, callback); } else { if (wrap === 'both' || wrap === 'last') { this._scroll(0, animate, callback); } else { this._scroll(Math.min(this.index(this._target) + scroll, end), animate, callback); } } } else { if (last === end && (wrap === 'both' || wrap === 'last')) { this._scroll(0, animate, callback); } else { first = this.index(this._target); index = first + scroll; if (this.circular && index > end) { i = end; curr = this.items().get(-1); while (i++ < index) { curr = this.items().eq(0); curr.after(curr.clone(true).attr('data-jcarousel-clone', true)); this.list().append(curr); // Force items reload this._items = null; } this._scroll(curr, animate, callback); } else { this._scroll(Math.min(index, end), animate, callback); } } } } else { if (this.inTail) { this._scroll(Math.max((this.index(this._first) - scroll) + 1, 0), animate, callback); } else { first = this.index(this._first); index = first - scroll; if (first === 0 && (wrap === 'both' || wrap === 'first')) { this._scroll(end, animate, callback); } else { if (this.circular && index < 0) { i = index; curr = this.items().get(0); while (i++ < 0) { curr = this.items().eq(-1); curr.after(curr.clone(true).attr('data-jcarousel-clone', true)); this.list().prepend(curr); // Force items reload this._items = null; var lt = toFloat(this.list().css(this.lt)), dim = this.dimension(curr); if (this.rtl && !this.vertical) { lt += dim; } else { lt -= dim; } this.list().css(this.lt, lt + 'px'); } this._scroll(curr, animate, callback); } else { this._scroll(Math.max(first - scroll, 0), animate, callback); } } } } } else { this._scroll(parsed.target, animate, callback); } this._trigger('scrollend'); return this; }, _scroll: function(item, animate, callback) { if (this.animating) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } if (typeof item !== 'object') { item = this.items().eq(item); } else if (typeof item.jquery === 'undefined') { item = $(item); } if (item.size() === 0) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } this.inTail = false; this._prepare(item); var pos = this._position(item), currPos = toFloat(this.list().css(this.lt)); if (pos === currPos) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } var properties = {}; properties[this.lt] = pos + 'px'; this._animate(properties, animate, callback); return this; }, _scrollTail: function(animate, callback) { if (this.animating || !this.tail) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } var pos = this.list().position()[this.lt]; if (this.rtl) { pos += this.tail; } else { pos -= this.tail; } this.inTail = true; var properties = {}; properties[this.lt] = pos + 'px'; this._update({ target: this._target.next(), fullyvisible: this._fullyvisible.slice(1).add(this._visible.last()) }); this._animate(properties, animate, callback); return this; }, _animate: function(properties, animate, callback) { if (false === this._trigger('animate')) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } this.animating = true; var animation = this.options('animation'); if (!animation || animate === false) { this.list().css(properties); this.onAnimationComplete(callback); } else { var self = this; if ($.isFunction(animation)) { animation.call(this, properties, function() { self.onAnimationComplete(callback); }); } else { var opts = typeof animation === 'object' ? $.extend({}, animation) : {duration: animation}, oldComplete = opts.complete; opts.complete = function() { self.onAnimationComplete(callback); if ($.isFunction(oldComplete)) { oldComplete.call(this); } }; this.list().animate(properties, opts); } } return this; }, _prepare: function(item) { var index = this.index(item), idx = index, wh = this.dimension(item), clip = this.clipping(), lrb = this.vertical ? 'bottom' : (this.rtl ? 'left' : 'right'), update = { target: item, first: item, last: item, visible: item, fullyvisible: wh <= clip ? item : $() }, curr, margin; if (this.options('center')) { wh /= 2; clip /= 2; } if (wh < clip) { while (true) { curr = this.items().eq(++idx); if (curr.size() === 0) { if (this.circular) { curr = this.items().eq(0); if (item.get(0) === curr.get(0)) { break; } curr.after(curr.clone(true).attr('data-jcarousel-clone', true)); this.list().append(curr); // Force items reload this._items = null; } else { break; } } wh += this.dimension(curr); update.last = curr; update.visible = update.visible.add(curr); // Remove right/bottom margin from total width margin = toFloat(curr.css('margin-' + lrb)); if ((wh - margin) <= clip) { update.fullyvisible = update.fullyvisible.add(curr); } if (wh >= clip) { break; } } } if (!this.circular && wh < clip) { idx = index; while (true) { if (--idx < 0) { break; } curr = this.items().eq(idx); if (curr.size() === 0) { break; } wh += this.dimension(curr); update.first = curr; update.visible = update.visible.add(curr); // Remove right/bottom margin from total width margin = toFloat(curr.css('margin-' + lrb)); if ((wh - margin) <= clip) { update.fullyvisible = update.fullyvisible.add(curr); } if (wh >= clip) { break; } } } this._update(update); this.tail = 0; if (this.options('wrap') !== 'circular' && this.options('wrap') !== 'custom' && this.index(update.last) === (this.items().size() - 1)) { // Remove right/bottom margin from total width wh -= toFloat(update.last.css('margin-' + lrb)); if (wh > clip) { this.tail = wh - clip; } } return this; }, _position: function(item) { var first = this._first, pos = first.position()[this.lt]; if (this.rtl && !this.vertical) { pos -= this.clipping() - this.dimension(first); } if (this.options('center')) { pos -= (this.clipping() / 2) - (this.dimension(first) / 2); } if ((this.index(item) > this.index(first) || this.inTail) && this.tail) { pos = this.rtl ? pos - this.tail : pos + this.tail; this.inTail = true; } else { this.inTail = false; } return -pos; }, _update: function(update) { var self = this, current = { target: this._target || $(), first: this._first || $(), last: this._last || $(), visible: this._visible || $(), fullyvisible: this._fullyvisible || $() }, back = this.index(update.first || current.first) < this.index(current.first), key, doUpdate = function(key) { var elIn = [], elOut = []; update[key].each(function() { if (current[key].index(this) < 0) { elIn.push(this); } }); current[key].each(function() { if (update[key].index(this) < 0) { elOut.push(this); } }); if (back) { elIn = elIn.reverse(); } else { elOut = elOut.reverse(); } self._trigger('item' + key + 'in', $(elIn)); self._trigger('item' + key + 'out', $(elOut)); self['_' + key] = update[key]; }; for (key in update) { doUpdate(key); } return this; } }); }(jQuery, window)); (function($) { 'use strict'; $.jcarousel.fn.scrollIntoView = function(target, animate, callback) { var index = typeof target !== 'object' ? parseInt(target, 10) : this.index(target), first = this.index(this._fullyvisible.first()); if (index < first) { return this.scroll(index, animate, callback); } if (index >= first && index <= this.index(this._fullyvisible.last())) { if ($.isFunction(callback)) { callback.call(this, false); } return this; } var items = this.items(), clip = this.clipping(), lrb = this.vertical ? 'bottom' : (this.rtl ? 'left' : 'right'), wh = 0, curr; while (true) { curr = items.eq(index); if (curr.size() === 0) { break; } wh += this.dimension(curr); if (wh >= clip) { var margin = parseFloat(curr.css('margin-' + lrb)) || 0; if ((wh - margin) !== clip) { index++; } break; } if (index <= 0) { break; } index--; } return this.scroll(index, animate, callback); }; }(jQuery)); (function($) { 'use strict'; $.jCarousel.plugin('jcarouselControl', { _options: { target: '+=1', event: 'click' }, _active: null, _init: function() { this.onDestroy = $.proxy(function() { this._destroy(); this.carousel() .one('createend.jcarousel', $.proxy(this._create, this)); }, this); this.onReload = $.proxy(this._reload, this); this.onEvent = $.proxy(function(e) { e.preventDefault(); this.carousel() .jcarousel('scroll', this.options('target')); }, this); }, _create: function() { this.carousel() .one('destroy.jcarousel', this.onDestroy) .bind('reloadend.jcarousel scrollend.jcarousel', this.onReload); this._element .bind(this.options('event') + '.jcarouselcontrol', this.onEvent); this._reload(); }, _destroy: function() { this._element .unbind('.jcarouselcontrol', this.onEvent); this.carousel() .unbind('destroy.jcarousel', this.onDestroy) .unbind('reloadend.jcarousel scrollend.jcarousel', this.onReload); }, _reload: function() { var parsed = $.jCarousel.parseTarget(this.options('target')), carousel = this.carousel(), active; if (parsed.relative) { active = carousel .jcarousel(parsed.target > 0 ? 'hasNext' : 'hasPrev'); } else { var target = typeof parsed.target !== 'object' ? carousel.jcarousel('items').eq(parsed.target) : parsed.target; active = carousel.jcarousel('target').index(target) >= 0; } if (this._active !== active) { this._trigger(active ? 'active' : 'inactive'); this._active = active; } return this; } }); }(jQuery)); (function($) { 'use strict'; $.jCarousel.plugin('jcarouselPagination', { _options: { perPage: null, item: function(page) { return '' + page + ''; } }, _pages: {}, _items: {}, _init: function() { this.onDestroy = $.proxy(function() { this._destroy(); this.carousel() .one('createend.jcarousel', $.proxy(this._create, this)); }, this); this.onReload = $.proxy(this._reload, this); }, _create: function() { this.carousel() .one('destroy.jcarousel', this.onDestroy) .bind('reloadend.jcarousel', this.onReload); this._reload(); }, _destroy: function() { if ($.fn.jcarouselControl) { $.each(this._items, function(page, item) { item.jcarouselControl('destroy'); }); } this._element.empty(); this.carousel() .unbind('destroy.jcarousel', this.onDestroy) .unbind('reloadend.jcarousel', this.onReload); }, _reload: function() { var perPage = this.options('perPage'); this._pages = {}; this._items = {}; // Calculate pages if ($.isFunction(perPage)) { perPage = perPage.call(this); } if (perPage == null) { this._pages = this._calculatePages(); } else { var pp = parseInt(perPage, 10) || 0, items = this.carousel().jcarousel('items'), page = 1, i = 0, curr; while (true) { curr = items.eq(i++); if (curr.size() === 0) { break; } if (!this._pages[page]) { this._pages[page] = curr; } else { this._pages[page] = this._pages[page].add(curr); } if (i % pp === 0) { page++; } } } var self = this, element = this._element.empty(), item = this.options('item'); $.each(this._pages, function(page, carouselItems) { var currItem = self._items[page] = $(item.call(self, page, carouselItems)); element.append(currItem); if ($.fn.jcarouselControl) { currItem.jcarouselControl({ carousel: self.carousel(), target: carouselItems.eq(0) }); } }); }, items: function() { return this._items; }, _calculatePages: function() { var carousel = this.carousel().data('jcarousel'), items = carousel.items(), clip = carousel.clipping(), wh = 0, idx = 0, page = 1, pages = {}, curr; while (true) { curr = items.eq(idx++); if (curr.size() === 0) { break; } if (!pages[page]) { pages[page] = curr; } else { pages[page] = pages[page].add(curr); } wh += carousel.dimension(curr); if (wh >= clip) { page++; wh = 0; } } return pages; } }); }(jQuery)); (function($) { 'use strict'; $.jCarousel.plugin('jcarouselAutoscroll', { _options: { target: '+=1', interval: 3000, autostart: true }, _timer: null, _init: function () { this.onDestroy = $.proxy(function() { this._destroy(); this.carousel() .one('createend.jcarousel', $.proxy(this._create, this)); }, this); this.onAnimateEnd = $.proxy(this.start, this); }, _create: function() { this.carousel() .one('destroy.jcarousel', this.onDestroy); if (this.options('autostart')) { this.start(); } }, _destroy: function() { this.stop(); this.carousel() .unbind('destroy.jcarousel', this.onDestroy); }, start: function() { this.stop(); this.carousel() .one('animateend.jcarousel', this.onAnimateEnd); this._timer = setTimeout($.proxy(function() { this.carousel().jcarousel('scroll', this.options('target')); }, this), this.options('interval')); return this; }, stop: function() { if (this._timer) { this._timer = clearTimeout(this._timer); } this.carousel() .unbind('animateend.jcarousel', this.onAnimateEnd); return this; } }); }(jQuery));