UNPKG

ngx-owl-carousel-o

Version:
1,197 lines 192 kB
import * as tslib_1 from "tslib"; import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { OwlCarouselOConfig, OwlOptionsMockedTypes } from '../carousel/owl-carousel-o-config'; import { OwlLogger } from './logger.service'; /** * Current state information and their tags. */ var States = /** @class */ (function () { function States() { } return States; }()); export { States }; /** * Enumeration for types. * @enum {String} */ export var Type; (function (Type) { Type["Event"] = "event"; Type["State"] = "state"; })(Type || (Type = {})); ; /** * Enumeration for width. * @enum {String} */ export var Width; (function (Width) { Width["Default"] = "default"; Width["Inner"] = "inner"; Width["Outer"] = "outer"; })(Width || (Width = {})); ; /** * Model for coords of .owl-stage */ var Coords = /** @class */ (function () { function Coords() { } return Coords; }()); export { Coords }; /** * Model for all current data of carousel */ var CarouselCurrentData = /** @class */ (function () { function CarouselCurrentData() { } return CarouselCurrentData; }()); export { CarouselCurrentData }; var CarouselService = /** @class */ (function () { function CarouselService(logger) { var _this = this; this.logger = logger; /** * Subject for passing data needed for managing View */ this._viewSettingsShipper$ = new Subject(); /** * Subject for notification when the carousel got initializes */ this._initializedCarousel$ = new Subject(); /** * Subject for notification when the carousel's settings start changinf */ this._changeSettingsCarousel$ = new Subject(); /** * Subject for notification when the carousel's settings have changed */ this._changedSettingsCarousel$ = new Subject(); /** * Subject for notification when the carousel starts translating or moving */ this._translateCarousel$ = new Subject(); /** * Subject for notification when the carousel stopped translating or moving */ this._translatedCarousel$ = new Subject(); /** * Subject for notification when the carousel's rebuilding caused by 'resize' event starts */ this._resizeCarousel$ = new Subject(); /** * Subject for notification when the carousel's rebuilding caused by 'resize' event is ended */ this._resizedCarousel$ = new Subject(); /** * Subject for notification when the refresh of carousel starts */ this._refreshCarousel$ = new Subject(); /** * Subject for notification when the refresh of carousel is ended */ this._refreshedCarousel$ = new Subject(); /** * Subject for notification when the dragging of carousel starts */ this._dragCarousel$ = new Subject(); /** * Subject for notification when the dragging of carousel is ended */ this._draggedCarousel$ = new Subject(); /** * Current settings for the carousel. */ this.settings = { items: 0 }; /** * Initial data for setting classes to element .owl-carousel */ this.owlDOMData = { rtl: false, isResponsive: false, isRefreshed: false, isLoaded: false, isLoading: false, isMouseDragable: false, isGrab: false, isTouchDragable: false }; /** * Initial data of .owl-stage */ this.stageData = { transform: 'translate3d(0px,0px,0px)', transition: '0s', width: 0, paddingL: 0, paddingR: 0 }; /** * All real items. */ this._items = []; // is equal to this.slides /** * Array with width of every slide. */ this._widths = []; /** * Currently suppressed events to prevent them from beeing retriggered. */ this._supress = {}; /** * References to the running plugins of this carousel. */ this._plugins = {}; /** * Absolute current position. */ this._current = null; /** * All cloned items. */ this._clones = []; /** * Merge values of all items. * @todo Maybe this could be part of a plugin. */ this._mergers = []; /** * Animation speed in milliseconds. */ this._speed = null; /** * Coordinates of all items in pixel. * @todo The name of this member is missleading. */ this._coordinates = []; /** * Current breakpoint. * @todo Real media queries would be nice. */ this._breakpoint = null; /** * Prefix for id of cloned slides */ this.clonedIdPrefix = 'cloned-'; /** * Current options set by the caller including defaults. */ this._options = {}; /** * Invalidated parts within the update process. */ this._invalidated = {}; /** * Current state information and their tags. */ this._states = { current: {}, tags: { initializing: ['busy'], animating: ['busy'], dragging: ['interacting'] } }; /** * Ordered list of workers for the update process. */ this._pipe = [ // { // filter: ['width', 'settings'], // run: () => { // this._width = this.carouselWindowWidth; // } // }, { filter: ['width', 'items', 'settings'], run: function (cache) { cache.current = _this._items && _this._items[_this.relative(_this._current)].id; } }, // { // filter: ['items', 'settings'], // run: function() { // // this.$stage.children('.cloned').remove(); // } // }, { filter: ['width', 'items', 'settings'], run: function (cache) { var margin = _this.settings.margin || '', grid = !_this.settings.autoWidth, rtl = _this.settings.rtl, css = { 'margin-left': rtl ? margin : '', 'margin-right': rtl ? '' : margin }; if (!grid) { _this.slidesData.forEach(function (slide) { slide.marginL = css['margin-left']; slide.marginR = css['margin-right']; }); } cache.css = css; } }, { filter: ['width', 'items', 'settings'], run: function (cache) { var width = +(_this.width() / _this.settings.items).toFixed(3) - _this.settings.margin, grid = !_this.settings.autoWidth, widths = []; var merge = null, iterator = _this._items.length; cache.items = { merge: false, width: width }; while (iterator--) { merge = _this._mergers[iterator]; merge = _this.settings.mergeFit && Math.min(merge, _this.settings.items) || merge; cache.items.merge = merge > 1 || cache.items.merge; widths[iterator] = !grid ? _this._items[iterator].width ? _this._items[iterator].width : width : width * merge; } _this._widths = widths; _this.slidesData.forEach(function (slide, i) { slide.width = _this._widths[i]; slide.marginR = cache.css['margin-right']; slide.marginL = cache.css['margin-left']; }); } }, { filter: ['items', 'settings'], run: function () { var clones = [], items = _this._items, settings = _this.settings, // TODO: Should be computed from number of min width items in stage view = Math.max(settings.items * 2, 4), size = Math.ceil(items.length / 2) * 2; var append = [], prepend = [], repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0; repeat /= 2; while (repeat--) { // Switch to only using appended clones clones.push(_this.normalize(clones.length / 2, true)); append.push(tslib_1.__assign({}, _this.slidesData[clones[clones.length - 1]])); clones.push(_this.normalize(items.length - 1 - (clones.length - 1) / 2, true)); prepend.unshift(tslib_1.__assign({}, _this.slidesData[clones[clones.length - 1]])); } _this._clones = clones; append = append.map(function (slide) { slide.id = "" + _this.clonedIdPrefix + slide.id; slide.isActive = false; slide.isCloned = true; return slide; }); prepend = prepend.map(function (slide) { slide.id = "" + _this.clonedIdPrefix + slide.id; slide.isActive = false; slide.isCloned = true; return slide; }); _this.slidesData = prepend.concat(_this.slidesData).concat(append); } }, { filter: ['width', 'items', 'settings'], run: function () { var rtl = _this.settings.rtl ? 1 : -1, size = _this._clones.length + _this._items.length, coordinates = []; var iterator = -1, previous = 0, current = 0; while (++iterator < size) { previous = coordinates[iterator - 1] || 0; current = _this._widths[_this.relative(iterator)] + _this.settings.margin; coordinates.push(previous + current * rtl); } _this._coordinates = coordinates; } }, { filter: ['width', 'items', 'settings'], run: function () { var padding = _this.settings.stagePadding, coordinates = _this._coordinates, css = { 'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2, 'padding-left': padding || '', 'padding-right': padding || '' }; _this.stageData.width = css.width; // use this property in *ngIf directive for .owl-stage element _this.stageData.paddingL = css['padding-left']; _this.stageData.paddingR = css['padding-right']; } }, { // filter: [ 'width', 'items', 'settings' ], // run: cache => { // // this method sets the width for every slide, but I set it in different way earlier // const grid = !this.settings.autoWidth, // items = this.$stage.children(); // use this.slidesData // let iterator = this._coordinates.length; // if (grid && cache.items.merge) { // while (iterator--) { // cache.css.width = this._widths[this.relative(iterator)]; // items.eq(iterator).css(cache.css); // } // } else if (grid) { // cache.css.width = cache.items.width; // items.css(cache.css); // } // } // }, { // filter: [ 'items' ], // run: function() { // this._coordinates.length < 1 && this.$stage.removeAttr('style'); // } // }, { filter: ['width', 'items', 'settings'], run: function (cache) { var current = cache.current ? _this.slidesData.findIndex(function (slide) { return slide.id === cache.current; }) : 0; current = Math.max(_this.minimum(), Math.min(_this.maximum(), current)); _this.reset(current); } }, { filter: ['position'], run: function () { _this.animate(_this.coordinates(_this._current)); } }, { filter: ['width', 'position', 'items', 'settings'], run: function () { var rtl = _this.settings.rtl ? 1 : -1, padding = _this.settings.stagePadding * 2, matches = []; var begin, end, inner, outer, i, n; begin = _this.coordinates(_this.current()); if (typeof begin === 'number') { begin += padding; } else { begin = 0; } end = begin + _this.width() * rtl; if (rtl === -1 && _this.settings.center) { var result = _this._coordinates.filter(function (element) { return _this.settings.items % 2 === 1 ? element >= begin : element > begin; }); begin = result.length ? result[result.length - 1] : begin; } for (i = 0, n = _this._coordinates.length; i < n; i++) { inner = Math.ceil(_this._coordinates[i - 1] || 0); outer = Math.ceil(Math.abs(_this._coordinates[i]) + padding * rtl); if ((_this._op(inner, '<=', begin) && (_this._op(inner, '>', end))) || (_this._op(outer, '<', begin) && _this._op(outer, '>', end))) { matches.push(i); } } _this.slidesData.forEach(function (slide) { slide.isActive = false; return slide; }); matches.forEach(function (item) { _this.slidesData[item].isActive = true; }); if (_this.settings.center) { _this.slidesData.forEach(function (slide) { slide.isCentered = false; return slide; }); _this.slidesData[_this.current()].isCentered = true; } } } ]; } Object.defineProperty(CarouselService.prototype, "invalidated", { // Is needed for tests get: function () { return this._invalidated; }, enumerable: true, configurable: true }); Object.defineProperty(CarouselService.prototype, "states", { // is needed for tests get: function () { return this._states; }, enumerable: true, configurable: true }); /** * Makes _viewSettingsShipper$ Subject become Observable * @returns Observable of _viewSettingsShipper$ Subject */ CarouselService.prototype.getViewCurSettings = function () { return this._viewSettingsShipper$.asObservable(); }; /** * Makes _initializedCarousel$ Subject become Observable * @returns Observable of _initializedCarousel$ Subject */ CarouselService.prototype.getInitializedState = function () { return this._initializedCarousel$.asObservable(); }; /** * Makes _changeSettingsCarousel$ Subject become Observable * @returns Observable of _changeSettingsCarousel$ Subject */ CarouselService.prototype.getChangeState = function () { return this._changeSettingsCarousel$.asObservable(); }; /** * Makes _changedSettingsCarousel$ Subject become Observable * @returns Observable of _changedSettingsCarousel$ Subject */ CarouselService.prototype.getChangedState = function () { return this._changedSettingsCarousel$.asObservable(); }; /** * Makes _translateCarousel$ Subject become Observable * @returns Observable of _translateCarousel$ Subject */ CarouselService.prototype.getTranslateState = function () { return this._translateCarousel$.asObservable(); }; /** * Makes _translatedCarousel$ Subject become Observable * @returns Observable of _translatedCarousel$ Subject */ CarouselService.prototype.getTranslatedState = function () { return this._translatedCarousel$.asObservable(); }; /** * Makes _resizeCarousel$ Subject become Observable * @returns Observable of _resizeCarousel$ Subject */ CarouselService.prototype.getResizeState = function () { return this._resizeCarousel$.asObservable(); }; /** * Makes _resizedCarousel$ Subject become Observable * @returns Observable of _resizedCarousel$ Subject */ CarouselService.prototype.getResizedState = function () { return this._resizedCarousel$.asObservable(); }; /** * Makes _refreshCarousel$ Subject become Observable * @returns Observable of _refreshCarousel$ Subject */ CarouselService.prototype.getRefreshState = function () { return this._refreshCarousel$.asObservable(); }; /** * Makes _refreshedCarousel$ Subject become Observable * @returns Observable of _refreshedCarousel$ Subject */ CarouselService.prototype.getRefreshedState = function () { return this._refreshedCarousel$.asObservable(); }; /** * Makes _dragCarousel$ Subject become Observable * @returns Observable of _dragCarousel$ Subject */ CarouselService.prototype.getDragState = function () { return this._dragCarousel$.asObservable(); }; /** * Makes _draggedCarousel$ Subject become Observable * @returns Observable of _draggedCarousel$ Subject */ CarouselService.prototype.getDraggedState = function () { return this._draggedCarousel$.asObservable(); }; /** * Setups custom options expanding default options * @param options custom options */ CarouselService.prototype.setOptions = function (options) { var configOptions = new OwlCarouselOConfig(); var checkedOptions = this._validateOptions(options, configOptions); this._options = tslib_1.__assign({}, configOptions, checkedOptions); }; /** * Checks whether user's option are set properly. Cheking is based on typings; * @param options options set by user * @param configOptions default options * @returns checked and modified (if it's needed) user's options * * Notes: * - if user set option with wrong type, it'll be written in console */ CarouselService.prototype._validateOptions = function (options, configOptions) { var _this = this; var checkedOptions = tslib_1.__assign({}, options); var mockedTypes = new OwlOptionsMockedTypes(); var setRightOption = function (type, key) { _this.logger.log("options." + key + " must be type of " + type + "; " + key + "=" + options[key] + " skipped to defaults: " + key + "=" + configOptions[key]); return configOptions[key]; }; var _loop_1 = function (key) { if (checkedOptions.hasOwnProperty(key)) { // condition could be shortened but it gets harder for understanding if (mockedTypes[key] === 'number') { if (this_1._isNumeric(checkedOptions[key])) { checkedOptions[key] = +checkedOptions[key]; checkedOptions[key] = key === 'items' ? this_1._validateItems(checkedOptions[key]) : checkedOptions[key]; } else { checkedOptions[key] = setRightOption(mockedTypes[key], key); } } else if (mockedTypes[key] === 'boolean' && typeof checkedOptions[key] !== 'boolean') { checkedOptions[key] = setRightOption(mockedTypes[key], key); } else if (mockedTypes[key] === 'number|boolean' && !this_1._isNumberOrBoolean(checkedOptions[key])) { checkedOptions[key] = setRightOption(mockedTypes[key], key); } else if (mockedTypes[key] === 'number|string' && !this_1._isNumberOrString(checkedOptions[key])) { checkedOptions[key] = setRightOption(mockedTypes[key], key); } else if (mockedTypes[key] === 'string|boolean' && !this_1._isStringOrBoolean(checkedOptions[key])) { checkedOptions[key] = setRightOption(mockedTypes[key], key); } else if (mockedTypes[key] === 'string[]') { if (Array.isArray(checkedOptions[key])) { var isString_1 = false; checkedOptions[key].forEach(function (element) { isString_1 = typeof element === 'string' ? true : false; }); if (!isString_1) { checkedOptions[key] = setRightOption(mockedTypes[key], key); } ; } else { checkedOptions[key] = setRightOption(mockedTypes[key], key); } } } }; var this_1 = this; for (var key in checkedOptions) { _loop_1(key); } return checkedOptions; }; /** * Checks option items set by user and if it bigger than number of slides then returns number of slides * @param items option items set by user * @returns right number of items */ CarouselService.prototype._validateItems = function (items) { var result; if (items > this._items.length) { result = this._items.length; this.logger.log('The option \'items\' in your options is bigger than the number of slides. This option is updated to the current number of slides and the navigation got disabled'); } else { if (items === this._items.length && (this.settings.dots || this.settings.nav)) { this.logger.log('Option \'items\' in your options is equal to the number of slides. So the navigation got disabled'); } result = items; } return result; }; /** * Set current width of carousel * @param width width of carousel Window */ CarouselService.prototype.setCarouselWidth = function (width) { this._width = width; }; /** * Setups the current settings. * @todo Remove responsive classes. Why should adaptive designs be brought into IE8? * @todo Support for media queries by using `matchMedia` would be nice. * @param carouselWidth width of carousel * @param slides array of slides * @param options options set by user */ CarouselService.prototype.setup = function (carouselWidth, slides, options) { this.setCarouselWidth(carouselWidth); this.setItems(slides); this._defineSlidesData(); this.setOptions(options); this.settings = tslib_1.__assign({}, this._options); this.setOptionsForViewport(); this._trigger('change', { property: { name: 'settings', value: this.settings } }); this.invalidate('settings'); // must be call of this function; this._trigger('changed', { property: { name: 'settings', value: this.settings } }); }; /** * Set options for current viewport */ CarouselService.prototype.setOptionsForViewport = function () { var _this = this; var viewport = this._width, overwrites = this._options.responsive; var match = -1; if (!Object.keys(overwrites).length) { return; } if (!viewport) { this.settings.items = 1; return; } for (var key in overwrites) { if (overwrites.hasOwnProperty(key)) { if (+key <= viewport && +key > match) { match = Number(key); } } } this.settings = tslib_1.__assign({}, this._options, overwrites[match], { items: (overwrites[match] && overwrites[match].items) ? this._validateItems(overwrites[match].items) : this._options.items }); // if (typeof this.settings.stagePadding === 'function') { // this.settings.stagePadding = this.settings.stagePadding(); // } delete this.settings.responsive; this.owlDOMData.isResponsive = true; this.owlDOMData.isMouseDragable = this.settings.mouseDrag; this.owlDOMData.isTouchDragable = this.settings.touchDrag; var mergers = []; this._items.forEach(function (item) { var mergeN = _this.settings.merge ? item.dataMerge : 1; mergers.push(mergeN); }); this._mergers = mergers; this._breakpoint = match; this.invalidate('settings'); }; /** * Initializes the carousel. * @param slides array of CarouselSlideDirective */ CarouselService.prototype.initialize = function (slides) { var _this = this; this.enter('initializing'); // this.trigger('initialize'); this.owlDOMData.rtl = this.settings.rtl; if (this._mergers.length) { this._mergers = []; } slides.forEach(function (item) { var mergeN = _this.settings.merge ? item.dataMerge : 1; _this._mergers.push(mergeN); }); this._clones = []; this.reset(this._isNumeric(this.settings.startPosition) ? +this.settings.startPosition : 0); this.invalidate('items'); this.refresh(); this.owlDOMData.isLoaded = true; this.owlDOMData.isMouseDragable = this.settings.mouseDrag; this.owlDOMData.isTouchDragable = this.settings.touchDrag; this.sendChanges(); this.leave('initializing'); this._trigger('initialized'); }; ; /** * Sends all data needed for View */ CarouselService.prototype.sendChanges = function () { this._viewSettingsShipper$.next({ owlDOMData: this.owlDOMData, stageData: this.stageData, slidesData: this.slidesData, navData: this.navData, dotsData: this.dotsData }); }; /** * Updates option logic if necessery */ CarouselService.prototype._optionsLogic = function () { if (this.settings.autoWidth) { this.settings.stagePadding = 0; this.settings.merge = false; } }; /** * Updates the view */ CarouselService.prototype.update = function () { var _this = this; var i = 0; var n = this._pipe.length, filter = function (item) { return _this._invalidated[item]; }, cache = {}; while (i < n) { var filteredPipe = this._pipe[i].filter.filter(filter); if (this._invalidated.all || filteredPipe.length > 0) { this._pipe[i].run(cache); } i++; } this.slidesData.forEach(function (slide) { return slide.classes = _this.setCurSlideClasses(slide); }); this.sendChanges(); this._invalidated = {}; if (!this.is('valid')) { this.enter('valid'); } }; /** * Gets the width of the view. * @param [dimension=Width.Default] The dimension to return * @returns The width of the view in pixel. */ CarouselService.prototype.width = function (dimension) { dimension = dimension || Width.Default; switch (dimension) { case Width.Inner: case Width.Outer: return this._width; default: return this._width - this.settings.stagePadding * 2 + this.settings.margin; } }; /** * Refreshes the carousel primarily for adaptive purposes. */ CarouselService.prototype.refresh = function () { this.enter('refreshing'); this._trigger('refresh'); this._defineSlidesData(); this.setOptionsForViewport(); this._optionsLogic(); // this.$element.addClass(this.options.refreshClass); this.update(); // this.$element.removeClass(this.options.refreshClass); this.leave('refreshing'); this._trigger('refreshed'); }; /** * Checks window `resize` event. * @param curWidth width of .owl-carousel */ CarouselService.prototype.onResize = function (curWidth) { if (!this._items.length) { return false; } this.setCarouselWidth(curWidth); this.enter('resizing'); // if (this.trigger('resize').isDefaultPrevented()) { // this.leave('resizing'); // return false; // } this._trigger('resize'); this.invalidate('width'); this.refresh(); this.leave('resizing'); this._trigger('resized'); }; /** * Prepares data for dragging carousel. It starts after firing `touchstart` and `mousedown` events. * @todo Horizontal swipe threshold as option * @todo #261 * @param event - The event arguments. * @returns stage - object with 'x' and 'y' coordinates of .owl-stage */ CarouselService.prototype.prepareDragging = function (event) { var stage = null, transformArr; // could be 5 commented lines below; However there's stage transform in stageData and in updates after each move of stage // stage = getComputedStyle(this.el.nativeElement).transform.replace(/.*\(|\)| /g, '').split(','); // stage = { // x: stage[stage.length === 16 ? 12 : 4], // y: stage[stage.length === 16 ? 13 : 5] // }; transformArr = this.stageData.transform.replace(/.*\(|\)| |[^,-\d]\w|\)/g, '').split(','); stage = { x: +transformArr[0], y: +transformArr[1] }; if (this.is('animating')) { this.invalidate('position'); } if (event.type === 'mousedown') { this.owlDOMData.isGrab = true; } this.speed(0); return stage; }; /** * Enters into a 'dragging' state */ CarouselService.prototype.enterDragging = function () { this.enter('dragging'); this._trigger('drag'); }; /** * Defines new coords for .owl-stage while dragging it * @todo #261 * @param event the event arguments. * @param dragData initial data got after starting dragging * @returns coords or false */ CarouselService.prototype.defineNewCoordsDrag = function (event, dragData) { var minimum = null, maximum = null, pull = null; var delta = this.difference(dragData.pointer, this.pointer(event)), stage = this.difference(dragData.stage.start, delta); if (!this.is('dragging')) { return false; } if (this.settings.loop) { minimum = this.coordinates(this.minimum()); maximum = +this.coordinates(this.maximum() + 1) - minimum; stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum; } else { minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum()); maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum()); pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0; stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull); } return stage; }; /** * Finishes dragging of carousel when `touchend` and `mouseup` events fire. * @todo #261 * @todo Threshold for click event * @param event the event arguments. * @param dragObj the object with dragging settings and states * @param clickAttacher function which attaches click handler to slide or its children elements in order to prevent event bubling */ CarouselService.prototype.finishDragging = function (event, dragObj, clickAttacher) { var directions = ['right', 'left'], delta = this.difference(dragObj.pointer, this.pointer(event)), stage = dragObj.stage.current, direction = directions[+(this.settings.rtl ? delta.x < +this.settings.rtl : delta.x > +this.settings.rtl)]; var currentSlideI, current, newCurrent; if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) { this.speed(+this.settings.dragEndSpeed || this.settings.smartSpeed); currentSlideI = this.closest(stage.x, delta.x !== 0 ? direction : dragObj.direction); current = this.current(); newCurrent = this.current(currentSlideI === -1 ? undefined : currentSlideI); if (current !== newCurrent) { this.invalidate('position'); this.update(); } dragObj.direction = direction; if (Math.abs(delta.x) > 3 || new Date().getTime() - dragObj.time > 300) { clickAttacher(); } } if (!this.is('dragging')) { return; } this.leave('dragging'); this._trigger('dragged'); }; /** * Gets absolute position of the closest item for a coordinate. * @todo Setting `freeDrag` makes `closest` not reusable. See #165. * @param coordinate The coordinate in pixel. * @param direction The direction to check for the closest item. Ether `left` or `right`. * @returns The absolute position of the closest item. */ CarouselService.prototype.closest = function (coordinate, direction) { var pull = 30, width = this.width(); var coordinates = this.coordinates(), position = -1; if (this.settings.center) { coordinates = coordinates.map(function (item) { if (item === 0) { item += 0.000001; } return item; }); } // option 'freeDrag' doesn't have realization and using it here creates problem: // variable 'position' stays unchanged (it equals -1 at the begging) and thus method returns -1 // Returning value is consumed by method current(), which taking -1 as argument calculates the index of new current slide // In case of having 5 slides ans 'loop=false; calling 'current(-1)' sets props '_current' as 4. Just last slide remains visible instead of 3 last slides. // if (!this.settings.freeDrag) { // check closest item for (var i = 0; i < coordinates.length; i++) { if (direction === 'left' && coordinate > coordinates[i] - pull && coordinate < coordinates[i] + pull) { position = i; // on a right pull, check on previous index // to do so, subtract width from value and set position = index + 1 } else if (direction === 'right' && coordinate > coordinates[i] - width - pull && coordinate < coordinates[i] - width + pull) { position = i + 1; } else if (this._op(coordinate, '<', coordinates[i]) && this._op(coordinate, '>', coordinates[i + 1] || coordinates[i] - width)) { position = direction === 'left' ? i + 1 : i; } else if (direction === null && coordinate > coordinates[i] - pull && coordinate < coordinates[i] + pull) { position = i; } if (position !== -1) { break; } ; } // } if (!this.settings.loop) { // non loop boundries if (this._op(coordinate, '>', coordinates[this.minimum()])) { position = coordinate = this.minimum(); } else if (this._op(coordinate, '<', coordinates[this.maximum()])) { position = coordinate = this.maximum(); } } return position; }; /** * Animates the stage. * @todo #270 * @param coordinate The coordinate in pixels. */ CarouselService.prototype.animate = function (coordinate) { var animate = this.speed() > 0; if (this.is('animating')) { this.onTransitionEnd(); } if (animate) { this.enter('animating'); this._trigger('translate'); } this.stageData.transform = 'translate3d(' + coordinate + 'px,0px,0px)'; this.stageData.transition = (this.speed() / 1000) + 's' + (this.settings.slideTransition ? ' ' + this.settings.slideTransition : ''); // also there was transition by means of JQuery.animate or css-changing property left }; /** * Checks whether the carousel is in a specific state or not. * @param state The state to check. * @returns The flag which indicates if the carousel is busy. */ CarouselService.prototype.is = function (state) { return this._states.current[state] && this._states.current[state] > 0; }; ; /** * Sets the absolute position of the current item. * @param position The new absolute position or nothing to leave it unchanged. * @returns The absolute position of the current item. */ CarouselService.prototype.current = function (position) { if (position === undefined) { return this._current; } if (this._items.length === 0) { return undefined; } position = this.normalize(position); if (this._current !== position) { var event_1 = this._trigger('change', { property: { name: 'position', value: position } }); // if (event.data !== undefined) { // position = this.normalize(event.data); // } this._current = position; this.invalidate('position'); this._trigger('changed', { property: { name: 'position', value: this._current } }); } return this._current; }; /** * Invalidates the given part of the update routine. * @param part The part to invalidate. * @returns The invalidated parts. */ CarouselService.prototype.invalidate = function (part) { if (typeof part === 'string') { this._invalidated[part] = true; if (this.is('valid')) { this.leave('valid'); } } return Object.keys(this._invalidated); }; ; /** * Resets the absolute position of the current item. * @param position the absolute position of the new item. */ CarouselService.prototype.reset = function (position) { position = this.normalize(position); if (position === undefined) { return; } this._speed = 0; this._current = position; this._suppress(['translate', 'translated']); this.animate(this.coordinates(position)); this._release(['translate', 'translated']); }; /** * Normalizes an absolute or a relative position of an item. * @param position The absolute or relative position to normalize. * @param relative Whether the given position is relative or not. * @returns The normalized position. */ CarouselService.prototype.normalize = function (position, relative) { var n = this._items.length, m = relative ? 0 : this._clones.length; if (!this._isNumeric(position) || n < 1) { position = undefined; } else if (position < 0 || position >= n + m) { position = ((position - m / 2) % n + n) % n + m / 2; } return position; }; /** * Converts an absolute position of an item into a relative one. * @param position The absolute position to convert. * @returns The converted position. */ CarouselService.prototype.relative = function (position) { position -= this._clones.length / 2; return this.normalize(position, true); }; /** * Gets the maximum position for the current item. * @param relative Whether to return an absolute position or a relative position. * @returns number of maximum position */ CarouselService.prototype.maximum = function (relative) { if (relative === void 0) { relative = false; } var settings = this.settings; var maximum = this._coordinates.length, iterator, reciprocalItemsWidth, elementWidth; if (settings.loop) { maximum = this._clones.length / 2 + this._items.length - 1; } else if (settings.autoWidth || settings.merge) { iterator = this._items.length; reciprocalItemsWidth = this.slidesData[--iterator].width; elementWidth = this._width; while (iterator--) { // it could be use this._items instead of this.slidesData; reciprocalItemsWidth += +this.slidesData[iterator].width + this.settings.margin; if (reciprocalItemsWidth > elementWidth) { break; } } maximum = iterator + 1; } else if (settings.center) { maximum = this._items.length - 1; } else { maximum = this._items.length - settings.items; } if (relative) { maximum -= this._clones.length / 2; } return Math.max(maximum, 0); }; /** * Gets the minimum position for the current item. * @param relative Whether to return an absolute position or a relative position. * @returns number of minimum position */ CarouselService.prototype.minimum = function (relative) { if (relative === void 0) { relative = false; } return relative ? 0 : this._clones.length / 2; }; /** * Gets an item at the specified relative position. * @param position The relative position of the item. * @returns The item at the given position or all items if no position was given. */ CarouselService.prototype.items = function (position) { if (position === undefined) { return this._items.slice(); } position = this.normalize(position, true); return [this._items[position]]; }; /** * Gets an item at the specified relative position. * @param position The relative position of the item. * @returns The item at the given position or all items if no position was given. */ CarouselService.prototype.mergers = function (position) { if (position === undefined) { return this._mergers.slice(); } position = this.normalize(position, true); return this._mergers[position]; }; /** * Gets the absolute positions of clones for an item. * @param position The relative position of the item. * @returns The absolute positions of clones for the item or all if no position was given. */ CarouselService.prototype.clones = function (position) { var odd = this._clones.length / 2, even = odd + this._items.length, map = function (index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2; }; if (position === undefined) { return this._clones.map(function (v, i) { return map(i); }); } return this._clones.map(function (v, i) { return v === position ? map(i) : null; }).filter(function (item) { return item; }); }; /** * Sets the current animation speed. * @param speed The animation speed in milliseconds or nothing to leave it unchanged. * @returns The current animation speed in milliseconds. */ CarouselService.prototype.speed = function (speed) { if (speed !== undefined) { this._speed = speed; } return this._speed; }; /** * Gets the coordinate of an item. * @todo The name of this method is missleanding. * @param position The absolute position of the item within `minimum()` and `maximum()`. * @returns The coordinate of the item in pixel or all coordinates. */ CarouselService.prototype.coordinates = function (position) { var _this = this; var multiplier = 1, newPosition = position - 1, coordinate, result; if (position === undefined) { result = this._coordinates.map(function (item, index) { return _this.coordinates(index); }); return result; } if (this.settings.center) { if (this.settings.rtl) { multiplier = -1; newPosition = position + 1; } coordinate = this._coordinates[position]; coordinate += (this.width() - coordinate + (this._coordinates[newPosition] || 0)) / 2 * multiplier; } else { coordinate = this._coordinates[newPosition] || 0; } coordinate = Math.ceil(coordinate); return coordinate; }; /** * Calculates the speed for a translation. * @param from The absolute position of the start item. * @param to The absolute position of the target item. * @param factor [factor=undefined] - The time factor in milliseconds. * @returns The time in milliseconds for the translation. */ CarouselService.prototype._duration = function (from, to, factor) { if (factor === 0) { return 0; } return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((+factor || this.settings.smartSpeed)); }; /** * Slides to the specified item. * @param position The position of the item. * @param speed The time in milliseconds for the transition. */ CarouselService.prototype.to = function (position, speed) { var _this = this; var current = this.current(), revert = null, distance = position - this.relative(current), maximum = this.maximum(), delayForLoop = 0; var direction = +(distance > 0) - +(distance < 0), items = this._items.length, minimum = this.minimum(); if (this.settings.loop) { if (!this.settings.rewind && Math.abs(distance) > items / 2) { distance += direction * -1 * items; } position = current + distance; revert = ((position - minimum) % items + items) % items + minimum; if (revert !== position && revert - distance <= maximum && revert - distance > 0) { current = revert - distance; position = revert; delayForLoop = 30; this.reset(current); this.sendChanges(); } } else if (this.settings.rewind) { maximum += 1; position = (position % maximum + maximum) % maximum; } else { position = Math.max(minimum, Math.min(maximum, position)); } setTimeout(function () { _this.speed(_this._duration(current, position, speed)); _this.current(position); _this.update(); }, delayForLoop); }; /** * Slides to the next item. * @param speed The time in milli