You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3069 lines
77 KiB

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