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 = 15, 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}
1044 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
1196- (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 p
1588hase 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);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.