carousel-angular
Version:
A simple carousel component for Angular 14+ based on 'angular-responsive-carousel' by Ivy Laboratory http://ivylab.space
1,391 lines (1,384 loc) • 60.8 kB
JavaScript
import * as i0 from '@angular/core';
import { EventEmitter, Component, Output, Input, HostBinding, HostListener, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
class Touches {
constructor(properties) {
this.eventType = undefined;
this.handlers = {};
this.startX = 0;
this.startY = 0;
this.lastTap = 0;
this.doubleTapMinTimeout = 300;
this.tapMinTimeout = 200;
this.touchstartTime = 0;
this.i = 0;
this.isMousedown = false;
this._touchListeners = {
touchstart: 'handleTouchstart',
touchmove: 'handleTouchmove',
touchend: 'handleTouchend'
};
this._mouseListeners = {
mousedown: 'handleMousedown',
mousemove: 'handleMousemove',
mouseup: 'handleMouseup',
wheel: 'handleWheel'
};
this._otherListeners = {
resize: 'handleResize'
};
/*
* Listeners
*/
/* Touchstart */
this.handleTouchstart = (event) => {
this.elementPosition = this.getElementPosition();
this.touchstartTime = new Date().getTime();
if (this.eventType === undefined) {
this.getTouchstartPosition(event);
}
this.runHandler('touchstart', event);
};
/* Touchmove */
this.handleTouchmove = (event) => {
const { touches } = event;
// Pan
if (this.detectPan(touches)) {
this.runHandler('pan', event);
}
// Pinch
if (this.detectPinch(event)) {
this.runHandler('pinch', event);
}
// Linear swipe
switch (this.detectLinearSwipe(event)) {
case 'horizontal-swipe':
event.swipeType = 'horizontal-swipe';
this.runHandler('horizontal-swipe', event);
break;
case 'vertical-swipe':
event.swipeType = 'vertical-swipe';
this.runHandler('vertical-swipe', event);
break;
}
// Linear swipe
if (this.detectLinearSwipe(event) ||
this.eventType === 'horizontal-swipe' ||
this.eventType === 'vertical-swipe') {
this.handleLinearSwipe(event);
}
};
/* Touchend */
this.handleTouchend = (event) => {
const { touches } = event;
// Double Tap
if (this.detectDoubleTap()) {
this.runHandler('double-tap', event);
}
// Tap
this.detectTap();
this.runHandler('touchend', event);
this.eventType = 'touchend';
if (touches && touches.length === 0) {
this.eventType = undefined;
this.i = 0;
}
};
/* Mousedown */
this.handleMousedown = (event) => {
this.isMousedown = true;
this.elementPosition = this.getElementPosition();
this.touchstartTime = new Date().getTime();
if (this.eventType === undefined) {
this.getMousedownPosition(event);
}
this.runHandler('mousedown', event);
};
/* Mousemove */
this.handleMousemove = (event) => {
// event.preventDefault();
if (!this.isMousedown) {
return;
}
// Pan
this.runHandler('pan', event);
// Linear swipe
switch (this.detectLinearSwipe(event)) {
case 'horizontal-swipe':
event.swipeType = 'horizontal-swipe';
this.runHandler('horizontal-swipe', event);
break;
case 'vertical-swipe':
event.swipeType = 'vertical-swipe';
this.runHandler('vertical-swipe', event);
break;
}
// Linear swipe
if (this.detectLinearSwipe(event) ||
this.eventType === 'horizontal-swipe' ||
this.eventType === 'vertical-swipe') {
this.handleLinearSwipe(event);
}
};
/* Mouseup */
this.handleMouseup = (event) => {
// Tap
this.detectTap();
this.isMousedown = false;
this.runHandler('mouseup', event);
this.eventType = undefined;
this.i = 0;
};
/* Wheel */
this.handleWheel = (event) => {
this.runHandler('wheel', event);
};
/* Resize */
this.handleResize = (event) => {
this.runHandler('resize', event);
};
this.properties = properties;
this.element = this.properties.element;
this.elementPosition = this.getElementPosition();
this.toggleEventListeners('addEventListener');
}
get touchListeners() {
return this.properties.touchListeners
? this.properties.touchListeners
: this._touchListeners;
}
get mouseListeners() {
return this.properties.mouseListeners
? this.properties.mouseListeners
: this._mouseListeners;
}
get otherListeners() {
return this.properties.otherListeners
? this.properties.otherListeners
: this._otherListeners;
}
destroy() {
this.toggleEventListeners('removeEventListener');
}
toggleEventListeners(action) {
let listeners;
if (this.properties.listeners === 'mouse and touch') {
listeners = Object.assign(this.touchListeners, this.mouseListeners);
}
else {
listeners = this.detectTouchScreen()
? this.touchListeners
: this.mouseListeners;
}
if (this.properties.resize) {
listeners = Object.assign(listeners, this.otherListeners);
}
for (const listener in listeners) {
const handler = listeners[listener];
// Window
if (listener === 'resize') {
if (action === 'addEventListener') {
window.addEventListener(listener, this[handler], false);
}
if (action === 'removeEventListener') {
window.removeEventListener(listener, this[handler], false);
}
// Document
}
else if (listener === 'mouseup' || listener === 'mousemove') {
if (action === 'addEventListener') {
document.addEventListener(listener, this[handler], {
passive: false
});
}
if (action === 'removeEventListener') {
document.removeEventListener(listener, this[handler], false);
}
// Element
}
else {
if (action === 'addEventListener') {
this.element.addEventListener(listener, this[handler], false);
}
if (action === 'removeEventListener') {
this.element.removeEventListener(listener, this[handler], false);
}
}
}
}
addEventListeners(listener) {
const handler = this._mouseListeners[listener];
window.addEventListener(listener, this[handler], false);
}
removeEventListeners(listener) {
const handler = this._mouseListeners[listener];
window.removeEventListener(listener, this[handler], false);
}
handleLinearSwipe(event) {
// event.preventDefault();
this.i++;
if (this.i > 3) {
this.eventType = this.getLinearSwipeType(event);
}
if (this.eventType === 'horizontal-swipe') {
this.runHandler('horizontal-swipe', event);
}
if (this.eventType === 'vertical-swipe') {
this.runHandler('vertical-swipe', event);
}
}
runHandler(eventName, response) {
if (this.handlers[eventName]) {
this.handlers[eventName](response);
}
}
/*
* Detection
*/
detectPan(touches) {
return ((touches.length === 1 && !this.eventType) || this.eventType === 'pan');
}
detectDoubleTap() {
if (this.eventType != undefined) {
return;
}
const currentTime = new Date().getTime();
const tapLength = currentTime - this.lastTap;
clearTimeout(this.doubleTapTimeout);
if (tapLength < this.doubleTapMinTimeout && tapLength > 0) {
return true;
}
this.doubleTapTimeout = setTimeout(() => {
clearTimeout(this.doubleTapTimeout);
}, this.doubleTapMinTimeout);
this.lastTap = currentTime;
return undefined;
}
detectTap() {
if (this.eventType != undefined) {
return;
}
const currentTime = new Date().getTime();
const tapLength = currentTime - this.touchstartTime;
if (tapLength > 0) {
if (tapLength < this.tapMinTimeout) {
this.runHandler('tap', event);
}
else {
this.runHandler('longtap', event);
}
}
}
detectPinch(event) {
const { touches } = event;
return ((touches.length === 2 && this.eventType === undefined) ||
this.eventType === 'pinch');
}
detectLinearSwipe(event) {
const { touches } = event;
if (touches) {
if ((touches.length === 1 && !this.eventType) ||
this.eventType === 'horizontal-swipe' ||
this.eventType === 'vertical-swipe') {
return this.getLinearSwipeType(event);
}
}
else if (!this.eventType ||
this.eventType === 'horizontal-swipe' ||
this.eventType === 'vertical-swipe') {
return this.getLinearSwipeType(event);
}
return undefined;
}
getLinearSwipeType(event) {
if (this.eventType !== 'horizontal-swipe' &&
this.eventType !== 'vertical-swipe') {
const movementX = Math.abs(this.moveLeft(0, event) - this.startX);
const movementY = Math.abs(this.moveTop(0, event) - this.startY);
if (movementY * 3 > movementX) {
return 'vertical-swipe';
}
return 'horizontal-swipe';
}
return this.eventType;
}
getElementPosition() {
return this.element.getBoundingClientRect();
}
getTouchstartPosition(event) {
this.startX = event.touches[0].clientX - this.elementPosition.left;
this.startY = event.touches[0].clientY - this.elementPosition.top;
}
getMousedownPosition(event) {
this.startX = event.clientX - this.elementPosition.left;
this.startY = event.clientY - this.elementPosition.top;
}
moveLeft(index, event) {
const { touches } = event;
if (touches) {
return touches[index].clientX - this.elementPosition.left;
}
return event.clientX - this.elementPosition.left;
}
moveTop(index, event) {
const { touches } = event;
if (touches) {
return touches[index].clientY - this.elementPosition.top;
}
return event.clientY - this.elementPosition.top;
}
detectTouchScreen() {
const prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');
const mq = function (query) {
return window.matchMedia(query).matches;
};
if ('ontouchstart' in window) {
return true;
}
// include the 'heartz' as a way to have a non matching MQ to help terminate the join
// https://git.io/vznFH
const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');
return mq(query);
}
/* Public properties and methods */
on(event, handler) {
if (event) {
this.handlers[event] = handler;
}
}
}
class Carousel {
constructor(properties, utils, cells, container, slide) {
this.properties = properties;
this.utils = utils;
this.cells = cells;
this.container = container;
this.slide = slide;
this.autoplayId = null;
this.handleTouchstart = (event) => {
this.container.handleTouchstart();
this.slide.handleTouchstart(event);
};
this.handleHorizontalSwipe = (event) => {
this.container.handleHorizontalSwipe();
};
this.handleTouchend = (event) => {
if (this.properties.freeScroll) {
this.container.handleTouchend();
}
else {
this.container.handleTouchend(true);
this.slide.handleTouchend(event);
}
};
this.isNextArrowDisabled = () => this.slide.isNextArrowDisabled();
this.isPrevArrowDisabled = () => this.slide.isPrevArrowDisabled();
this.init();
}
get cellLength() {
return this.cells.cellLength;
}
get lastCellIndex() {
return this.cells.cellLength - 1;
}
get overflowCellsLimit() {
return this.utils.overflowCellsLimit;
}
get autoplayIsPossible() {
return this.properties.autoplayIsPossible;
}
get margin() {
return this.properties.margin;
}
get minSwipeDistance() {
return this.properties.minSwipeDistance;
}
get transitionDuration() {
return this.properties.transitionDuration;
}
get transitionTimingFunction() {
return this.properties.transitionTimingFunction;
}
get fullCellWidth() {
return this.properties.cellWidth + this.margin;
}
get numberOfVisibleCells() {
return this.utils.numberOfVisibleCells;
}
get slideCounter() {
return this.slide.counter;
}
updateProperties(properties) {
this.properties = properties;
}
init() {
this.cellsElement = this.properties.cellsElement;
this.visibleWidth =
this.properties.visibleWidth ||
this.cellsElement.parentElement.clientWidth;
}
destroy() {
clearInterval(this.autoplayId);
}
lineUpCells() {
this.cells.lineUp();
}
handleTransitionend() {
this.slide.handleTransitionend();
}
next(length = 1) {
if (!this.isNextArrowDisabled()) {
this.slide.next(length);
}
}
prev(length = 1) {
this.slide.prev(length);
}
autoplay() {
if (this.autoplayId === null) {
this.autoplayId = setInterval(() => {
if (this.autoplayIsPossible) {
this.next();
}
}, this.properties.autoplayInterval);
}
}
stopAutoplay() {
if (this.autoplayId != null) {
clearInterval(this.autoplayId);
this.autoplayId = null;
}
}
}
class Container {
constructor(carouselProperties, utils, cells) {
this.carouselProperties = carouselProperties;
this.utils = utils;
this.cells = cells;
/* The index of the new position relative to
* the active index, for example -1 or +1
*/
this.initialPositionX = 0;
this.initialElementPositionX = 0;
this.pullLimit = 100;
this.startTime = 0;
this.startX = 0;
this.moveX = 0;
this.isSwipeInProgress = false;
this.init();
}
get visibleWidth() {
return this.utils.visibleWidth;
}
get overflowCellsLimit() {
return this.utils.overflowCellsLimit;
}
get element() {
return this.carouselProperties.cellsElement;
}
get freeScroll() {
return this.carouselProperties.freeScroll;
}
get fullCellWidth() {
return this.carouselProperties.cellWidth + this.carouselProperties.margin;
}
get numberOfVisibleCells() {
return this.utils.numberOfVisibleCells;
}
get transitionDuration() {
return this.carouselProperties.transitionDuration;
}
get transitionTimingFunction() {
return this.carouselProperties.transitionTimingFunction;
}
get cellLength() {
return this.cells.cellLength;
}
get tooFewCells() {
return this.numberOfVisibleCells > this.cellLength;
}
get disabled() {
return this.tooFewCells;
}
get margin() {
return this.carouselProperties.margin;
}
updateProperties(carouselProperties) {
this.carouselProperties = carouselProperties;
}
init() {
this.setWidth();
}
handleTouchstart() {
this.startX = this.utils.getStartX(event);
this.startTime = new Date().getTime();
this.initialElementPositionX = this.getInitialElementPositionX();
}
handleHorizontalSwipe() {
if (this.disabled) {
return;
}
if (!this.isSwipeInProgress) {
this.startX = this.utils.getStartX(event);
this.startTime = new Date().getTime();
this.initialElementPositionX = this.getInitialElementPositionX();
}
this.isSwipeInProgress = true;
this.moveX = this.utils.getMoveX(event);
this.move();
}
handleTouchend(simpleProcessing = false) {
if (this.disabled) {
return;
}
/* If touchend was passed to the Slide class */
if (simpleProcessing) {
this.isSwipeInProgress = false;
return;
}
this.isSwipeInProgress = false;
this.finishMoving();
this.clearInitialValues();
}
move() {
let positionX = this.getMovePositionX();
const isPulled = this.detectPulled();
const direction = this.getDirection();
if (isPulled) {
if ((isPulled.edge === 'left' && direction === 'right') ||
(isPulled.edge === 'right' && direction === 'left')) {
positionX = this.slowdownOnPull(positionX);
}
}
this.transformPositionX(positionX, 0);
if (this.freeScroll) {
this.initialPositionX = positionX;
}
if (isPulled) {
if (isPulled.edge === 'left' && isPulled.overflowX > this.pullLimit) {
this.initialPositionX = 0;
}
if (isPulled.edge === 'right' && isPulled.overflowX > this.pullLimit) {
this.initialPositionX = positionX;
}
}
}
getMovePositionX() {
const distance = this.getDistance();
return this.initialElementPositionX - distance;
}
getDistance() {
return this.startX - this.moveX;
}
/* If the container is pulled out of the left or right border */
detectPulled() {
const currentPositionX = this.getCurrentPositionX();
if (currentPositionX > 0) {
return {
edge: 'left',
positionX: currentPositionX,
overflowX: Math.abs(currentPositionX)
};
}
if (currentPositionX < this.getEndPosition()) {
return {
edge: 'right',
positionX: currentPositionX,
overflowX: Math.abs(currentPositionX - this.getEndPosition())
};
}
return undefined;
}
slowdownOnPull(_positionX) {
let distance = Math.abs(this.getDistance());
const endPosition = this.getEndPosition();
const isPulled = this.detectPulled();
if (!isPulled) {
return 0;
}
const decelerationRatio = 3 + isPulled.overflowX / 50;
let positionX = 0;
if (isPulled.edge === 'left') {
if (this.initialElementPositionX < 0) {
distance -= Math.abs(this.initialElementPositionX);
}
const rubberPositionX = distance / decelerationRatio;
positionX = rubberPositionX;
if (this.initialElementPositionX > 0) {
positionX = this.initialElementPositionX + rubberPositionX;
}
if (positionX > this.pullLimit) {
positionX = this.pullLimit;
}
}
if (isPulled.edge === 'right') {
const rubberPositionX = endPosition +
(this.initialElementPositionX - distance - endPosition) /
decelerationRatio;
const containerWidth = this.getWidth();
positionX = rubberPositionX;
if (this.initialElementPositionX < -(containerWidth - this.visibleWidth)) {
positionX =
containerWidth -
this.visibleWidth +
this.initialElementPositionX +
rubberPositionX;
}
if (positionX < endPosition - this.pullLimit) {
positionX = endPosition - this.pullLimit;
}
}
return positionX;
}
finishMoving() {
const positionX = this.getMovePositionX();
let newPositionX = 0;
if (this.freeScroll) {
newPositionX = this.getInertia();
}
/* Align container while pulling */
newPositionX = this.getAlignedPositionOnPull(newPositionX);
this.transformPositionX(newPositionX);
this.setInitialPosition(positionX);
}
/* Returns the new position of the container with inertia */
getInertia() {
const distance = this.getDistance();
const currentTime = new Date().getTime();
const tapLength = currentTime - this.startTime;
const inertia = (distance / tapLength) * 100;
return this.initialPositionX - inertia;
}
getAlignedPositionOnPull(newPositionX) {
const direction = this.getDirection();
if (direction === 'left') {
const endPosition = this.getEndPosition();
if (newPositionX < endPosition) {
return endPosition;
}
}
return newPositionX;
}
getCurrentPositionX() {
const parentPosition = this.element.parentElement.getBoundingClientRect();
const position = this.element.getBoundingClientRect();
return position.left - parentPosition.left;
}
getEndPosition() {
const width = this.getWidth();
const visibleWidth = this.element.parentElement.clientWidth;
return visibleWidth - width;
}
transformPositionX(value, duration = this.transitionDuration) {
if (value === undefined) {
return;
}
this.element.style.transition = `transform ${duration}ms ${this.transitionTimingFunction}`;
this.element.style.transform = `translateX(${value}px)`;
}
getWidth() {
return this.cellLength * this.fullCellWidth;
}
setWidth() {
const width = this.getWidth();
this.element.style.width = `${width}px`;
}
setInitialPosition(position) {
this.initialPositionX = position;
}
getElementPosition() {
return this.element.getBoundingClientRect();
}
getInitialElementPositionX() {
const carouselElementPosition = this.utils.getCarouselElementPosition().left;
return this.getElementPosition().left - carouselElementPosition;
}
clearInitialValues() {
this.startX = this.moveX = 0;
}
getDirection() {
const direction = Math.sign(this.startX - this.moveX);
if (direction === -1) {
return 'right';
}
if (direction === 1) {
return 'left';
}
return undefined;
}
}
class Cells {
constructor(carouselProperties, utils) {
this.carouselProperties = carouselProperties;
this.utils = utils;
this.counter = 0;
this.init(carouselProperties);
}
get cellLength() {
return this.cells ? this.cells.length : 0;
}
get fullCellWidth() {
return this.carouselProperties.cellWidth + this.carouselProperties.margin;
}
get cellLengthInLightDOMMode() {
return this.cellLength;
}
get numberOfVisibleCells() {
return this.utils.numberOfVisibleCells;
}
get overflowCellsLimit() {
return this.utils.overflowCellsLimit;
}
updateProperties(carouselProperties) {
this.carouselProperties = carouselProperties;
}
lineUp() {
const cells = this.element ? this.element.children : [];
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
const positionX = this.getCellPositionInContainer(i);
cell.style.transform = `translateX(${positionX}px)`;
cell.style.width = `${this.carouselProperties.cellWidth}px`;
}
}
ifSequenceOfCellsIsChanged() {
const cells = this.element.children;
return cells[0].style.transform !== 'translateX(0px)';
}
getCellPositionInContainer(cellIndexInDOMTree) {
return cellIndexInDOMTree * this.fullCellWidth;
}
setCounter(value) {
this.counter = value;
}
init(carouselProperties) {
this.element = this.carouselProperties.cellsElement;
this.cells = this.element.children;
this.visibleWidth =
this.carouselProperties.visibleWidth ||
this.element.parentElement.clientWidth;
}
}
class Slide {
constructor(carouselProperties, utils, cells, container) {
this.carouselProperties = carouselProperties;
this.utils = utils;
this.cells = cells;
this.container = container;
this.slideLength = 0;
this.isSlideInProgress = false;
this.counter = 0;
this._counter = 0;
this.distance = 0;
this.distanceAbs = 0;
this.isNotClickOnArrow = false;
this.initialPositionX = 0;
this.currentPositionX = 0;
/* The slide length has been limited by the limitSlideLength() method */
this.isSlideLengthLimited = false;
this.init();
}
get fullCellWidth() {
return this.carouselProperties.cellWidth + this.carouselProperties.margin;
}
get margin() {
return this.carouselProperties.margin;
}
get minSwipeDistance() {
return this.carouselProperties.minSwipeDistance;
}
get numberOfVisibleCells() {
return this.utils.numberOfVisibleCells;
}
get visibleCellsOverflowContainer() {
return this.utils.visibleCellsOverflowContainer;
}
/* The position to which the container returns after each slide
* in the light DUM tree mode.
*/
get fixedContainerPosition() {
return -(this.overflowCellsLimit * this.fullCellWidth);
}
get overflowCellsLimit() {
return this.utils.overflowCellsLimit;
}
/* Number of cell elements in the DUM tree */
get cellLength() {
return this.cells.cellLength;
}
updateProperties(carouselProperties) {
this.carouselProperties = carouselProperties;
this.setVisibleWidth();
}
init() {
this.visibleWidth =
this.carouselProperties.visibleWidth ||
this.carouselProperties.hostElement.clientWidth;
}
handleTouchstart() {
/* Touchstart event is not called for arrow */
this.isNotClickOnArrow = true;
this.isSlideLengthLimited = false;
if (!this.isSlideInProgress) {
this.initialPositionX = this.container.getCurrentPositionX();
}
}
handleTouchend() {
if (!this.isNotClickOnArrow) {
return;
}
this.currentPositionX = this.container.getCurrentPositionX();
this.distanceAbs = Math.abs(this.initialPositionX - this.currentPositionX);
this.distance = this.initialPositionX - this.currentPositionX;
this.direction = this.getDirection();
this.isNotClickOnArrow = false;
this.handleSlide();
}
handleTransitionend() {
this.setCounter();
this.isSlideInProgress = false;
}
handleSlide(customSlideLength = undefined) {
const isUsingButton = customSlideLength;
let newPositionX;
if ((isUsingButton && this.isSlideInProgress) || !this.direction) {
return;
}
/* Custom slide length is used in arrows */
if (customSlideLength) {
this.slideLength = this.limitSlideLength(customSlideLength);
if (!this.isSlideInProgress) {
this.initialPositionX = this.container.getCurrentPositionX();
}
}
else {
this.slideLength = this.getSlideLength(this.distanceAbs);
}
/* Store intermediate counter value */
this._counter = this.getPreliminaryCounter();
if (this.direction === 'left') {
if (!customSlideLength) {
this.slideLength = this.limitSlideLength(this.getSlideLength(this.distanceAbs));
}
this._counter = this.getPreliminaryCounter();
const isSlidesEnd = this.isSlidesEnd(this._counter);
newPositionX = this.getPositionByIndex(this._counter);
if (isSlidesEnd) {
this._counter = this.counter;
newPositionX = this.getPositionByIndex(this.counter);
this.slideLength = 0;
}
}
if (this.direction === 'right') {
if (!customSlideLength) {
this.slideLength = this.getSlideLength(this.distanceAbs);
}
if (this._counter < 0) {
this._counter = this.counter;
this.slideLength = this.counter;
}
newPositionX = this.getPositionByIndex(this.counter - this.slideLength);
}
if (this.container.getCurrentPositionX() !== newPositionX) {
this.isSlideInProgress = true;
this.container.transformPositionX(newPositionX);
}
}
next(length = 1) {
this.direction = 'left';
this.handleSlide(length);
}
prev(length = 1) {
this.direction = 'right';
this.handleSlide(length);
}
select(index) {
if (index > this.cellLength - 1) {
return;
}
if (index > this.counter) {
const length = index - this.counter;
this.next(length);
}
if (index < this.counter) {
const length = this.counter - index;
this.prev(length);
}
}
getPreliminaryCounter() {
if (this.direction === 'left') {
return this.counter + this.slideLength;
}
if (this.direction === 'right') {
return this.counter - this.slideLength;
}
return 0;
}
/*
* Limits the length of the slide during calls to the next() and prev()
* methods if the specified position is outside the cell length
*/
limitSlideLength(slideLength) {
if (slideLength > 1) {
for (let i = 0; i < slideLength; i++) {
const newCounter = this.counter + (slideLength - i);
if (!this.isSlidesEnd(newCounter)) {
slideLength -= i;
this.isSlideLengthLimited = i > 0;
break;
}
}
}
return slideLength;
}
/* Offset the container to show the last cell completely */
getPositionCorrection(counter) {
let correction = 0;
const isLastSlide = this.isLastSlide(counter);
if (this.isSlideLengthLimited || isLastSlide) {
const cellsWidth = this.cells.cellLengthInLightDOMMode * this.fullCellWidth;
if (this.visibleWidth < cellsWidth) {
correction = -(this.numberOfVisibleCells * this.fullCellWidth -
this.visibleWidth -
this.margin);
}
if (correction >= -this.margin) {
correction = 0;
}
}
return correction;
}
getSlideLength(distanceAbs) {
let length = Math.floor(distanceAbs / this.fullCellWidth);
if (distanceAbs % this.fullCellWidth >= this.minSwipeDistance) {
length++;
}
return length;
}
getDistanceAbs() {
return Math.abs(this.initialPositionX - this.currentPositionX);
}
getDirection() {
const direction = Math.sign(this.initialPositionX - this.currentPositionX);
if (direction === -1) {
return 'right';
}
if (direction === 1) {
return 'left';
}
return undefined;
}
isSlidesEnd(counter) {
const margin = this.visibleCellsOverflowContainer ? 1 : 0;
const imageLength = this.cells.cellLength;
return imageLength - counter + margin < this.numberOfVisibleCells;
}
isLastSlide(counter) {
return this.isSlidesEnd(counter + 1);
}
setCounter() {
if (this.direction === 'left') {
this.counter += this.slideLength;
}
if (this.direction === 'right') {
this.counter -= this.slideLength;
}
}
getPositionByIndex(_counter) {
let correction = this.getPositionCorrection(this.counter + this.slideLength);
let position;
if (correction !== 0) {
correction += this.fullCellWidth;
}
if (this.direction === 'right') {
correction = 0;
}
position = -(_counter * this.fullCellWidth - correction);
position = this.provideSafePosition(position);
return position;
}
provideSafePosition(position) {
const endPosition = this.container.getEndPosition();
if (this.direction === 'left') {
if (position > 0) {
position = 0;
}
}
if (this.direction === 'right') {
if (position < endPosition) {
position = endPosition;
}
}
return position;
}
getPositionWithoutCorrection(value) {
const remainder = Math.round(value) % this.fullCellWidth;
if (remainder !== 0) {
return value - (this.fullCellWidth + remainder);
}
return value;
}
isNextArrowDisabled() {
return (this.isLastSlide(this.counter) ||
(!this.visibleCellsOverflowContainer &&
this.cellLength <= this.numberOfVisibleCells) ||
(this.visibleCellsOverflowContainer &&
this.cellLength < this.numberOfVisibleCells));
}
isPrevArrowDisabled() {
return this.counter === 0;
}
alignContainerFast() {
if (this.ifLeftDOMModeToBeginning(this.counter)) {
/* If we have already exited the light DOM mode but
* the cells are still out of place
*/
if (this.cells.ifSequenceOfCellsIsChanged()) {
const positionX = -(this.counter * this.fullCellWidth);
this.container.transformPositionX(positionX, 0);
this.cells.setCounter(this.counter);
this.cells.lineUp();
}
}
}
ifLeftDOMModeToBeginning(counter) {
let flag;
if (counter <= this.overflowCellsLimit) {
flag = true;
}
if (this.counter <= this.overflowCellsLimit) {
flag = true;
}
return flag;
}
setVisibleWidth() {
this.visibleWidth =
this.carouselProperties.visibleWidth ||
this.carouselProperties.hostElement.clientWidth;
}
}
class Utils {
constructor(carouselProperties) {
this.carouselProperties = carouselProperties;
}
get margin() {
return this.carouselProperties.margin;
}
get overflowCellsLimit() {
return this.carouselProperties.overflowCellsLimit;
}
get numberOfVisibleCells() {
return Math.ceil(this.visibleWidth / this.fullCellWidth);
}
get visibleCellsOverflowContainer() {
return (this.numberOfVisibleCells * this.fullCellWidth - this.margin >
this.visibleWidth);
}
get fullCellWidth() {
return this.carouselProperties.cellWidth + this.carouselProperties.margin;
}
get visibleWidth() {
return (this.carouselProperties.visibleWidth ||
this.carouselProperties.cellsElement.parentElement.clientWidth);
}
updateProperties(carouselProperties) {
this.carouselProperties = carouselProperties;
}
getStartX(event) {
const { touches } = event;
const carouselElementPosition = this.getCarouselElementPosition().left;
let startX;
if (touches) {
startX = touches[0].clientX - carouselElementPosition;
}
else {
startX = event.clientX - carouselElementPosition;
}
return startX;
}
getMoveX(event) {
const { touches } = event;
const carouselElementPositionX = this.getCarouselElementPosition().left;
if (touches) {
return touches[0].clientX - carouselElementPositionX;
}
return event.clientX - carouselElementPositionX;
}
getCarouselElementPosition() {
return this.carouselProperties.hostElement.getBoundingClientRect();
}
}
class CarouselComponent {
constructor(elementRef, ref) {
this.elementRef = elementRef;
this.ref = ref;
this._isCounter = false;
this._cellWidth = 200;
this.isMoving = false;
this.isNgContent = false;
this.events = new EventEmitter();
this.height = 450;
this.autoplay = true;
this.autoplayInterval = 5000;
this.pauseOnHover = true;
this.dots = false;
this.margin = 10;
this.objectFit = 'cover';
this.minSwipeDistance = 10;
this.transitionDuration = 200;
this.transitionTimingFunction = 'ease-out';
this.counterSeparator = ' / ';
this.overflowCellsLimit = 3;
this.listeners = 'mouse and touch';
this.cellsToShow = 1;
this.cellsToScroll = 1;
this.freeScroll = false;
this.arrows = true;
this.arrowsOutside = false;
this.arrowsTheme = 'light';
this.hostClassCarousel = true;
this.handleTouchstart = (event) => {
this.touches.addEventListeners('mousemove', 'handleMousemove');
this.carousel.handleTouchstart(event);
this.isMoving = true;
};
this.handleHorizontalSwipe = (event) => {
event.preventDefault();
this.carousel.handleHorizontalSwipe(event);
};
this.handleTouchend = (event) => {
const { touches } = event;
this.carousel.handleTouchend(event);
this.touches.removeEventListeners('mousemove', 'handleMousemove');
this.isMoving = false;
};
this.handleTap = (event) => {
const outboundEvent = {
name: 'click'
};
const nodes = Array.prototype.slice.call(this.cellsElement.children);
const cellElement = event.srcElement.closest('.carousel-cell');
const i = nodes.indexOf(cellElement);
const cellIndex = nodes.indexOf(cellElement);
outboundEvent.cellIndex = cellIndex;
};
}
get slideCounter() {
if (this.carousel) {
return this.carousel.slideCounter;
}
}
get lapCounter() {
if (this.carousel) {
return this.carousel.lapCounter;
}
}
get isLandscape() {
return window.innerWidth > window.innerHeight;
}
get isSafari() {
const ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('safari') !== -1) {
return !(ua.indexOf('chrome') > -1);
}
}
get counter() {
const counter = this.slideCounter;
return counter + 1 + this.counterSeparator + this.cellLength;
}
get cellsElement() {
return this.elementRef.nativeElement.querySelector('.carousel-cells');
}
get isArrows() {
return this.arrows && !this.freeScroll;
}
get isCounter() {
return this._isCounter && this.cellLength > 1;
}
get activeDotIndex() {
return this.slideCounter % this.cellLength;
}
get cellLimit() {
if (this.carousel) {
return this.carousel.cellLimit;
}
}
get carouselWidth() {
return this.elementRef.nativeElement.clientWidth;
}
set cellWidth(value) {
if (value) {
this._cellWidth = value;
}
}
set isCounter(value) {
if (value) {
this._isCounter = value;
}
}
onWindowResize(event) {
if (this.utils.visibleWidth !== this.savedCarouselWidth) {
this.resize();
}
}
onMouseEnter() {
if (this.autoplay && this.pauseOnHover) {
this.carouselProperties.autoplayIsPossible = false;
this.carousel.stopAutoplay();
}
}
onMouseLeave() {
if (this.autoplay && this.pauseOnHover) {
this.carouselProperties.autoplayIsPossible = true;
this.carousel.autoplay();
}
}
onDragStart() {
return false;
}
ngOnInit() {
this.isNgContent = this.cellsElement.children.length > 0;
this.touches = new Touches({
element: this.cellsElement,
listeners: this.listeners,
mouseListeners: {
mousedown: 'handleMousedown',
mouseup: 'handleMouseup'
}
});
this.touches.on('touchstart', this.handleTouchstart);
this.touches.on('horizontal-swipe', this.handleHorizontalSwipe);
this.touches.on('touchend', this.handleTouchend);
this.touches.on('mousedown', this.handleTouchstart);
this.touches.on('mouseup', this.handleTouchend);
this.touches.on('tap', this.handleTap);
this.setDimensions();
}
ngAfterViewInit() {
this.initCarousel();
this.cellLength = this.getCellLength();
this.dotsArr = Array(this.cellLength).fill(1);
this.ref.detectChanges();
this.carousel.lineUpCells();
this.savedCarouselWidth = this.carouselWidth;
/* Start detecting changes in the DOM tree */
this.detectDomChanges();
}
ngOnChanges(changes) {
const isFirstChange = Object.values(changes).some(change => change.isFirstChange());
if (!isFirstChange && (changes.width || changes.height)) {
this.setDimensions();
this.initCarousel();
this.carousel.lineUpCells();
this.ref.detectChanges();
}
}
ngOnDestroy() {
this.touches.destroy();
// this.carousel.destroy();
}
initCarousel() {
this.carouselProperties = {
id: this.id,
cellsElement: this.elementRef.nativeElement.querySelector('.carousel-cells'),
hostElement: this.elementRef.nativeElement,
cellWidth: this.getCellWidth(),
autoplayInterval: this.autoplayInterval,
autoplayIsPossible: true,
overflowCellsLimit: this.overflowCellsLimit,
visibleWidth: this.width,
margin: this.margin,
minSwipeDistance: this.minSwipeDistance,
transitionDuration: this.transitionDuration,
transitionTimingFunction: this.transitionTimingFunction,
videoProperties: this.videoProperties,
eventHandler: this.events,
freeScroll: this.freeScroll
};
this.utils = new Utils(this.carouselProperties);
this.cells = new Cells(this.carouselProperties, this.utils);
this.container = new Container(this.carouselProperties, this.utils, this.cells);
this.slide = new Slide(this.carouselProperties, this.utils, this.cells, this.container);
if (this.autoplay && this.carousel) {
this.carousel.stopAutoplay();
}
this.carousel = new Carousel(this.carouselProperties, this.utils, this.cells, this.container, this.slide);
if (this.autoplay) {
this.carousel.autoplay();
}
}
resize() {
this.landscapeMode = this.isLandscape;
this.savedCarouselWidth = this.carouselWidth;
this.carouselProperties.cellWidth = this.getCellWidth();
this.cells.updateProperties(this.carouselProperties);
this.carousel.updateProperties(this.carouselProperties);
this.container.updateProperties(this.carouselProperties);
this.slide.updateProperties(this.carouselProperties);
this.utils.updateProperties(this.carouselProperties);
this.carousel.lineUpCells();
this.slide.select(0);
this.ref.detectChanges();
}
detectDomChanges() {
const observer = new MutationObserver((mutations) => {
this.onDomChanges();
});
const config = {
attributes: true,
childList: true,
characterData: true
};
observer.observe(this.cellsElement, config);
}
onDomChanges() {
this.cellLength = this.getCellLength();
this.carousel.lineUpCells();
this.ref.detectChanges();
}
setDimensions() {
this.hostStyleHeight = `${this.height}px`;
this.hostStyleWidth = `${this.width}px`;
}
handleTransitionendCellContainer(event) {
if (event.target.className === 'carousel-cells') {
this.carousel.handleTransitionend();
}
}
getCellWidth() {
const elementWidth = this.carouselWidth;
if (this.cellsToShow) {
const margin = this.cellsToShow > 1 ? this.margin : 0;
const totalMargin = margin * (this.cellsToShow - 1);
return (elementWidth - totalMargin) / this.cellsToShow;
}
if (this._cellWidth === '100%') {
return elementWidth;
}
return this._cellWidth;
}
next() {
this.carousel.next(this.cellsToScroll);
this.carousel.stopAutoplay();
}
prev() {
this.carousel.prev(this.cellsToScroll);
this.carousel.stopAutoplay();
}
isNextArrowDisabled() {
if (this.carousel) {
return this.carousel.isNextArrowDisabled();
}
}
isPrevArrowDisabled() {
if (this.carousel) {
return this.carousel.isPrevArrowDisabled();
}
}
getCellLength() {
return this.cellsElement.children.length;
}
}
CarouselComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: CarouselComponent, deps: [{ token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
CarouselComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.12", type: CarouselComponent, selector: "carousel, [carousel]", inputs: { id: "id", height: "height", width: "width", autoplay: "autoplay", autoplayInterval: "autoplayInterval", pauseOnHover: "pauseOnHover", dots: "dots", borderRadius: "borderRadius", margin: "margin", objectFit: "objectFit", minSwipeDistance: "minSwipeDistance", transitionDuration: "transitionDuration", transitionTimingFunction: "transitionTimingFunction", videoProperties: "videoProperties", counterSeparator: "counterSeparator", overflowCellsLimit: "overflowCellsLimit", listeners: "listeners", cellsToShow: "cellsToShow", cellsToScroll: "cellsToScroll", freeScroll: "freeScroll", arrows: "arrows", arrowsOutside: "arrowsOutside", arrowsTheme: "arrowsTheme", cellWidth: "cellWidth", isCounter: ["counter", "isCounter"] }, outputs: { events: "events" }, host: { listeners: { "window:resize": "onWindowResize($event)", "mouseenter": "onMouseEnter($event)", "mouseleave": "onMouseLeave($event)", "dragstart": "onDragStart($event)" }, properties: { "class.carousel": "this.hostClassCarousel", "style.height": "this.hostStyleHeight", "style.width": "this.h