File name
Commit message
Commit date
15 hours ago
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
/**
* 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);