diff options
Diffstat (limited to 'src/main/resources/META-INF/resources/designer/lib/owl.carousel.js')
-rw-r--r-- | src/main/resources/META-INF/resources/designer/lib/owl.carousel.js | 3069 |
1 files changed, 3069 insertions, 0 deletions
diff --git a/src/main/resources/META-INF/resources/designer/lib/owl.carousel.js b/src/main/resources/META-INF/resources/designer/lib/owl.carousel.js new file mode 100644 index 00000000..f6ef78b7 --- /dev/null +++ b/src/main/resources/META-INF/resources/designer/lib/owl.carousel.js @@ -0,0 +1,3069 @@ +/** + * 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('<div class="owl-stage-outer">'); + + // 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.<jQuery>} - 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.<jQuery>} - 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.<Number>} - 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.<Number>} - 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.<String>} 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.<String>} 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.<jQuery>} + */ + 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 = '<div class="owl-video-play-icon"></div>'; + + if (settings.lazyLoad) { + tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>'; + } else { + tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>'; + } + target.after(tnLink); + target.after(icon); + }; + + // wrap video content into owl-video-wrapper div + target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>'); + + 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 = '<iframe width="' + width + '" height="' + height + '" src="http://www.youtube.com/embed/' + + video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>'; + } else if (video.type === 'vimeo') { + html = '<iframe src="http://player.vimeo.com/video/' + video.id + '?autoplay=1" width="' + width + + '" height="' + height + + '" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>'; + } + + item.addClass('owl-video-playing'); + this._playing = item; + + wrap = $('<div style="height:' + height + 'px; width:' + width + 'px" class="owl-video-frame">' + + html + '</div>'); + 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 <IE10 + if (document.hidden === true) { + return; + } + + if (this.core.state.isTouch || this.core.state.isScrolling + || this.core.state.isSwiping || this.core.state.inMotion) { + return; + } + + if (this.core.settings.autoplay === false) { + window.clearInterval(this.interval); + return; + } + + this.core.next(this.core.settings.autoplaySpeed); + }; + + /** + * Stops the autoplay. + * @public + */ + Autoplay.prototype.stop = function() { + window.clearInterval(this.interval); + }; + + /** + * Pauses the autoplay. + * @public + */ + Autoplay.prototype.pause = function() { + window.clearInterval(this.interval); + }; + + /** + * Destroys the plugin. + */ + Autoplay.prototype.destroy = function() { + var handler, property; + + window.clearInterval(this.interval); + + 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.autoplay = Autoplay; + +})(window.Zepto || window.jQuery, window, document); + +/** + * Navigation Plugin + * @version 2.0.0 + * @author Artus Kolanowski + * @license The MIT License (MIT) + */ +;(function($, window, document, undefined) { + 'use strict'; + + /** + * Creates the navigation plugin. + * @class The Navigation Plugin + * @param {Owl} carousel - The Owl Carousel. + */ + var Navigation = function(carousel) { + /** + * Reference to the core. + * @protected + * @type {Owl} + */ + this._core = carousel; + + /** + * Indicates whether the plugin is initialized or not. + * @protected + * @type {Boolean} + */ + this._initialized = false; + + /** + * The current paging indexes. + * @protected + * @type {Array} + */ + this._pages = []; + + /** + * All DOM elements of the user interface. + * @protected + * @type {Object} + */ + this._controls = {}; + + /** + * Markup for an indicator. + * @protected + * @type {Array.<String>} + */ + 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 = [ $('<div>') + .addClass(options.dotClass) + .append($('<span>')) + .prop('outerHTML') ]; + } + + // create controls container if needed + if (!options.navContainer || !options.dotsContainer) { + this._controls.$container = $('<div>') + .addClass(options.controlsClass) + .appendTo(this.$element); + } + + // create DOM structure for absolute navigation + this._controls.$indicators = options.dotsContainer ? $(options.dotsContainer) + : $('<div>').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) + : $('<div>').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); |