UNPKG

angular-responsive-carousel-ng16

Version:

Carousel for Angular. A simple solution for horizontal scrolling images with lazy loading.

1 lines 124 kB
{"version":3,"file":"angular-responsive-carousel-ng16.mjs","sources":["../../../projects/angular-responsive-carousel-ng16/src/lib/touches.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/carousel.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/container.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/cells.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/slide.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/utils.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/carousel.component.ts","../../../projects/angular-responsive-carousel-ng16/src/lib/carousel.component.html","../../../projects/angular-responsive-carousel-ng16/src/lib/carousel.module.ts","../../../projects/angular-responsive-carousel-ng16/src/public-api.ts","../../../projects/angular-responsive-carousel-ng16/src/angular-responsive-carousel-ng16.ts"],"sourcesContent":["export interface Properties {\n element: HTMLElement;\n listeners?: 'auto' | 'mouse and touch';\n touchListeners?: any;\n mouseListeners?: any;\n otherListeners?: any;\n resize?: boolean;\n}\n\nexport type EventType = undefined | 'touchend' | 'pan' | 'pinch' | 'horizontal-swipe' | 'vertical-swipe' | 'tap' | 'longtap';\nexport type TouchHandler = 'handleTouchstart' | 'handleTouchmove' | 'handleTouchend';\nexport type MouseHandler = 'handleMousedown' | 'handleMousemove' | 'handleMouseup';\n\nexport class Touches {\n properties: Properties;\n element: HTMLElement;\n elementPosition: ClientRect;\n eventType: EventType = undefined;\n handlers: any = {};\n startX = 0;\n startY = 0;\n lastTap = 0;\n doubleTapTimeout: any;\n doubleTapMinTimeout = 300;\n tapMinTimeout = 200;\n touchstartTime = 0;\n i: number = 0;\n isMousedown = false;\n\n _touchListeners: any = {\n \"touchstart\": \"handleTouchstart\",\n \"touchmove\": \"handleTouchmove\",\n \"touchend\": \"handleTouchend\"\n }\n _mouseListeners: any = {\n \"mousedown\": \"handleMousedown\",\n \"mousemove\": \"handleMousemove\",\n \"mouseup\": \"handleMouseup\",\n \"wheel\": \"handleWheel\"\n }\n _otherListeners: any = {\n \"resize\": \"handleResize\"\n }\n\n get touchListeners() {\n return this.properties.touchListeners ? this.properties.touchListeners : this._touchListeners;\n }\n\n get mouseListeners() {\n return this.properties.mouseListeners ? this.properties.mouseListeners : this._mouseListeners;\n }\n\n get otherListeners() {\n return this.properties.otherListeners ? this.properties.otherListeners : this._otherListeners;\n }\n\n constructor(properties: Properties) {\n this.properties = properties;\n this.element = this.properties.element;\n this.elementPosition = this.getElementPosition();\n\n this.toggleEventListeners('addEventListener');\n }\n\n destroy() {\n this.toggleEventListeners('removeEventListener');\n }\n\n toggleEventListeners(action: 'addEventListener' | 'removeEventListener') {\n let listeners;\n\n if (this.properties.listeners === 'mouse and touch') {\n listeners = Object.assign(this.touchListeners, this.mouseListeners);\n } else {\n listeners = this.detectTouchScreen() ? this.touchListeners : this.mouseListeners;\n }\n\n if (this.properties.resize) {\n listeners = Object.assign(listeners, this.otherListeners);\n }\n\n for (var listener in listeners) {\n const handler: MouseHandler = listeners[listener];\n\n // Window\n if (listener === \"resize\") {\n if (action === 'addEventListener') {\n window.addEventListener(listener, this[handler], false);\n }\n if (action === 'removeEventListener') {\n window.removeEventListener(listener, this[handler], false);\n }\n // Document\n } else if (listener === 'mouseup' || listener === \"mousemove\") {\n if (action === 'addEventListener') {\n document.addEventListener(listener, this[handler], {passive: false});\n }\n if (action === 'removeEventListener') {\n document.removeEventListener(listener, this[handler], false);\n }\n // Element\n } else {\n if (action === 'addEventListener') {\n this.element.addEventListener(listener, this[handler], false);\n }\n if (action === 'removeEventListener') {\n this.element.removeEventListener(listener, this[handler], false);\n }\n }\n }\n }\n\n addEventListeners(listener: string) {\n const handler: MouseHandler = this._mouseListeners[listener];\n window.addEventListener(listener, this[handler], false);\n }\n\n removeEventListeners(listener: string) {\n const handler: MouseHandler = this._mouseListeners[listener];\n window.removeEventListener(listener, this[handler], false);\n }\n\n /*\n * Listeners\n */\n\n /* Touchstart */\n\n handleTouchstart = (event: any) => {\n this.elementPosition = this.getElementPosition();\n this.touchstartTime = new Date().getTime();\n\n if (this.eventType === undefined) {\n this.getTouchstartPosition(event);\n }\n\n this.runHandler(\"touchstart\", event);\n }\n\n\n /* Touchmove */\n\n handleTouchmove = (event: any) => {\n const touches = event.touches;\n\n // Pan\n if (this.detectPan(touches)) {\n this.runHandler(\"pan\", event);\n }\n\n // Pinch\n if (this.detectPinch(event)) {\n this.runHandler(\"pinch\", event);\n }\n\n // Linear swipe\n switch (this.detectLinearSwipe(event)) {\n case \"horizontal-swipe\":\n event.swipeType = \"horizontal-swipe\";\n this.runHandler(\"horizontal-swipe\", event);\n break;\n case \"vertical-swipe\":\n event.swipeType = \"vertical-swipe\";\n this.runHandler(\"vertical-swipe\", event);\n break;\n }\n\n // Linear swipe\n if (this.detectLinearSwipe(event) ||\n this.eventType === 'horizontal-swipe' ||\n this.eventType === 'vertical-swipe') {\n\n this.handleLinearSwipe(event);\n }\n }\n\n handleLinearSwipe(event: any) {\n //event.preventDefault();\n\n this.i++;\n\n if (this.i > 3) {\n this.eventType = this.getLinearSwipeType(event);\n }\n\n if (this.eventType === 'horizontal-swipe') {\n this.runHandler('horizontal-swipe', event);\n }\n\n if (this.eventType === 'vertical-swipe') {\n this.runHandler('vertical-swipe', event);\n }\n }\n\n\n /* Touchend */\n\n handleTouchend = (event: any) => {\n const touches = event.touches;\n\n // Double Tap\n if (this.detectDoubleTap()) {\n this.runHandler(\"double-tap\", event);\n }\n\n // Tap\n this.detectTap();\n\n this.runHandler(\"touchend\", event);\n this.eventType = 'touchend';\n\n if (touches && touches.length === 0) {\n this.eventType = undefined;\n this.i = 0;\n }\n }\n\n\n /* Mousedown */\n\n handleMousedown = (event: any) => {\n this.isMousedown = true;\n this.elementPosition = this.getElementPosition();\n this.touchstartTime = new Date().getTime();\n\n if (this.eventType === undefined) {\n this.getMousedownPosition(event);\n }\n\n this.runHandler(\"mousedown\", event);\n }\n\n\n /* Mousemove */\n\n handleMousemove = (event: any) => {\n //event.preventDefault();\n \n if (!this.isMousedown) {\n return;\n }\n\n // Pan\n this.runHandler(\"pan\", event);\n\n // Linear swipe\n switch (this.detectLinearSwipe(event)) {\n case \"horizontal-swipe\":\n event.swipeType = \"horizontal-swipe\";\n this.runHandler(\"horizontal-swipe\", event);\n break;\n case \"vertical-swipe\":\n event.swipeType = \"vertical-swipe\";\n this.runHandler(\"vertical-swipe\", event);\n break;\n }\n\n // Linear swipe\n if (this.detectLinearSwipe(event) ||\n this.eventType === 'horizontal-swipe' ||\n this.eventType === 'vertical-swipe') {\n\n this.handleLinearSwipe(event);\n }\n }\n\n\n /* Mouseup */\n\n handleMouseup = (event: any) => {\n\n // Tap\n this.detectTap();\n\n this.isMousedown = false;\n this.runHandler(\"mouseup\", event);\n this.eventType = undefined;\n this.i = 0;\n }\n\n\n /* Wheel */\n\n handleWheel = (event: any) => {\n this.runHandler(\"wheel\", event);\n }\n\n /* Resize */\n\n handleResize = (event: any) => {\n this.runHandler(\"resize\", event);\n }\n\n runHandler(eventName: any, response: any) {\n if (this.handlers[eventName]) {\n this.handlers[eventName](response);\n }\n }\n\n\n /*\n * Detection\n */\n\n detectPan(touches: any) {\n return touches.length === 1 && !this.eventType || this.eventType === 'pan';\n }\n\n detectDoubleTap() {\n if (this.eventType != undefined) {\n return;\n }\n\n const currentTime = new Date().getTime();\n const tapLength = currentTime - this.lastTap;\n\n clearTimeout(this.doubleTapTimeout);\n\n if (tapLength < this.doubleTapMinTimeout && tapLength > 0) {\n return true;\n } else {\n this.doubleTapTimeout = setTimeout(() => {\n clearTimeout(this.doubleTapTimeout);\n }, this.doubleTapMinTimeout);\n }\n this.lastTap = currentTime;\n\n return undefined;\n }\n\n detectTap(): void {\n if (this.eventType != undefined) {\n return;\n }\n\n const currentTime = new Date().getTime();\n const tapLength = currentTime - this.touchstartTime;\n\n if (tapLength > 0) {\n if (tapLength < this.tapMinTimeout) {\n this.runHandler(\"tap\", event);\n } else {\n this.runHandler(\"longtap\", event);\n }\n }\n }\n\n detectPinch(event: any) {\n const touches = event.touches;\n return (touches.length === 2 && this.eventType === undefined) || this.eventType === 'pinch';\n }\n\n detectLinearSwipe(event: any) {\n const touches = event.touches;\n\n if (touches) {\n if (touches.length === 1 && !this.eventType || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') {\n return this.getLinearSwipeType(event);\n }\n } else {\n if (!this.eventType || this.eventType === 'horizontal-swipe' || this.eventType === 'vertical-swipe') {\n return this.getLinearSwipeType(event);\n }\n }\n\n return undefined;\n }\n\n getLinearSwipeType(event: any) {\n if (this.eventType !== 'horizontal-swipe' && this.eventType !== 'vertical-swipe') {\n const movementX = Math.abs(this.moveLeft(0, event) - this.startX);\n const movementY = Math.abs(this.moveTop(0, event) - this.startY);\n\n if ((movementY * 3) > movementX) {\n return 'vertical-swipe';\n } else {\n return 'horizontal-swipe';\n }\n } else {\n return this.eventType;\n }\n }\n\n getElementPosition() {\n return this.element.getBoundingClientRect();\n }\n\n getTouchstartPosition(event: any) {\n this.startX = event.touches[0].clientX - this.elementPosition.left;\n this.startY = event.touches[0].clientY - this.elementPosition.top;\n }\n\n getMousedownPosition(event: any) {\n this.startX = event.clientX - this.elementPosition.left;\n this.startY = event.clientY - this.elementPosition.top;\n }\n\n moveLeft(index: any, event: any) {\n const touches = event.touches;\n\n if (touches) {\n return touches[index].clientX - this.elementPosition.left;\n } else {\n return event.clientX - this.elementPosition.left;\n }\n }\n\n moveTop(index: any, event: any) {\n const touches = event.touches;\n\n if (touches) {\n return touches[index].clientY - this.elementPosition.top;\n } else {\n return event.clientY - this.elementPosition.top;\n }\n }\n\n detectTouchScreen() {\n var prefixes = ' -webkit- -moz- -o- -ms- '.split(' ');\n var mq = function(query: any) {\n return window.matchMedia(query).matches;\n }\n\n if (('ontouchstart' in window)) {\n return true;\n }\n\n // include the 'heartz' as a way to have a non matching MQ to help terminate the join\n // https://git.io/vznFH\n var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join('');\n return mq(query);\n }\n\n\n /* Public properties and methods */\n on(event: EventType, handler: Function) {\n if (event) {\n this.handlers[event] = handler;\n }\n }\n}","import {Properties} from './interfaces';\n\nexport class Carousel {\n cellsElement: HTMLElement | undefined;\n\n /* The slide length has been limited by the limitSlideLength() method */\n isSlideLengthLimited: boolean = false;\n\n isContentImages: boolean = true;\n visibleWidth!: number;\n isLazyLoad: boolean = true;\n isContainerLocked: boolean = true;\n alignCells: \"left\" | \"center\" = \"left\";\n initialContainerPosition: number = 0;\n autoplayId: any;\n containerPullLimit = 100;\n\n get cellLength() {\n return this.cells.cellLength;\n }\n\n get cellLengthInLightDOMMode() {\n if (this.images) {\n let cellLength = this.numberOfVisibleCells + this.overflowCellsLimit * 2;\n if (cellLength > this.images.length) {\n cellLength = this.images.length;\n }\n return cellLength;\n } else {\n return this.cellLength;\n }\n }\n\n get lastCellIndex() {\n return this.images.length ? (this.images.length - 1) : (this.cells.cellLength - 1);\n }\n\n get overflowCellsLimit() {\n return this.utils.overflowCellsLimit;\n }\n\n get cellLimit() {\n if (this.isLightDOM) {\n let cellLimit = this.numberOfVisibleCells + this.overflowCellsLimit * 2;\n\n if (cellLimit < this.numberOfVisibleCells) {\n cellLimit = this.numberOfVisibleCells;\n }\n\n return cellLimit;\n } else {\n return this.properties.images.length;\n }\n }\n\n get isLightDOM() {\n return this.properties.lightDOM || this.properties.loop;\n }\n\n get images() {\n return this.properties.images;\n }\n\n get margin() {\n return this.properties.margin;\n }\n\n get minSwipeDistance() {\n return this.properties.minSwipeDistance;\n }\n\n get transitionDuration() {\n return this.properties.transitionDuration;\n }\n\n get transitionTimingFunction() {\n return this.properties.transitionTimingFunction;\n }\n\n get fullCellWidth() {\n return this.properties.cellWidth + this.margin;\n }\n\n get numberOfVisibleCells() {\n return this.utils.numberOfVisibleCells;\n }\n\n get lapCounter() {\n return Math.floor(this.slide.counter / this.cellLengthInLightDOMMode);\n }\n\n get slideCounter() {\n return this.slide.counter;\n }\n\n constructor(\n private properties: Properties,\n private utils: any,\n private cells: any,\n private container: any,\n private slide: any) {\n\n this.init();\n }\n\n updateProperties(properties: Properties) {\n this.properties = properties;\n }\n\n init() {\n this.cellsElement = this.properties.cellsElement;\n this.visibleWidth = this.properties.visibleWidth || this.cellsElement!.parentElement!.clientWidth;\n }\n\n destroy() {\n clearInterval(this.autoplayId);\n }\n\n lineUpCells() {\n this.cells.lineUp();\n }\n\n handleTouchstart = (event: any) => {\n this.container.handleTouchstart();\n this.slide.handleTouchstart(event);\n }\n\n handleHorizontalSwipe = (event: any) => {\n this.container.handleHorizontalSwipe();\n }\n\n handleTouchend = (event: any) => {\n if (this.properties.freeScroll) {\n this.container.handleTouchend();\n } else {\n this.container.handleTouchend(true);\n this.slide.handleTouchend(event);\n }\n }\n\n handleTransitionend() {\n this.slide.handleTransitionend();\n }\n\n getImage(index:number) {\n return this.cells.getImage(index);\n }\n\n next(length: number = 1) {\n if (!this.isNextArrowDisabled()) {\n this.slide.next(length);\n }\n }\n\n prev(length: number = 1) {\n this.slide.prev(length);\n }\n\n isNextArrowDisabled = () => {\n return this.slide.isNextArrowDisabled();\n }\n\n isPrevArrowDisabled = () => {\n return this.slide.isPrevArrowDisabled();\n }\n\n autoplay() {\n this.autoplayId = setInterval(() => {\n this.next();\n }, this.properties.autoplayInterval);\n }\n\n stopAutoplay() {\n if (this.autoplayId) {\n clearInterval(this.autoplayId);\n }\n }\n}","import {Properties as CarouselProperties} from './interfaces';\n\nexport class Container {\n /* The index of the new position relative to \n * the active index, for example -1 or +1 \n */\n newPositionIndex: number = 0;\n isPositionCorrection: boolean = false;\n initialPositionX: number = 0;\n initialElementPositionX: number = 0;\n isLocked: boolean = true;\n pullLimit: number = 100;\n startTime: number = 0;\n startX: number = 0;\n moveX: number = 0;\n isSwipeInProgress: boolean = false;\n\n get visibleWidth() {\n return this.utils.visibleWidth;\n }\n\n get overflowCellsLimit() {\n return this.utils.overflowCellsLimit;\n }\n\n get images() {\n return this.carouselProperties.images;\n }\n\n get element() {\n return this.carouselProperties.cellsElement;\n }\n\n get freeScroll() {\n return this.carouselProperties.freeScroll;\n }\n\n get fullCellWidth() {\n return this.carouselProperties.cellWidth + this.carouselProperties.margin;\n }\n\n get numberOfVisibleCells() {\n return this.utils.numberOfVisibleCells;\n }\n\n get transitionDuration() {\n return this.carouselProperties.transitionDuration;\n }\n\n get transitionTimingFunction() {\n return this.carouselProperties.transitionTimingFunction;\n }\n\n get cellLength() {\n if (this.images) {\n return this.images.length;\n } else {\n return this.cells.cellLength;\n }\n }\n\n get cellLengthInLightDOMMode() {\n if (this.images) {\n let cellLength = this.numberOfVisibleCells + this.overflowCellsLimit * 2;\n if (cellLength > this.images.length) {\n cellLength = this.images.length;\n }\n return cellLength;\n } else {\n return this.cellLength;\n }\n }\n\n get tooFewCells() {\n return this.numberOfVisibleCells > this.cellLength;\n }\n\n get disabled() {\n return this.tooFewCells;\n }\n\n get margin() {\n return this.carouselProperties.margin;\n }\n\n get isLightDOM() {\n return this.carouselProperties.lightDOM || this.carouselProperties.loop;\n }\n\n constructor(private carouselProperties: CarouselProperties,\n private utils:any,\n private cells:any) {\n\n this.init()\n }\n\n updateProperties(carouselProperties: CarouselProperties) {\n this.carouselProperties = carouselProperties;\n }\n\n init() {\n this.setWidth();\n }\n\n handleTouchstart() {\n this.startX = this.utils.getStartX(event);\n this.startTime = new Date().getTime();\n this.initialElementPositionX = this.getInitialElementPositionX();\n }\n\n handleHorizontalSwipe() {\n if (this.disabled) {\n return;\n }\n\n if (!this.isSwipeInProgress) {\n this.startX = this.utils.getStartX(event);\n this.startTime = new Date().getTime();\n this.initialElementPositionX = this.getInitialElementPositionX();\n }\n\n this.isSwipeInProgress = true;\n this.moveX = this.utils.getMoveX(event);\n this.move();\n }\n\n handleTouchend(simpleProcessing: boolean = false) {\n if (this.disabled) {\n return;\n }\n\n /* If touchend was passed to the Slide class */\n if (simpleProcessing) {\n this.isSwipeInProgress = false;\n return;\n }\n\n this.isSwipeInProgress = false;\n this.finishMoving();\n this.clearInitialValues();\n }\n\n move() {\n let positionX: number = this.getMovePositionX();\n const isPulled = this.detectPulled();\n const direction = this.getDirection();\n\n if (isPulled) {\n if (isPulled.edge === \"left\" && direction === \"right\" ||\n isPulled.edge === \"right\" && direction === \"left\") {\n positionX = this.slowdownOnPull(positionX);\n }\n }\n\n this.transformPositionX(positionX, 0);\n\n if (this.freeScroll) {\n this.initialPositionX = positionX;\n }\n\n if (isPulled) {\n if (isPulled.edge === 'left' && isPulled.overflowX > this.pullLimit) {\n this.initialPositionX = 0;\n }\n if (isPulled.edge === 'right' && isPulled.overflowX > this.pullLimit) {\n this.initialPositionX = positionX;\n }\n }\n }\n\n getMovePositionX() {\n const distance = this.getDistance();\n return this.initialElementPositionX - distance;\n }\n\n getDistance() {\n return this.startX - this.moveX;\n }\n\n /* If the container is pulled out of the left or right border */\n detectPulled() {\n const currentPositionX = this.getCurrentPositionX();\n\n if (currentPositionX > 0) {\n return {\n edge: 'left',\n positionX: currentPositionX,\n overflowX: Math.abs(currentPositionX)\n }\n }\n\n if (currentPositionX < this.getEndPosition()) {\n return {\n edge: 'right',\n positionX: currentPositionX,\n overflowX: Math.abs(currentPositionX - this.getEndPosition())\n }\n }\n\n return undefined;\n }\n\n slowdownOnPull(_positionX: number) {\n let distance = Math.abs(this.getDistance());\n const endPosition = this.getEndPosition();\n const isPulled = this.detectPulled();\n\n if (!isPulled) {\n return 0;\n }\n\n const decelerationRatio = 3 + isPulled.overflowX / 50;\n let positionX:number = 0;\n\n if (isPulled.edge === 'left') {\n\n if (this.initialElementPositionX < 0) {\n distance = distance - Math.abs(this.initialElementPositionX);\n }\n\n const rubberPositionX = distance / decelerationRatio;\n positionX = rubberPositionX;\n\n if (this.initialElementPositionX > 0) {\n positionX = this.initialElementPositionX + rubberPositionX;\n }\n\n if (positionX > this.pullLimit) {\n positionX = this.pullLimit;\n }\n }\n\n if (isPulled.edge === 'right') {\n const rubberPositionX = endPosition + (((this.initialElementPositionX - distance) - endPosition) / decelerationRatio);\n const containerWidth = this.getWidth();\n\n positionX = rubberPositionX;\n\n if (this.initialElementPositionX < -(containerWidth - this.visibleWidth)) {\n positionX = ((containerWidth - this.visibleWidth) + this.initialElementPositionX) + rubberPositionX;\n }\n\n if (positionX < endPosition - this.pullLimit) {\n positionX = endPosition - this.pullLimit;\n }\n }\n\n return positionX;\n }\n\n finishMoving() {\n const positionX = this.getMovePositionX();\n let newPositionX:number = 0;\n\n if (this.freeScroll) {\n newPositionX = this.getInertia();\n }\n\n /* Align container while pulling */\n newPositionX = this.getAlignedPositionOnPull(newPositionX);\n\n this.transformPositionX(newPositionX);\n this.setInitialPosition(positionX);\n }\n\n /* Returns the new position of the container with inertia */\n getInertia() {\n const distance = this.getDistance();\n const currentTime = new Date().getTime();\n const tapLength = currentTime - this.startTime;\n let inertia = (distance / tapLength) * 100;\n\n return this.initialPositionX - inertia;\n }\n\n getAlignedPositionOnPull(newPositionX:number) {\n const direction = this.getDirection();\n\n if (direction === 'left') {\n let endPosition = this.getEndPosition();\n if (newPositionX < endPosition) {\n return endPosition;\n }\n }\n if (direction === 'right') {\n if (newPositionX > 0) {\n return 0;\n }\n }\n\n return newPositionX;\n }\n\n getCurrentPositionX() {\n const parentPosition = this.element!.parentElement!.getBoundingClientRect();\n const position = this.element.getBoundingClientRect();\n return position.left - parentPosition.left;\n }\n\n getEndPosition() {\n if (this.isLightDOM) {\n let imagesInContainer = this.cells.imageUtils.getImages();\n return -(imagesInContainer.length * this.fullCellWidth - this.visibleWidth - this.margin);\n } else {\n const width = this.getWidth();\n const visibleWidth = this.element!.parentElement!.clientWidth;\n return visibleWidth - width;\n }\n }\n\n transformPositionX(value:number, duration = this.transitionDuration) {\n if (value === undefined) {\n return;\n }\n\n this.element.style.transition = 'transform ' + duration + 'ms ' + this.transitionTimingFunction;\n this.element.style.transform = 'translateX(' + value + 'px)';\n }\n\n getWidth() {\n let width = this.cellLengthInLightDOMMode * this.fullCellWidth;\n let totalImageWidth = this.cellLength * this.fullCellWidth;\n\n if (totalImageWidth < width) {\n width = totalImageWidth;\n }\n\n return this.isLightDOM ? width : totalImageWidth;\n }\n\n setWidth() {\n const width = this.getWidth();\n this.element.style.width = width + \"px\";\n }\n\n setInitialPosition(position:number) {\n this.initialPositionX = position;\n }\n\n getElementPosition() {\n return this.element.getBoundingClientRect();\n }\n\n getInitialElementPositionX() {\n const carouselElementPosition = this.utils.getCarouselElementPosition()['left'];\n return this.getElementPosition()['left'] - carouselElementPosition;\n }\n\n clearInitialValues() {\n this.startX = this.moveX = 0;\n }\n\n getDirection() {\n const direction = Math.sign(this.startX - this.moveX);\n\n if (direction === -1) {\n return 'right';\n }\n if (direction === 1) {\n return 'left';\n }\n\n return undefined;\n }\n}","import {Properties as CarouselProperties, Image} from './interfaces';\n\nexport interface Cell {\n index: number,\n positionX: number,\n img: {\n image: Image,\n imageIndex: number\n }\n}\n\nexport class ImageUtils {\n cellStack: Cell[] = [];\n element:any;\n\n constructor(element: HTMLElement | undefined) {\n this.element = element;\n }\n\n getImages() {\n return this.cellStack.filter(this.filter);\n }\n\n filter(cell: Cell) {\n return cell.img !== undefined;\n }\n}\n\nexport class Cells {\n cells: HTMLCollection | undefined;\n element!: HTMLElement;\n visibleWidth: number | undefined;\n counter: number = 0;\n imageUtils;\n\n get images() {\n return this.carouselProperties.images;\n }\n\n get cellLength() {\n return this.cells ? this.cells.length : 0;\n }\n\n get fullCellWidth() {\n return this.carouselProperties.cellWidth + this.carouselProperties.margin;\n }\n\n get cellLengthInLightDOMMode() {\n if (this.images) {\n let cellLength = this.numberOfVisibleCells + this.overflowCellsLimit * 2;\n\n if (cellLength > this.images.length) {\n cellLength = this.images.length;\n }\n return cellLength;\n } else {\n return this.cellLength;\n }\n }\n\n get numberOfVisibleCells() {\n return this.utils.numberOfVisibleCells;\n }\n\n get overflowCellsLimit() {\n return this.utils.overflowCellsLimit;\n }\n\n get isLightDOM() {\n return this.carouselProperties.lightDOM || this.carouselProperties.loop;\n }\n\n constructor(\n private carouselProperties: CarouselProperties,\n private utils: any) {\n\n this.imageUtils = new ImageUtils(this.element);\n this.init(carouselProperties);\n }\n\n updateProperties(carouselProperties: CarouselProperties) {\n this.carouselProperties = carouselProperties;\n }\n\n lineUp() {\n const cells = this.element ? this.element.children : [];\n this.imageUtils.cellStack = [];\n\n for (var i = 0; i < cells.length; i++) {\n let cell = cells[i];\n let positionX = this.getCellPositionInContainer(i);\n (cell as HTMLElement).style.transform = 'translateX(' + positionX + 'px)';\n (cell as HTMLElement).style.width = this.carouselProperties.cellWidth + 'px';\n\n if (this.getImage(i)) {\n this.imageUtils.cellStack.push({\n index: i,\n positionX,\n img: this.getImage(i)!['image']\n });\n }\n };\n }\n\n ifSequenceOfCellsIsChanged() {\n const cells:any = this.element.children;\n return cells[0]['style'].transform !== 'translateX(0px)';\n }\n\n getCellPositionInContainer(cellIndexInDOMTree: number) {\n let positionIndex = this.getCellIndexInContainer(cellIndexInDOMTree);\n return positionIndex * this.fullCellWidth;\n }\n\n getCellIndexInContainer(cellIndexInDOMTree: number) {\n let positionIndex;\n\n if (!this.isLightDOM) {\n return cellIndexInDOMTree;\n }\n\n let cellLength = this.cellLengthInLightDOMMode;\n let counter = this.counter - this.overflowCellsLimit;\n\n if (counter > cellLength) {\n counter = counter % cellLength;\n }\n\n if (counter < 0) {\n return cellIndexInDOMTree;\n } else {\n positionIndex = cellIndexInDOMTree - counter;\n if (positionIndex < 0) {\n positionIndex = cellLength + positionIndex;\n }\n }\n\n return positionIndex;\n }\n\n getImage(cellIndex: number) {\n if (!this.images) {\n return;\n }\n\n let imageIndex = this.getImageIndex(cellIndex);\n let file = this.images[imageIndex];\n\n if (file && !file.type) {\n file.type = 'image';\n }\n\n return {\n image: this.images[imageIndex],\n imageIndex\n };\n }\n\n getImageIndex(cellIndexInDOMTree: number) {\n const positionIndex = this.getCellIndexInContainer(cellIndexInDOMTree);\n let imageIndex;\n\n if (this.counter > this.overflowCellsLimit) {\n let cellLimitOverflow = this.counter - this.overflowCellsLimit;\n imageIndex = positionIndex + cellLimitOverflow;\n\n if (this.images && this.carouselProperties.loop) {\n imageIndex = imageIndex % this.images.length;\n }\n } else {\n imageIndex = cellIndexInDOMTree;\n }\n\n return imageIndex;\n }\n\n setCounter(value: number) {\n this.counter = value;\n }\n\n init(carouselProperties: CarouselProperties) {\n this.element = this.carouselProperties.cellsElement;\n this.cells = this.element.children;\n this.visibleWidth = this.carouselProperties.visibleWidth || this.element!.parentElement!.clientWidth;\n }\n}","import {Properties as CarouselProperties} from './interfaces';\n\nexport interface Properties {\n carouselProperties: CarouselProperties;\n}\n\nexport class Slide {\n slideLength: number = 0;\n isSlideInProgress: boolean = false;\n direction: 'left' | 'right' | undefined;\n counter: number = 0;\n _counter: number = 0;\n distance: number = 0;\n distanceAbs: number = 0;\n visibleWidth!: number;\n isNotClickOnArrow: boolean = false;\n initialPositionX: number = 0;\n currentPositionX: number = 0;\n\n /* The slide length has been limited by the limitSlideLength() method */\n isSlideLengthLimited: boolean = false;\n\n get fullCellWidth() {\n return this.carouselProperties.cellWidth + this.carouselProperties.margin;\n }\n\n get margin() {\n return this.carouselProperties.margin;\n }\n\n get minSwipeDistance() {\n return this.carouselProperties.minSwipeDistance;\n }\n\n get numberOfVisibleCells() {\n return this.utils.numberOfVisibleCells;\n }\n\n get visibleCellsOverflowContainer() {\n return this.utils.visibleCellsOverflowContainer;\n }\n\n /* The position to which the container returns after each slide \n * in the light DUM tree mode. \n */\n get fixedContainerPosition() {\n return -(this.overflowCellsLimit * this.fullCellWidth);\n }\n\n get overflowCellsLimit() {\n return this.utils.overflowCellsLimit;\n }\n\n get images() {\n return this.carouselProperties.images;\n }\n\n /* Number of cell elements in the DUM tree */\n get cellLength() {\n if (this.isLightDOM) {\n return this.cells.cellLengthInLightDOMMode;\n } else {\n if (this.images) {\n return this.images.length;\n } else {\n return this.cells.cellLength;\n }\n }\n }\n\n get isLightDOM() {\n return this.carouselProperties.lightDOM || this.carouselProperties.loop;\n }\n\n constructor(private carouselProperties: CarouselProperties,\n private utils: any,\n private cells: any,\n private container: any) {\n\n this.init();\n }\n\n updateProperties(carouselProperties: CarouselProperties) {\n this.carouselProperties = carouselProperties;\n this.setVisibleWidth();\n }\n\n init() {\n this.visibleWidth = this.carouselProperties.visibleWidth || this.carouselProperties.hostElement.clientWidth;\n }\n\n handleTouchstart() {\n /* Touchstart event is not called for arrow */\n this.isNotClickOnArrow = true;\n this.isSlideLengthLimited = false;\n\n if (!this.isSlideInProgress) {\n this.initialPositionX = this.container.getCurrentPositionX();\n }\n }\n\n handleTouchend() {\n if (!this.isNotClickOnArrow) {\n return;\n }\n this.currentPositionX = this.container.getCurrentPositionX();\n this.distanceAbs = Math.abs(this.initialPositionX - this.currentPositionX);\n this.distance = this.initialPositionX - this.currentPositionX;\n this.direction = this.getDirection();\n this.isNotClickOnArrow = false;\n this.handleSlide();\n }\n\n handleTransitionend() {\n this.setCounter();\n this.isSlideInProgress = false;\n\n if (this.isLightDOM) {\n this.alignContainerFast();\n }\n }\n\n handleSlide(customSlideLength: number | undefined = undefined) {\n let isUsingButton = customSlideLength;\n let newPositionX;\n\n if (isUsingButton && this.isSlideInProgress || !this.direction) {\n return;\n }\n\n /* Custom slide length is used in arrows */\n if (customSlideLength) {\n this.slideLength = this.limitSlideLength(customSlideLength);\n\n if (!this.isSlideInProgress) {\n this.initialPositionX = this.container.getCurrentPositionX();\n }\n } else {\n this.slideLength = this.getSlideLength(this.distanceAbs);\n }\n\n /* Store intermediate counter value */\n this._counter = this.getPreliminaryCounter();\n\n if (this.direction === 'left') {\n if (!customSlideLength) {\n this.slideLength = this.limitSlideLength(this.getSlideLength(this.distanceAbs));\n }\n\n this._counter = this.getPreliminaryCounter();\n let isSlidesEnd = this.isSlidesEnd(this._counter);\n newPositionX = this.getPositionByIndex(this._counter);\n\n if (isSlidesEnd) {\n this._counter = this.counter;\n\n newPositionX = this.getPositionByIndex(this.counter);\n this.slideLength = 0;\n }\n }\n\n if (this.direction === 'right') {\n if (!customSlideLength) {\n this.slideLength = this.getSlideLength(this.distanceAbs);\n }\n\n if (this._counter < 0) {\n this._counter = this.counter;\n this.slideLength = this.counter;\n }\n\n newPositionX = this.getPositionByIndex(this.counter - this.slideLength);\n }\n\n if (this.container.getCurrentPositionX() !== newPositionX) {\n this.isSlideInProgress = true;\n this.container.transformPositionX(newPositionX);\n }\n }\n\n next(length: number = 1) {\n this.direction = 'left';\n this.handleSlide(length);\n }\n\n prev(length: number = 1) {\n this.direction = 'right';\n this.handleSlide(length);\n }\n\n select(index: number) {\n if (index > this.cellLength - 1) {\n return;\n }\n\n if (index > this.counter) {\n let length = index - this.counter;\n this.next(length);\n }\n\n if (index < this.counter) {\n let length = this.counter - index;\n this.prev(length);\n }\n }\n\n getPreliminaryCounter() {\n if (this.direction === 'left') {\n return this.counter + this.slideLength;\n }\n\n if (this.direction === 'right') {\n return this.counter - this.slideLength;\n }\n\n return 0;\n }\n\n /* \n * Limits the length of the slide during calls to the next() and prev() \n * methods if the specified position is outside the cell length \n */\n limitSlideLength(slideLength: number) {\n if (slideLength > 1) {\n for (var i = 0; i < slideLength; i++) {\n let newCounter = this.counter + (slideLength - i);\n\n if (!this.isSlidesEnd(newCounter)) {\n slideLength = slideLength - i;\n this.isSlideLengthLimited = i > 0;\n break;\n }\n }\n }\n return slideLength;\n }\n\n /* Offset the container to show the last cell completely */\n getPositionCorrection(counter:number) {\n let correction = 0;\n let isLastSlide = this.isLastSlide(counter);\n\n if (this.carouselProperties.loop || this.direction === \"right\") {\n return 0;\n }\n\n if (this.isSlideLengthLimited || isLastSlide) {\n let cellsWidth = this.cells.cellLengthInLightDOMMode * this.fullCellWidth;\n\n if (this.visibleWidth < cellsWidth) {\n correction = -(this.numberOfVisibleCells * this.fullCellWidth - this.visibleWidth - this.margin);\n }\n\n if (correction >= -this.margin) {\n correction = 0;\n }\n }\n\n return correction;\n }\n\n getSlideLength(distanceAbs: number) {\n let isLastSlide = this.isLastSlide(this.counter);\n\n /* If the last cell does not fit entirely, then the \n * length of the swipe to the left, from the extreme \n * right position, may be shorter than usual. \n */\n if (isLastSlide && this.direction === \"right\") {\n distanceAbs = distanceAbs + this.visibleWidth % this.fullCellWidth;\n }\n\n let length = Math.floor(distanceAbs / this.fullCellWidth);\n\n if (distanceAbs % this.fullCellWidth >= this.minSwipeDistance) {\n length++;\n }\n\n return length;\n }\n\n getDistanceAbs() {\n return Math.abs(this.initialPositionX - this.currentPositionX);\n }\n\n getDirection() {\n const direction = Math.sign(this.initialPositionX - this.currentPositionX);\n\n if (direction === -1) {\n return 'right';\n }\n if (direction === 1) {\n return 'left';\n }\n\n return undefined;\n }\n\n isSlidesEnd(counter: number) {\n let margin = this.visibleCellsOverflowContainer ? 1 : 0;\n let imageLength = this.images ? this.images.length : this.cells.cellLength;\n\n if (this.carouselProperties.loop) {\n return false;\n } else {\n return (imageLength - counter + margin) < this.numberOfVisibleCells;\n }\n }\n\n isLastSlide(counter: number) {\n return this.isSlidesEnd(counter + 1)\n }\n\n setCounter() {\n if (this.direction === 'left') {\n this.counter = this.counter + this.slideLength;\n }\n\n if (this.direction === 'right') {\n this.counter = this.counter - this.slideLength;\n }\n }\n\n getPositionByIndex(_counter: number) {\n let correction = this.getPositionCorrection(this.counter + this.slideLength);\n let position;\n\n if (correction !== 0) {\n correction = correction + this.fullCellWidth\n }\n\n if (this.direction === 'right') {\n correction = 0;\n }\n\n if (this.isLightDOM && this.isLightDOMMode(_counter) ||\n this.isLightDOM && this.ifLeftDOMModeAtEnd(_counter)) {\n\n let initialPosition = this.getPositionWithoutCorrection(this.initialPositionX);\n let counterDifference = _counter - this.counter;\n position = initialPosition - ((counterDifference * this.fullCellWidth) - correction);\n } else {\n position = -((_counter * this.fullCellWidth) - correction);\n }\n\n position = this.provideSafePosition(position);\n\n return position;\n }\n\n provideSafePosition(position: number) {\n const endPosition = this.container.getEndPosition();\n\n if (this.direction === 'left') {\n if (position > 0) {\n position = 0;\n }\n }\n\n if (this.direction === 'right') {\n if (position < endPosition) {\n position = endPosition;\n }\n }\n\n return position;\n }\n\n getPositionWithoutCorrection(value: number) {\n let remainder = Math.round(value) % this.fullCellWidth;\n\n if (remainder !== 0) {\n return value - (this.fullCellWidth + remainder);\n } else {\n return value;\n }\n }\n\n isNextArrowDisabled() {\n return this.isLastSlide(this.counter) || \n (!this.visibleCellsOverflowContainer && this.cellLength <= this.numberOfVisibleCells) ||\n (this.visibleCellsOverflowContainer && this.cellLength < this.numberOfVisibleCells)\n }\n\n isPrevArrowDisabled() {\n return this.counter === 0;\n }\n\n alignContainerFast() {\n if (this.isLightDOMMode(this.counter)) {\n let positionX = this.fixedContainerPosition;\n this.container.transformPositionX(positionX, 0);\n\n this.cells.setCounter(this.counter);\n this.cells.lineUp();\n } else if (this.ifLeftDOMModeToBeginning(this.counter)) {\n /* If we have already exited the light DOM mode but \n * the cells are still out of place \n */\n if (this.cells.ifSequenceOfCellsIsChanged()) {\n let positionX = -(this.counter * this.fullCellWidth);\n this.container.transformPositionX(positionX, 0);\n\n this.cells.setCounter(this.counter);\n this.cells.lineUp();\n }\n } else if (this.ifLeftDOMModeAtEnd(this.counter)) {\n let containerPositionX = this.container.getCurrentPositionX();\n let containerWidth = this.container.getWidth();\n this.visibleWidth;\n\n if (this.isLastSlide(this.counter) &&\n containerWidth + containerPositionX >= this.visibleWidth) {\n return;\n }\n\n let correction = this.getPositionCorrection(this.counter);\n\n if (correction !== 0) {\n correction = correction + this.fullCellWidth\n }\n\n if (this.direction === 'right') {\n correction = 0;\n }\n\n let positionX = this.fixedContainerPosition + correction;\n\n this.container.transformPositionX(positionX, 0);\n this.cells.setCounter(this.counter);\n this.cells.lineUp();\n }\n }\n\n isLightDOMMode(counter: number) {\n let flag;\n let remainderOfCells = this.images.length - this.overflowCellsLimit - this.numberOfVisibleCells;\n\n if (!this.isLightDOM) {\n return false;\n }\n\n if (counter > this.overflowCellsLimit && this.direction === \"left\" &&\n counter <= remainderOfCells) {\n flag = true;\n }\n\n if (counter >= this.overflowCellsLimit && this.direction === \"right\" &&\n counter < remainderOfCells) {\n flag = true;\n }\n\n if (this.counter > this.overflowCellsLimit && this.direction === \"left\" &&\n this.counter <= remainderOfCells) {\n flag = true;\n }\n\n if (this.counter >= this.overflowCellsLimit && this.direction === \"right\" &&\n this.counter < remainderOfCells) {\n flag = true;\n }\n\n return flag;\n }\n\n ifLeftDOMModeAtEnd(counter: number) {\n let flag;\n let remainderOfCells = this.images.length - this.overflowCellsLimit - this.numberOfVisibleCells;\n\n if (counter >= remainderOfCells) {\n flag = true;\n }\n\n if (this.counter >= remainderOfCells) {\n flag = true;\n }\n\n return flag;\n }\n\n ifLeftDOMModeToBeginning(counter: number) {\n let flag;\n\n if (counter <= this.overflowCellsLimit) {\n flag = true;\n }\n\n if (this.counter <= this.overflowCellsLimit) {\n flag = true;\n }\n\n return flag;\n }\n\n setVisibleWidth() {\n this.visibleWidth = this.carouselProperties.visibleWidth || this.carouselProperties.hostElement.clientWidth;\n }\n}","import {Properties as CarouselProperties} from './interfaces';\n\nexport class Utils {\n\n get images() {\n return this.carouselProperties.images;\n }\n\n get margin() {\n return this.carouselProperties.margin;\n }\n\n get overflowCellsLimit() {\n if (this.images && this.isImagesLessCellLimit) {\n let overflowCellsLimit = Math.floor((this.images.length - this.numberOfVisibleCells) / 2);\n\n if (overflowCellsLimit < 0) {\n overflowCellsLimit = 0;\n }\n \n return overflowCellsLimit;\n } else {\n return this.carouselProperties.overflowCellsLimit;\n }\n }\n\n get isImagesLessCellLimit() {\n return this.carouselProperties.overflowCellsLimit * 2 + this.numberOfVisibleCells > this.images.length;\n }\n\n get numberOfVisibleCells() {\n return Math.ceil(this.visibleWidth / this.fullCellWidth);\n }\n\n get visibleCellsOverflowContainer() {\n return (this.num