/** * owl carousel * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) * @todo lazy load icon * @todo prevent animationend bubling * @todo itemsscaleup * @todo test zepto * @todo stagepadding calculate wrong active classes */ ;(function($, window, document, undefined) { var drag, state, e; /** * template for status information about drag and touch events. * @private */ drag = { start: 0, startx: 0, starty: 0, current: 0, currentx: 0, currenty: 0, offsetx: 0, offsety: 0, distance: null, starttime: 0, endtime: 0, updatedx: 0, targetel: null }; /** * template for some status informations. * @private */ state = { istouch: false, isscrolling: false, isswiping: false, direction: false, inmotion: false }; /** * event functions references. * @private */ e = { _ondragstart: null, _ondragmove: null, _ondragend: null, _transitionend: null, _resizer: null, _responsivecall: null, _gotoloop: null, _checkvisibile: null }; /** * creates a carousel. * @class the owl carousel. * @public * @param {htmlelement|jquery} element - the element to create the carousel for. * @param {object} [options] - the options */ function owl(element, options) { /** * current settings for the carousel. * @public */ this.settings = null; /** * current options set by the caller including defaults. * @public */ this.options = $.extend({}, owl.defaults, options); /** * plugin element. * @public */ this.$element = $(element); /** * caches informations about drag and touch events. */ this.drag = $.extend({}, drag); /** * caches some status informations. * @protected */ this.state = $.extend({}, state); /** * @protected * @todo must be documented */ this.e = $.extend({}, e); /** * references to the running plugins of this carousel. * @protected */ this._plugins = {}; /** * currently suppressed events to prevent them from beeing retriggered. * @protected */ this._supress = {}; /** * absolute current position. * @protected */ this._current = null; /** * animation speed in milliseconds. * @protected */ this._speed = null; /** * coordinates of all items in pixel. * @todo the name of this member is missleading. * @protected */ this._coordinates = []; /** * current breakpoint. * @todo real media queries would be nice. * @protected */ this._breakpoint = null; /** * current width of the plugin element. */ this._width = null; /** * all real items. * @protected */ this._items = []; /** * all cloned items. * @protected */ this._clones = []; /** * merge values of all items. * @todo maybe this could be part of a plugin. * @protected */ this._mergers = []; /** * invalidated parts within the update process. * @protected */ this._invalidated = {}; /** * ordered list of workers for the update process. * @protected */ this._pipe = []; $.each(owl.plugins, $.proxy(function(key, plugin) { this._plugins[key[0].tolowercase() + key.slice(1)] = new plugin(this); }, this)); $.each(owl.pipe, $.proxy(function(priority, worker) { this._pipe.push({ 'filter': worker.filter, 'run': $.proxy(worker.run, this) }); }, this)); this.setup(); this.initialize(); } /** * default options for the carousel. * @public */ owl.defaults = { items: 3, loop: false, center: false, mousedrag: true, touchdrag: true, pulldrag: true, freedrag: false, margin: 0, stagepadding: 0, merge: false, mergefit: true, autowidth: false, startposition: 0, rtl: false, smartspeed: 250, fluidspeed: false, dragendspeed: false, responsive: {}, responsiverefreshrate: 200, responsivebaseelement: window, responsiveclass: false, fallbackeasing: 'swing', info: false, nesteditemselector: false, itemelement: 'div', stageelement: 'div', // classes and names themeclass: 'owl-theme', baseclass: 'owl-carousel', itemclass: 'owl-item', centerclass: 'center', activeclass: 'active' }; /** * enumeration for width. * @public * @readonly * @enum {string} */ owl.width = { default: 'default', inner: 'inner', outer: 'outer' }; /** * contains all registered plugins. * @public */ owl.plugins = {}; /** * update pipe. */ owl.pipe = [ { filter: [ 'width', 'items', 'settings' ], run: function(cache) { cache.current = this._items && this._items[this.relative(this._current)]; } }, { filter: [ 'items', 'settings' ], run: function() { var cached = this._clones, clones = this.$stage.children('.cloned'); if (clones.length !== cached.length || (!this.settings.loop && cached.length > 0)) { this.$stage.children('.cloned').remove(); this._clones = []; } } }, { filter: [ 'items', 'settings' ], run: function() { var i, n, clones = this._clones, items = this._items, delta = this.settings.loop ? clones.length - math.max(this.settings.items * 2, 4) : 0; for (i = 0, n = math.abs(delta / 2); i < n; i++) { if (delta > 0) { this.$stage.children().eq(items.length + clones.length - 1).remove(); clones.pop(); this.$stage.children().eq(0).remove(); clones.pop(); } else { clones.push(clones.length / 2); this.$stage.append(items[clones[clones.length - 1]].clone().addclass('cloned')); clones.push(items.length - 1 - (clones.length - 1) / 2); this.$stage.prepend(items[clones[clones.length - 1]].clone().addclass('cloned')); } } } }, { filter: [ 'width', 'items', 'settings' ], run: function() { var rtl = (this.settings.rtl ? 1 : -1), width = (this.width() / this.settings.items).tofixed(3), coordinate = 0, merge, i, n; this._coordinates = []; for (i = 0, n = this._clones.length + this._items.length; i < n; i++) { merge = this._mergers[this.relative(i)]; merge = (this.settings.mergefit && math.min(merge, this.settings.items)) || merge; coordinate += (this.settings.autowidth ? this._items[this.relative(i)].width() + this.settings.margin : width * merge) * rtl; this._coordinates.push(coordinate); } } }, { filter: [ 'width', 'items', 'settings' ], run: function() { var i, n, width = (this.width() / this.settings.items).tofixed(3), css = { 'width': math.abs(this._coordinates[this._coordinates.length - 1]) + this.settings.stagepadding * 2, 'padding-left': this.settings.stagepadding || '', 'padding-right': this.settings.stagepadding || '' }; this.$stage.css(css); css = { 'width': this.settings.autowidth ? 'auto' : width - this.settings.margin }; css[this.settings.rtl ? 'margin-left' : 'margin-right'] = this.settings.margin; if (!this.settings.autowidth && $.grep(this._mergers, function(v) { return v > 1 }).length > 0) { for (i = 0, n = this._coordinates.length; i < n; i++) { css.width = math.abs(this._coordinates[i]) - math.abs(this._coordinates[i - 1] || 0) - this.settings.margin; this.$stage.children().eq(i).css(css); } } else { this.$stage.children().css(css); } } }, { filter: [ 'width', 'items', 'settings' ], run: function(cache) { cache.current && this.reset(this.$stage.children().index(cache.current)); } }, { filter: [ 'position' ], run: function() { this.animate(this.coordinates(this._current)); } }, { filter: [ 'width', 'position', 'items', 'settings' ], run: function() { var rtl = this.settings.rtl ? 1 : -1, padding = this.settings.stagepadding * 2, begin = this.coordinates(this.current()) + padding, end = begin + this.width() * rtl, inner, outer, matches = [], i, n; for (i = 0, n = this._coordinates.length; i < n; i++) { inner = this._coordinates[i - 1] || 0; outer = math.abs(this._coordinates[i]) + padding * rtl; if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end))) || (this.op(outer, '<', begin) && this.op(outer, '>', end))) { matches.push(i); } } this.$stage.children('.' + this.settings.activeclass).removeclass(this.settings.activeclass); this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addclass(this.settings.activeclass); if (this.settings.center) { this.$stage.children('.' + this.settings.centerclass).removeclass(this.settings.centerclass); this.$stage.children().eq(this.current()).addclass(this.settings.centerclass); } } } ]; /** * initializes the carousel. * @protected */ owl.prototype.initialize = function() { this.trigger('initialize'); this.$element .addclass(this.settings.baseclass) .addclass(this.settings.themeclass) .toggleclass('owl-rtl', this.settings.rtl); // check support this.browsersupport(); if (this.settings.autowidth && this.state.imagesloaded !== true) { var imgs, nestedselector, width; imgs = this.$element.find('img'); nestedselector = this.settings.nesteditemselector ? '.' + this.settings.nesteditemselector : undefined; width = this.$element.children(nestedselector).width(); if (imgs.length && width <= 0) { this.preloadautowidthimages(imgs); return false; } } this.$element.addclass('owl-loading'); // create stage this.$stage = $('<' + this.settings.stageelement + ' class="owl-stage"/>') .wrap('
'); // append stage this.$element.append(this.$stage.parent()); // append content this.replace(this.$element.children().not(this.$stage.parent())); // set view width this._width = this.$element.width(); // update view this.refresh(); this.$element.removeclass('owl-loading').addclass('owl-loaded'); // attach generic events this.eventscall(); // attach generic events this.internalevents(); // attach custom control events this.addtriggerableevents(); this.trigger('initialized'); }; /** * setups the current settings. * @todo remove responsive classes. why should adaptive designs be brought into ie8? * @todo support for media queries by using `matchmedia` would be nice. * @public */ owl.prototype.setup = function() { var viewport = this.viewport(), overwrites = this.options.responsive, match = -1, settings = null; if (!overwrites) { settings = $.extend({}, this.options); } else { $.each(overwrites, function(breakpoint) { if (breakpoint <= viewport && breakpoint > match) { match = number(breakpoint); } }); settings = $.extend({}, this.options, overwrites[match]); delete settings.responsive; // responsive class if (settings.responsiveclass) { this.$element.attr('class', function(i, c) { return c.replace(/\b owl-responsive-\s+/g, ''); }).addclass('owl-responsive-' + match); } } if (this.settings === null || this._breakpoint !== match) { this.trigger('change', { property: { name: 'settings', value: settings } }); this._breakpoint = match; this.settings = settings; this.invalidate('settings'); this.trigger('changed', { property: { name: 'settings', value: this.settings } }); } }; /** * updates option logic if necessery. * @protected */ owl.prototype.optionslogic = function() { // toggle center class this.$element.toggleclass('owl-center', this.settings.center); // if items number is less than in body if (this.settings.loop && this._items.length < this.settings.items) { this.settings.loop = false; } if (this.settings.autowidth) { this.settings.stagepadding = false; this.settings.merge = false; } }; /** * prepares an item before add. * @todo rename event parameter `content` to `item`. * @protected * @returns {jquery|htmlelement} - the item container. */ owl.prototype.prepare = function(item) { var event = this.trigger('prepare', { content: item }); if (!event.data) { event.data = $('<' + this.settings.itemelement + '/>') .addclass(this.settings.itemclass).append(item) } this.trigger('prepared', { content: event.data }); return event.data; }; /** * updates the view. * @public */ owl.prototype.update = function() { var i = 0, n = this._pipe.length, filter = $.proxy(function(p) { return this[p] }, this._invalidated), cache = {}; while (i < n) { if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) { this._pipe[i].run(cache); } i++; } this._invalidated = {}; }; /** * gets the width of the view. * @public * @param {owl.width} [dimension=owl.width.default] - the dimension to return. * @returns {number} - the width of the view in pixel. */ owl.prototype.width = function(dimension) { dimension = dimension || owl.width.default; switch (dimension) { case owl.width.inner: case owl.width.outer: return this._width; default: return this._width - this.settings.stagepadding * 2 + this.settings.margin; } }; /** * refreshes the carousel primarily for adaptive purposes. * @public */ owl.prototype.refresh = function() { if (this._items.length === 0) { return false; } var start = new date().gettime(); this.trigger('refresh'); this.setup(); this.optionslogic(); // hide and show methods helps here to set a proper widths, // this prevents scrollbar to be calculated in stage width this.$stage.addclass('owl-refresh'); this.update(); this.$stage.removeclass('owl-refresh'); this.state.orientation = window.orientation; this.watchvisibility(); this.trigger('refreshed'); }; /** * save internal event references and add event based functions. * @protected */ owl.prototype.eventscall = function() { // save events references this.e._ondragstart = $.proxy(function(e) { this.ondragstart(e); }, this); this.e._ondragmove = $.proxy(function(e) { this.ondragmove(e); }, this); this.e._ondragend = $.proxy(function(e) { this.ondragend(e); }, this); this.e._onresize = $.proxy(function(e) { this.onresize(e); }, this); this.e._transitionend = $.proxy(function(e) { this.transitionend(e); }, this); this.e._preventclick = $.proxy(function(e) { this.preventclick(e); }, this); }; /** * checks window `resize` event. * @protected */ owl.prototype.onthrottledresize = function() { window.cleartimeout(this.resizetimer); this.resizetimer = window.settimeout(this.e._onresize, this.settings.responsiverefreshrate); }; /** * checks window `resize` event. * @protected */ owl.prototype.onresize = function() { if (!this._items.length) { return false; } if (this._width === this.$element.width()) { return false; } if (this.trigger('resize').isdefaultprevented()) { return false; } this._width = this.$element.width(); this.invalidate('width'); this.refresh(); this.trigger('resized'); }; /** * checks for touch/mouse drag event type and add run event handlers. * @protected */ owl.prototype.eventsrouter = function(event) { var type = event.type; if (type === "mousedown" || type === "touchstart") { this.ondragstart(event); } else if (type === "mousemove" || type === "touchmove") { this.ondragmove(event); } else if (type === "mouseup" || type === "touchend") { this.ondragend(event); } else if (type === "touchcancel") { this.ondragend(event); } }; /** * checks for touch/mouse drag options and add necessery event handlers. * @protected */ owl.prototype.internalevents = function() { var istouch = istouchsupport(), istouchie = istouchsupportie(); if (this.settings.mousedrag){ this.$stage.on('mousedown', $.proxy(function(event) { this.eventsrouter(event) }, this)); this.$stage.on('dragstart', function() { return false }); this.$stage.get(0).onselectstart = function() { return false }; } else { this.$element.addclass('owl-text-select-on'); } if (this.settings.touchdrag && !istouchie){ this.$stage.on('touchstart touchcancel', $.proxy(function(event) { this.eventsrouter(event) }, this)); } // catch transitionend event if (this.transitionendvendor) { this.on(this.$stage.get(0), this.transitionendvendor, this.e._transitionend, false); } // responsive if (this.settings.responsive !== false) { this.on(window, 'resize', $.proxy(this.onthrottledresize, this)); } }; /** * handles touchstart/mousedown event. * @protected * @param {event} event - the event arguments. */ owl.prototype.ondragstart = function(event) { var ev, istouchevent, pagex, pagey, animatedpos; ev = event.originalevent || event || window.event; // prevent right click if (ev.which === 3 || this.state.istouch) { return false; } if (ev.type === 'mousedown') { this.$stage.addclass('owl-grab'); } this.trigger('drag'); this.drag.starttime = new date().gettime(); this.speed(0); this.state.istouch = true; this.state.isscrolling = false; this.state.isswiping = false; this.drag.distance = 0; pagex = gettouches(ev).x; pagey = gettouches(ev).y; // get stage position left this.drag.offsetx = this.$stage.position().left; this.drag.offsety = this.$stage.position().top; if (this.settings.rtl) { this.drag.offsetx = this.$stage.position().left + this.$stage.width() - this.width() + this.settings.margin; } // catch position // ie to fix if (this.state.inmotion && this.support3d) { animatedpos = this.gettransformproperty(); this.drag.offsetx = animatedpos; this.animate(animatedpos); this.state.inmotion = true; } else if (this.state.inmotion && !this.support3d) { this.state.inmotion = false; return false; } this.drag.startx = pagex - this.drag.offsetx; this.drag.starty = pagey - this.drag.offsety; this.drag.start = pagex - this.drag.startx; this.drag.targetel = ev.target || ev.srcelement; this.drag.updatedx = this.drag.start; // to do/check // prevent links and images dragging; if (this.drag.targetel.tagname === "img" || this.drag.targetel.tagname === "a") { this.drag.targetel.draggable = false; } $(document).on('mousemove.owl.dragevents mouseup.owl.dragevents touchmove.owl.dragevents touchend.owl.dragevents', $.proxy(function(event) {this.eventsrouter(event)},this)); }; /** * handles the touchmove/mousemove events. * @todo simplify * @protected * @param {event} event - the event arguments. */ owl.prototype.ondragmove = function(event) { var ev, istouchevent, pagex, pagey, minvalue, maxvalue, pull; if (!this.state.istouch) { return; } if (this.state.isscrolling) { return; } ev = event.originalevent || event || window.event; pagex = gettouches(ev).x; pagey = gettouches(ev).y; // drag direction this.drag.currentx = pagex - this.drag.startx; this.drag.currenty = pagey - this.drag.starty; this.drag.distance = this.drag.currentx - this.drag.offsetx; // check move direction if (this.drag.distance < 0) { this.state.direction = this.settings.rtl ? 'right' : 'left'; } else if (this.drag.distance > 0) { this.state.direction = this.settings.rtl ? 'left' : 'right'; } // loop if (this.settings.loop) { if (this.op(this.drag.currentx, '>', this.coordinates(this.minimum())) && this.state.direction === 'right') { this.drag.currentx -= (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length); } else if (this.op(this.drag.currentx, '<', this.coordinates(this.maximum())) && this.state.direction === 'left') { this.drag.currentx += (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length); } } else { // pull minvalue = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum()); maxvalue = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum()); pull = this.settings.pulldrag ? this.drag.distance / 5 : 0; this.drag.currentx = math.max(math.min(this.drag.currentx, minvalue + pull), maxvalue + pull); } // lock browser if swiping horizontal if ((this.drag.distance > 8 || this.drag.distance < -8)) { if (ev.preventdefault !== undefined) { } else { ev.returnvalue = false; } this.state.isswiping = true; } this.drag.updatedx = this.drag.currentx; // lock owl if scrolling if ((this.drag.currenty > 16 || this.drag.currenty < -16) && this.state.isswiping === false) { this.state.isscrolling = true; this.drag.updatedx = this.drag.start; } this.animate(this.drag.updatedx); }; /** * handles the touchend/mouseup events. * @protected */ owl.prototype.ondragend = function(event) { var comparetimes, distanceabs, closest; if (!this.state.istouch) { return; } if (event.type === 'mouseup') { this.$stage.removeclass('owl-grab'); } this.trigger('dragged'); // prevent links and images dragging; this.drag.targetel.removeattribute("draggable"); // remove drag event listeners this.state.istouch = false; this.state.isscrolling = false; this.state.isswiping = false; // to check if (this.drag.distance === 0 && this.state.inmotion !== true) { this.state.inmotion = false; return false; } // prevent clicks while scrolling this.drag.endtime = new date().gettime(); comparetimes = this.drag.endtime - this.drag.starttime; distanceabs = math.abs(this.drag.distance); // to test if (distanceabs > 3 || comparetimes > 300) { this.removeclick(this.drag.targetel); } closest = this.closest(this.drag.updatedx); this.speed(this.settings.dragendspeed || this.settings.smartspeed); this.current(closest); this.invalidate('position'); this.update(); // if pulldrag is off then fire transitionend event manually when stick // to border if (!this.settings.pulldrag && this.drag.updatedx === this.coordinates(closest)) { this.transitionend(); } this.drag.distance = 0; $(document).off('.owl.dragevents'); }; /** * attaches `preventclick` to disable link while swipping. * @protected * @param {htmlelement} [target] - the target of the `click` event. */ owl.prototype.removeclick = function(target) { this.drag.targetel = target; $(target).on('click.preventclick', this.e._preventclick); // to make sure click is removed: window.settimeout(function() { $(target).off('click.preventclick'); }, 300); }; /** * suppresses click event. * @protected * @param {event} ev - the event arguments. */ owl.prototype.preventclick = function(ev) { if (ev.preventdefault) { ev.preventdefault(); } else { ev.returnvalue = false; } if (ev.stoppropagation) { ev.stoppropagation(); } $(ev.target).off('click.preventclick'); }; /** * catches stage position while animate (only css3). * @protected * @returns */ owl.prototype.gettransformproperty = function() { var transform, matrix3d; transform = window.getcomputedstyle(this.$stage.get(0), null).getpropertyvalue(this.vendorname + 'transform'); // var transform = this.$stage.css(this.vendorname + 'transform') transform = transform.replace(/matrix(3d)?\(|\)/g, '').split(','); matrix3d = transform.length === 16; return matrix3d !== true ? transform[4] : transform[12]; }; /** * gets absolute position of the closest item for a coordinate. * @todo setting `freedrag` makes `closest` not reusable. see #165. * @protected * @param {number} coordinate - the coordinate in pixel. * @return {number} - the absolute position of the closest item. */ owl.prototype.closest = function(coordinate) { var position = -1, pull = 30, width = this.width(), coordinates = this.coordinates(); if (!this.settings.freedrag) { // check closest item $.each(coordinates, $.proxy(function(index, value) { if (coordinate > value - pull && coordinate < value + pull) { position = index; } else if (this.op(coordinate, '<', value) && this.op(coordinate, '>', coordinates[index + 1] || value - width)) { position = this.state.direction === 'left' ? index + 1 : index; } return position === -1; }, this)); } if (!this.settings.loop) { // non loop boundries if (this.op(coordinate, '>', coordinates[this.minimum()])) { position = coordinate = this.minimum(); } else if (this.op(coordinate, '<', coordinates[this.maximum()])) { position = coordinate = this.maximum(); } } return position; }; /** * animates the stage. * @public * @param {number} coordinate - the coordinate in pixels. */ owl.prototype.animate = function(coordinate) { this.trigger('translate'); this.state.inmotion = this.speed() > 0; if (this.support3d) { this.$stage.css({ transform: 'translate3d(' + coordinate + 'px' + ',0px, 0px)', transition: (this.speed() / 1000) + 's' }); } else if (this.state.istouch) { this.$stage.css({ left: coordinate + 'px' }); } else { this.$stage.animate({ left: coordinate }, this.speed() / 1000, this.settings.fallbackeasing, $.proxy(function() { if (this.state.inmotion) { this.transitionend(); } }, this)); } }; /** * sets the absolute position of the current item. * @public * @param {number} [position] - the new absolute position or nothing to leave it unchanged. * @returns {number} - the absolute position of the current item. */ owl.prototype.current = function(position) { if (position === undefined) { return this._current; } if (this._items.length === 0) { return undefined; } position = this.normalize(position); if (this._current !== position) { var event = this.trigger('change', { property: { name: 'position', value: position } }); if (event.data !== undefined) { position = this.normalize(event.data); } this._current = position; this.invalidate('position'); this.trigger('changed', { property: { name: 'position', value: this._current } }); } return this._current; }; /** * invalidates the given part of the update routine. * @param {string} part - the part to invalidate. */ owl.prototype.invalidate = function(part) { this._invalidated[part] = true; } /** * resets the absolute position of the current item. * @public * @param {number} position - the absolute position of the new item. */ owl.prototype.reset = function(position) { position = this.normalize(position); if (position === undefined) { return; } this._speed = 0; this._current = position; this.suppress([ 'translate', 'translated' ]); this.animate(this.coordinates(position)); this.release([ 'translate', 'translated' ]); }; /** * normalizes an absolute or a relative position for an item. * @public * @param {number} position - the absolute or relative position to normalize. * @param {boolean} [relative=false] - whether the given position is relative or not. * @returns {number} - the normalized position. */ owl.prototype.normalize = function(position, relative) { var n = (relative ? this._items.length : this._items.length + this._clones.length); if (!$.isnumeric(position) || n < 1) { return undefined; } if (this._clones.length) { position = ((position % n) + n) % n; } else { position = math.max(this.minimum(relative), math.min(this.maximum(relative), position)); } return position; }; /** * converts an absolute position for an item into a relative position. * @public * @param {number} position - the absolute position to convert. * @returns {number} - the converted position. */ owl.prototype.relative = function(position) { position = this.normalize(position); position = position - this._clones.length / 2; return this.normalize(position, true); }; /** * gets the maximum position for an item. * @public * @param {boolean} [relative=false] - whether to return an absolute position or a relative position. * @returns {number} */ owl.prototype.maximum = function(relative) { var maximum, width, i = 0, coordinate, settings = this.settings; if (relative) { return this._items.length - 1; } if (!settings.loop && settings.center) { maximum = this._items.length - 1; } else if (!settings.loop && !settings.center) { maximum = this._items.length - settings.items; } else if (settings.loop || settings.center) { maximum = this._items.length + settings.items; } else if (settings.autowidth || settings.merge) { revert = settings.rtl ? 1 : -1; width = this.$stage.width() - this.$element.width(); while (coordinate = this.coordinates(i)) { if (coordinate * revert >= width) { break; } maximum = ++i; } } else { throw 'can not detect maximum absolute position.' } return maximum; }; /** * gets the minimum position for an item. * @public * @param {boolean} [relative=false] - whether to return an absolute position or a relative position. * @returns {number} */ owl.prototype.minimum = function(relative) { if (relative) { return 0; } return this._clones.length / 2; }; /** * gets an item at the specified relative position. * @public * @param {number} [position] - the relative position of the item. * @return {jquery|array.} - the item at the given position or all items if no position was given. */ owl.prototype.items = function(position) { if (position === undefined) { return this._items.slice(); } position = this.normalize(position, true); return this._items[position]; }; /** * gets an item at the specified relative position. * @public * @param {number} [position] - the relative position of the item. * @return {jquery|array.} - the item at the given position or all items if no position was given. */ owl.prototype.mergers = function(position) { if (position === undefined) { return this._mergers.slice(); } position = this.normalize(position, true); return this._mergers[position]; }; /** * gets the absolute positions of clones for an item. * @public * @param {number} [position] - the relative position of the item. * @returns {array.} - the absolute positions of clones for the item or all if no position was given. */ owl.prototype.clones = function(position) { var odd = this._clones.length / 2, even = odd + this._items.length, map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 }; if (position === undefined) { return $.map(this._clones, function(v, i) { return map(i) }); } return $.map(this._clones, function(v, i) { return v === position ? map(i) : null }); }; /** * sets the current animation speed. * @public * @param {number} [speed] - the animation speed in milliseconds or nothing to leave it unchanged. * @returns {number} - the current animation speed in milliseconds. */ owl.prototype.speed = function(speed) { if (speed !== undefined) { this._speed = speed; } return this._speed; }; /** * gets the coordinate of an item. * @todo the name of this method is missleanding. * @public * @param {number} position - the absolute position of the item within `minimum()` and `maximum()`. * @returns {number|array.} - the coordinate of the item in pixel or all coordinates. */ owl.prototype.coordinates = function(position) { var coordinate = null; if (position === undefined) { return $.map(this._coordinates, $.proxy(function(coordinate, index) { return this.coordinates(index); }, this)); } if (this.settings.center) { coordinate = this._coordinates[position]; coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1); } else { coordinate = this._coordinates[position - 1] || 0; } return coordinate; }; /** * calculates the speed for a translation. * @protected * @param {number} from - the absolute position of the start item. * @param {number} to - the absolute position of the target item. * @param {number} [factor=undefined] - the time factor in milliseconds. * @returns {number} - the time in milliseconds for the translation. */ owl.prototype.duration = function(from, to, factor) { return math.min(math.max(math.abs(to - from), 1), 6) * math.abs((factor || this.settings.smartspeed)); }; /** * slides to the specified item. * @public * @param {number} position - the position of the item. * @param {number} [speed] - the time in milliseconds for the transition. */ owl.prototype.to = function(position, speed) { if (this.settings.loop) { var distance = position - this.relative(this.current()), revert = this.current(), before = this.current(), after = this.current() + distance, direction = before - after < 0 ? true : false, items = this._clones.length + this._items.length; if (after < this.settings.items && direction === false) { revert = before + this._items.length; this.reset(revert); } else if (after >= items - this.settings.items && direction === true) { revert = before - this._items.length; this.reset(revert); } window.cleartimeout(this.e._gotoloop); this.e._gotoloop = window.settimeout($.proxy(function() { this.speed(this.duration(this.current(), revert + distance, speed)); this.current(revert + distance); this.update(); }, this), 30); } else { this.speed(this.duration(this.current(), position, speed)); this.current(position); this.update(); } }; /** * slides to the next item. * @public * @param {number} [speed] - the time in milliseconds for the transition. */ owl.prototype.next = function(speed) { speed = speed || false; this.to(this.relative(this.current()) + 1, speed); }; /** * slides to the previous item. * @public * @param {number} [speed] - the time in milliseconds for the transition. */ owl.prototype.prev = function(speed) { speed = speed || false; this.to(this.relative(this.current()) - 1, speed); }; /** * handles the end of an animation. * @protected * @param {event} event - the event arguments. */ owl.prototype.transitionend = function(event) { // if css2 animation then event object is undefined if (event !== undefined) { event.stoppropagation(); // catch only owl-stage transitionend event if ((event.target || event.srcelement || event.originaltarget) !== this.$stage.get(0)) { return false; } } this.state.inmotion = false; this.trigger('translated'); }; /** * gets viewport width. * @protected * @return {number} - the width in pixel. */ owl.prototype.viewport = function() { var width; if (this.options.responsivebaseelement !== window) { width = $(this.options.responsivebaseelement).width(); } else if (window.innerwidth) { width = window.innerwidth; } else if (document.documentelement && document.documentelement.clientwidth) { width = document.documentelement.clientwidth; } else { throw 'can not detect viewport width.'; } return width; }; /** * replaces the current content. * @public * @param {htmlelement|jquery|string} content - the new content. */ owl.prototype.replace = function(content) { this.$stage.empty(); this._items = []; if (content) { content = (content instanceof jquery) ? content : $(content); } if (this.settings.nesteditemselector) { content = content.find('.' + this.settings.nesteditemselector); } content.filter(function() { return this.nodetype === 1; }).each($.proxy(function(index, item) { item = this.prepare(item); this.$stage.append(item); this._items.push(item); this._mergers.push(item.find('[data-merge]').andself('[data-merge]').attr('data-merge') * 1 || 1); }, this)); this.reset($.isnumeric(this.settings.startposition) ? this.settings.startposition : 0); this.invalidate('items'); }; /** * adds an item. * @todo use `item` instead of `content` for the event arguments. * @public * @param {htmlelement|jquery|string} content - the item content to add. * @param {number} [position] - the relative position at which to insert the item otherwise the item will be added to the end. */ owl.prototype.add = function(content, position) { position = position === undefined ? this._items.length : this.normalize(position, true); this.trigger('add', { content: content, position: position }); if (this._items.length === 0 || position === this._items.length) { this.$stage.append(content); this._items.push(content); this._mergers.push(content.find('[data-merge]').andself('[data-merge]').attr('data-merge') * 1 || 1); } else { this._items[position].before(content); this._items.splice(position, 0, content); this._mergers.splice(position, 0, content.find('[data-merge]').andself('[data-merge]').attr('data-merge') * 1 || 1); } this.invalidate('items'); this.trigger('added', { content: content, position: position }); }; /** * removes an item by its position. * @todo use `item` instead of `content` for the event arguments. * @public * @param {number} position - the relative position of the item to remove. */ owl.prototype.remove = function(position) { position = this.normalize(position, true); if (position === undefined) { return; } this.trigger('remove', { content: this._items[position], position: position }); this._items[position].remove(); this._items.splice(position, 1); this._mergers.splice(position, 1); this.invalidate('items'); this.trigger('removed', { content: null, position: position }); }; /** * adds triggerable events. * @protected */ owl.prototype.addtriggerableevents = function() { var handler = $.proxy(function(callback, event) { return $.proxy(function(e) { if (e.relatedtarget !== this) { this.suppress([ event ]); callback.apply(this, [].slice.call(arguments, 1)); this.release([ event ]); } }, this); }, this); $.each({ 'next': this.next, 'prev': this.prev, 'to': this.to, 'destroy': this.destroy, 'refresh': this.refresh, 'replace': this.replace, 'add': this.add, 'remove': this.remove }, $.proxy(function(event, callback) { this.$element.on(event + '.owl.carousel', handler(callback, event + '.owl.carousel')); }, this)); }; /** * watches the visibility of the carousel element. * @protected */ owl.prototype.watchvisibility = function() { // test on zepto if (!iselvisible(this.$element.get(0))) { this.$element.addclass('owl-hidden'); window.clearinterval(this.e._checkvisibile); this.e._checkvisibile = window.setinterval($.proxy(checkvisible, this), 500); } function iselvisible(el) { return el.offsetwidth > 0 && el.offsetheight > 0; } function checkvisible() { if (iselvisible(this.$element.get(0))) { this.$element.removeclass('owl-hidden'); this.refresh(); window.clearinterval(this.e._checkvisibile); } } }; /** * preloads images with auto width. * @protected * @todo still to test */ owl.prototype.preloadautowidthimages = function(imgs) { var loaded, that, $el, img; loaded = 0; that = this; imgs.each(function(i, el) { $el = $(el); img = new image(); img.onload = function() { loaded++; $el.attr('src', img.src); $el.css('opacity', 1); if (loaded >= imgs.length) { that.state.imagesloaded = true; that.initialize(); } }; img.src = $el.attr('src') || $el.attr('data-src') || $el.attr('data-src-retina'); }); }; /** * destroys the carousel. * @public */ owl.prototype.destroy = function() { if (this.$element.hasclass(this.settings.themeclass)) { this.$element.removeclass(this.settings.themeclass); } if (this.settings.responsive !== false) { $(window).off('resize.owl.carousel'); } if (this.transitionendvendor) { this.off(this.$stage.get(0), this.transitionendvendor, this.e._transitionend); } for ( var i in this._plugins) { this._plugins[i].destroy(); } if (this.settings.mousedrag || this.settings.touchdrag) { this.$stage.off('mousedown touchstart touchcancel'); $(document).off('.owl.dragevents'); this.$stage.get(0).onselectstart = function() {}; this.$stage.off('dragstart', function() { return false }); } // remove event handlers in the ".owl.carousel" namespace this.$element.off('.owl'); this.$stage.children('.cloned').remove(); this.e = null; this.$element.removedata('owlcarousel'); this.$stage.children().contents().unwrap(); this.$stage.children().unwrap(); this.$stage.unwrap(); }; /** * operators to calculate right-to-left and left-to-right. * @protected * @param {number} [a] - the left side operand. * @param {string} [o] - the operator. * @param {number} [b] - the right side operand. */ owl.prototype.op = function(a, o, b) { var rtl = this.settings.rtl; switch (o) { case '<': return rtl ? a > b : a < b; case '>': return rtl ? a < b : a > b; case '>=': return rtl ? a <= b : a >= b; case '<=': return rtl ? a >= b : a <= b; default: break; } }; /** * attaches to an internal event. * @protected * @param {htmlelement} element - the event source. * @param {string} event - the event name. * @param {function} listener - the event handler to attach. * @param {boolean} capture - wether the event should be handled at the capturing phase or not. */ owl.prototype.on = function(element, event, listener, capture) { if (element.addeventlistener) { element.addeventlistener(event, listener, capture); } else if (element.attachevent) { element.attachevent('on' + event, listener); } }; /** * detaches from an internal event. * @protected * @param {htmlelement} element - the event source. * @param {string} event - the event name. * @param {function} listener - the attached event handler to detach. * @param {boolean} capture - wether the attached event handler was registered as a capturing listener or not. */ owl.prototype.off = function(element, event, listener, capture) { if (element.removeeventlistener) { element.removeeventlistener(event, listener, capture); } else if (element.detachevent) { element.detachevent('on' + event, listener); } }; /** * triggers an public event. * @protected * @param {string} name - the event name. * @param {*} [data=null] - the event data. * @param {string} [namespace=.owl.carousel] - the event namespace. * @returns {event} - the event arguments. */ owl.prototype.trigger = function(name, data, namespace) { var status = { item: { count: this._items.length, index: this.current() } }, handler = $.camelcase( $.grep([ 'on', name, namespace ], function(v) { return v }) .join('-').tolowercase() ), event = $.event( [ name, 'owl', namespace || 'carousel' ].join('.').tolowercase(), $.extend({ relatedtarget: this }, status, data) ); if (!this._supress[name]) { $.each(this._plugins, function(name, plugin) { if (plugin.ontrigger) { plugin.ontrigger(event); } }); this.$element.trigger(event); if (this.settings && typeof this.settings[handler] === 'function') { this.settings[handler].apply(this, event); } } return event; }; /** * suppresses events. * @protected * @param {array.} events - the events to suppress. */ owl.prototype.suppress = function(events) { $.each(events, $.proxy(function(index, event) { this._supress[event] = true; }, this)); } /** * releases suppressed events. * @protected * @param {array.} events - the events to release. */ owl.prototype.release = function(events) { $.each(events, $.proxy(function(index, event) { delete this._supress[event]; }, this)); } /** * checks the availability of some browser features. * @protected */ owl.prototype.browsersupport = function() { this.support3d = isperspective(); if (this.support3d) { this.transformvendor = istransform(); // take transitionend event name by detecting transition var endvendors = [ 'transitionend', 'webkittransitionend', 'transitionend', 'otransitionend' ]; this.transitionendvendor = endvendors[istransition()]; // take vendor name from transform name this.vendorname = this.transformvendor.replace(/transform/i, ''); this.vendorname = this.vendorname !== '' ? '-' + this.vendorname.tolowercase() + '-' : ''; } this.state.orientation = window.orientation; }; /** * get touch/drag coordinats. * @private * @param {event} - mousedown/touchstart event * @returns {object} - contains x and y of current mouse/touch position */ function gettouches(event) { if (event.touches !== undefined) { return { x: event.touches[0].pagex, y: event.touches[0].pagey }; } if (event.touches === undefined) { if (event.pagex !== undefined) { return { x: event.pagex, y: event.pagey }; } if (event.pagex === undefined) { return { x: event.clientx, y: event.clienty }; } } } /** * checks for css support. * @private * @param {array} array - the css properties to check for. * @returns {array} - contains the supported css property name and its index or `false`. */ function isstylesupported(array) { var p, s, fake = document.createelement('div'), list = array; for (p in list) { s = list[p]; if (typeof fake.style[s] !== 'undefined') { fake = null; return [ s, p ]; } } return [ false ]; } /** * checks for css transition support. * @private * @todo realy bad design * @returns {number} */ function istransition() { return isstylesupported([ 'transition', 'webkittransition', 'moztransition', 'otransition' ])[1]; } /** * checks for css transform support. * @private * @returns {string} the supported property name or false. */ function istransform() { return isstylesupported([ 'transform', 'webkittransform', 'moztransform', 'otransform', 'mstransform' ])[0]; } /** * checks for css perspective support. * @private * @returns {string} the supported property name or false. */ function isperspective() { return isstylesupported([ 'perspective', 'webkitperspective', 'mozperspective', 'operspective', 'msperspective' ])[0]; } /** * checks wether touch is supported or not. * @private * @returns {boolean} */ function istouchsupport() { return 'ontouchstart' in window || !!(navigator.msmaxtouchpoints); } /** * checks wether touch is supported or not for ie. * @private * @returns {boolean} */ function istouchsupportie() { return window.navigator.mspointerenabled; } /** * the jquery plugin for the owl carousel * @public */ $.fn.owlcarousel = function(options) { return this.each(function() { if (!$(this).data('owlcarousel')) { $(this).data('owlcarousel', new owl(this, options)); } }); }; /** * the constructor for the jquery plugin * @public */ $.fn.owlcarousel.constructor = owl; })(window.zepto || window.jquery, window, document); /** * lazy plugin * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { /** * creates the lazy plugin. * @class the lazy plugin * @param {owl} carousel - the owl carousel */ var lazy = function(carousel) { /** * reference to the core. * @protected * @type {owl} */ this._core = carousel; /** * already loaded items. * @protected * @type {array.} */ this._loaded = []; /** * event handlers. * @protected * @type {object} */ this._handlers = { 'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) { if (!e.namespace) { return; } if (!this._core.settings || !this._core.settings.lazyload) { return; } if ((e.property && e.property.name == 'position') || e.type == 'initialized') { var settings = this._core.settings, n = (settings.center && math.ceil(settings.items / 2) || settings.items), i = ((settings.center && n * -1) || 0), position = ((e.property && e.property.value) || this._core.current()) + i, clones = this._core.clones().length, load = $.proxy(function(i, v) { this.load(v) }, this); while (i++ < n) { this.load(clones / 2 + this._core.relative(position)); clones && $.each(this._core.clones(this._core.relative(position++)), load); } } }, this) }; // set the default options this._core.options = $.extend({}, lazy.defaults, this._core.options); // register event handler this._core.$element.on(this._handlers); } /** * default options. * @public */ lazy.defaults = { lazyload: false } /** * loads all resources of an item at the specified position. * @param {number} position - the absolute position of the item. * @protected */ lazy.prototype.load = function(position) { var $item = this._core.$stage.children().eq(position), $elements = $item && $item.find('.owl-lazy'); if (!$elements || $.inarray($item.get(0), this._loaded) > -1) { return; } $elements.each($.proxy(function(index, element) { var $element = $(element), image, url = (window.devicepixelratio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src'); this._core.trigger('load', { element: $element, url: url }, 'lazy'); if ($element.is('img')) { $element.one('load.owl.lazy', $.proxy(function() { $element.css('opacity', 1); this._core.trigger('loaded', { element: $element, url: url }, 'lazy'); }, this)).attr('src', url); } else { image = new image(); image.onload = $.proxy(function() { $element.css({ 'background-image': 'url(' + url + ')', 'opacity': '1' }); this._core.trigger('loaded', { element: $element, url: url }, 'lazy'); }, this); image.src = url; } }, this)); this._loaded.push($item.get(0)); } /** * destroys the plugin. * @public */ lazy.prototype.destroy = function() { var handler, property; for (handler in this.handlers) { this._core.$element.off(handler, this.handlers[handler]); } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } } $.fn.owlcarousel.constructor.plugins.lazy = lazy; })(window.zepto || window.jquery, window, document); /** * autoheight plugin * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { /** * creates the auto height plugin. * @class the auto height plugin * @param {owl} carousel - the owl carousel */ var autoheight = function(carousel) { /** * reference to the core. * @protected * @type {owl} */ this._core = carousel; /** * all event handlers. * @protected * @type {object} */ this._handlers = { 'initialized.owl.carousel': $.proxy(function() { if (this._core.settings.autoheight) { this.update(); } }, this), 'changed.owl.carousel': $.proxy(function(e) { if (this._core.settings.autoheight && e.property.name == 'position'){ this.update(); } }, this), 'loaded.owl.lazy': $.proxy(function(e) { if (this._core.settings.autoheight && e.element.closest('.' + this._core.settings.itemclass) === this._core.$stage.children().eq(this._core.current())) { this.update(); } }, this) }; // set default options this._core.options = $.extend({}, autoheight.defaults, this._core.options); // register event handlers this._core.$element.on(this._handlers); }; /** * default options. * @public */ autoheight.defaults = { autoheight: false, autoheightclass: 'owl-height' }; /** * updates the view. */ autoheight.prototype.update = function() { this._core.$stage.parent() .height(this._core.$stage.children().eq(this._core.current()).height()) .addclass(this._core.settings.autoheightclass); }; autoheight.prototype.destroy = function() { var handler, property; for (handler in this._handlers) { this._core.$element.off(handler, this._handlers[handler]); } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } }; $.fn.owlcarousel.constructor.plugins.autoheight = autoheight; })(window.zepto || window.jquery, window, document); /** * video plugin * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { /** * creates the video plugin. * @class the video plugin * @param {owl} carousel - the owl carousel */ var video = function(carousel) { /** * reference to the core. * @protected * @type {owl} */ this._core = carousel; /** * cache all video urls. * @protected * @type {object} */ this._videos = {}; /** * current playing item. * @protected * @type {jquery} */ this._playing = null; /** * whether this is in fullscreen or not. * @protected * @type {boolean} */ this._fullscreen = false; /** * all event handlers. * @protected * @type {object} */ this._handlers = { 'resize.owl.carousel': $.proxy(function(e) { if (this._core.settings.video && !this.isinfullscreen()) { e.preventdefault(); } }, this), 'refresh.owl.carousel changed.owl.carousel': $.proxy(function(e) { if (this._playing) { this.stop(); } }, this), 'prepared.owl.carousel': $.proxy(function(e) { var $element = $(e.content).find('.owl-video'); if ($element.length) { $element.css('display', 'none'); this.fetch($element, $(e.content)); } }, this) }; // set default options this._core.options = $.extend({}, video.defaults, this._core.options); // register event handlers this._core.$element.on(this._handlers); this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) { this.play(e); }, this)); }; /** * default options. * @public */ video.defaults = { video: false, videoheight: false, videowidth: false }; /** * gets the video id and the type (youtube/vimeo only). * @protected * @param {jquery} target - the target containing the video data. * @param {jquery} item - the item containing the video. */ video.prototype.fetch = function(target, item) { var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube', id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'), width = target.attr('data-width') || this._core.settings.videowidth, height = target.attr('data-height') || this._core.settings.videoheight, url = target.attr('href'); if (url) { id = url.match(/(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([a-za-z0-9._%-]*)(\&\s+)?/); if (id[3].indexof('youtu') > -1) { type = 'youtube'; } else if (id[3].indexof('vimeo') > -1) { type = 'vimeo'; } else { throw new error('video url not supported.'); } id = id[6]; } else { throw new error('missing video url.'); } this._videos[url] = { type: type, id: id, width: width, height: height }; item.attr('data-video', url); this.thumbnail(target, this._videos[url]); }; /** * creates video thumbnail. * @protected * @param {jquery} target - the target containing the video data. * @param {object} info - the video info object. * @see `fetch` */ video.prototype.thumbnail = function(target, video) { var tnlink, icon, path, dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '', customtn = target.find('img'), srctype = 'src', lazyclass = '', settings = this._core.settings, create = function(path) { icon = '
'; if (settings.lazyload) { tnlink = '
'; } else { tnlink = '
'; } target.after(tnlink); target.after(icon); }; // wrap video content into owl-video-wrapper div target.wrap('
'); if (this._core.settings.lazyload) { srctype = 'data-src'; lazyclass = 'owl-lazy'; } // custom thumbnail if (customtn.length) { create(customtn.attr(srctype)); customtn.remove(); return false; } if (video.type === 'youtube') { path = "http://img.youtube.com/vi/" + video.id + "/hqdefault.jpg"; create(path); } else if (video.type === 'vimeo') { $.ajax({ type: 'get', url: 'http://vimeo.com/api/v2/video/' + video.id + '.json', jsonp: 'callback', datatype: 'jsonp', success: function(data) { path = data[0].thumbnail_large; create(path); } }); } }; /** * stops the current video. * @public */ video.prototype.stop = function() { this._core.trigger('stop', null, 'video'); this._playing.find('.owl-video-frame').remove(); this._playing.removeclass('owl-video-playing'); this._playing = null; }; /** * starts the current video. * @public * @param {event} ev - the event arguments. */ video.prototype.play = function(ev) { this._core.trigger('play', null, 'video'); if (this._playing) { this.stop(); } var target = $(ev.target || ev.srcelement), item = target.closest('.' + this._core.settings.itemclass), video = this._videos[item.attr('data-video')], width = video.width || '100%', height = video.height || this._core.$stage.height(), html, wrap; if (video.type === 'youtube') { html = ''; } else if (video.type === 'vimeo') { html = ''; } item.addclass('owl-video-playing'); this._playing = item; wrap = $('
' + html + '
'); target.after(wrap); }; /** * checks whether an video is currently in full screen mode or not. * @todo bad style because looks like a readonly method but changes members. * @protected * @returns {boolean} */ video.prototype.isinfullscreen = function() { // if vimeo fullscreen mode var element = document.fullscreenelement || document.mozfullscreenelement || document.webkitfullscreenelement; if (element && $(element).parent().hasclass('owl-video-frame')) { this._core.speed(0); this._fullscreen = true; } if (element && this._fullscreen && this._playing) { return false; } // comming back from fullscreen if (this._fullscreen) { this._fullscreen = false; return false; } // check full screen mode and window orientation if (this._playing) { if (this._core.state.orientation !== window.orientation) { this._core.state.orientation = window.orientation; return false; } } return true; }; /** * destroys the plugin. */ video.prototype.destroy = function() { var handler, property; this._core.$element.off('click.owl.video'); for (handler in this._handlers) { this._core.$element.off(handler, this._handlers[handler]); } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } }; $.fn.owlcarousel.constructor.plugins.video = video; })(window.zepto || window.jquery, window, document); /** * animate plugin * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { /** * creates the animate plugin. * @class the navigation plugin * @param {owl} scope - the owl carousel */ var animate = function(scope) { this.core = scope; this.core.options = $.extend({}, animate.defaults, this.core.options); this.swapping = true; this.previous = undefined; this.next = undefined; this.handlers = { 'change.owl.carousel': $.proxy(function(e) { if (e.property.name == 'position') { this.previous = this.core.current(); this.next = e.property.value; } }, this), 'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) { this.swapping = e.type == 'translated'; }, this), 'translate.owl.carousel': $.proxy(function(e) { if (this.swapping && (this.core.options.animateout || this.core.options.animatein)) { this.swap(); } }, this) }; this.core.$element.on(this.handlers); }; /** * default options. * @public */ animate.defaults = { animateout: false, animatein: false }; /** * toggles the animation classes whenever an translations starts. * @protected * @returns {boolean|undefined} */ animate.prototype.swap = function() { if (this.core.settings.items !== 1 || !this.core.support3d) { return; } this.core.speed(0); var left, clear = $.proxy(this.clear, this), previous = this.core.$stage.children().eq(this.previous), next = this.core.$stage.children().eq(this.next), incoming = this.core.settings.animatein, outgoing = this.core.settings.animateout; if (this.core.current() === this.previous) { return; } if (outgoing) { left = this.core.coordinates(this.previous) - this.core.coordinates(this.next); previous.css( { 'left': left + 'px' } ) .addclass('animated owl-animated-out') .addclass(outgoing) .one('webkitanimationend mozanimationend msanimationend oanimationend animationend', clear); } if (incoming) { next.addclass('animated owl-animated-in') .addclass(incoming) .one('webkitanimationend mozanimationend msanimationend oanimationend animationend', clear); } }; animate.prototype.clear = function(e) { $(e.target).css( { 'left': '' } ) .removeclass('animated owl-animated-out owl-animated-in') .removeclass(this.core.settings.animatein) .removeclass(this.core.settings.animateout); this.core.transitionend(); } /** * destroys the plugin. * @public */ animate.prototype.destroy = function() { var handler, property; for (handler in this.handlers) { this.core.$element.off(handler, this.handlers[handler]); } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } }; $.fn.owlcarousel.constructor.plugins.animate = animate; })(window.zepto || window.jquery, window, document); /** * autoplay plugin * @version 2.0.0 * @author bartosz wojciechowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { /** * creates the autoplay plugin. * @class the autoplay plugin * @param {owl} scope - the owl carousel */ var autoplay = function(scope) { this.core = scope; this.core.options = $.extend({}, autoplay.defaults, this.core.options); this.handlers = { 'translated.owl.carousel refreshed.owl.carousel': $.proxy(function() { this.autoplay(); }, this), 'play.owl.autoplay': $.proxy(function(e, t, s) { this.play(t, s); }, this), 'stop.owl.autoplay': $.proxy(function() { this.stop(); }, this), 'mouseover.owl.autoplay': $.proxy(function() { if (this.core.settings.autoplayhoverpause) { this.pause(); } }, this), 'mouseleave.owl.autoplay': $.proxy(function() { if (this.core.settings.autoplayhoverpause) { this.autoplay(); } }, this) }; this.core.$element.on(this.handlers); }; /** * default options. * @public */ autoplay.defaults = { autoplay: false, autoplaytimeout: 5000, autoplayhoverpause: false, autoplayspeed: false }; /** * @protected * @todo must be documented. */ autoplay.prototype.autoplay = function() { if (this.core.settings.autoplay && !this.core.state.videoplay) { window.clearinterval(this.interval); this.interval = window.setinterval($.proxy(function() { this.play(); }, this), this.core.settings.autoplaytimeout); } else { window.clearinterval(this.interval); } }; /** * starts the autoplay. * @public * @param {number} [timeout] - ... * @param {number} [speed] - ... * @returns {boolean|undefined} - ... * @todo must be documented. */ autoplay.prototype.play = function(timeout, speed) { // if tab is inactive - doesnt work in } */ this._templates = []; /** * the carousel element. * @type {jquery} */ this.$element = this._core.$element; /** * overridden methods of the carousel. * @protected * @type {object} */ this._overrides = { next: this._core.next, prev: this._core.prev, to: this._core.to }; /** * all event handlers. * @protected * @type {object} */ this._handlers = { 'prepared.owl.carousel': $.proxy(function(e) { if (this._core.settings.dotsdata) { this._templates.push($(e.content).find('[data-dot]').andself('[data-dot]').attr('data-dot')); } }, this), 'add.owl.carousel': $.proxy(function(e) { if (this._core.settings.dotsdata) { this._templates.splice(e.position, 0, $(e.content).find('[data-dot]').andself('[data-dot]').attr('data-dot')); } }, this), 'remove.owl.carousel prepared.owl.carousel': $.proxy(function(e) { if (this._core.settings.dotsdata) { this._templates.splice(e.position, 1); } }, this), 'change.owl.carousel': $.proxy(function(e) { if (e.property.name == 'position') { if (!this._core.state.revert && !this._core.settings.loop && this._core.settings.navrewind) { var current = this._core.current(), maximum = this._core.maximum(), minimum = this._core.minimum(); e.data = e.property.value > maximum ? current >= maximum ? minimum : maximum : e.property.value < minimum ? maximum : e.property.value; } } }, this), 'changed.owl.carousel': $.proxy(function(e) { if (e.property.name == 'position') { this.draw(); } }, this), 'refreshed.owl.carousel': $.proxy(function() { if (!this._initialized) { this.initialize(); this._initialized = true; } this._core.trigger('refresh', null, 'navigation'); this.update(); this.draw(); this._core.trigger('refreshed', null, 'navigation'); }, this) }; // set default options this._core.options = $.extend({}, navigation.defaults, this._core.options); // register event handlers this.$element.on(this._handlers); } /** * default options. * @public * @todo rename `slideby` to `navby` */ navigation.defaults = { nav: false, navrewind: true, navtext: [ 'prev', 'next' ], navspeed: false, navelement: 'div', navcontainer: false, navcontainerclass: 'owl-nav', navclass: [ 'owl-prev', 'owl-next' ], slideby: 1, dotclass: 'owl-dot', dotsclass: 'owl-dots', dots: true, dotseach: false, dotdata: false, dotsspeed: false, dotscontainer: false, controlsclass: 'owl-controls' } /** * initializes the layout of the plugin and extends the carousel. * @protected */ navigation.prototype.initialize = function() { var $container, override, options = this._core.settings; // create the indicator template if (!options.dotsdata) { this._templates = [ $('
') .addclass(options.dotclass) .append($('')) .prop('outerhtml') ]; } // create controls container if needed if (!options.navcontainer || !options.dotscontainer) { this._controls.$container = $('
') .addclass(options.controlsclass) .appendto(this.$element); } // create dom structure for absolute navigation this._controls.$indicators = options.dotscontainer ? $(options.dotscontainer) : $('
').hide().addclass(options.dotsclass).appendto(this._controls.$container); this._controls.$indicators.on('click', 'div', $.proxy(function(e) { var index = $(e.target).parent().is(this._controls.$indicators) ? $(e.target).index() : $(e.target).parent().index(); e.preventdefault(); this.to(index, options.dotsspeed); }, this)); // create dom structure for relative navigation $container = options.navcontainer ? $(options.navcontainer) : $('
').addclass(options.navcontainerclass).prependto(this._controls.$container); this._controls.$next = $('<' + options.navelement + '>'); this._controls.$previous = this._controls.$next.clone(); this._controls.$previous .addclass(options.navclass[0]) .html(options.navtext[0]) .hide() .prependto($container) .on('click', $.proxy(function(e) { this.prev(options.navspeed); }, this)); this._controls.$next .addclass(options.navclass[1]) .html(options.navtext[1]) .hide() .appendto($container) .on('click', $.proxy(function(e) { this.next(options.navspeed); }, this)); // override public methods of the carousel for (override in this._overrides) { this._core[override] = $.proxy(this[override], this); } } /** * destroys the plugin. * @protected */ navigation.prototype.destroy = function() { var handler, control, property, override; for (handler in this._handlers) { this.$element.off(handler, this._handlers[handler]); } for (control in this._controls) { this._controls[control].remove(); } for (override in this.overides) { this._core[override] = this._overrides[override]; } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } } /** * updates the internal state. * @protected */ navigation.prototype.update = function() { var i, j, k, options = this._core.settings, lower = this._core.clones().length / 2, upper = lower + this._core.items().length, size = options.center || options.autowidth || options.dotdata ? 1 : options.dotseach || options.items; if (options.slideby !== 'page') { options.slideby = math.min(options.slideby, options.items); } if (options.dots || options.slideby == 'page') { this._pages = []; for (i = lower, j = 0, k = 0; i < upper; i++) { if (j >= size || j === 0) { this._pages.push({ start: i - lower, end: i - lower + size - 1 }); j = 0, ++k; } j += this._core.mergers(this._core.relative(i)); } } } /** * draws the user interface. * @todo the option `dotdata` wont work. * @protected */ navigation.prototype.draw = function() { var difference, i, html = '', options = this._core.settings, $items = this._core.$stage.children(), index = this._core.relative(this._core.current()); if (options.nav && !options.loop && !options.navrewind) { this._controls.$previous.toggleclass('disabled', index <= 0); this._controls.$next.toggleclass('disabled', index >= this._core.maximum()); } this._controls.$previous.toggle(options.nav); this._controls.$next.toggle(options.nav); if (options.dots) { difference = this._pages.length - this._controls.$indicators.children().length; if (options.dotdata && difference !== 0) { for (i = 0; i < this._controls.$indicators.children().length; i++) { html += this._templates[this._core.relative(i)]; } this._controls.$indicators.html(html); } else if (difference > 0) { html = new array(difference + 1).join(this._templates[0]); this._controls.$indicators.append(html); } else if (difference < 0) { this._controls.$indicators.children().slice(difference).remove(); } this._controls.$indicators.find('.active').removeclass('active'); this._controls.$indicators.children().eq($.inarray(this.current(), this._pages)).addclass('active'); } this._controls.$indicators.toggle(options.dots); } /** * extends event data. * @protected * @param {event} event - the event object which gets thrown. */ navigation.prototype.ontrigger = function(event) { var settings = this._core.settings; event.page = { index: $.inarray(this.current(), this._pages), count: this._pages.length, size: settings && (settings.center || settings.autowidth || settings.dotdata ? 1 : settings.dotseach || settings.items) }; } /** * gets the current page position of the carousel. * @protected * @returns {number} */ navigation.prototype.current = function() { var index = this._core.relative(this._core.current()); return $.grep(this._pages, function(o) { return o.start <= index && o.end >= index; }).pop(); } /** * gets the current succesor/predecessor position. * @protected * @returns {number} */ navigation.prototype.getposition = function(successor) { var position, length, options = this._core.settings; if (options.slideby == 'page') { position = $.inarray(this.current(), this._pages); length = this._pages.length; successor ? ++position : --position; position = this._pages[((position % length) + length) % length].start; } else { position = this._core.relative(this._core.current()); length = this._core.items().length; successor ? position += options.slideby : position -= options.slideby; } return position; } /** * slides to the next item or page. * @public * @param {number} [speed=false] - the time in milliseconds for the transition. */ navigation.prototype.next = function(speed) { $.proxy(this._overrides.to, this._core)(this.getposition(true), speed); } /** * slides to the previous item or page. * @public * @param {number} [speed=false] - the time in milliseconds for the transition. */ navigation.prototype.prev = function(speed) { $.proxy(this._overrides.to, this._core)(this.getposition(false), speed); } /** * slides to the specified item or page. * @public * @param {number} position - the position of the item or page. * @param {number} [speed] - the time in milliseconds for the transition. * @param {boolean} [standard=false] - whether to use the standard behaviour or not. */ navigation.prototype.to = function(position, speed, standard) { var length; if (!standard) { length = this._pages.length; $.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed); } else { $.proxy(this._overrides.to, this._core)(position, speed); } } $.fn.owlcarousel.constructor.plugins.navigation = navigation; })(window.zepto || window.jquery, window, document); /** * hash plugin * @version 2.0.0 * @author artus kolanowski * @license the mit license (mit) */ ;(function($, window, document, undefined) { 'use strict'; /** * creates the hash plugin. * @class the hash plugin * @param {owl} carousel - the owl carousel */ var hash = function(carousel) { /** * reference to the core. * @protected * @type {owl} */ this._core = carousel; /** * hash table for the hashes. * @protected * @type {object} */ this._hashes = {}; /** * the carousel element. * @type {jquery} */ this.$element = this._core.$element; /** * all event handlers. * @protected * @type {object} */ this._handlers = { 'initialized.owl.carousel': $.proxy(function() { if (this._core.settings.startposition == 'urlhash') { $(window).trigger('hashchange.owl.navigation'); } }, this), 'prepared.owl.carousel': $.proxy(function(e) { var hash = $(e.content).find('[data-hash]').andself('[data-hash]').attr('data-hash'); this._hashes[hash] = e.content; }, this) }; // set default options this._core.options = $.extend({}, hash.defaults, this._core.options); // register the event handlers this.$element.on(this._handlers); // register event listener for hash navigation $(window).on('hashchange.owl.navigation', $.proxy(function() { var hash = window.location.hash.substring(1), items = this._core.$stage.children(), position = this._hashes[hash] && items.index(this._hashes[hash]) || 0; if (!hash) { return false; } this._core.to(position, false, true); }, this)); } /** * default options. * @public */ hash.defaults = { urlhashlistener: false } /** * destroys the plugin. * @public */ hash.prototype.destroy = function() { var handler, property; $(window).off('hashchange.owl.navigation'); for (handler in this._handlers) { this._core.$element.off(handler, this._handlers[handler]); } for (property in object.getownpropertynames(this)) { typeof this[property] != 'function' && (this[property] = null); } } $.fn.owlcarousel.constructor.plugins.hash = hash; })(window.zepto || window.jquery, window, document);