f6ef78b7110981689ff1e0286cc32459bd0705d0
[clamp.git] / src / main / resources / META-INF / resources / designer / lib / owl.carousel.js
1 /**
2  * Owl carousel
3  * @version 2.0.0
4  * @author Bartosz Wojciechowski
5  * @license The MIT License (MIT)
6  * @todo Lazy Load Icon
7  * @todo prevent animationend bubling
8  * @todo itemsScaleUp
9  * @todo Test Zepto
10  * @todo stagePadding calculate wrong active classes
11  */
12 ;(function($, window, document, undefined) {
13
14         var drag, state, e;
15
16         /**
17          * Template for status information about drag and touch events.
18          * @private
19          */
20         drag = {
21                 start: 0,
22                 startX: 0,
23                 startY: 0,
24                 current: 0,
25                 currentX: 0,
26                 currentY: 0,
27                 offsetX: 0,
28                 offsetY: 0,
29                 distance: null,
30                 startTime: 0,
31                 endTime: 0,
32                 updatedX: 0,
33                 targetEl: null
34         };
35
36         /**
37          * Template for some status informations.
38          * @private
39          */
40         state = {
41                 isTouch: false,
42                 isScrolling: false,
43                 isSwiping: false,
44                 direction: false,
45                 inMotion: false
46         };
47
48         /**
49          * Event functions references.
50          * @private
51          */
52         e = {
53                 _onDragStart: null,
54                 _onDragMove: null,
55                 _onDragEnd: null,
56                 _transitionEnd: null,
57                 _resizer: null,
58                 _responsiveCall: null,
59                 _goToLoop: null,
60                 _checkVisibile: null
61         };
62
63         /**
64          * Creates a carousel.
65          * @class The Owl Carousel.
66          * @public
67          * @param {HTMLElement|jQuery} element - The element to create the carousel for.
68          * @param {Object} [options] - The options
69          */
70         function Owl(element, options) {
71
72                 /**
73                  * Current settings for the carousel.
74                  * @public
75                  */
76                 this.settings = null;
77
78                 /**
79                  * Current options set by the caller including defaults.
80                  * @public
81                  */
82                 this.options = $.extend({}, Owl.Defaults, options);
83
84                 /**
85                  * Plugin element.
86                  * @public
87                  */
88                 this.$element = $(element);
89
90                 /**
91                  * Caches informations about drag and touch events.
92                  */
93                 this.drag = $.extend({}, drag);
94
95                 /**
96                  * Caches some status informations.
97                  * @protected
98                  */
99                 this.state = $.extend({}, state);
100
101                 /**
102                  * @protected
103                  * @todo Must be documented
104                  */
105                 this.e = $.extend({}, e);
106
107                 /**
108                  * References to the running plugins of this carousel.
109                  * @protected
110                  */
111                 this._plugins = {};
112
113                 /**
114                  * Currently suppressed events to prevent them from beeing retriggered.
115                  * @protected
116                  */
117                 this._supress = {};
118
119                 /**
120                  * Absolute current position.
121                  * @protected
122                  */
123                 this._current = null;
124
125                 /**
126                  * Animation speed in milliseconds.
127                  * @protected
128                  */
129                 this._speed = null;
130
131                 /**
132                  * Coordinates of all items in pixel.
133                  * @todo The name of this member is missleading.
134                  * @protected
135                  */
136                 this._coordinates = [];
137
138                 /**
139                  * Current breakpoint.
140                  * @todo Real media queries would be nice.
141                  * @protected
142                  */
143                 this._breakpoint = null;
144
145                 /**
146                  * Current width of the plugin element.
147                  */
148                 this._width = null;
149
150                 /**
151                  * All real items.
152                  * @protected
153                  */
154                 this._items = [];
155
156                 /**
157                  * All cloned items.
158                  * @protected
159                  */
160                 this._clones = [];
161
162                 /**
163                  * Merge values of all items.
164                  * @todo Maybe this could be part of a plugin.
165                  * @protected
166                  */
167                 this._mergers = [];
168
169                 /**
170                  * Invalidated parts within the update process.
171                  * @protected
172                  */
173                 this._invalidated = {};
174
175                 /**
176                  * Ordered list of workers for the update process.
177                  * @protected
178                  */
179                 this._pipe = [];
180
181                 $.each(Owl.Plugins, $.proxy(function(key, plugin) {
182                         this._plugins[key[0].toLowerCase() + key.slice(1)]
183                                 = new plugin(this);
184                 }, this));
185
186                 $.each(Owl.Pipe, $.proxy(function(priority, worker) {
187                         this._pipe.push({
188                                 'filter': worker.filter,
189                                 'run': $.proxy(worker.run, this)
190                         });
191                 }, this));
192
193                 this.setup();
194                 this.initialize();
195         }
196
197         /**
198          * Default options for the carousel.
199          * @public
200          */
201         Owl.Defaults = {
202                 items: 3,
203                 loop: false,
204                 center: false,
205
206                 mouseDrag: true,
207                 touchDrag: true,
208                 pullDrag: true,
209                 freeDrag: false,
210
211                 margin: 0,
212                 stagePadding: 0,
213
214                 merge: false,
215                 mergeFit: true,
216                 autoWidth: false,
217
218                 startPosition: 0,
219                 rtl: false,
220
221                 smartSpeed: 250,
222                 fluidSpeed: false,
223                 dragEndSpeed: false,
224
225                 responsive: {},
226                 responsiveRefreshRate: 200,
227                 responsiveBaseElement: window,
228                 responsiveClass: false,
229
230                 fallbackEasing: 'swing',
231
232                 info: false,
233
234                 nestedItemSelector: false,
235                 itemElement: 'div',
236                 stageElement: 'div',
237
238                 // Classes and Names
239                 themeClass: 'owl-theme',
240                 baseClass: 'owl-carousel',
241                 itemClass: 'owl-item',
242                 centerClass: 'center',
243                 activeClass: 'active'
244         };
245
246         /**
247          * Enumeration for width.
248          * @public
249          * @readonly
250          * @enum {String}
251          */
252         Owl.Width = {
253                 Default: 'default',
254                 Inner: 'inner',
255                 Outer: 'outer'
256         };
257
258         /**
259          * Contains all registered plugins.
260          * @public
261          */
262         Owl.Plugins = {};
263
264         /**
265          * Update pipe.
266          */
267         Owl.Pipe = [ {
268                 filter: [ 'width', 'items', 'settings' ],
269                 run: function(cache) {
270                         cache.current = this._items && this._items[this.relative(this._current)];
271                 }
272         }, {
273                 filter: [ 'items', 'settings' ],
274                 run: function() {
275                         var cached = this._clones,
276                                 clones = this.$stage.children('.cloned');
277
278                         if (clones.length !== cached.length || (!this.settings.loop && cached.length > 0)) {
279                                 this.$stage.children('.cloned').remove();
280                                 this._clones = [];
281                         }
282                 }
283         }, {
284                 filter: [ 'items', 'settings' ],
285                 run: function() {
286                         var i, n,
287                                 clones = this._clones,
288                                 items = this._items,
289                                 delta = this.settings.loop ? clones.length - Math.max(this.settings.items * 2, 4) : 0;
290
291                         for (i = 0, n = Math.abs(delta / 2); i < n; i++) {
292                                 if (delta > 0) {
293                                         this.$stage.children().eq(items.length + clones.length - 1).remove();
294                                         clones.pop();
295                                         this.$stage.children().eq(0).remove();
296                                         clones.pop();
297                                 } else {
298                                         clones.push(clones.length / 2);
299                                         this.$stage.append(items[clones[clones.length - 1]].clone().addClass('cloned'));
300                                         clones.push(items.length - 1 - (clones.length - 1) / 2);
301                                         this.$stage.prepend(items[clones[clones.length - 1]].clone().addClass('cloned'));
302                                 }
303                         }
304                 }
305         }, {
306                 filter: [ 'width', 'items', 'settings' ],
307                 run: function() {
308                         var rtl = (this.settings.rtl ? 1 : -1),
309                                 width = (this.width() / this.settings.items).toFixed(3),
310                                 coordinate = 0, merge, i, n;
311
312                         this._coordinates = [];
313                         for (i = 0, n = this._clones.length + this._items.length; i < n; i++) {
314                                 merge = this._mergers[this.relative(i)];
315                                 merge = (this.settings.mergeFit && Math.min(merge, this.settings.items)) || merge;
316                                 coordinate += (this.settings.autoWidth ? this._items[this.relative(i)].width() + this.settings.margin : width * merge) * rtl;
317
318                                 this._coordinates.push(coordinate);
319                         }
320                 }
321         }, {
322                 filter: [ 'width', 'items', 'settings' ],
323                 run: function() {
324                         var i, n, width = (this.width() / this.settings.items).toFixed(3), css = {
325                                 'width': Math.abs(this._coordinates[this._coordinates.length - 1]) + this.settings.stagePadding * 2,
326                                 'padding-left': this.settings.stagePadding || '',
327                                 'padding-right': this.settings.stagePadding || ''
328                         };
329
330                         this.$stage.css(css);
331
332                         css = { 'width': this.settings.autoWidth ? 'auto' : width - this.settings.margin };
333                         css[this.settings.rtl ? 'margin-left' : 'margin-right'] = this.settings.margin;
334
335                         if (!this.settings.autoWidth && $.grep(this._mergers, function(v) { return v > 1 }).length > 0) {
336                                 for (i = 0, n = this._coordinates.length; i < n; i++) {
337                                         css.width = Math.abs(this._coordinates[i]) - Math.abs(this._coordinates[i - 1] || 0) - this.settings.margin;
338                                         this.$stage.children().eq(i).css(css);
339                                 }
340                         } else {
341                                 this.$stage.children().css(css);
342                         }
343                 }
344         }, {
345                 filter: [ 'width', 'items', 'settings' ],
346                 run: function(cache) {
347                         cache.current && this.reset(this.$stage.children().index(cache.current));
348                 }
349         }, {
350                 filter: [ 'position' ],
351                 run: function() {
352                         this.animate(this.coordinates(this._current));
353                 }
354         }, {
355                 filter: [ 'width', 'position', 'items', 'settings' ],
356                 run: function() {
357                         var rtl = this.settings.rtl ? 1 : -1,
358                                 padding = this.settings.stagePadding * 2,
359                                 begin = this.coordinates(this.current()) + padding,
360                                 end = begin + this.width() * rtl,
361                                 inner, outer, matches = [], i, n;
362
363                         for (i = 0, n = this._coordinates.length; i < n; i++) {
364                                 inner = this._coordinates[i - 1] || 0;
365                                 outer = Math.abs(this._coordinates[i]) + padding * rtl;
366
367                                 if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
368                                         || (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
369                                         matches.push(i);
370                                 }
371                         }
372
373                         this.$stage.children('.' + this.settings.activeClass).removeClass(this.settings.activeClass);
374                         this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass(this.settings.activeClass);
375
376                         if (this.settings.center) {
377                                 this.$stage.children('.' + this.settings.centerClass).removeClass(this.settings.centerClass);
378                                 this.$stage.children().eq(this.current()).addClass(this.settings.centerClass);
379                         }
380                 }
381         } ];
382
383         /**
384          * Initializes the carousel.
385          * @protected
386          */
387         Owl.prototype.initialize = function() {
388                 this.trigger('initialize');
389
390                 this.$element
391                         .addClass(this.settings.baseClass)
392                         .addClass(this.settings.themeClass)
393                         .toggleClass('owl-rtl', this.settings.rtl);
394
395                 // check support
396                 this.browserSupport();
397
398                 if (this.settings.autoWidth && this.state.imagesLoaded !== true) {
399                         var imgs, nestedSelector, width;
400                         imgs = this.$element.find('img');
401                         nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
402                         width = this.$element.children(nestedSelector).width();
403
404                         if (imgs.length && width <= 0) {
405                                 this.preloadAutoWidthImages(imgs);
406                                 return false;
407                         }
408                 }
409
410                 this.$element.addClass('owl-loading');
411
412                 // create stage
413                 this.$stage = $('<' + this.settings.stageElement + ' class="owl-stage"/>')
414                         .wrap('<div class="owl-stage-outer">');
415
416                 // append stage
417                 this.$element.append(this.$stage.parent());
418
419                 // append content
420                 this.replace(this.$element.children().not(this.$stage.parent()));
421
422                 // set view width
423                 this._width = this.$element.width();
424
425                 // update view
426                 this.refresh();
427
428                 this.$element.removeClass('owl-loading').addClass('owl-loaded');
429
430                 // attach generic events
431                 this.eventsCall();
432
433                 // attach generic events
434                 this.internalEvents();
435
436                 // attach custom control events
437                 this.addTriggerableEvents();
438
439                 this.trigger('initialized');
440         };
441
442         /**
443          * Setups the current settings.
444          * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
445          * @todo Support for media queries by using `matchMedia` would be nice.
446          * @public
447          */
448         Owl.prototype.setup = function() {
449                 var viewport = this.viewport(),
450                         overwrites = this.options.responsive,
451                         match = -1,
452                         settings = null;
453
454                 if (!overwrites) {
455                         settings = $.extend({}, this.options);
456                 } else {
457                         $.each(overwrites, function(breakpoint) {
458                                 if (breakpoint <= viewport && breakpoint > match) {
459                                         match = Number(breakpoint);
460                                 }
461                         });
462
463                         settings = $.extend({}, this.options, overwrites[match]);
464                         delete settings.responsive;
465
466                         // responsive class
467                         if (settings.responsiveClass) {
468                                 this.$element.attr('class', function(i, c) {
469                                         return c.replace(/\b owl-responsive-\S+/g, '');
470                                 }).addClass('owl-responsive-' + match);
471                         }
472                 }
473
474                 if (this.settings === null || this._breakpoint !== match) {
475                         this.trigger('change', { property: { name: 'settings', value: settings } });
476                         this._breakpoint = match;
477                         this.settings = settings;
478                         this.invalidate('settings');
479                         this.trigger('changed', { property: { name: 'settings', value: this.settings } });
480                 }
481         };
482
483         /**
484          * Updates option logic if necessery.
485          * @protected
486          */
487         Owl.prototype.optionsLogic = function() {
488                 // Toggle Center class
489                 this.$element.toggleClass('owl-center', this.settings.center);
490
491                 // if items number is less than in body
492                 if (this.settings.loop && this._items.length < this.settings.items) {
493                         this.settings.loop = false;
494                 }
495
496                 if (this.settings.autoWidth) {
497                         this.settings.stagePadding = false;
498                         this.settings.merge = false;
499                 }
500         };
501
502         /**
503          * Prepares an item before add.
504          * @todo Rename event parameter `content` to `item`.
505          * @protected
506          * @returns {jQuery|HTMLElement} - The item container.
507          */
508         Owl.prototype.prepare = function(item) {
509                 var event = this.trigger('prepare', { content: item });
510
511                 if (!event.data) {
512                         event.data = $('<' + this.settings.itemElement + '/>')
513                                 .addClass(this.settings.itemClass).append(item)
514                 }
515
516                 this.trigger('prepared', { content: event.data });
517
518                 return event.data;
519         };
520
521         /**
522          * Updates the view.
523          * @public
524          */
525         Owl.prototype.update = function() {
526                 var i = 0,
527                         n = this._pipe.length,
528                         filter = $.proxy(function(p) { return this[p] }, this._invalidated),
529                         cache = {};
530
531                 while (i < n) {
532                         if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
533                                 this._pipe[i].run(cache);
534                         }
535                         i++;
536                 }
537
538                 this._invalidated = {};
539         };
540
541         /**
542          * Gets the width of the view.
543          * @public
544          * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
545          * @returns {Number} - The width of the view in pixel.
546          */
547         Owl.prototype.width = function(dimension) {
548                 dimension = dimension || Owl.Width.Default;
549                 switch (dimension) {
550                         case Owl.Width.Inner:
551                         case Owl.Width.Outer:
552                                 return this._width;
553                         default:
554                                 return this._width - this.settings.stagePadding * 2 + this.settings.margin;
555                 }
556         };
557
558         /**
559          * Refreshes the carousel primarily for adaptive purposes.
560          * @public
561          */
562         Owl.prototype.refresh = function() {
563                 if (this._items.length === 0) {
564                         return false;
565                 }
566
567                 var start = new Date().getTime();
568
569                 this.trigger('refresh');
570
571                 this.setup();
572
573                 this.optionsLogic();
574
575                 // hide and show methods helps here to set a proper widths,
576                 // this prevents scrollbar to be calculated in stage width
577                 this.$stage.addClass('owl-refresh');
578
579                 this.update();
580
581                 this.$stage.removeClass('owl-refresh');
582
583                 this.state.orientation = window.orientation;
584
585                 this.watchVisibility();
586
587                 this.trigger('refreshed');
588         };
589
590         /**
591          * Save internal event references and add event based functions.
592          * @protected
593          */
594         Owl.prototype.eventsCall = function() {
595                 // Save events references
596                 this.e._onDragStart = $.proxy(function(e) {
597                         this.onDragStart(e);
598                 }, this);
599                 this.e._onDragMove = $.proxy(function(e) {
600                         this.onDragMove(e);
601                 }, this);
602                 this.e._onDragEnd = $.proxy(function(e) {
603                         this.onDragEnd(e);
604                 }, this);
605                 this.e._onResize = $.proxy(function(e) {
606                         this.onResize(e);
607                 }, this);
608                 this.e._transitionEnd = $.proxy(function(e) {
609                         this.transitionEnd(e);
610                 }, this);
611                 this.e._preventClick = $.proxy(function(e) {
612                         this.preventClick(e);
613                 }, this);
614         };
615
616         /**
617          * Checks window `resize` event.
618          * @protected
619          */
620         Owl.prototype.onThrottledResize = function() {
621                 window.clearTimeout(this.resizeTimer);
622                 this.resizeTimer = window.setTimeout(this.e._onResize, this.settings.responsiveRefreshRate);
623         };
624
625         /**
626          * Checks window `resize` event.
627          * @protected
628          */
629         Owl.prototype.onResize = function() {
630                 if (!this._items.length) {
631                         return false;
632                 }
633
634                 if (this._width === this.$element.width()) {
635                         return false;
636                 }
637
638                 if (this.trigger('resize').isDefaultPrevented()) {
639                         return false;
640                 }
641
642                 this._width = this.$element.width();
643
644                 this.invalidate('width');
645
646                 this.refresh();
647
648                 this.trigger('resized');
649         };
650
651         /**
652          * Checks for touch/mouse drag event type and add run event handlers.
653          * @protected
654          */
655         Owl.prototype.eventsRouter = function(event) {
656                 var type = event.type;
657
658                 if (type === "mousedown" || type === "touchstart") {
659                         this.onDragStart(event);
660                 } else if (type === "mousemove" || type === "touchmove") {
661                         this.onDragMove(event);
662                 } else if (type === "mouseup" || type === "touchend") {
663                         this.onDragEnd(event);
664                 } else if (type === "touchcancel") {
665                         this.onDragEnd(event);
666                 }
667         };
668
669         /**
670          * Checks for touch/mouse drag options and add necessery event handlers.
671          * @protected
672          */
673         Owl.prototype.internalEvents = function() {
674                 var isTouch = isTouchSupport(),
675                         isTouchIE = isTouchSupportIE();
676
677                 if (this.settings.mouseDrag){
678                         this.$stage.on('mousedown', $.proxy(function(event) { this.eventsRouter(event) }, this));
679                         this.$stage.on('dragstart', function() { return false });
680                         this.$stage.get(0).onselectstart = function() { return false };
681                 } else {
682                         this.$element.addClass('owl-text-select-on');
683                 }
684
685                 if (this.settings.touchDrag && !isTouchIE){
686                         this.$stage.on('touchstart touchcancel', $.proxy(function(event) { this.eventsRouter(event) }, this));
687                 }
688
689                 // catch transitionEnd event
690                 if (this.transitionEndVendor) {
691                         this.on(this.$stage.get(0), this.transitionEndVendor, this.e._transitionEnd, false);
692                 }
693
694                 // responsive
695                 if (this.settings.responsive !== false) {
696                         this.on(window, 'resize', $.proxy(this.onThrottledResize, this));
697                 }
698         };
699
700         /**
701          * Handles touchstart/mousedown event.
702          * @protected
703          * @param {Event} event - The event arguments.
704          */
705         Owl.prototype.onDragStart = function(event) {
706                 var ev, isTouchEvent, pageX, pageY, animatedPos;
707
708                 ev = event.originalEvent || event || window.event;
709
710                 // prevent right click
711                 if (ev.which === 3 || this.state.isTouch) {
712                         return false;
713                 }
714
715                 if (ev.type === 'mousedown') {
716                         this.$stage.addClass('owl-grab');
717                 }
718
719                 this.trigger('drag');
720                 this.drag.startTime = new Date().getTime();
721                 this.speed(0);
722                 this.state.isTouch = true;
723                 this.state.isScrolling = false;
724                 this.state.isSwiping = false;
725                 this.drag.distance = 0;
726
727                 pageX = getTouches(ev).x;
728                 pageY = getTouches(ev).y;
729
730                 // get stage position left
731                 this.drag.offsetX = this.$stage.position().left;
732                 this.drag.offsetY = this.$stage.position().top;
733
734                 if (this.settings.rtl) {
735                         this.drag.offsetX = this.$stage.position().left + this.$stage.width() - this.width()
736                                 + this.settings.margin;
737                 }
738
739                 // catch position // ie to fix
740                 if (this.state.inMotion && this.support3d) {
741                         animatedPos = this.getTransformProperty();
742                         this.drag.offsetX = animatedPos;
743                         this.animate(animatedPos);
744                         this.state.inMotion = true;
745                 } else if (this.state.inMotion && !this.support3d) {
746                         this.state.inMotion = false;
747                         return false;
748                 }
749
750                 this.drag.startX = pageX - this.drag.offsetX;
751                 this.drag.startY = pageY - this.drag.offsetY;
752
753                 this.drag.start = pageX - this.drag.startX;
754                 this.drag.targetEl = ev.target || ev.srcElement;
755                 this.drag.updatedX = this.drag.start;
756
757                 // to do/check
758                 // prevent links and images dragging;
759                 if (this.drag.targetEl.tagName === "IMG" || this.drag.targetEl.tagName === "A") {
760                         this.drag.targetEl.draggable = false;
761                 }
762
763                 $(document).on('mousemove.owl.dragEvents mouseup.owl.dragEvents touchmove.owl.dragEvents touchend.owl.dragEvents', $.proxy(function(event) {this.eventsRouter(event)},this));
764         };
765
766         /**
767          * Handles the touchmove/mousemove events.
768          * @todo Simplify
769          * @protected
770          * @param {Event} event - The event arguments.
771          */
772         Owl.prototype.onDragMove = function(event) {
773                 var ev, isTouchEvent, pageX, pageY, minValue, maxValue, pull;
774
775                 if (!this.state.isTouch) {
776                         return;
777                 }
778
779                 if (this.state.isScrolling) {
780                         return;
781                 }
782
783                 ev = event.originalEvent || event || window.event;
784
785                 pageX = getTouches(ev).x;
786                 pageY = getTouches(ev).y;
787
788                 // Drag Direction
789                 this.drag.currentX = pageX - this.drag.startX;
790                 this.drag.currentY = pageY - this.drag.startY;
791                 this.drag.distance = this.drag.currentX - this.drag.offsetX;
792
793                 // Check move direction
794                 if (this.drag.distance < 0) {
795                         this.state.direction = this.settings.rtl ? 'right' : 'left';
796                 } else if (this.drag.distance > 0) {
797                         this.state.direction = this.settings.rtl ? 'left' : 'right';
798                 }
799                 // Loop
800                 if (this.settings.loop) {
801                         if (this.op(this.drag.currentX, '>', this.coordinates(this.minimum())) && this.state.direction === 'right') {
802                                 this.drag.currentX -= (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length);
803                         } else if (this.op(this.drag.currentX, '<', this.coordinates(this.maximum())) && this.state.direction === 'left') {
804                                 this.drag.currentX += (this.settings.center && this.coordinates(0)) - this.coordinates(this._items.length);
805                         }
806                 } else {
807                         // pull
808                         minValue = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
809                         maxValue = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
810                         pull = this.settings.pullDrag ? this.drag.distance / 5 : 0;
811                         this.drag.currentX = Math.max(Math.min(this.drag.currentX, minValue + pull), maxValue + pull);
812                 }
813
814                 // Lock browser if swiping horizontal
815
816                 if ((this.drag.distance > 8 || this.drag.distance < -8)) {
817                         if (ev.preventDefault !== undefined) {
818                                 ev.preventDefault();
819                         } else {
820                                 ev.returnValue = false;
821                         }
822                         this.state.isSwiping = true;
823                 }
824
825                 this.drag.updatedX = this.drag.currentX;
826
827                 // Lock Owl if scrolling
828                 if ((this.drag.currentY > 16 || this.drag.currentY < -16) && this.state.isSwiping === false) {
829                         this.state.isScrolling = true;
830                         this.drag.updatedX = this.drag.start;
831                 }
832
833                 this.animate(this.drag.updatedX);
834         };
835
836         /**
837          * Handles the touchend/mouseup events.
838          * @protected
839          */
840         Owl.prototype.onDragEnd = function(event) {
841                 var compareTimes, distanceAbs, closest;
842
843                 if (!this.state.isTouch) {
844                         return;
845                 }
846
847                 if (event.type === 'mouseup') {
848                         this.$stage.removeClass('owl-grab');
849                 }
850
851                 this.trigger('dragged');
852
853                 // prevent links and images dragging;
854                 this.drag.targetEl.removeAttribute("draggable");
855
856                 // remove drag event listeners
857
858                 this.state.isTouch = false;
859                 this.state.isScrolling = false;
860                 this.state.isSwiping = false;
861
862                 // to check
863                 if (this.drag.distance === 0 && this.state.inMotion !== true) {
864                         this.state.inMotion = false;
865                         return false;
866                 }
867
868                 // prevent clicks while scrolling
869
870                 this.drag.endTime = new Date().getTime();
871                 compareTimes = this.drag.endTime - this.drag.startTime;
872                 distanceAbs = Math.abs(this.drag.distance);
873
874                 // to test
875                 if (distanceAbs > 3 || compareTimes > 300) {
876                         this.removeClick(this.drag.targetEl);
877                 }
878
879                 closest = this.closest(this.drag.updatedX);
880
881                 this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
882                 this.current(closest);
883                 this.invalidate('position');
884                 this.update();
885
886                 // if pullDrag is off then fire transitionEnd event manually when stick
887                 // to border
888                 if (!this.settings.pullDrag && this.drag.updatedX === this.coordinates(closest)) {
889                         this.transitionEnd();
890                 }
891
892                 this.drag.distance = 0;
893
894                 $(document).off('.owl.dragEvents');
895         };
896
897         /**
898          * Attaches `preventClick` to disable link while swipping.
899          * @protected
900          * @param {HTMLElement} [target] - The target of the `click` event.
901          */
902         Owl.prototype.removeClick = function(target) {
903                 this.drag.targetEl = target;
904                 $(target).on('click.preventClick', this.e._preventClick);
905                 // to make sure click is removed:
906                 window.setTimeout(function() {
907                         $(target).off('click.preventClick');
908                 }, 300);
909         };
910
911         /**
912          * Suppresses click event.
913          * @protected
914          * @param {Event} ev - The event arguments.
915          */
916         Owl.prototype.preventClick = function(ev) {
917                 if (ev.preventDefault) {
918                         ev.preventDefault();
919                 } else {
920                         ev.returnValue = false;
921                 }
922                 if (ev.stopPropagation) {
923                         ev.stopPropagation();
924                 }
925                 $(ev.target).off('click.preventClick');
926         };
927
928         /**
929          * Catches stage position while animate (only CSS3).
930          * @protected
931          * @returns
932          */
933         Owl.prototype.getTransformProperty = function() {
934                 var transform, matrix3d;
935
936                 transform = window.getComputedStyle(this.$stage.get(0), null).getPropertyValue(this.vendorName + 'transform');
937                 // var transform = this.$stage.css(this.vendorName + 'transform')
938                 transform = transform.replace(/matrix(3d)?\(|\)/g, '').split(',');
939                 matrix3d = transform.length === 16;
940
941                 return matrix3d !== true ? transform[4] : transform[12];
942         };
943
944         /**
945          * Gets absolute position of the closest item for a coordinate.
946          * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
947          * @protected
948          * @param {Number} coordinate - The coordinate in pixel.
949          * @return {Number} - The absolute position of the closest item.
950          */
951         Owl.prototype.closest = function(coordinate) {
952                 var position = -1, pull = 30, width = this.width(), coordinates = this.coordinates();
953
954                 if (!this.settings.freeDrag) {
955                         // check closest item
956                         $.each(coordinates, $.proxy(function(index, value) {
957                                 if (coordinate > value - pull && coordinate < value + pull) {
958                                         position = index;
959                                 } else if (this.op(coordinate, '<', value)
960                                         && this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
961                                         position = this.state.direction === 'left' ? index + 1 : index;
962                                 }
963                                 return position === -1;
964                         }, this));
965                 }
966
967                 if (!this.settings.loop) {
968                         // non loop boundries
969                         if (this.op(coordinate, '>', coordinates[this.minimum()])) {
970                                 position = coordinate = this.minimum();
971                         } else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
972                                 position = coordinate = this.maximum();
973                         }
974                 }
975
976                 return position;
977         };
978
979         /**
980          * Animates the stage.
981          * @public
982          * @param {Number} coordinate - The coordinate in pixels.
983          */
984         Owl.prototype.animate = function(coordinate) {
985                 this.trigger('translate');
986                 this.state.inMotion = this.speed() > 0;
987
988                 if (this.support3d) {
989                         this.$stage.css({
990                                 transform: 'translate3d(' + coordinate + 'px' + ',0px, 0px)',
991                                 transition: (this.speed() / 1000) + 's'
992                         });
993                 } else if (this.state.isTouch) {
994                         this.$stage.css({
995                                 left: coordinate + 'px'
996                         });
997                 } else {
998                         this.$stage.animate({
999                                 left: coordinate
1000                         }, this.speed() / 1000, this.settings.fallbackEasing, $.proxy(function() {
1001                                 if (this.state.inMotion) {
1002                                         this.transitionEnd();
1003                                 }
1004                         }, this));
1005                 }
1006         };
1007
1008         /**
1009          * Sets the absolute position of the current item.
1010          * @public
1011          * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
1012          * @returns {Number} - The absolute position of the current item.
1013          */
1014         Owl.prototype.current = function(position) {
1015                 if (position === undefined) {
1016                         return this._current;
1017                 }
1018
1019                 if (this._items.length === 0) {
1020                         return undefined;
1021                 }
1022
1023                 position = this.normalize(position);
1024
1025                 if (this._current !== position) {
1026                         var event = this.trigger('change', { property: { name: 'position', value: position } });
1027
1028                         if (event.data !== undefined) {
1029                                 position = this.normalize(event.data);
1030                         }
1031
1032                         this._current = position;
1033
1034                         this.invalidate('position');
1035
1036                         this.trigger('changed', { property: { name: 'position', value: this._current } });
1037                 }
1038
1039                 return this._current;
1040         };
1041
1042         /**
1043          * Invalidates the given part of the update routine.
1044          * @param {String} part - The part to invalidate.
1045          */
1046         Owl.prototype.invalidate = function(part) {
1047                 this._invalidated[part] = true;
1048         }
1049
1050         /**
1051          * Resets the absolute position of the current item.
1052          * @public
1053          * @param {Number} position - The absolute position of the new item.
1054          */
1055         Owl.prototype.reset = function(position) {
1056                 position = this.normalize(position);
1057
1058                 if (position === undefined) {
1059                         return;
1060                 }
1061
1062                 this._speed = 0;
1063                 this._current = position;
1064
1065                 this.suppress([ 'translate', 'translated' ]);
1066
1067                 this.animate(this.coordinates(position));
1068
1069                 this.release([ 'translate', 'translated' ]);
1070         };
1071
1072         /**
1073          * Normalizes an absolute or a relative position for an item.
1074          * @public
1075          * @param {Number} position - The absolute or relative position to normalize.
1076          * @param {Boolean} [relative=false] - Whether the given position is relative or not.
1077          * @returns {Number} - The normalized position.
1078          */
1079         Owl.prototype.normalize = function(position, relative) {
1080                 var n = (relative ? this._items.length : this._items.length + this._clones.length);
1081
1082                 if (!$.isNumeric(position) || n < 1) {
1083                         return undefined;
1084                 }
1085
1086                 if (this._clones.length) {
1087                         position = ((position % n) + n) % n;
1088                 } else {
1089                         position = Math.max(this.minimum(relative), Math.min(this.maximum(relative), position));
1090                 }
1091
1092                 return position;
1093         };
1094
1095         /**
1096          * Converts an absolute position for an item into a relative position.
1097          * @public
1098          * @param {Number} position - The absolute position to convert.
1099          * @returns {Number} - The converted position.
1100          */
1101         Owl.prototype.relative = function(position) {
1102                 position = this.normalize(position);
1103                 position = position - this._clones.length / 2;
1104                 return this.normalize(position, true);
1105         };
1106
1107         /**
1108          * Gets the maximum position for an item.
1109          * @public
1110          * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
1111          * @returns {Number}
1112          */
1113         Owl.prototype.maximum = function(relative) {
1114                 var maximum, width, i = 0, coordinate,
1115                         settings = this.settings;
1116
1117                 if (relative) {
1118                         return this._items.length - 1;
1119                 }
1120
1121                 if (!settings.loop && settings.center) {
1122                         maximum = this._items.length - 1;
1123                 } else if (!settings.loop && !settings.center) {
1124                         maximum = this._items.length - settings.items;
1125                 } else if (settings.loop || settings.center) {
1126                         maximum = this._items.length + settings.items;
1127                 } else if (settings.autoWidth || settings.merge) {
1128                         revert = settings.rtl ? 1 : -1;
1129                         width = this.$stage.width() - this.$element.width();
1130                         while (coordinate = this.coordinates(i)) {
1131                                 if (coordinate * revert >= width) {
1132                                         break;
1133                                 }
1134                                 maximum = ++i;
1135                         }
1136                 } else {
1137                         throw 'Can not detect maximum absolute position.'
1138                 }
1139
1140                 return maximum;
1141         };
1142
1143         /**
1144          * Gets the minimum position for an item.
1145          * @public
1146          * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
1147          * @returns {Number}
1148          */
1149         Owl.prototype.minimum = function(relative) {
1150                 if (relative) {
1151                         return 0;
1152                 }
1153
1154                 return this._clones.length / 2;
1155         };
1156
1157         /**
1158          * Gets an item at the specified relative position.
1159          * @public
1160          * @param {Number} [position] - The relative position of the item.
1161          * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
1162          */
1163         Owl.prototype.items = function(position) {
1164                 if (position === undefined) {
1165                         return this._items.slice();
1166                 }
1167
1168                 position = this.normalize(position, true);
1169                 return this._items[position];
1170         };
1171
1172         /**
1173          * Gets an item at the specified relative position.
1174          * @public
1175          * @param {Number} [position] - The relative position of the item.
1176          * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
1177          */
1178         Owl.prototype.mergers = function(position) {
1179                 if (position === undefined) {
1180                         return this._mergers.slice();
1181                 }
1182
1183                 position = this.normalize(position, true);
1184                 return this._mergers[position];
1185         };
1186
1187         /**
1188          * Gets the absolute positions of clones for an item.
1189          * @public
1190          * @param {Number} [position] - The relative position of the item.
1191          * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
1192          */
1193         Owl.prototype.clones = function(position) {
1194                 var odd = this._clones.length / 2,
1195                         even = odd + this._items.length,
1196                         map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
1197
1198                 if (position === undefined) {
1199                         return $.map(this._clones, function(v, i) { return map(i) });
1200                 }
1201
1202                 return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
1203         };
1204
1205         /**
1206          * Sets the current animation speed.
1207          * @public
1208          * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
1209          * @returns {Number} - The current animation speed in milliseconds.
1210          */
1211         Owl.prototype.speed = function(speed) {
1212                 if (speed !== undefined) {
1213                         this._speed = speed;
1214                 }
1215
1216                 return this._speed;
1217         };
1218
1219         /**
1220          * Gets the coordinate of an item.
1221          * @todo The name of this method is missleanding.
1222          * @public
1223          * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
1224          * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
1225          */
1226         Owl.prototype.coordinates = function(position) {
1227                 var coordinate = null;
1228
1229                 if (position === undefined) {
1230                         return $.map(this._coordinates, $.proxy(function(coordinate, index) {
1231                                 return this.coordinates(index);
1232                         }, this));
1233                 }
1234
1235                 if (this.settings.center) {
1236                         coordinate = this._coordinates[position];
1237                         coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
1238                 } else {
1239                         coordinate = this._coordinates[position - 1] || 0;
1240                 }
1241
1242                 return coordinate;
1243         };
1244
1245         /**
1246          * Calculates the speed for a translation.
1247          * @protected
1248          * @param {Number} from - The absolute position of the start item.
1249          * @param {Number} to - The absolute position of the target item.
1250          * @param {Number} [factor=undefined] - The time factor in milliseconds.
1251          * @returns {Number} - The time in milliseconds for the translation.
1252          */
1253         Owl.prototype.duration = function(from, to, factor) {
1254                 return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
1255         };
1256
1257         /**
1258          * Slides to the specified item.
1259          * @public
1260          * @param {Number} position - The position of the item.
1261          * @param {Number} [speed] - The time in milliseconds for the transition.
1262          */
1263         Owl.prototype.to = function(position, speed) {
1264                 if (this.settings.loop) {
1265                         var distance = position - this.relative(this.current()),
1266                                 revert = this.current(),
1267                                 before = this.current(),
1268                                 after = this.current() + distance,
1269                                 direction = before - after < 0 ? true : false,
1270                                 items = this._clones.length + this._items.length;
1271
1272                         if (after < this.settings.items && direction === false) {
1273                                 revert = before + this._items.length;
1274                                 this.reset(revert);
1275                         } else if (after >= items - this.settings.items && direction === true) {
1276                                 revert = before - this._items.length;
1277                                 this.reset(revert);
1278                         }
1279                         window.clearTimeout(this.e._goToLoop);
1280                         this.e._goToLoop = window.setTimeout($.proxy(function() {
1281                                 this.speed(this.duration(this.current(), revert + distance, speed));
1282                                 this.current(revert + distance);
1283                                 this.update();
1284                         }, this), 30);
1285                 } else {
1286                         this.speed(this.duration(this.current(), position, speed));
1287                         this.current(position);
1288                         this.update();
1289                 }
1290         };
1291
1292         /**
1293          * Slides to the next item.
1294          * @public
1295          * @param {Number} [speed] - The time in milliseconds for the transition.
1296          */
1297         Owl.prototype.next = function(speed) {
1298                 speed = speed || false;
1299                 this.to(this.relative(this.current()) + 1, speed);
1300         };
1301
1302         /**
1303          * Slides to the previous item.
1304          * @public
1305          * @param {Number} [speed] - The time in milliseconds for the transition.
1306          */
1307         Owl.prototype.prev = function(speed) {
1308                 speed = speed || false;
1309                 this.to(this.relative(this.current()) - 1, speed);
1310         };
1311
1312         /**
1313          * Handles the end of an animation.
1314          * @protected
1315          * @param {Event} event - The event arguments.
1316          */
1317         Owl.prototype.transitionEnd = function(event) {
1318
1319                 // if css2 animation then event object is undefined
1320                 if (event !== undefined) {
1321                         event.stopPropagation();
1322
1323                         // Catch only owl-stage transitionEnd event
1324                         if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
1325                                 return false;
1326                         }
1327                 }
1328
1329                 this.state.inMotion = false;
1330                 this.trigger('translated');
1331         };
1332
1333         /**
1334          * Gets viewport width.
1335          * @protected
1336          * @return {Number} - The width in pixel.
1337          */
1338         Owl.prototype.viewport = function() {
1339                 var width;
1340                 if (this.options.responsiveBaseElement !== window) {
1341                         width = $(this.options.responsiveBaseElement).width();
1342                 } else if (window.innerWidth) {
1343                         width = window.innerWidth;
1344                 } else if (document.documentElement && document.documentElement.clientWidth) {
1345                         width = document.documentElement.clientWidth;
1346                 } else {
1347                         throw 'Can not detect viewport width.';
1348                 }
1349                 return width;
1350         };
1351
1352         /**
1353          * Replaces the current content.
1354          * @public
1355          * @param {HTMLElement|jQuery|String} content - The new content.
1356          */
1357         Owl.prototype.replace = function(content) {
1358                 this.$stage.empty();
1359                 this._items = [];
1360
1361                 if (content) {
1362                         content = (content instanceof jQuery) ? content : $(content);
1363                 }
1364
1365                 if (this.settings.nestedItemSelector) {
1366                         content = content.find('.' + this.settings.nestedItemSelector);
1367                 }
1368
1369                 content.filter(function() {
1370                         return this.nodeType === 1;
1371                 }).each($.proxy(function(index, item) {
1372                         item = this.prepare(item);
1373                         this.$stage.append(item);
1374                         this._items.push(item);
1375                         this._mergers.push(item.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
1376                 }, this));
1377
1378                 this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
1379
1380                 this.invalidate('items');
1381         };
1382
1383         /**
1384          * Adds an item.
1385          * @todo Use `item` instead of `content` for the event arguments.
1386          * @public
1387          * @param {HTMLElement|jQuery|String} content - The item content to add.
1388          * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
1389          */
1390         Owl.prototype.add = function(content, position) {
1391                 position = position === undefined ? this._items.length : this.normalize(position, true);
1392
1393                 this.trigger('add', { content: content, position: position });
1394
1395                 if (this._items.length === 0 || position === this._items.length) {
1396                         this.$stage.append(content);
1397                         this._items.push(content);
1398                         this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
1399                 } else {
1400                         this._items[position].before(content);
1401                         this._items.splice(position, 0, content);
1402                         this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
1403                 }
1404
1405                 this.invalidate('items');
1406
1407                 this.trigger('added', { content: content, position: position });
1408         };
1409
1410         /**
1411          * Removes an item by its position.
1412          * @todo Use `item` instead of `content` for the event arguments.
1413          * @public
1414          * @param {Number} position - The relative position of the item to remove.
1415          */
1416         Owl.prototype.remove = function(position) {
1417                 position = this.normalize(position, true);
1418
1419                 if (position === undefined) {
1420                         return;
1421                 }
1422
1423                 this.trigger('remove', { content: this._items[position], position: position });
1424
1425                 this._items[position].remove();
1426                 this._items.splice(position, 1);
1427                 this._mergers.splice(position, 1);
1428
1429                 this.invalidate('items');
1430
1431                 this.trigger('removed', { content: null, position: position });
1432         };
1433
1434         /**
1435          * Adds triggerable events.
1436          * @protected
1437          */
1438         Owl.prototype.addTriggerableEvents = function() {
1439                 var handler = $.proxy(function(callback, event) {
1440                         return $.proxy(function(e) {
1441                                 if (e.relatedTarget !== this) {
1442                                         this.suppress([ event ]);
1443                                         callback.apply(this, [].slice.call(arguments, 1));
1444                                         this.release([ event ]);
1445                                 }
1446                         }, this);
1447                 }, this);
1448
1449                 $.each({
1450                         'next': this.next,
1451                         'prev': this.prev,
1452                         'to': this.to,
1453                         'destroy': this.destroy,
1454                         'refresh': this.refresh,
1455                         'replace': this.replace,
1456                         'add': this.add,
1457                         'remove': this.remove
1458                 }, $.proxy(function(event, callback) {
1459                         this.$element.on(event + '.owl.carousel', handler(callback, event + '.owl.carousel'));
1460                 }, this));
1461
1462         };
1463
1464         /**
1465          * Watches the visibility of the carousel element.
1466          * @protected
1467          */
1468         Owl.prototype.watchVisibility = function() {
1469
1470                 // test on zepto
1471                 if (!isElVisible(this.$element.get(0))) {
1472                         this.$element.addClass('owl-hidden');
1473                         window.clearInterval(this.e._checkVisibile);
1474                         this.e._checkVisibile = window.setInterval($.proxy(checkVisible, this), 500);
1475                 }
1476
1477                 function isElVisible(el) {
1478                         return el.offsetWidth > 0 && el.offsetHeight > 0;
1479                 }
1480
1481                 function checkVisible() {
1482                         if (isElVisible(this.$element.get(0))) {
1483                                 this.$element.removeClass('owl-hidden');
1484                                 this.refresh();
1485                                 window.clearInterval(this.e._checkVisibile);
1486                         }
1487                 }
1488         };
1489
1490         /**
1491          * Preloads images with auto width.
1492          * @protected
1493          * @todo Still to test
1494          */
1495         Owl.prototype.preloadAutoWidthImages = function(imgs) {
1496                 var loaded, that, $el, img;
1497
1498                 loaded = 0;
1499                 that = this;
1500                 imgs.each(function(i, el) {
1501                         $el = $(el);
1502                         img = new Image();
1503
1504                         img.onload = function() {
1505                                 loaded++;
1506                                 $el.attr('src', img.src);
1507                                 $el.css('opacity', 1);
1508                                 if (loaded >= imgs.length) {
1509                                         that.state.imagesLoaded = true;
1510                                         that.initialize();
1511                                 }
1512                         };
1513
1514                         img.src = $el.attr('src') || $el.attr('data-src') || $el.attr('data-src-retina');
1515                 });
1516         };
1517
1518         /**
1519          * Destroys the carousel.
1520          * @public
1521          */
1522         Owl.prototype.destroy = function() {
1523
1524                 if (this.$element.hasClass(this.settings.themeClass)) {
1525                         this.$element.removeClass(this.settings.themeClass);
1526                 }
1527
1528                 if (this.settings.responsive !== false) {
1529                         $(window).off('resize.owl.carousel');
1530                 }
1531
1532                 if (this.transitionEndVendor) {
1533                         this.off(this.$stage.get(0), this.transitionEndVendor, this.e._transitionEnd);
1534                 }
1535
1536                 for ( var i in this._plugins) {
1537                         this._plugins[i].destroy();
1538                 }
1539
1540                 if (this.settings.mouseDrag || this.settings.touchDrag) {
1541                         this.$stage.off('mousedown touchstart touchcancel');
1542                         $(document).off('.owl.dragEvents');
1543                         this.$stage.get(0).onselectstart = function() {};
1544                         this.$stage.off('dragstart', function() { return false });
1545                 }
1546
1547                 // remove event handlers in the ".owl.carousel" namespace
1548                 this.$element.off('.owl');
1549
1550                 this.$stage.children('.cloned').remove();
1551                 this.e = null;
1552                 this.$element.removeData('owlCarousel');
1553
1554                 this.$stage.children().contents().unwrap();
1555                 this.$stage.children().unwrap();
1556                 this.$stage.unwrap();
1557         };
1558
1559         /**
1560          * Operators to calculate right-to-left and left-to-right.
1561          * @protected
1562          * @param {Number} [a] - The left side operand.
1563          * @param {String} [o] - The operator.
1564          * @param {Number} [b] - The right side operand.
1565          */
1566         Owl.prototype.op = function(a, o, b) {
1567                 var rtl = this.settings.rtl;
1568                 switch (o) {
1569                         case '<':
1570                                 return rtl ? a > b : a < b;
1571                         case '>':
1572                                 return rtl ? a < b : a > b;
1573                         case '>=':
1574                                 return rtl ? a <= b : a >= b;
1575                         case '<=':
1576                                 return rtl ? a >= b : a <= b;
1577                         default:
1578                                 break;
1579                 }
1580         };
1581
1582         /**
1583          * Attaches to an internal event.
1584          * @protected
1585          * @param {HTMLElement} element - The event source.
1586          * @param {String} event - The event name.
1587          * @param {Function} listener - The event handler to attach.
1588          * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
1589          */
1590         Owl.prototype.on = function(element, event, listener, capture) {
1591                 if (element.addEventListener) {
1592                         element.addEventListener(event, listener, capture);
1593                 } else if (element.attachEvent) {
1594                         element.attachEvent('on' + event, listener);
1595                 }
1596         };
1597
1598         /**
1599          * Detaches from an internal event.
1600          * @protected
1601          * @param {HTMLElement} element - The event source.
1602          * @param {String} event - The event name.
1603          * @param {Function} listener - The attached event handler to detach.
1604          * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
1605          */
1606         Owl.prototype.off = function(element, event, listener, capture) {
1607                 if (element.removeEventListener) {
1608                         element.removeEventListener(event, listener, capture);
1609                 } else if (element.detachEvent) {
1610                         element.detachEvent('on' + event, listener);
1611                 }
1612         };
1613
1614         /**
1615          * Triggers an public event.
1616          * @protected
1617          * @param {String} name - The event name.
1618          * @param {*} [data=null] - The event data.
1619          * @param {String} [namespace=.owl.carousel] - The event namespace.
1620          * @returns {Event} - The event arguments.
1621          */
1622         Owl.prototype.trigger = function(name, data, namespace) {
1623                 var status = {
1624                         item: { count: this._items.length, index: this.current() }
1625                 }, handler = $.camelCase(
1626                         $.grep([ 'on', name, namespace ], function(v) { return v })
1627                                 .join('-').toLowerCase()
1628                 ), event = $.Event(
1629                         [ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
1630                         $.extend({ relatedTarget: this }, status, data)
1631                 );
1632
1633                 if (!this._supress[name]) {
1634                         $.each(this._plugins, function(name, plugin) {
1635                                 if (plugin.onTrigger) {
1636                                         plugin.onTrigger(event);
1637                                 }
1638                         });
1639
1640                         this.$element.trigger(event);
1641
1642                         if (this.settings && typeof this.settings[handler] === 'function') {
1643                                 this.settings[handler].apply(this, event);
1644                         }
1645                 }
1646
1647                 return event;
1648         };
1649
1650         /**
1651          * Suppresses events.
1652          * @protected
1653          * @param {Array.<String>} events - The events to suppress.
1654          */
1655         Owl.prototype.suppress = function(events) {
1656                 $.each(events, $.proxy(function(index, event) {
1657                         this._supress[event] = true;
1658                 }, this));
1659         }
1660
1661         /**
1662          * Releases suppressed events.
1663          * @protected
1664          * @param {Array.<String>} events - The events to release.
1665          */
1666         Owl.prototype.release = function(events) {
1667                 $.each(events, $.proxy(function(index, event) {
1668                         delete this._supress[event];
1669                 }, this));
1670         }
1671
1672         /**
1673          * Checks the availability of some browser features.
1674          * @protected
1675          */
1676         Owl.prototype.browserSupport = function() {
1677                 this.support3d = isPerspective();
1678
1679                 if (this.support3d) {
1680                         this.transformVendor = isTransform();
1681
1682                         // take transitionend event name by detecting transition
1683                         var endVendors = [ 'transitionend', 'webkitTransitionEnd', 'transitionend', 'oTransitionEnd' ];
1684                         this.transitionEndVendor = endVendors[isTransition()];
1685
1686                         // take vendor name from transform name
1687                         this.vendorName = this.transformVendor.replace(/Transform/i, '');
1688                         this.vendorName = this.vendorName !== '' ? '-' + this.vendorName.toLowerCase() + '-' : '';
1689                 }
1690
1691                 this.state.orientation = window.orientation;
1692         };
1693
1694         /**
1695          * Get touch/drag coordinats.
1696          * @private
1697          * @param {event} - mousedown/touchstart event
1698          * @returns {object} - Contains X and Y of current mouse/touch position
1699          */
1700
1701         function getTouches(event) {
1702                 if (event.touches !== undefined) {
1703                         return {
1704                                 x: event.touches[0].pageX,
1705                                 y: event.touches[0].pageY
1706                         };
1707                 }
1708
1709                 if (event.touches === undefined) {
1710                         if (event.pageX !== undefined) {
1711                                 return {
1712                                         x: event.pageX,
1713                                         y: event.pageY
1714                                 };
1715                         }
1716
1717                 if (event.pageX === undefined) {
1718                         return {
1719                                         x: event.clientX,
1720                                         y: event.clientY
1721                                 };
1722                         }
1723                 }
1724         }
1725
1726         /**
1727          * Checks for CSS support.
1728          * @private
1729          * @param {Array} array - The CSS properties to check for.
1730          * @returns {Array} - Contains the supported CSS property name and its index or `false`.
1731          */
1732         function isStyleSupported(array) {
1733                 var p, s, fake = document.createElement('div'), list = array;
1734                 for (p in list) {
1735                         s = list[p];
1736                         if (typeof fake.style[s] !== 'undefined') {
1737                                 fake = null;
1738                                 return [ s, p ];
1739                         }
1740                 }
1741                 return [ false ];
1742         }
1743
1744         /**
1745          * Checks for CSS transition support.
1746          * @private
1747          * @todo Realy bad design
1748          * @returns {Number}
1749          */
1750         function isTransition() {
1751                 return isStyleSupported([ 'transition', 'WebkitTransition', 'MozTransition', 'OTransition' ])[1];
1752         }
1753
1754         /**
1755          * Checks for CSS transform support.
1756          * @private
1757          * @returns {String} The supported property name or false.
1758          */
1759         function isTransform() {
1760                 return isStyleSupported([ 'transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ])[0];
1761         }
1762
1763         /**
1764          * Checks for CSS perspective support.
1765          * @private
1766          * @returns {String} The supported property name or false.
1767          */
1768         function isPerspective() {
1769                 return isStyleSupported([ 'perspective', 'webkitPerspective', 'MozPerspective', 'OPerspective', 'MsPerspective' ])[0];
1770         }
1771
1772         /**
1773          * Checks wether touch is supported or not.
1774          * @private
1775          * @returns {Boolean}
1776          */
1777         function isTouchSupport() {
1778                 return 'ontouchstart' in window || !!(navigator.msMaxTouchPoints);
1779         }
1780
1781         /**
1782          * Checks wether touch is supported or not for IE.
1783          * @private
1784          * @returns {Boolean}
1785          */
1786         function isTouchSupportIE() {
1787                 return window.navigator.msPointerEnabled;
1788         }
1789
1790         /**
1791          * The jQuery Plugin for the Owl Carousel
1792          * @public
1793          */
1794         $.fn.owlCarousel = function(options) {
1795                 return this.each(function() {
1796                         if (!$(this).data('owlCarousel')) {
1797                                 $(this).data('owlCarousel', new Owl(this, options));
1798                         }
1799                 });
1800         };
1801
1802         /**
1803          * The constructor for the jQuery Plugin
1804          * @public
1805          */
1806         $.fn.owlCarousel.Constructor = Owl;
1807
1808 })(window.Zepto || window.jQuery, window, document);
1809
1810 /**
1811  * Lazy Plugin
1812  * @version 2.0.0
1813  * @author Bartosz Wojciechowski
1814  * @license The MIT License (MIT)
1815  */
1816 ;(function($, window, document, undefined) {
1817
1818         /**
1819          * Creates the lazy plugin.
1820          * @class The Lazy Plugin
1821          * @param {Owl} carousel - The Owl Carousel
1822          */
1823         var Lazy = function(carousel) {
1824
1825                 /**
1826                  * Reference to the core.
1827                  * @protected
1828                  * @type {Owl}
1829                  */
1830                 this._core = carousel;
1831
1832                 /**
1833                  * Already loaded items.
1834                  * @protected
1835                  * @type {Array.<jQuery>}
1836                  */
1837                 this._loaded = [];
1838
1839                 /**
1840                  * Event handlers.
1841                  * @protected
1842                  * @type {Object}
1843                  */
1844                 this._handlers = {
1845                         'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
1846                                 if (!e.namespace) {
1847                                         return;
1848                                 }
1849
1850                                 if (!this._core.settings || !this._core.settings.lazyLoad) {
1851                                         return;
1852                                 }
1853
1854                                 if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
1855                                         var settings = this._core.settings,
1856                                                 n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
1857                                                 i = ((settings.center && n * -1) || 0),
1858                                                 position = ((e.property && e.property.value) || this._core.current()) + i,
1859                                                 clones = this._core.clones().length,
1860                                                 load = $.proxy(function(i, v) { this.load(v) }, this);
1861
1862                                         while (i++ < n) {
1863                                                 this.load(clones / 2 + this._core.relative(position));
1864                                                 clones && $.each(this._core.clones(this._core.relative(position++)), load);
1865                                         }
1866                                 }
1867                         }, this)
1868                 };
1869
1870                 // set the default options
1871                 this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
1872
1873                 // register event handler
1874                 this._core.$element.on(this._handlers);
1875         }
1876
1877         /**
1878          * Default options.
1879          * @public
1880          */
1881         Lazy.Defaults = {
1882                 lazyLoad: false
1883         }
1884
1885         /**
1886          * Loads all resources of an item at the specified position.
1887          * @param {Number} position - The absolute position of the item.
1888          * @protected
1889          */
1890         Lazy.prototype.load = function(position) {
1891                 var $item = this._core.$stage.children().eq(position),
1892                         $elements = $item && $item.find('.owl-lazy');
1893
1894                 if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
1895                         return;
1896                 }
1897
1898                 $elements.each($.proxy(function(index, element) {
1899                         var $element = $(element), image,
1900                                 url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
1901
1902                         this._core.trigger('load', { element: $element, url: url }, 'lazy');
1903
1904                         if ($element.is('img')) {
1905                                 $element.one('load.owl.lazy', $.proxy(function() {
1906                                         $element.css('opacity', 1);
1907                                         this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
1908                                 }, this)).attr('src', url);
1909                         } else {
1910                                 image = new Image();
1911                                 image.onload = $.proxy(function() {
1912                                         $element.css({
1913                                                 'background-image': 'url(' + url + ')',
1914                                                 'opacity': '1'
1915                                         });
1916                                         this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
1917                                 }, this);
1918                                 image.src = url;
1919                         }
1920                 }, this));
1921
1922                 this._loaded.push($item.get(0));
1923         }
1924
1925         /**
1926          * Destroys the plugin.
1927          * @public
1928          */
1929         Lazy.prototype.destroy = function() {
1930                 var handler, property;
1931
1932                 for (handler in this.handlers) {
1933                         this._core.$element.off(handler, this.handlers[handler]);
1934                 }
1935                 for (property in Object.getOwnPropertyNames(this)) {
1936                         typeof this[property] != 'function' && (this[property] = null);
1937                 }
1938         }
1939
1940         $.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
1941
1942 })(window.Zepto || window.jQuery, window, document);
1943
1944 /**
1945  * AutoHeight Plugin
1946  * @version 2.0.0
1947  * @author Bartosz Wojciechowski
1948  * @license The MIT License (MIT)
1949  */
1950 ;(function($, window, document, undefined) {
1951
1952         /**
1953          * Creates the auto height plugin.
1954          * @class The Auto Height Plugin
1955          * @param {Owl} carousel - The Owl Carousel
1956          */
1957         var AutoHeight = function(carousel) {
1958                 /**
1959                  * Reference to the core.
1960                  * @protected
1961                  * @type {Owl}
1962                  */
1963                 this._core = carousel;
1964
1965                 /**
1966                  * All event handlers.
1967                  * @protected
1968                  * @type {Object}
1969                  */
1970                 this._handlers = {
1971                         'initialized.owl.carousel': $.proxy(function() {
1972                                 if (this._core.settings.autoHeight) {
1973                                         this.update();
1974                                 }
1975                         }, this),
1976                         'changed.owl.carousel': $.proxy(function(e) {
1977                                 if (this._core.settings.autoHeight && e.property.name == 'position'){
1978                                         this.update();
1979                                 }
1980                         }, this),
1981                         'loaded.owl.lazy': $.proxy(function(e) {
1982                                 if (this._core.settings.autoHeight && e.element.closest('.' + this._core.settings.itemClass)
1983                                         === this._core.$stage.children().eq(this._core.current())) {
1984                                         this.update();
1985                                 }
1986                         }, this)
1987                 };
1988
1989                 // set default options
1990                 this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
1991
1992                 // register event handlers
1993                 this._core.$element.on(this._handlers);
1994         };
1995
1996         /**
1997          * Default options.
1998          * @public
1999          */
2000         AutoHeight.Defaults = {
2001                 autoHeight: false,
2002                 autoHeightClass: 'owl-height'
2003         };
2004
2005         /**
2006          * Updates the view.
2007          */
2008         AutoHeight.prototype.update = function() {
2009                 this._core.$stage.parent()
2010                         .height(this._core.$stage.children().eq(this._core.current()).height())
2011                         .addClass(this._core.settings.autoHeightClass);
2012         };
2013
2014         AutoHeight.prototype.destroy = function() {
2015                 var handler, property;
2016
2017                 for (handler in this._handlers) {
2018                         this._core.$element.off(handler, this._handlers[handler]);
2019                 }
2020                 for (property in Object.getOwnPropertyNames(this)) {
2021                         typeof this[property] != 'function' && (this[property] = null);
2022                 }
2023         };
2024
2025         $.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
2026
2027 })(window.Zepto || window.jQuery, window, document);
2028
2029 /**
2030  * Video Plugin
2031  * @version 2.0.0
2032  * @author Bartosz Wojciechowski
2033  * @license The MIT License (MIT)
2034  */
2035 ;(function($, window, document, undefined) {
2036
2037         /**
2038          * Creates the video plugin.
2039          * @class The Video Plugin
2040          * @param {Owl} carousel - The Owl Carousel
2041          */
2042         var Video = function(carousel) {
2043                 /**
2044                  * Reference to the core.
2045                  * @protected
2046                  * @type {Owl}
2047                  */
2048                 this._core = carousel;
2049
2050                 /**
2051                  * Cache all video URLs.
2052                  * @protected
2053                  * @type {Object}
2054                  */
2055                 this._videos = {};
2056
2057                 /**
2058                  * Current playing item.
2059                  * @protected
2060                  * @type {jQuery}
2061                  */
2062                 this._playing = null;
2063
2064                 /**
2065                  * Whether this is in fullscreen or not.
2066                  * @protected
2067                  * @type {Boolean}
2068                  */
2069                 this._fullscreen = false;
2070
2071                 /**
2072                  * All event handlers.
2073                  * @protected
2074                  * @type {Object}
2075                  */
2076                 this._handlers = {
2077                         'resize.owl.carousel': $.proxy(function(e) {
2078                                 if (this._core.settings.video && !this.isInFullScreen()) {
2079                                         e.preventDefault();
2080                                 }
2081                         }, this),
2082                         'refresh.owl.carousel changed.owl.carousel': $.proxy(function(e) {
2083                                 if (this._playing) {
2084                                         this.stop();
2085                                 }
2086                         }, this),
2087                         'prepared.owl.carousel': $.proxy(function(e) {
2088                                 var $element = $(e.content).find('.owl-video');
2089                                 if ($element.length) {
2090                                         $element.css('display', 'none');
2091                                         this.fetch($element, $(e.content));
2092                                 }
2093                         }, this)
2094                 };
2095
2096                 // set default options
2097                 this._core.options = $.extend({}, Video.Defaults, this._core.options);
2098
2099                 // register event handlers
2100                 this._core.$element.on(this._handlers);
2101
2102                 this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
2103                         this.play(e);
2104                 }, this));
2105         };
2106
2107         /**
2108          * Default options.
2109          * @public
2110          */
2111         Video.Defaults = {
2112                 video: false,
2113                 videoHeight: false,
2114                 videoWidth: false
2115         };
2116
2117         /**
2118          * Gets the video ID and the type (YouTube/Vimeo only).
2119          * @protected
2120          * @param {jQuery} target - The target containing the video data.
2121          * @param {jQuery} item - The item containing the video.
2122          */
2123         Video.prototype.fetch = function(target, item) {
2124
2125                 var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
2126                         id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
2127                         width = target.attr('data-width') || this._core.settings.videoWidth,
2128                         height = target.attr('data-height') || this._core.settings.videoHeight,
2129                         url = target.attr('href');
2130
2131                 if (url) {
2132                         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+)?/);
2133
2134                         if (id[3].indexOf('youtu') > -1) {
2135                                 type = 'youtube';
2136                         } else if (id[3].indexOf('vimeo') > -1) {
2137                                 type = 'vimeo';
2138                         } else {
2139                                 throw new Error('Video URL not supported.');
2140                         }
2141                         id = id[6];
2142                 } else {
2143                         throw new Error('Missing video URL.');
2144                 }
2145
2146                 this._videos[url] = {
2147                         type: type,
2148                         id: id,
2149                         width: width,
2150                         height: height
2151                 };
2152
2153                 item.attr('data-video', url);
2154
2155                 this.thumbnail(target, this._videos[url]);
2156         };
2157
2158         /**
2159          * Creates video thumbnail.
2160          * @protected
2161          * @param {jQuery} target - The target containing the video data.
2162          * @param {Object} info - The video info object.
2163          * @see `fetch`
2164          */
2165         Video.prototype.thumbnail = function(target, video) {
2166
2167                 var tnLink,
2168                         icon,
2169                         path,
2170                         dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
2171                         customTn = target.find('img'),
2172                         srcType = 'src',
2173                         lazyClass = '',
2174                         settings = this._core.settings,
2175                         create = function(path) {
2176                                 icon = '<div class="owl-video-play-icon"></div>';
2177
2178                                 if (settings.lazyLoad) {
2179                                         tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
2180                                 } else {
2181                                         tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
2182                                 }
2183                                 target.after(tnLink);
2184                                 target.after(icon);
2185                         };
2186
2187                 // wrap video content into owl-video-wrapper div
2188                 target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
2189
2190                 if (this._core.settings.lazyLoad) {
2191                         srcType = 'data-src';
2192                         lazyClass = 'owl-lazy';
2193                 }
2194
2195                 // custom thumbnail
2196                 if (customTn.length) {
2197                         create(customTn.attr(srcType));
2198                         customTn.remove();
2199                         return false;
2200                 }
2201
2202                 if (video.type === 'youtube') {
2203                         path = "http://img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
2204                         create(path);
2205                 } else if (video.type === 'vimeo') {
2206                         $.ajax({
2207                                 type: 'GET',
2208                                 url: 'http://vimeo.com/api/v2/video/' + video.id + '.json',
2209                                 jsonp: 'callback',
2210                                 dataType: 'jsonp',
2211                                 success: function(data) {
2212                                         path = data[0].thumbnail_large;
2213                                         create(path);
2214                                 }
2215                         });
2216                 }
2217         };
2218
2219         /**
2220          * Stops the current video.
2221          * @public
2222          */
2223         Video.prototype.stop = function() {
2224                 this._core.trigger('stop', null, 'video');
2225                 this._playing.find('.owl-video-frame').remove();
2226                 this._playing.removeClass('owl-video-playing');
2227                 this._playing = null;
2228         };
2229
2230         /**
2231          * Starts the current video.
2232          * @public
2233          * @param {Event} ev - The event arguments.
2234          */
2235         Video.prototype.play = function(ev) {
2236                 this._core.trigger('play', null, 'video');
2237
2238                 if (this._playing) {
2239                         this.stop();
2240                 }
2241
2242                 var target = $(ev.target || ev.srcElement),
2243                         item = target.closest('.' + this._core.settings.itemClass),
2244                         video = this._videos[item.attr('data-video')],
2245                         width = video.width || '100%',
2246                         height = video.height || this._core.$stage.height(),
2247                         html, wrap;
2248
2249                 if (video.type === 'youtube') {
2250                         html = '<iframe width="' + width + '" height="' + height + '" src="http://www.youtube.com/embed/'
2251                                 + video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
2252                 } else if (video.type === 'vimeo') {
2253                         html = '<iframe src="http://player.vimeo.com/video/' + video.id + '?autoplay=1" width="' + width
2254                                 + '" height="' + height
2255                                 + '" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
2256                 }
2257
2258                 item.addClass('owl-video-playing');
2259                 this._playing = item;
2260
2261                 wrap = $('<div style="height:' + height + 'px; width:' + width + 'px" class="owl-video-frame">'
2262                         + html + '</div>');
2263                 target.after(wrap);
2264         };
2265
2266         /**
2267          * Checks whether an video is currently in full screen mode or not.
2268          * @todo Bad style because looks like a readonly method but changes members.
2269          * @protected
2270          * @returns {Boolean}
2271          */
2272         Video.prototype.isInFullScreen = function() {
2273
2274                 // if Vimeo Fullscreen mode
2275                 var element = document.fullscreenElement || document.mozFullScreenElement
2276                         || document.webkitFullscreenElement;
2277
2278                 if (element && $(element).parent().hasClass('owl-video-frame')) {
2279                         this._core.speed(0);
2280                         this._fullscreen = true;
2281                 }
2282
2283                 if (element && this._fullscreen && this._playing) {
2284                         return false;
2285                 }
2286
2287                 // comming back from fullscreen
2288                 if (this._fullscreen) {
2289                         this._fullscreen = false;
2290                         return false;
2291                 }
2292
2293                 // check full screen mode and window orientation
2294                 if (this._playing) {
2295                         if (this._core.state.orientation !== window.orientation) {
2296                                 this._core.state.orientation = window.orientation;
2297                                 return false;
2298                         }
2299                 }
2300
2301                 return true;
2302         };
2303
2304         /**
2305          * Destroys the plugin.
2306          */
2307         Video.prototype.destroy = function() {
2308                 var handler, property;
2309
2310                 this._core.$element.off('click.owl.video');
2311
2312                 for (handler in this._handlers) {
2313                         this._core.$element.off(handler, this._handlers[handler]);
2314                 }
2315                 for (property in Object.getOwnPropertyNames(this)) {
2316                         typeof this[property] != 'function' && (this[property] = null);
2317                 }
2318         };
2319
2320         $.fn.owlCarousel.Constructor.Plugins.Video = Video;
2321
2322 })(window.Zepto || window.jQuery, window, document);
2323
2324 /**
2325  * Animate Plugin
2326  * @version 2.0.0
2327  * @author Bartosz Wojciechowski
2328  * @license The MIT License (MIT)
2329  */
2330 ;(function($, window, document, undefined) {
2331
2332         /**
2333          * Creates the animate plugin.
2334          * @class The Navigation Plugin
2335          * @param {Owl} scope - The Owl Carousel
2336          */
2337         var Animate = function(scope) {
2338                 this.core = scope;
2339                 this.core.options = $.extend({}, Animate.Defaults, this.core.options);
2340                 this.swapping = true;
2341                 this.previous = undefined;
2342                 this.next = undefined;
2343
2344                 this.handlers = {
2345                         'change.owl.carousel': $.proxy(function(e) {
2346                                 if (e.property.name == 'position') {
2347                                         this.previous = this.core.current();
2348                                         this.next = e.property.value;
2349                                 }
2350                         }, this),
2351                         'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
2352                                 this.swapping = e.type == 'translated';
2353                         }, this),
2354                         'translate.owl.carousel': $.proxy(function(e) {
2355                                 if (this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
2356                                         this.swap();
2357                                 }
2358                         }, this)
2359                 };
2360
2361                 this.core.$element.on(this.handlers);
2362         };
2363
2364         /**
2365          * Default options.
2366          * @public
2367          */
2368         Animate.Defaults = {
2369                 animateOut: false,
2370                 animateIn: false
2371         };
2372
2373         /**
2374          * Toggles the animation classes whenever an translations starts.
2375          * @protected
2376          * @returns {Boolean|undefined}
2377          */
2378         Animate.prototype.swap = function() {
2379
2380                 if (this.core.settings.items !== 1 || !this.core.support3d) {
2381                         return;
2382                 }
2383
2384                 this.core.speed(0);
2385
2386                 var left,
2387                         clear = $.proxy(this.clear, this),
2388                         previous = this.core.$stage.children().eq(this.previous),
2389                         next = this.core.$stage.children().eq(this.next),
2390                         incoming = this.core.settings.animateIn,
2391                         outgoing = this.core.settings.animateOut;
2392
2393                 if (this.core.current() === this.previous) {
2394                         return;
2395                 }
2396
2397                 if (outgoing) {
2398                         left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
2399                         previous.css( { 'left': left + 'px' } )
2400                                 .addClass('animated owl-animated-out')
2401                                 .addClass(outgoing)
2402                                 .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear);
2403                 }
2404
2405                 if (incoming) {
2406                         next.addClass('animated owl-animated-in')
2407                                 .addClass(incoming)
2408                                 .one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear);
2409                 }
2410         };
2411
2412         Animate.prototype.clear = function(e) {
2413                 $(e.target).css( { 'left': '' } )
2414                         .removeClass('animated owl-animated-out owl-animated-in')
2415                         .removeClass(this.core.settings.animateIn)
2416                         .removeClass(this.core.settings.animateOut);
2417                 this.core.transitionEnd();
2418         }
2419
2420         /**
2421          * Destroys the plugin.
2422          * @public
2423          */
2424         Animate.prototype.destroy = function() {
2425                 var handler, property;
2426
2427                 for (handler in this.handlers) {
2428                         this.core.$element.off(handler, this.handlers[handler]);
2429                 }
2430                 for (property in Object.getOwnPropertyNames(this)) {
2431                         typeof this[property] != 'function' && (this[property] = null);
2432                 }
2433         };
2434
2435         $.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
2436
2437 })(window.Zepto || window.jQuery, window, document);
2438
2439 /**
2440  * Autoplay Plugin
2441  * @version 2.0.0
2442  * @author Bartosz Wojciechowski
2443  * @license The MIT License (MIT)
2444  */
2445 ;(function($, window, document, undefined) {
2446
2447         /**
2448          * Creates the autoplay plugin.
2449          * @class The Autoplay Plugin
2450          * @param {Owl} scope - The Owl Carousel
2451          */
2452         var Autoplay = function(scope) {
2453                 this.core = scope;
2454                 this.core.options = $.extend({}, Autoplay.Defaults, this.core.options);
2455
2456                 this.handlers = {
2457                         'translated.owl.carousel refreshed.owl.carousel': $.proxy(function() {
2458                                 this.autoplay();
2459                         }, this),
2460                         'play.owl.autoplay': $.proxy(function(e, t, s) {
2461                                 this.play(t, s);
2462                         }, this),
2463                         'stop.owl.autoplay': $.proxy(function() {
2464                                 this.stop();
2465                         }, this),
2466                         'mouseover.owl.autoplay': $.proxy(function() {
2467                                 if (this.core.settings.autoplayHoverPause) {
2468                                         this.pause();
2469                                 }
2470                         }, this),
2471                         'mouseleave.owl.autoplay': $.proxy(function() {
2472                                 if (this.core.settings.autoplayHoverPause) {
2473                                         this.autoplay();
2474                                 }
2475                         }, this)
2476                 };
2477
2478                 this.core.$element.on(this.handlers);
2479         };
2480
2481         /**
2482          * Default options.
2483          * @public
2484          */
2485         Autoplay.Defaults = {
2486                 autoplay: false,
2487                 autoplayTimeout: 5000,
2488                 autoplayHoverPause: false,
2489                 autoplaySpeed: false
2490         };
2491
2492         /**
2493          * @protected
2494          * @todo Must be documented.
2495          */
2496         Autoplay.prototype.autoplay = function() {
2497                 if (this.core.settings.autoplay && !this.core.state.videoPlay) {
2498                         window.clearInterval(this.interval);
2499
2500                         this.interval = window.setInterval($.proxy(function() {
2501                                 this.play();
2502                         }, this), this.core.settings.autoplayTimeout);
2503                 } else {
2504                         window.clearInterval(this.interval);
2505                 }
2506         };
2507
2508         /**
2509          * Starts the autoplay.
2510          * @public
2511          * @param {Number} [timeout] - ...
2512          * @param {Number} [speed] - ...
2513          * @returns {Boolean|undefined} - ...
2514          * @todo Must be documented.
2515          */
2516         Autoplay.prototype.play = function(timeout, speed) {
2517                 // if tab is inactive - doesnt work in <IE10
2518                 if (document.hidden === true) {
2519                         return;
2520                 }
2521
2522                 if (this.core.state.isTouch || this.core.state.isScrolling
2523                         || this.core.state.isSwiping || this.core.state.inMotion) {
2524                         return;
2525                 }
2526
2527                 if (this.core.settings.autoplay === false) {
2528                         window.clearInterval(this.interval);
2529                         return;
2530                 }
2531
2532                 this.core.next(this.core.settings.autoplaySpeed);
2533         };
2534
2535         /**
2536          * Stops the autoplay.
2537          * @public
2538          */
2539         Autoplay.prototype.stop = function() {
2540                 window.clearInterval(this.interval);
2541         };
2542
2543         /**
2544          * Pauses the autoplay.
2545          * @public
2546          */
2547         Autoplay.prototype.pause = function() {
2548                 window.clearInterval(this.interval);
2549         };
2550
2551         /**
2552          * Destroys the plugin.
2553          */
2554         Autoplay.prototype.destroy = function() {
2555                 var handler, property;
2556
2557                 window.clearInterval(this.interval);
2558
2559                 for (handler in this.handlers) {
2560                         this.core.$element.off(handler, this.handlers[handler]);
2561                 }
2562                 for (property in Object.getOwnPropertyNames(this)) {
2563                         typeof this[property] != 'function' && (this[property] = null);
2564                 }
2565         };
2566
2567         $.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
2568
2569 })(window.Zepto || window.jQuery, window, document);
2570
2571 /**
2572  * Navigation Plugin
2573  * @version 2.0.0
2574  * @author Artus Kolanowski
2575  * @license The MIT License (MIT)
2576  */
2577 ;(function($, window, document, undefined) {
2578         'use strict';
2579
2580         /**
2581          * Creates the navigation plugin.
2582          * @class The Navigation Plugin
2583          * @param {Owl} carousel - The Owl Carousel.
2584          */
2585         var Navigation = function(carousel) {
2586                 /**
2587                  * Reference to the core.
2588                  * @protected
2589                  * @type {Owl}
2590                  */
2591                 this._core = carousel;
2592
2593                 /**
2594                  * Indicates whether the plugin is initialized or not.
2595                  * @protected
2596                  * @type {Boolean}
2597                  */
2598                 this._initialized = false;
2599
2600                 /**
2601                  * The current paging indexes.
2602                  * @protected
2603                  * @type {Array}
2604                  */
2605                 this._pages = [];
2606
2607                 /**
2608                  * All DOM elements of the user interface.
2609                  * @protected
2610                  * @type {Object}
2611                  */
2612                 this._controls = {};
2613
2614                 /**
2615                  * Markup for an indicator.
2616                  * @protected
2617                  * @type {Array.<String>}
2618                  */
2619                 this._templates = [];
2620
2621                 /**
2622                  * The carousel element.
2623                  * @type {jQuery}
2624                  */
2625                 this.$element = this._core.$element;
2626
2627                 /**
2628                  * Overridden methods of the carousel.
2629                  * @protected
2630                  * @type {Object}
2631                  */
2632                 this._overrides = {
2633                         next: this._core.next,
2634                         prev: this._core.prev,
2635                         to: this._core.to
2636                 };
2637
2638                 /**
2639                  * All event handlers.
2640                  * @protected
2641                  * @type {Object}
2642                  */
2643                 this._handlers = {
2644                         'prepared.owl.carousel': $.proxy(function(e) {
2645                                 if (this._core.settings.dotsData) {
2646                                         this._templates.push($(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot'));
2647                                 }
2648                         }, this),
2649                         'add.owl.carousel': $.proxy(function(e) {
2650                                 if (this._core.settings.dotsData) {
2651                                         this._templates.splice(e.position, 0, $(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot'));
2652                                 }
2653                         }, this),
2654                         'remove.owl.carousel prepared.owl.carousel': $.proxy(function(e) {
2655                                 if (this._core.settings.dotsData) {
2656                                         this._templates.splice(e.position, 1);
2657                                 }
2658                         }, this),
2659                         'change.owl.carousel': $.proxy(function(e) {
2660                                 if (e.property.name == 'position') {
2661                                         if (!this._core.state.revert && !this._core.settings.loop && this._core.settings.navRewind) {
2662                                                 var current = this._core.current(),
2663                                                         maximum = this._core.maximum(),
2664                                                         minimum = this._core.minimum();
2665                                                 e.data = e.property.value > maximum
2666                                                         ? current >= maximum ? minimum : maximum
2667                                                         : e.property.value < minimum ? maximum : e.property.value;
2668                                         }
2669                                 }
2670                         }, this),
2671                         'changed.owl.carousel': $.proxy(function(e) {
2672                                 if (e.property.name == 'position') {
2673                                         this.draw();
2674                                 }
2675                         }, this),
2676                         'refreshed.owl.carousel': $.proxy(function() {
2677                                 if (!this._initialized) {
2678                                         this.initialize();
2679                                         this._initialized = true;
2680                                 }
2681                                 this._core.trigger('refresh', null, 'navigation');
2682                                 this.update();
2683                                 this.draw();
2684                                 this._core.trigger('refreshed', null, 'navigation');
2685                         }, this)
2686                 };
2687
2688                 // set default options
2689                 this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
2690
2691                 // register event handlers
2692                 this.$element.on(this._handlers);
2693         }
2694
2695         /**
2696          * Default options.
2697          * @public
2698          * @todo Rename `slideBy` to `navBy`
2699          */
2700         Navigation.Defaults = {
2701                 nav: false,
2702                 navRewind: true,
2703                 navText: [ 'prev', 'next' ],
2704                 navSpeed: false,
2705                 navElement: 'div',
2706                 navContainer: false,
2707                 navContainerClass: 'owl-nav',
2708                 navClass: [ 'owl-prev', 'owl-next' ],
2709                 slideBy: 1,
2710                 dotClass: 'owl-dot',
2711                 dotsClass: 'owl-dots',
2712                 dots: true,
2713                 dotsEach: false,
2714                 dotData: false,
2715                 dotsSpeed: false,
2716                 dotsContainer: false,
2717                 controlsClass: 'owl-controls'
2718         }
2719
2720         /**
2721          * Initializes the layout of the plugin and extends the carousel.
2722          * @protected
2723          */
2724         Navigation.prototype.initialize = function() {
2725                 var $container, override,
2726                         options = this._core.settings;
2727
2728                 // create the indicator template
2729                 if (!options.dotsData) {
2730                         this._templates = [ $('<div>')
2731                                 .addClass(options.dotClass)
2732                                 .append($('<span>'))
2733                                 .prop('outerHTML') ];
2734                 }
2735
2736                 // create controls container if needed
2737                 if (!options.navContainer || !options.dotsContainer) {
2738                         this._controls.$container = $('<div>')
2739                                 .addClass(options.controlsClass)
2740                                 .appendTo(this.$element);
2741                 }
2742
2743                 // create DOM structure for absolute navigation
2744                 this._controls.$indicators = options.dotsContainer ? $(options.dotsContainer)
2745                         : $('<div>').hide().addClass(options.dotsClass).appendTo(this._controls.$container);
2746
2747                 this._controls.$indicators.on('click', 'div', $.proxy(function(e) {
2748                         var index = $(e.target).parent().is(this._controls.$indicators)
2749                                 ? $(e.target).index() : $(e.target).parent().index();
2750
2751                         e.preventDefault();
2752
2753                         this.to(index, options.dotsSpeed);
2754                 }, this));
2755
2756                 // create DOM structure for relative navigation
2757                 $container = options.navContainer ? $(options.navContainer)
2758                         : $('<div>').addClass(options.navContainerClass).prependTo(this._controls.$container);
2759
2760                 this._controls.$next = $('<' + options.navElement + '>');
2761                 this._controls.$previous = this._controls.$next.clone();
2762
2763                 this._controls.$previous
2764                         .addClass(options.navClass[0])
2765                         .html(options.navText[0])
2766                         .hide()
2767                         .prependTo($container)
2768                         .on('click', $.proxy(function(e) {
2769                                 this.prev(options.navSpeed);
2770                         }, this));
2771                 this._controls.$next
2772                         .addClass(options.navClass[1])
2773                         .html(options.navText[1])
2774                         .hide()
2775                         .appendTo($container)
2776                         .on('click', $.proxy(function(e) {
2777                                 this.next(options.navSpeed);
2778                         }, this));
2779
2780                 // override public methods of the carousel
2781                 for (override in this._overrides) {
2782                         this._core[override] = $.proxy(this[override], this);
2783                 }
2784         }
2785
2786         /**
2787          * Destroys the plugin.
2788          * @protected
2789          */
2790         Navigation.prototype.destroy = function() {
2791                 var handler, control, property, override;
2792
2793                 for (handler in this._handlers) {
2794                         this.$element.off(handler, this._handlers[handler]);
2795                 }
2796                 for (control in this._controls) {
2797                         this._controls[control].remove();
2798                 }
2799                 for (override in this.overides) {
2800                         this._core[override] = this._overrides[override];
2801                 }
2802                 for (property in Object.getOwnPropertyNames(this)) {
2803                         typeof this[property] != 'function' && (this[property] = null);
2804                 }
2805         }
2806
2807         /**
2808          * Updates the internal state.
2809          * @protected
2810          */
2811         Navigation.prototype.update = function() {
2812                 var i, j, k,
2813                         options = this._core.settings,
2814                         lower = this._core.clones().length / 2,
2815                         upper = lower + this._core.items().length,
2816                         size = options.center || options.autoWidth || options.dotData
2817                                 ? 1 : options.dotsEach || options.items;
2818
2819                 if (options.slideBy !== 'page') {
2820                         options.slideBy = Math.min(options.slideBy, options.items);
2821                 }
2822
2823                 if (options.dots || options.slideBy == 'page') {
2824                         this._pages = [];
2825
2826                         for (i = lower, j = 0, k = 0; i < upper; i++) {
2827                                 if (j >= size || j === 0) {
2828                                         this._pages.push({
2829                                                 start: i - lower,
2830                                                 end: i - lower + size - 1
2831                                         });
2832                                         j = 0, ++k;
2833                                 }
2834                                 j += this._core.mergers(this._core.relative(i));
2835                         }
2836                 }
2837         }
2838
2839         /**
2840          * Draws the user interface.
2841          * @todo The option `dotData` wont work.
2842          * @protected
2843          */
2844         Navigation.prototype.draw = function() {
2845                 var difference, i, html = '',
2846                         options = this._core.settings,
2847                         $items = this._core.$stage.children(),
2848                         index = this._core.relative(this._core.current());
2849
2850                 if (options.nav && !options.loop && !options.navRewind) {
2851                         this._controls.$previous.toggleClass('disabled', index <= 0);
2852                         this._controls.$next.toggleClass('disabled', index >= this._core.maximum());
2853                 }
2854
2855                 this._controls.$previous.toggle(options.nav);
2856                 this._controls.$next.toggle(options.nav);
2857
2858                 if (options.dots) {
2859                         difference = this._pages.length - this._controls.$indicators.children().length;
2860
2861                         if (options.dotData && difference !== 0) {
2862                                 for (i = 0; i < this._controls.$indicators.children().length; i++) {
2863                                         html += this._templates[this._core.relative(i)];
2864                                 }
2865                                 this._controls.$indicators.html(html);
2866                         } else if (difference > 0) {
2867                                 html = new Array(difference + 1).join(this._templates[0]);
2868                                 this._controls.$indicators.append(html);
2869                         } else if (difference < 0) {
2870                                 this._controls.$indicators.children().slice(difference).remove();
2871                         }
2872
2873                         this._controls.$indicators.find('.active').removeClass('active');
2874                         this._controls.$indicators.children().eq($.inArray(this.current(), this._pages)).addClass('active');
2875                 }
2876
2877                 this._controls.$indicators.toggle(options.dots);
2878         }
2879
2880         /**
2881          * Extends event data.
2882          * @protected
2883          * @param {Event} event - The event object which gets thrown.
2884          */
2885         Navigation.prototype.onTrigger = function(event) {
2886                 var settings = this._core.settings;
2887
2888                 event.page = {
2889                         index: $.inArray(this.current(), this._pages),
2890                         count: this._pages.length,
2891                         size: settings && (settings.center || settings.autoWidth || settings.dotData
2892                                 ? 1 : settings.dotsEach || settings.items)
2893                 };
2894         }
2895
2896         /**
2897          * Gets the current page position of the carousel.
2898          * @protected
2899          * @returns {Number}
2900          */
2901         Navigation.prototype.current = function() {
2902                 var index = this._core.relative(this._core.current());
2903                 return $.grep(this._pages, function(o) {
2904                         return o.start <= index && o.end >= index;
2905                 }).pop();
2906         }
2907
2908         /**
2909          * Gets the current succesor/predecessor position.
2910          * @protected
2911          * @returns {Number}
2912          */
2913         Navigation.prototype.getPosition = function(successor) {
2914                 var position, length,
2915                         options = this._core.settings;
2916
2917                 if (options.slideBy == 'page') {
2918                         position = $.inArray(this.current(), this._pages);
2919                         length = this._pages.length;
2920                         successor ? ++position : --position;
2921                         position = this._pages[((position % length) + length) % length].start;
2922                 } else {
2923                         position = this._core.relative(this._core.current());
2924                         length = this._core.items().length;
2925                         successor ? position += options.slideBy : position -= options.slideBy;
2926                 }
2927                 return position;
2928         }
2929
2930         /**
2931          * Slides to the next item or page.
2932          * @public
2933          * @param {Number} [speed=false] - The time in milliseconds for the transition.
2934          */
2935         Navigation.prototype.next = function(speed) {
2936                 $.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
2937         }
2938
2939         /**
2940          * Slides to the previous item or page.
2941          * @public
2942          * @param {Number} [speed=false] - The time in milliseconds for the transition.
2943          */
2944         Navigation.prototype.prev = function(speed) {
2945                 $.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
2946         }
2947
2948         /**
2949          * Slides to the specified item or page.
2950          * @public
2951          * @param {Number} position - The position of the item or page.
2952          * @param {Number} [speed] - The time in milliseconds for the transition.
2953          * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
2954          */
2955         Navigation.prototype.to = function(position, speed, standard) {
2956                 var length;
2957
2958                 if (!standard) {
2959                         length = this._pages.length;
2960                         $.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
2961                 } else {
2962                         $.proxy(this._overrides.to, this._core)(position, speed);
2963                 }
2964         }
2965
2966         $.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
2967
2968 })(window.Zepto || window.jQuery, window, document);
2969
2970 /**
2971  * Hash Plugin
2972  * @version 2.0.0
2973  * @author Artus Kolanowski
2974  * @license The MIT License (MIT)
2975  */
2976 ;(function($, window, document, undefined) {
2977         'use strict';
2978
2979         /**
2980          * Creates the hash plugin.
2981          * @class The Hash Plugin
2982          * @param {Owl} carousel - The Owl Carousel
2983          */
2984         var Hash = function(carousel) {
2985                 /**
2986                  * Reference to the core.
2987                  * @protected
2988                  * @type {Owl}
2989                  */
2990                 this._core = carousel;
2991
2992                 /**
2993                  * Hash table for the hashes.
2994                  * @protected
2995                  * @type {Object}
2996                  */
2997                 this._hashes = {};
2998
2999                 /**
3000                  * The carousel element.
3001                  * @type {jQuery}
3002                  */
3003                 this.$element = this._core.$element;
3004
3005                 /**
3006                  * All event handlers.
3007                  * @protected
3008                  * @type {Object}
3009                  */
3010                 this._handlers = {
3011                         'initialized.owl.carousel': $.proxy(function() {
3012                                 if (this._core.settings.startPosition == 'URLHash') {
3013                                         $(window).trigger('hashchange.owl.navigation');
3014                                 }
3015                         }, this),
3016                         'prepared.owl.carousel': $.proxy(function(e) {
3017                                 var hash = $(e.content).find('[data-hash]').andSelf('[data-hash]').attr('data-hash');
3018                                 this._hashes[hash] = e.content;
3019                         }, this)
3020                 };
3021
3022                 // set default options
3023                 this._core.options = $.extend({}, Hash.Defaults, this._core.options);
3024
3025                 // register the event handlers
3026                 this.$element.on(this._handlers);
3027
3028                 // register event listener for hash navigation
3029                 $(window).on('hashchange.owl.navigation', $.proxy(function() {
3030                         var hash = window.location.hash.substring(1),
3031                                 items = this._core.$stage.children(),
3032                                 position = this._hashes[hash] && items.index(this._hashes[hash]) || 0;
3033
3034                         if (!hash) {
3035                                 return false;
3036                         }
3037
3038                         this._core.to(position, false, true);
3039                 }, this));
3040         }
3041
3042         /**
3043          * Default options.
3044          * @public
3045          */
3046         Hash.Defaults = {
3047                 URLhashListener: false
3048         }
3049
3050         /**
3051          * Destroys the plugin.
3052          * @public
3053          */
3054         Hash.prototype.destroy = function() {
3055                 var handler, property;
3056
3057                 $(window).off('hashchange.owl.navigation');
3058
3059                 for (handler in this._handlers) {
3060                         this._core.$element.off(handler, this._handlers[handler]);
3061                 }
3062                 for (property in Object.getOwnPropertyNames(this)) {
3063                         typeof this[property] != 'function' && (this[property] = null);
3064                 }
3065         }
3066
3067         $.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
3068
3069 })(window.Zepto || window.jQuery, window, document);