/** * 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: 750, fluidSpeed: false, dragEndSpeed: false, responsive: { 768: { items: 3, }, 1024: { items: 3, }, 1440: { items: 5, }, 2560: { items: 5, }, 425: { items: 1, }, 375: { items: 1, }, 320: { items: 1, }, }, 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) { ev.preventDefault(); } 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);