From ab94fff5b5d9beb59fce8275b3d5c8e4f2b77e13 Mon Sep 17 00:00:00 2001 From: NaotoshiFujita Date: Sun, 17 Oct 2021 20:07:04 +0900 Subject: [PATCH] Bug Fix: Prevent scrolling when the active slide gets focus. --- dist/js/splide-renderer.min.js | 2 +- dist/js/splide.cjs.js | 14 +++++++++----- dist/js/splide.esm.js | 14 +++++++++----- dist/js/splide.js | 18 ++++++++++++------ dist/js/splide.js.map | 2 +- dist/js/splide.min.js | 4 ++-- dist/js/splide.min.js.gz | Bin 11208 -> 11233 bytes .../components/Controller/Controller.d.ts.map | 2 +- dist/types/components/Layout/Layout.d.ts.map | 2 +- .../components/Pagination/Pagination.d.ts.map | 2 +- dist/types/utils/dom/index.d.ts | 1 + dist/types/utils/dom/index.d.ts.map | 2 +- package-lock.json | 2 +- package.json | 2 +- src/js/components/Pagination/Pagination.ts | 15 +++++++++++++-- src/js/utils/dom/focus/focus.test.ts | 17 +++++++++++++++++ src/js/utils/dom/focus/focus.ts | 8 ++++++++ src/js/utils/dom/index.ts | 1 + 18 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 src/js/utils/dom/focus/focus.test.ts create mode 100644 src/js/utils/dom/focus/focus.ts diff --git a/dist/js/splide-renderer.min.js b/dist/js/splide-renderer.min.js index 73e9afb2..4ddc7f73 100644 --- a/dist/js/splide-renderer.min.js +++ b/dist/js/splide-renderer.min.js @@ -1,6 +1,6 @@ /*! * Splide.js - * Version : 3.1.6 + * Version : 3.1.7 * License : MIT * Copyright: 2021 Naotoshi Fujita */ diff --git a/dist/js/splide.cjs.js b/dist/js/splide.cjs.js index 714942e9..c0111880 100644 --- a/dist/js/splide.cjs.js +++ b/dist/js/splide.cjs.js @@ -1,6 +1,6 @@ /*! * Splide.js - * Version : 3.1.6 + * Version : 3.1.7 * License : MIT * Copyright: 2021 Naotoshi Fujita */ @@ -200,6 +200,10 @@ function display(elm, display2) { style(elm, "display", display2); } +function focus(elm) { + elm["setActive"] && elm["setActive"]() || elm.focus({ preventScroll: true }); +} + function getAttribute(elm, attr) { return elm.getAttribute(attr); } @@ -1854,12 +1858,12 @@ function Drag(Splide2, Components2, options) { function coordOf(e, orthogonal) { return (isTouchEvent(e) ? e.touches[0] : e)[`page${resolve(orthogonal ? "Y" : "X")}`]; } - function isTouchEvent(e) { - return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; - } function timeOf(e) { return e.timeStamp; } + function isTouchEvent(e) { + return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; + } function constrain(diff) { return diff / (hasExceeded && Splide2.is(SLIDE) ? FRICTION : 1); } @@ -2056,7 +2060,7 @@ function Pagination(Splide2, Components2, options) { function onClick(page) { Controller.go(`>${page}`, true, () => { const Slide = Slides.getAt(Controller.toIndex(page)); - Slide && Slide.slide.focus(); + Slide && focus(Slide.slide); }); } function getAt(index) { diff --git a/dist/js/splide.esm.js b/dist/js/splide.esm.js index d5db45d8..2702e386 100644 --- a/dist/js/splide.esm.js +++ b/dist/js/splide.esm.js @@ -1,6 +1,6 @@ /*! * Splide.js - * Version : 3.1.6 + * Version : 3.1.7 * License : MIT * Copyright: 2021 Naotoshi Fujita */ @@ -196,6 +196,10 @@ function display(elm, display2) { style(elm, "display", display2); } +function focus(elm) { + elm["setActive"] && elm["setActive"]() || elm.focus({ preventScroll: true }); +} + function getAttribute(elm, attr) { return elm.getAttribute(attr); } @@ -1850,12 +1854,12 @@ function Drag(Splide2, Components2, options) { function coordOf(e, orthogonal) { return (isTouchEvent(e) ? e.touches[0] : e)[`page${resolve(orthogonal ? "Y" : "X")}`]; } - function isTouchEvent(e) { - return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; - } function timeOf(e) { return e.timeStamp; } + function isTouchEvent(e) { + return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; + } function constrain(diff) { return diff / (hasExceeded && Splide2.is(SLIDE) ? FRICTION : 1); } @@ -2052,7 +2056,7 @@ function Pagination(Splide2, Components2, options) { function onClick(page) { Controller.go(`>${page}`, true, () => { const Slide = Slides.getAt(Controller.toIndex(page)); - Slide && Slide.slide.focus(); + Slide && focus(Slide.slide); }); } function getAt(index) { diff --git a/dist/js/splide.js b/dist/js/splide.js index d570e791..8299bd41 100644 --- a/dist/js/splide.js +++ b/dist/js/splide.js @@ -4,7 +4,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d /*! * Splide.js - * Version : 3.1.6 + * Version : 3.1.7 * License : MIT * Copyright: 2021 Naotoshi Fujita */ @@ -219,6 +219,12 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d style(elm, "display", display2); } + function focus(elm) { + elm["setActive"] && elm["setActive"]() || elm.focus({ + preventScroll: true + }); + } + function getAttribute(elm, attr) { return elm.getAttribute(attr); } @@ -2237,14 +2243,14 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d return (isTouchEvent(e) ? e.touches[0] : e)["page" + resolve(orthogonal ? "Y" : "X")]; } - function isTouchEvent(e) { - return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; - } - function timeOf(e) { return e.timeStamp; } + function isTouchEvent(e) { + return typeof TouchEvent !== "undefined" && e instanceof TouchEvent; + } + function constrain(diff) { return diff / (hasExceeded && Splide2.is(SLIDE) ? FRICTION : 1); } @@ -2514,7 +2520,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d function onClick(page) { Controller.go(">" + page, true, function () { var Slide = Slides.getAt(Controller.toIndex(page)); - Slide && Slide.slide.focus(); + Slide && focus(Slide.slide); }); } diff --git a/dist/js/splide.js.map b/dist/js/splide.js.map index 66671273..e25b27d4 100644 --- a/dist/js/splide.js.map +++ b/dist/js/splide.js.map @@ -1 +1 @@ -{"version":3,"file":"splide.js","sources":["../../src/js/constants/project.ts","../../src/js/constants/states.ts","../../src/js/constants/priority.ts","../../src/js/utils/array/empty/empty.ts","../../src/js/utils/type/type.ts","../../src/js/utils/array/toArray/toArray.ts","../../src/js/utils/array/forEach/forEach.ts","../../src/js/utils/array/includes/includes.ts","../../src/js/utils/array/push/push.ts","../../src/js/utils/array/index.ts","../../src/js/utils/arrayLike/slice/slice.ts","../../src/js/utils/arrayLike/find/find.ts","../../src/js/utils/dom/toggleClass/toggleClass.ts","../../src/js/utils/dom/addClass/addClass.ts","../../src/js/utils/dom/append/append.ts","../../src/js/utils/dom/before/before.ts","../../src/js/utils/dom/matches/matches.ts","../../src/js/utils/dom/children/children.ts","../../src/js/utils/dom/child/child.ts","../../src/js/utils/object/forOwn/forOwn.ts","../../src/js/utils/object/assign/assign.ts","../../src/js/utils/object/merge/merge.ts","../../src/js/utils/dom/removeAttribute/removeAttribute.ts","../../src/js/utils/dom/setAttribute/setAttribute.ts","../../src/js/utils/dom/create/create.ts","../../src/js/utils/dom/style/style.ts","../../src/js/utils/dom/display/display.ts","../../src/js/utils/dom/getAttribute/getAttribute.ts","../../src/js/utils/dom/hasClass/hasClass.ts","../../src/js/utils/dom/rect/rect.ts","../../src/js/utils/dom/remove/remove.ts","../../src/js/utils/dom/measure/measure.ts","../../src/js/utils/dom/parseHtml/parseHtml.ts","../../src/js/utils/dom/prevent/prevent.ts","../../src/js/utils/dom/query/query.ts","../../src/js/utils/dom/queryAll/queryAll.ts","../../src/js/utils/dom/removeClass/removeClass.ts","../../src/js/utils/dom/unit/unit.ts","../../src/js/utils/error/assert/assert.ts","../../src/js/utils/function/nextTick/nextTick.ts","../../src/js/utils/function/noop/noop.ts","../../src/js/utils/function/raf/raf.ts","../../src/js/utils/math/math/math.ts","../../src/js/utils/math/approximatelyEqual/approximatelyEqual.ts","../../src/js/utils/math/between/between.ts","../../src/js/utils/math/clamp/clamp.ts","../../src/js/utils/math/sign/sign.ts","../../src/js/utils/string/format/format.ts","../../src/js/utils/string/pad/pad.ts","../../src/js/utils/string/uniqueId/uniqueId.ts","../../src/js/constructors/EventBus/EventBus.ts","../../src/js/constants/events.ts","../../src/js/constructors/EventInterface/EventInterface.ts","../../src/js/constructors/RequestInterval/RequestInterval.ts","../../src/js/constructors/State/State.ts","../../src/js/constructors/Throttle/Throttle.ts","../../src/js/components/Options/Options.ts","../../src/js/constants/directions.ts","../../src/js/components/Direction/Direction.ts","../../src/js/constants/classes.ts","../../src/js/components/Elements/Elements.ts","../../src/js/constants/attributes.ts","../../src/js/constants/types.ts","../../src/js/components/Slides/Slide.ts","../../src/js/components/Slides/Slides.ts","../../src/js/components/Layout/Layout.ts","../../src/js/components/Clones/Clones.ts","../../src/js/components/Move/Move.ts","../../src/js/components/Controller/Controller.ts","../../src/js/components/Arrows/path.ts","../../src/js/components/Arrows/Arrows.ts","../../src/js/components/Autoplay/Autoplay.ts","../../src/js/components/Cover/Cover.ts","../../src/js/components/Scroll/constants.ts","../../src/js/components/Scroll/Scroll.ts","../../src/js/components/Drag/constants.ts","../../src/js/components/Drag/Drag.ts","../../src/js/components/Keyboard/Keyboard.ts","../../src/js/components/LazyLoad/constants.ts","../../src/js/components/LazyLoad/LazyLoad.ts","../../src/js/components/Pagination/Pagination.ts","../../src/js/components/Sync/Sync.ts","../../src/js/components/Wheel/Wheel.ts","../../src/js/constants/i18n.ts","../../src/js/constants/defaults.ts","../../src/js/transitions/Fade/Fade.ts","../../src/js/transitions/Slide/Slide.ts","../../src/js/core/Splide/Splide.ts"],"sourcesContent":["/**\r\n * The project code.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const PROJECT_CODE = 'splide';\r\n\r\n/**\r\n * The data attribute prefix.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const DATA_ATTRIBUTE = `data-${ PROJECT_CODE }`;\r\n","/**\r\n * Splide has been just created.\r\n */\r\nexport const CREATED = 1;\r\n\r\n/**\r\n * Splide has mounted components.\r\n */\r\nexport const MOUNTED = 2;\r\n\r\n/**\r\n * Splide is ready.\r\n */\r\nexport const IDLE = 3;\r\n\r\n/**\r\n * Splide is moving.\r\n */\r\nexport const MOVING = 4;\r\n\r\n/**\r\n * Splide has been destroyed.\r\n */\r\nexport const DESTROYED = 5;\r\n\r\n/**\r\n * The collection of all states.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const STATES = {\r\n CREATED,\r\n MOUNTED,\r\n IDLE,\r\n MOVING,\r\n DESTROYED,\r\n};\r\n","/**\r\n * The default priority for internal handlers.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const DEFAULT_EVENT_PRIORITY = 10;\r\n\r\n/**\r\n * The default priority for users' handlers.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const DEFAULT_USER_EVENT_PRIORITY = 20;\r\n","/**\r\n * Empties the array.\r\n *\r\n * @param array - A array to empty.\r\n */\r\nexport function empty( array: any[] ): void {\r\n array.length = 0;\r\n}\r\n","/**\r\n * Checks if the given subject is an object or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is an object, or otherwise `false`.\r\n */\r\nexport function isObject( subject: unknown ): subject is object {\r\n return ! isNull( subject ) && typeof subject === 'object';\r\n}\r\n\r\n/**\r\n * Checks if the given subject is an array or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is an array, or otherwise `false`.\r\n */\r\nexport function isArray( subject: unknown ): subject is T[] {\r\n return Array.isArray( subject );\r\n}\r\n\r\n/**\r\n * Checks if the given subject is a function or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is a function, or otherwise `false`.\r\n */\r\nexport function isFunction( subject: unknown ): subject is ( ...args: any[] ) => any {\r\n return typeof subject === 'function';\r\n}\r\n\r\n/**\r\n * Checks if the given subject is a string or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is a string, or otherwise `false`.\r\n */\r\nexport function isString( subject: unknown ): subject is string {\r\n return typeof subject === 'string';\r\n}\r\n\r\n/**\r\n * Checks if the given subject is `undefined` or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is `undefined`, or otherwise `false`.\r\n */\r\nexport function isUndefined( subject: unknown ): subject is undefined {\r\n return typeof subject === 'undefined';\r\n}\r\n\r\n/**\r\n * Checks if the given subject is `null` or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is `null`, or otherwise `false`.\r\n */\r\nexport function isNull( subject: unknown ): subject is null {\r\n return subject === null;\r\n}\r\n\r\n/**\r\n * Checks if the given subject is an HTMLElement or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is an HTMLElement instance, or otherwise `false`.\r\n */\r\nexport function isHTMLElement( subject: unknown ): subject is HTMLElement {\r\n return subject instanceof HTMLElement;\r\n}\r\n\r\n/**\r\n * Checks if the given subject is an HTMLButtonElement or not.\r\n *\r\n * @param subject - A subject to check.\r\n *\r\n * @return `true` if the subject is an HTMLButtonElement, or otherwise `false`.\r\n */\r\nexport function isHTMLButtonElement( subject: unknown ): subject is HTMLButtonElement {\r\n return subject instanceof HTMLButtonElement;\r\n}\r\n","import { isArray } from '../../type/type';\r\n\r\n\r\n/**\r\n * Push the provided value to an array if the value is not an array.\r\n *\r\n * @param value - A value to push.\r\n *\r\n * @return An array containing the value, or the value itself if it is already an array.\r\n */\r\nexport function toArray( value: T | T[] ): T[] {\r\n return isArray( value ) ? value : [ value ];\r\n}\r\n","import { toArray } from '../toArray/toArray';\r\n\r\n\r\n/**\r\n * The extended `Array#forEach` method that accepts a single value as an argument.\r\n *\r\n * @param values - A value or values to iterate over.\r\n * @param iteratee - An iteratee function.\r\n */\r\nexport function forEach( values: T | T[], iteratee: ( value: T, index: number, array: T[] ) => void ): void {\r\n toArray( values ).forEach( iteratee );\r\n}\r\n","/**\r\n * Checks if the array includes the value or not.\r\n * `Array#includes` is not supported by IE.\r\n *\r\n * @param array - An array.\r\n * @param value - A value to search for.\r\n *\r\n * @return `true` if the array includes the value, or otherwise `false`.\r\n */\r\nexport function includes( array: T[], value: T ): boolean {\r\n return array.indexOf( value ) > -1;\r\n}\r\n","import { toArray } from '../toArray/toArray';\r\n\r\n\r\n/**\r\n * Extended `Array#push()` that accepts an item or an array with items.\r\n *\r\n * @param array - An array to push items.\r\n * @param items - An item or items to push.\r\n *\r\n * @return A provided array itself.\r\n */\r\nexport function push( array: T[], items: T | T[] ): T[] {\r\n array.push( ...toArray( items ) );\r\n return array;\r\n}\r\n","export { empty } from './empty/empty';\r\nexport { forEach } from './forEach/forEach';\r\nexport { includes } from './includes/includes';\r\nexport { push } from './push/push';\r\nexport { toArray } from './toArray/toArray';\r\n\r\nexport const arrayProto = Array.prototype;\r\n","import { arrayProto } from '../../array';\r\n\r\n\r\n/**\r\n * The slice method for an array-like object.\r\n *\r\n * @param arrayLike - An array-like object.\r\n * @param start - Optional. A start index.\r\n * @param end - Optional. A end index.\r\n *\r\n * @return An array with sliced elements.\r\n */\r\nexport function slice( arrayLike: ArrayLike, start?: number, end?: number ): T[] {\r\n return arrayProto.slice.call( arrayLike, start, end );\r\n}\r\n","import { slice } from '../slice/slice';\r\n\r\n\r\n/**\r\n * The find method for an array or array-like object, works in IE.\r\n * This method is not performant for a huge array.\r\n *\r\n * @param arrayLike - An array-like object.\r\n * @param predicate - The predicate function to test each element in the object.\r\n *\r\n * @return A found value if available, or otherwise `undefined`.\r\n */\r\nexport function find(\r\n arrayLike: ArrayLike,\r\n predicate: ( value: T, index: number, array: T[] ) => any\r\n): T | undefined {\r\n return slice( arrayLike ).filter( predicate )[ 0 ];\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Toggles the provided class or classes by following the `add` boolean.\r\n *\r\n * @param elm - An element whose classes are toggled.\r\n * @param classes - A class or class names.\r\n * @param add - Whether to add or remove a class.\r\n */\r\nexport function toggleClass( elm: Element, classes: string | string[], add: boolean ): void {\r\n if ( elm ) {\r\n forEach( classes, name => {\r\n if ( name ) {\r\n elm.classList[ add ? 'add' : 'remove' ]( name );\r\n }\r\n } );\r\n }\r\n}\r\n","import { isString } from '../../type/type';\r\nimport { toggleClass } from '../toggleClass/toggleClass';\r\n\r\n\r\n/**\r\n * Adds classes to the element.\r\n *\r\n * @param elm - An element to add classes to.\r\n * @param classes - Classes to add.\r\n */\r\nexport function addClass( elm: Element, classes: string | string[] ): void {\r\n toggleClass( elm, isString( classes ) ? classes.split( ' ' ) : classes, true );\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Appends children to the parent element.\r\n *\r\n * @param parent - A parent element.\r\n * @param children - A child or children to append to the parent.\r\n */\r\nexport function append( parent: Element, children: Node | Node[] ): void {\r\n forEach( children, parent.appendChild.bind( parent ) );\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Inserts a node or nodes before the specified reference node.\r\n *\r\n * @param nodes - A node or nodes to insert.\r\n * @param ref - A reference node.\r\n */\r\nexport function before( nodes: Node | Node[], ref: Node ): void {\r\n forEach( nodes, node => {\r\n const parent = ref.parentNode;\r\n\r\n if ( parent ) {\r\n parent.insertBefore( node, ref );\r\n }\r\n } );\r\n}\r\n","/**\r\n * Checks if the element can be selected by the provided selector or not.\r\n *\r\n * @param elm - An element to check.\r\n * @param selector - A selector to test.\r\n *\r\n * @return `true` if the selector matches the element, or otherwise `false`.\r\n */\r\nexport function matches( elm: Element, selector: string ): boolean {\r\n return ( elm[ 'msMatchesSelector' ] || elm.matches ).call( elm, selector );\r\n}\r\n","import { slice } from '../../arrayLike';\r\nimport { matches } from '../matches/matches';\r\n\r\n\r\n/**\r\n * Finds children that has the specified tag or class name.\r\n *\r\n * @param parent - A parent element.\r\n * @param selector - A selector to filter children.\r\n *\r\n * @return An array with filtered children.\r\n */\r\nexport function children( parent: HTMLElement, selector: string ): E[] {\r\n return parent ? slice( parent.children ).filter( child => matches( child, selector ) ) as E[] : [];\r\n}\r\n","import { children } from '../children/children';\r\n\r\n\r\n/**\r\n * Returns a child element that matches the specified tag or class name.\r\n *\r\n * @param parent - A parent element.\r\n * @param selector - A selector to filter children.\r\n *\r\n * @return A matched child element if available, or otherwise `undefined`.\r\n */\r\nexport function child( parent: HTMLElement, selector?: string ): E | undefined {\r\n return selector ? children( parent, selector )[ 0 ] : parent.firstElementChild as E;\r\n}\r\n","/**\r\n * Iterates over the provided object by own enumerable keys with calling the iteratee function.\r\n *\r\n * @param object - An object to iterate over.\r\n * @param iteratee - An iteratee function that takes the value and key as arguments.\r\n *\r\n * @return A provided object itself.\r\n */\r\nexport function forOwn(\r\n object: T,\r\n iteratee: ( value: T[ keyof T ], key: string ) => boolean | void\r\n): T {\r\n if ( object ) {\r\n const keys = Object.keys( object );\r\n\r\n for ( let i = 0; i < keys.length; i++ ) {\r\n const key = keys[ i ];\r\n\r\n if ( key !== '__proto__' ) {\r\n if ( iteratee( object[ key ], key ) === false ) {\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return object;\r\n}\r\n","import { slice } from '../../arrayLike';\r\nimport { forOwn } from '../forOwn/forOwn';\r\n\r\n\r\n/**\r\n * Assign U to T.\r\n *\r\n * @typeParam T - An object to assign to.\r\n * @typeParam U - An object to assign.\r\n *\r\n * @return An assigned object type.\r\n */\r\nexport type Assign = Omit & U;\r\n\r\nexport function assign( object: T ): T;\r\n\r\n// There is a way to type arguments recursively, but these fixed definitions are enough for this project.\r\nexport function assign( object: T, source: U ): Assign;\r\n\r\nexport function assign(\r\n object: T, source1: U1, source2: U2\r\n): Assign, U2>;\r\n\r\nexport function assign(\r\n object: T, source1: U1, source2: U2, source3: U3\r\n): Assign, U2>, U3>;\r\n\r\n/**\r\n * Assigns all own enumerable properties of all source objects to the provided object.\r\n * `undefined` in source objects will be skipped.\r\n *\r\n * @param object - An object to assign properties to.\r\n *\r\n * @return An object assigned properties of the sources to.\r\n */\r\nexport function assign( object: T ): any {\r\n // eslint-disable-next-line prefer-rest-params, prefer-spread\r\n slice( arguments, 1 ).forEach( source => {\r\n forOwn( source, ( value, key ) => {\r\n object[ key ] = source[ key ];\r\n } );\r\n } );\r\n\r\n return object;\r\n}\r\n","import { isArray, isObject } from '../../type/type';\r\nimport { forOwn } from '../forOwn/forOwn';\r\n\r\n\r\n/**\r\n * Merges U to T.\r\n *\r\n * @typeParam T - An object to merge to.\r\n * @typeParam U - An object to to.\r\n *\r\n * @return An merged object type.\r\n */\r\nexport type Merge = Omit & {\r\n [ K in ( keyof T & keyof U ) ]: U[ K ] extends object\r\n ? U[ K ] extends any[]\r\n ? T[ K ] extends any[]\r\n ? Array\r\n : U[ K ]\r\n : T[ K ] extends object\r\n ? Merge extends infer A ? Cast : never\r\n : U[ K ]\r\n : U[ K ];\r\n} & Omit;\r\n\r\ntype Cast = T extends U ? T : U;\r\n\r\n/**\r\n * Recursively merges source properties to the object.\r\n * Be aware that this method does not merge arrays. They are just duplicated by `slice()`.\r\n *\r\n * @param object - An object to merge properties to.\r\n * @param source - A source object to merge properties from.\r\n *\r\n * @return A new object with merged properties.\r\n */\r\nexport function merge( object: T, source: U ): Merge {\r\n forOwn( source, ( value, key ) => {\r\n if ( isArray( value ) ) {\r\n object[ key ] = value.slice();\r\n } else if ( isObject( value ) ) {\r\n object[ key ] = merge( isObject( object[ key ] ) ? object[ key ] : {}, value );\r\n } else {\r\n object[ key ] = value;\r\n }\r\n } );\r\n\r\n return object as Merge;\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Removes attributes from the element.\r\n *\r\n * @param elm - An element.\r\n * @param attrs - An attribute or attributes to remove.\r\n */\r\nexport function removeAttribute( elm: Element, attrs: string | string[] ): void {\r\n if ( elm ) {\r\n forEach( attrs, attr => {\r\n elm.removeAttribute( attr );\r\n } );\r\n }\r\n}\r\n","import { forOwn } from '../../object';\r\nimport { isNull, isObject } from '../../type/type';\r\nimport { removeAttribute } from '../removeAttribute/removeAttribute';\r\n\r\n\r\nexport function setAttribute( elm: Element, attr: string, value: string | number | boolean ): void;\r\nexport function setAttribute( elm: Element, attrs: Record ): void;\r\n\r\nexport function setAttribute(\r\n elm: Element,\r\n attrs: string | Record,\r\n value?: string | number | boolean\r\n): void {\r\n if ( isObject( attrs ) ) {\r\n forOwn( attrs, ( value, name ) => {\r\n setAttribute( elm, name, value );\r\n } );\r\n } else {\r\n isNull( value ) ? removeAttribute( elm, attrs ) : elm.setAttribute( attrs, String( value ) );\r\n }\r\n}\r\n","import { isString } from '../../type/type';\r\nimport { addClass } from '../addClass/addClass';\r\nimport { append } from '../append/append';\r\nimport { setAttribute } from '../setAttribute/setAttribute';\r\n\r\n\r\nexport function create(\r\n tag: K,\r\n attrs?: Record | string | string[],\r\n parent?: HTMLElement\r\n): HTMLElementTagNameMap[ K ];\r\n\r\nexport function create(\r\n tag: string,\r\n attrs?: Record | string | string[],\r\n parent?: HTMLElement\r\n): HTMLElement;\r\n\r\n/**\r\n * Creates a HTML element.\r\n *\r\n * @param tag - A tag name.\r\n * @param attrs - Optional. An object with attributes to apply the created element to, or a string with classes.\r\n * @param parent - Optional. A parent element where the created element is appended.\r\n */\r\nexport function create(\r\n tag: K,\r\n attrs?: Record | string,\r\n parent?: HTMLElement\r\n): HTMLElementTagNameMap[ K ] {\r\n const elm = document.createElement( tag );\r\n\r\n if ( attrs ) {\r\n isString( attrs ) ? addClass( elm, attrs ) : setAttribute( elm, attrs );\r\n }\r\n\r\n parent && append( parent, elm );\r\n\r\n return elm;\r\n}\r\n","import { isNull, isUndefined } from '../../type/type';\n\n\nexport function style(\n elm: HTMLElement,\n prop: K,\n): CSSStyleDeclaration[ K ];\n\nexport function style(\n elm: HTMLElement,\n prop: string,\n): string;\n\nexport function style(\n elm: HTMLElement,\n prop: string,\n value: string | number\n): void;\n\n\n/**\n * Applies inline styles to the provided element by an object literal.\n *\n * @param elm - An element to apply styles to.\n * @param prop - An object literal with styles or a property name.\n * @param value - A value to set.\n */\nexport function style(\n elm: HTMLElement,\n prop: string,\n value?: string | number\n): string | void {\n if ( isUndefined( value ) ) {\n return getComputedStyle( elm )[ prop ];\n }\n\n if ( ! isNull( value ) ) {\n const { style } = elm;\n value = `${ value }`;\n\n if ( style[ prop ] !== value ) {\n style[ prop ] = value;\n }\n }\n}\n","import { style } from '../style/style';\n\n\n/**\n * Sets the `display` CSS value to the element.\n *\n * @param elm - An element to set a new value to.\n * @param display - A new `display` value.\n */\nexport function display( elm: HTMLElement, display: string ): void {\n style( elm, 'display', display );\n}\n","/**\r\n * Returns the specified attribute value.\r\n *\r\n * @param elm - An element.\r\n * @param attr - An attribute to get.\r\n */\r\nexport function getAttribute( elm: Element, attr: string ): string {\r\n return elm.getAttribute( attr );\r\n}\r\n","/**\r\n * Checks if the element contains the specified class or not.\r\n *\r\n * @param elm - An element to check.\r\n * @param className - A class name that may be contained by the element.\r\n *\r\n * @return `true` if the element contains the class, or otherwise `false`.\r\n */\r\nexport function hasClass( elm: Element, className: string ): boolean {\r\n return elm && elm.classList.contains( className );\r\n}\r\n","/**\r\n * Returns a DOMRect object of the provided element.\r\n *\r\n * @param target - An element.\r\n */\r\nexport function rect( target: Element ): DOMRect {\r\n return target.getBoundingClientRect();\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Removes the provided node from its parent.\r\n *\r\n * @param nodes - A node or nodes to remove.\r\n */\r\nexport function remove( nodes: Node | Node[] ): void {\r\n forEach( nodes, node => {\r\n if ( node && node.parentNode ) {\r\n node.parentNode.removeChild( node );\r\n }\r\n } );\r\n}\r\n","import { isString } from '../../type/type';\r\nimport { create } from '../create/create';\r\nimport { rect } from '../rect/rect';\r\nimport { remove } from '../remove/remove';\r\n\r\n\r\n/**\r\n * Attempts to convert the provided value to pixel as the relative value to the parent element.\r\n *\r\n * @param parent - A parent element.\r\n * @param value - A value to convert.\r\n *\r\n * @return A converted value in pixel. Unhandled values will become 0.\r\n */\r\nexport function measure( parent: HTMLElement, value: number | string ): number {\r\n if ( isString( value ) ) {\r\n const div = create( 'div', { style: `width: ${ value }; position: absolute;` }, parent );\r\n value = rect( div ).width;\r\n remove( div );\r\n }\r\n\r\n return value;\r\n}\r\n","import { child } from '../child/child';\r\n\r\n\r\n/**\r\n * Parses the provided HTML string and returns the first element.\r\n *\r\n * @param html - An HTML string to parse.\r\n *\r\n * @return An Element on success, or otherwise `undefined`.\r\n */\r\nexport function parseHtml( html: string ): E | undefined {\r\n return child( new DOMParser().parseFromString( html, 'text/html' ).body );\r\n}\r\n","/**\r\n * Call the `preventDefault()` of the provided event.\r\n *\r\n * @param e - An Event object.\r\n * @param stopPropagation - Optional. Whether to stop the event propagation or not.\r\n */\r\nexport function prevent( e: Event, stopPropagation?: boolean ): void {\r\n e.preventDefault();\r\n\r\n if ( stopPropagation ) {\r\n e.stopPropagation();\r\n e.stopImmediatePropagation();\r\n }\r\n}\r\n","/**\r\n * Returns an element that matches the provided selector.\r\n *\r\n * @param parent - A parent element to start searching from.\r\n * @param selector - A selector to query.\r\n *\r\n * @return A found element or `null`.\r\n */\r\nexport function query( parent: Element | Document, selector: string ): E | null {\r\n return parent && parent.querySelector( selector );\r\n}\r\n","import { slice } from '../../arrayLike';\r\n\r\n\r\n/**\r\n * Returns elements that match the provided selector.\r\n *\r\n * @param parent - A parent element to start searching from.\r\n * @param selector - A selector to query.\r\n *\r\n * @return An array with matched elements.\r\n */\r\nexport function queryAll( parent: Element | Document, selector: string ): E[] {\r\n return slice( parent.querySelectorAll( selector ) );\r\n}\r\n","import { toggleClass } from '../toggleClass/toggleClass';\r\n\r\n\r\n/**\r\n * Removes classes from the element.\r\n *\r\n * @param elm - An element to remove classes from.\r\n * @param classes - Classes to remove.\r\n */\r\nexport function removeClass( elm: Element, classes: string | string[] ): void {\r\n toggleClass( elm, classes, false );\r\n}\r\n","import { isString } from '../../type/type';\r\n\r\n\r\n/**\r\n * Appends `px` to the provided number.\r\n * If the value is already string, just returns it.\r\n *\r\n * @param value - A value to append `px` to.\r\n *\r\n * @return A string with the CSS unit.\r\n */\r\nexport function unit( value: number | string ): string {\r\n return isString( value ) ? value : value ? `${ value }px` : '';\r\n}\r\n","import { PROJECT_CODE } from '../../../constants/project';\r\n\r\n\r\n/**\r\n * Throws an error if the provided condition is falsy.\r\n *\r\n * @param condition - If falsy, an error is thrown.\r\n * @param message - Optional. A message to display.\r\n */\r\nexport function assert( condition: any, message = '' ): void {\r\n if ( ! condition ) {\r\n throw new Error( `[${ PROJECT_CODE }] ${ message }` );\r\n }\r\n}\r\n","import { AnyFunction } from '../../../types';\r\n\r\n\r\n/**\r\n * Invokes the callback on the next tick.\r\n *\r\n * @param callback - A callback function.\r\n */\r\nexport function nextTick( callback: AnyFunction ): void {\r\n setTimeout( callback );\r\n}\r\n","/**\r\n * No operation.\r\n */\r\nexport const noop = (): void => {}; // eslint-disable-line no-empty-function, @typescript-eslint/no-empty-function\r\n","/**\r\n * The arias of `window.requestAnimationFrame()`.\r\n */\r\nexport function raf( func: FrameRequestCallback ): number {\r\n return requestAnimationFrame( func );\r\n}\r\n","export const { min, max, floor, ceil, abs } = Math;\r\n","import { abs } from '../math/math';\r\n\r\n\r\n/**\r\n * Checks if the provided 2 numbers are approximately equal or not.\r\n *\r\n * @param x - A number.\r\n * @param y - Another number to compare.\r\n * @param epsilon - An accuracy that defines the approximation.\r\n *\r\n * @return `true` if 2 numbers are considered to be equal, or otherwise `false`.\r\n */\r\nexport function approximatelyEqual( x: number, y: number, epsilon: number ): boolean {\r\n return abs( x - y ) < epsilon;\r\n}\r\n","import { max, min } from '../math/math';\r\n\r\n\r\n/**\r\n * Checks if the subject number is between `minOrMax` and `maxOrMin`.\r\n *\r\n * @param number - A subject number to check.\r\n * @param minOrMax - A min or max number.\r\n * @param maxOrMin - A max or min number.\r\n * @param exclusive - Optional. Whether to exclude `x` or `y`.\r\n */\r\nexport function between( number: number, minOrMax: number, maxOrMin: number, exclusive?: boolean ): boolean {\r\n const minimum = min( minOrMax, maxOrMin );\r\n const maximum = max( minOrMax, maxOrMin );\r\n return exclusive ? minimum < number && number < maximum : minimum <= number && number <= maximum;\r\n}\r\n","import { max, min } from '../math/math';\r\n\r\n\r\n/**\r\n * Clamps a number.\r\n *\r\n * @param number - A subject number to check.\r\n * @param x - A min or max number.\r\n * @param y - A min or max number.\r\n */\r\nexport function clamp( number: number, x: number, y: number ): number {\r\n const minimum = min( x, y );\r\n const maximum = max( x, y );\r\n return min( max( minimum, number ), maximum );\r\n}\r\n","/**\r\n * Returns the sign of the provided number.\r\n *\r\n * @param x - A number.\r\n *\r\n * @return `1` for positive numbers, `-1` for negative numbers, or `0` for `0`.\r\n */\r\nexport function sign( x: number ): number {\r\n return +( x > 0 ) - +( x < 0 );\r\n}\r\n","import { forEach } from '../../array';\r\n\r\n\r\n/**\r\n * Formats a string.\r\n *\r\n * @param string - A string to format.\r\n * @param replacements - A replacement or replacements.\r\n *\r\n * @return A formatted string.\r\n */\r\nexport function format( string: string, replacements: string | number | Array ): string {\r\n forEach( replacements, replacement => {\r\n string = string.replace( '%s', `${ replacement }` );\r\n } );\r\n\r\n return string;\r\n}\r\n","/**\r\n * Pads the number with 0.\r\n *\r\n * @param number - A number to pad.\r\n *\r\n * @return string - Padded number.\r\n */\r\nexport function pad( number: number ): string {\r\n return number < 10 ? `0${ number }` : `${ number }`;\r\n}\r\n","import { pad } from '../pad/pad';\r\n\r\n\r\n/**\r\n * Stores unique IDs.\r\n *\r\n * @since 3.0.0\r\n */\r\nconst ids: Record = {};\r\n\r\n/**\r\n * Returns a sequential unique ID as \"{ prefix }-{ number }\".\r\n *\r\n * @param prefix - A prefix for the ID.\r\n */\r\nexport function uniqueId( prefix: string ): string {\r\n return `${ prefix }${ pad( ( ids[ prefix ] = ( ids[ prefix ] || 0 ) + 1 ) ) }`;\r\n}\r\n","import { DEFAULT_EVENT_PRIORITY } from '../../constants/priority';\nimport { AnyFunction } from '../../types';\nimport { forOwn, push, slice, toArray } from '../../utils';\n\n\n/**\n * The interface for the EventBus instance.\n *\n * @since 3.0.0\n */\nexport interface EventBusObject {\n on( events: string | string[], callback: EventBusCallback, key?: object, priority?: number ): void;\n off( events: string | string[], key?: object ): void;\n offBy( key: object ): void;\n emit( event: string, ...args: any[] ): void;\n destroy(): void;\n}\n\n/**\n * The interface for each event handler object.\n *\n * @since 3.0.0\n */\nexport interface EventHandler {\n _event: string;\n _callback: AnyFunction;\n _namespace: string;\n _priority: number;\n _key?: object;\n}\n\n/**\n * The type for a callback function of the EventBus.\n *\n * @since 3.0.0\n */\nexport type EventBusCallback = AnyFunction;\n\n/**\n * The constructor to provided a simple event system.\n *\n * @since 3.0.0\n *\n * @return An EventBus object.\n */\nexport function EventBus(): EventBusObject {\n /**\n * The collection of registered handlers.\n */\n let handlers: Record = {};\n\n /**\n * Registers an event handler.\n *\n * @param events - An event name or names separated by spaces. Use a dot(.) to add a namespace.\n * @param callback - A callback function to register.\n * @param key - Optional. An object for an identifier of the handler.\n * @param priority - Optional. A priority number for the order in which the callbacks are invoked.\n * Lower numbers correspond with earlier execution. The default value is 10.\n */\n function on(\n events: string | string[],\n callback: EventBusCallback,\n key?: object,\n priority = DEFAULT_EVENT_PRIORITY\n ): void {\n forEachEvent( events, ( event, namespace ) => {\n handlers[ event ] = handlers[ event ] || [];\n\n push( handlers[ event ], {\n _event : event,\n _callback : callback,\n _namespace: namespace,\n _priority : priority,\n _key : key,\n } ).sort( ( handler1, handler2 ) => handler1._priority - handler2._priority );\n } );\n }\n\n /**\n * Removes event handlers registered by `on()`.\n * If only the event name is provided, all handlers that associate with the event are removed.\n * If the event name and namespace are specified, handlers that associate with the event and namespace are removed.\n *\n * @param events - An event name or names separated by spaces. Use a dot(.) to add a namespace.\n * @param key - Optional. An object for an identifier of the handler.\n */\n function off( events: string | string[], key?: object ): void {\n forEachEvent( events, ( event, namespace ) => {\n const eventHandlers = handlers[ event ];\n\n handlers[ event ] = eventHandlers && eventHandlers.filter( handler => {\n return handler._key ? handler._key !== key : key || handler._namespace !== namespace;\n } );\n } );\n }\n\n /**\n * Removes all handlers locked by the specified key.\n *\n * @param key - A key.\n */\n function offBy( key: object ): void {\n forOwn( handlers, ( eventHandlers, event ) => {\n off( event, key );\n } );\n }\n\n /**\n * Triggers callback functions.\n * This accepts additional arguments and passes them to callbacks.\n *\n * @param event - An event name.\n */\n function emit( event: string ): void {\n ( handlers[ event ] || [] ).forEach( handler => {\n // eslint-disable-next-line prefer-rest-params, prefer-spread\n handler._callback.apply( handler, slice( arguments, 1 ) );\n } );\n }\n\n /**\n * Removes all handlers.\n */\n function destroy(): void {\n handlers = {};\n }\n\n /**\n * Parses provided events and iterates over them.\n *\n * @param events - An event or events.\n * @param iteratee - An iteratee function.\n */\n function forEachEvent( events: string | string[], iteratee: ( event: string, namespace: string ) => void ): void {\n toArray( events ).join( ' ' ).split( ' ' ).forEach( eventNS => {\n const fragments = eventNS.split( '.' );\n iteratee( fragments[ 0 ], fragments[ 1 ] );\n } );\n }\n\n return {\n on,\n off,\n offBy,\n emit,\n destroy,\n };\n}\n","export const EVENT_MOUNTED = 'mounted';\r\nexport const EVENT_READY = 'ready';\r\nexport const EVENT_MOVE = 'move';\r\nexport const EVENT_MOVED = 'moved';\r\nexport const EVENT_CLICK = 'click';\r\nexport const EVENT_ACTIVE = 'active';\r\nexport const EVENT_INACTIVE = 'inactive';\r\nexport const EVENT_VISIBLE = 'visible';\r\nexport const EVENT_HIDDEN = 'hidden';\r\nexport const EVENT_SLIDE_KEYDOWN = 'slide:keydown';\r\nexport const EVENT_REFRESH = 'refresh';\r\nexport const EVENT_UPDATED = 'updated';\r\nexport const EVENT_RESIZE = 'resize';\r\nexport const EVENT_RESIZED = 'resized';\r\nexport const EVENT_REPOSITIONED = 'repositioned';\r\nexport const EVENT_DRAG = 'drag';\r\nexport const EVENT_DRAGGING = 'dragging';\r\nexport const EVENT_DRAGGED = 'dragged';\r\nexport const EVENT_SCROLL = 'scroll';\r\nexport const EVENT_SCROLLED = 'scrolled';\r\nexport const EVENT_DESTROY = 'destroy';\r\nexport const EVENT_ARROWS_MOUNTED = 'arrows:mounted';\r\nexport const EVENT_ARROWS_UPDATED = 'arrows:updated';\r\nexport const EVENT_PAGINATION_MOUNTED = 'pagination:mounted';\r\nexport const EVENT_PAGINATION_UPDATED = 'pagination:updated';\r\nexport const EVENT_NAVIGATION_MOUNTED = 'navigation:mounted';\r\nexport const EVENT_AUTOPLAY_PLAY = 'autoplay:play';\r\nexport const EVENT_AUTOPLAY_PLAYING = 'autoplay:playing';\r\nexport const EVENT_AUTOPLAY_PAUSE = 'autoplay:pause';\r\nexport const EVENT_LAZYLOAD_LOADED = 'lazyload:loaded';\r\n\r\n","import { EVENT_DESTROY } from '../../constants/events';\nimport { Splide } from '../../core/Splide/Splide';\nimport { AnyFunction, EventMap } from '../../types';\nimport { forEach } from '../../utils';\nimport { EventBusCallback } from '../EventBus/EventBus';\n\n\n/**\n * The interface for the EventInterface object.\n *\n * @since 3.0.0\n */\nexport interface EventInterfaceObject {\n on( event: K, callback: EventMap[ K ], priority?: number ): void;\n on( events: string | string[], callback: EventBusCallback, priority?: number ): void;\n off( events: K | K[] | string | string[] ): void;\n emit( event: K, ...args: Parameters ): void\n emit( event: string, ...args: any[] ): void;\n bind(\n target: Element | Window | Document | Array,\n events: string,\n callback: AnyFunction,\n options?: AddEventListenerOptions\n ): void\n unbind(\n target: Element | Window | Document | Array,\n events: string,\n callback?: AnyFunction,\n ): void;\n destroy(): void;\n}\n\n/**\n * The type for event targets.\n *\n * @since 3.0.0\n */\ntype EventTarget = Element | Window | Document;\n\n/**\n * The function that provides interface for internal and native events.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n *\n * @return A collection of interface functions.\n */\nexport function EventInterface( Splide: Splide ): EventInterfaceObject {\n /**\n * Holds the event object.\n */\n const { event } = Splide;\n\n /**\n * The key for events.\n */\n const key = {};\n\n /**\n * Stores all handlers that listen to native events.\n */\n let listeners: [ EventTarget, string, AnyFunction, AddEventListenerOptions? ][] = [];\n\n /**\n * Registers an event handler with an unique key.\n * It can only be removed by `off()` method below.\n *\n * @param events - An event name or names separated by spaces. Use a dot(.) to add a namespace.\n * @param callback - A callback function to register.\n * @param priority - Optional. A priority number for the order in which the callbacks are invoked.\n * Lower numbers correspond with earlier execution. The default value is 10.\n */\n function on( events: string | string[], callback: EventBusCallback, priority?: number ): void {\n event.on( events, callback, key, priority );\n }\n\n /**\n * Removes event handlers registered by `on()`.\n *\n * @param events - An event name or names separated by spaces. Use a dot(.) to add a namespace.\n */\n function off( events: string | string[] ): void {\n event.off( events, key );\n }\n\n /**\n * Listens to native events.\n * Splide#destory() will remove all registered listeners.\n *\n * @param targets - A target element, the window object or the document object.\n * @param events - An event or events to listen to.\n * @param callback - A callback function.\n * @param options - Optional. The options to pass to the `addEventListener` function.\n */\n function bind(\n targets: EventTarget | EventTarget[],\n events: string,\n callback: AnyFunction,\n options?: AddEventListenerOptions\n ): void {\n forEachEvent( targets, events, ( target, event ) => {\n listeners.push( [ target, event, callback, options ] );\n target.addEventListener( event, callback, options );\n } );\n }\n\n /**\n * Removes the event handler.\n *\n * @param targets - A target element, the window object or the document object.\n * @param events - An event name or names to remove.\n * @param callback - Optional. Specify the callback to remove.\n */\n function unbind( targets: EventTarget | EventTarget[], events: string, callback?: AnyFunction ): void {\n forEachEvent( targets, events, ( target, event ) => {\n listeners = listeners.filter( listener => {\n if ( listener[ 0 ] === target && listener[ 1 ] === event && ( ! callback || listener[ 2 ] === callback ) ) {\n target.removeEventListener( event, listener[ 2 ], listener[ 3 ] );\n return false;\n }\n\n return true;\n } );\n } );\n }\n\n /**\n * Iterates over each target and event.\n *\n * @param targets - A target element, the window object or the document object.\n * @param events - An event name or names.\n * @param iteratee - An iteratee function.\n */\n function forEachEvent(\n targets: EventTarget | EventTarget[],\n events: string,\n iteratee: ( target: EventTarget, event: string ) => void\n ): void {\n forEach( targets, target => {\n if ( target ) {\n events.split( ' ' ).forEach( iteratee.bind( null, target ) );\n }\n } );\n }\n\n /**\n * Removes all listeners.\n */\n function destroy(): void {\n listeners = listeners.filter( data => unbind( data[ 0 ], data[ 1 ] ) );\n event.offBy( key );\n }\n\n /**\n * Invokes destroy when the slider is destroyed.\n */\n event.on( EVENT_DESTROY, destroy, key );\n\n return {\n on,\n off,\n emit: event.emit,\n bind,\n unbind,\n destroy,\n };\n}\n","import { raf } from '../../utils';\r\n\r\n\r\n/**\r\n * The interface for the returning value of the RequestInterval.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface RequestIntervalInterface {\r\n start( resume?: boolean ): void;\r\n pause(): void;\r\n rewind(): void;\r\n cancel(): void;\r\n isPaused(): boolean;\r\n}\r\n\r\n/**\r\n * Requests interval like the native `setInterval()` with using `requestAnimationFrame`.\r\n *\r\n * @since 3.0.0\r\n *\r\n * @param interval - The interval duration in milliseconds.\r\n * @param onInterval - The callback fired on every interval.\r\n * @param onUpdate - Optional. Called on every animation frame, taking the progress rate.\r\n * @param limit - Optional. Limits the number of interval.\r\n */\r\nexport function RequestInterval(\r\n interval: number,\r\n onInterval: () => void,\r\n onUpdate?: ( rate: number ) => void,\r\n limit?: number\r\n): RequestIntervalInterface {\r\n const { now } = Date;\r\n\r\n /**\r\n * The time when the interval starts.\r\n */\r\n let startTime: number;\r\n\r\n /**\r\n * The current progress rate.\r\n */\r\n let rate = 0;\r\n\r\n /**\r\n * The animation frame ID.\r\n */\r\n let id: number;\r\n\r\n /**\r\n * Indicates whether the interval is currently paused or not.\r\n */\r\n let paused = true;\r\n\r\n /**\r\n * The loop count. This only works when the `limit` argument is provided.\r\n */\r\n let count = 0;\r\n\r\n /**\r\n * The update function called on every animation frame.\r\n */\r\n function update(): void {\r\n if ( ! paused ) {\r\n const elapsed = now() - startTime;\r\n\r\n if ( elapsed >= interval ) {\r\n rate = 1;\r\n startTime = now();\r\n } else {\r\n rate = elapsed / interval;\r\n }\r\n\r\n if ( onUpdate ) {\r\n onUpdate( rate );\r\n }\r\n\r\n if ( rate === 1 ) {\r\n onInterval();\r\n\r\n if ( limit && ++count >= limit ) {\r\n return pause();\r\n }\r\n }\r\n\r\n raf( update );\r\n }\r\n }\r\n\r\n /**\r\n * Starts the interval.\r\n *\r\n * @param resume - Optional. Whether to resume the paused progress or not.\r\n */\r\n function start( resume?: boolean ): void {\r\n ! resume && cancel();\r\n startTime = now() - ( resume ? rate * interval : 0 );\r\n paused = false;\r\n raf( update );\r\n }\r\n\r\n /**\r\n * Pauses the interval.\r\n */\r\n function pause(): void {\r\n paused = true;\r\n }\r\n\r\n /**\r\n * Rewinds the current progress.\r\n */\r\n function rewind(): void {\r\n startTime = now();\r\n rate = 0;\r\n\r\n if ( onUpdate ) {\r\n onUpdate( rate );\r\n }\r\n }\r\n\r\n /**\r\n * Cancels the interval.\r\n */\r\n function cancel() {\r\n cancelAnimationFrame( id );\r\n rate = 0;\r\n id = 0;\r\n paused = true;\r\n }\r\n\r\n /**\r\n * Checks if the interval is paused or not.\r\n *\r\n * @return `true` if the interval is paused, or otherwise `false`.\r\n */\r\n function isPaused(): boolean {\r\n return paused;\r\n }\r\n\r\n return {\r\n start,\r\n rewind,\r\n pause,\r\n cancel,\r\n isPaused,\r\n };\r\n}\r\n","import { includes, toArray } from '../../utils';\r\n\r\n\r\n/**\r\n * The interface for the State object.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface StateObject {\r\n set( state: number ): void;\r\n is( states: number | number[] ): boolean;\r\n}\r\n\r\n/**\r\n * The function providing a super simple state system.\r\n *\r\n * @param initialState - Specifies the initial state.\r\n */\r\nexport function State( initialState: number ): StateObject {\r\n /**\r\n * The current state.\r\n */\r\n let state = initialState;\r\n\r\n /**\r\n * Sets a new state.\r\n *\r\n * @param value - A new state value.\r\n */\r\n function set( value: number ): void {\r\n state = value;\r\n }\r\n\r\n /**\r\n * Checks if the current state matches the provided one.\r\n *\r\n * @param states - A state to check.\r\n *\r\n * @return `true` if the current state is the provided one.\r\n */\r\n function is( states: number | number[] ): boolean {\r\n return includes( toArray( states ), state );\r\n }\r\n\r\n return { set, is };\r\n}\r\n","import { AnyFunction } from '../../types';\r\nimport { RequestInterval, RequestIntervalInterface } from '../RequestInterval/RequestInterval';\r\n\r\n\r\n/**\r\n * The interface for the returning value of the RequestInterval.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface ThrottleInstance extends Function {\r\n ( ...args: Parameters ): void;\r\n}\r\n\r\n/**\r\n * Returns the throttled function.\r\n *\r\n * @param func - A function to throttle.\r\n * @param duration - Optional. Throttle duration in milliseconds.\r\n *\r\n * @return A throttled function.\r\n */\r\nexport function Throttle(\r\n func: F,\r\n duration?: number\r\n): ThrottleInstance {\r\n let interval: RequestIntervalInterface;\r\n\r\n function throttled( this: ThisParameterType ): void {\r\n if ( ! interval ) {\r\n interval = RequestInterval( duration || 0, () => {\r\n // eslint-disable-next-line prefer-rest-params\r\n func.apply( this, arguments );\r\n interval = null;\r\n }, null, 1 );\r\n\r\n interval.start();\r\n }\r\n }\r\n\r\n return throttled;\r\n}\r\n","import { DATA_ATTRIBUTE } from '../../constants/project';\r\nimport { DESTROYED } from '../../constants/states';\r\nimport { Throttle } from '../../constructors';\r\nimport { Splide } from '../../core/Splide/Splide';\r\nimport { BaseComponent, Components, Options } from '../../types';\r\nimport { assert, find, getAttribute, merge } from '../../utils';\r\n\r\n\r\n/**\r\n * The interface for the Options component.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface OptionsComponent extends BaseComponent {\r\n}\r\n\r\n/**\r\n * The component for managing options.\r\n *\r\n * @since 3.0.0\r\n *\r\n * @param Splide - A Splide instance.\r\n * @param Components - A collection of components.\r\n * @param options - Options.\r\n *\r\n * @return An Options component object.\r\n */\r\nexport function Options( Splide: Splide, Components: Components, options: Options ): OptionsComponent {\r\n /**\r\n * The throttled `observe` function.\r\n */\r\n const throttledObserve = Throttle( observe );\r\n\r\n /**\r\n * Keeps the initial options to apply when no matched query exists.\r\n */\r\n let initialOptions: Options;\r\n\r\n /**\r\n * Stores breakpoints with the MediaQueryList object.\r\n */\r\n let points: [ string, MediaQueryList ][];\r\n\r\n /**\r\n * Holds the current breakpoint.\r\n */\r\n let currPoint: string | undefined;\r\n\r\n /**\r\n * Called when the component is constructed.\r\n */\r\n function setup(): void {\r\n try {\r\n merge( options, JSON.parse( getAttribute( Splide.root, DATA_ATTRIBUTE ) ) );\r\n } catch ( e ) {\r\n assert( false, e.message );\r\n }\r\n\r\n initialOptions = merge( {}, options );\r\n\r\n const { breakpoints } = options;\r\n\r\n if ( breakpoints ) {\r\n const isMin = options.mediaQuery === 'min';\r\n\r\n points = Object.keys( breakpoints )\r\n .sort( ( n, m ) => isMin ? +m - +n : +n - +m )\r\n .map( point => [\r\n point,\r\n matchMedia( `(${ isMin ? 'min' : 'max' }-width:${ point }px)` ),\r\n ] );\r\n\r\n observe();\r\n }\r\n }\r\n\r\n /**\r\n * Called when the component is mounted.\r\n */\r\n function mount(): void {\r\n if ( points ) {\r\n addEventListener( 'resize', throttledObserve );\r\n }\r\n }\r\n\r\n /**\r\n * Destroys the component.\r\n *\r\n * @param completely - Will be `true` for complete destruction.\r\n */\r\n function destroy( completely: boolean ): void {\r\n if ( completely ) {\r\n removeEventListener( 'resize', throttledObserve );\r\n }\r\n }\r\n\r\n /**\r\n * Observes breakpoints.\r\n * The `currPoint` may be `undefined`.\r\n */\r\n function observe(): void {\r\n const item = find( points, item => item[ 1 ].matches ) || [];\r\n\r\n if ( item[ 0 ] !== currPoint ) {\r\n onMatch( ( currPoint = item[ 0 ] ) );\r\n }\r\n }\r\n\r\n /**\r\n * Called when the media query matches breakpoints.\r\n *\r\n * @param point - A matched point, or `undefined` that means no breakpoint matches a media query.\r\n */\r\n function onMatch( point: string | undefined ): void {\r\n const newOptions = options.breakpoints[ point ] || initialOptions;\r\n\r\n if ( newOptions.destroy ) {\r\n Splide.options = initialOptions;\r\n Splide.destroy( newOptions.destroy === 'completely' );\r\n } else {\r\n if ( Splide.state.is( DESTROYED ) ) {\r\n destroy( true );\r\n Splide.mount();\r\n }\r\n\r\n Splide.options = newOptions;\r\n }\r\n }\r\n\r\n return {\r\n setup,\r\n mount,\r\n destroy,\r\n };\r\n}\r\n","/**\r\n * Enumerates slides from left to right.\r\n */\r\nexport const LTR = 'ltr';\r\n\r\n/**\r\n * Enumerates slides from right to left.\r\n */\r\nexport const RTL = 'rtl';\r\n\r\n/**\r\n * Enumerates slides in a col.\r\n */\r\nexport const TTB = 'ttb';\r\n","import { RTL, TTB } from '../../constants/directions';\r\nimport { Splide } from '../../core/Splide/Splide';\r\nimport { BaseComponent, Components, Options } from '../../types';\r\n\r\n\r\n/**\r\n * The interface for the Direction component.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface DirectionComponent extends BaseComponent {\r\n resolve( prop: string, axisOnly?: boolean ): string;\r\n orient( value: number ): number;\r\n}\r\n\r\n/**\r\n * The translation map for directions.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const ORIENTATION_MAP = {\r\n marginRight : [ 'marginBottom', 'marginLeft' ],\r\n autoWidth : [ 'autoHeight' ],\r\n fixedWidth : [ 'fixedHeight' ],\r\n paddingLeft : [ 'paddingTop', 'paddingRight' ],\r\n paddingRight: [ 'paddingBottom', 'paddingLeft' ],\r\n width : [ 'height' ],\r\n left : [ 'top', 'right' ],\r\n right : [ 'bottom', 'left' ],\r\n x : [ 'y' ],\r\n X : [ 'Y' ],\r\n Y : [ 'X' ],\r\n ArrowLeft : [ 'ArrowUp', 'ArrowRight' ],\r\n ArrowRight : [ 'ArrowDown', 'ArrowLeft' ],\r\n};\r\n\r\n/**\r\n * The component that absorbs the difference among directions.\r\n *\r\n * @since 3.0.0\r\n *\r\n * @param Splide - A Splide instance.\r\n * @param Components - A collection of components.\r\n * @param options - Options.\r\n *\r\n * @return A Direction component object.\r\n */\r\nexport function Direction( Splide: Splide, Components: Components, options: Options ): DirectionComponent {\r\n /**\r\n * Resolves the provided property name.\r\n *\r\n * @param prop - A property name to translate.\r\n * @param axisOnly - Optional. If `ture`, returns the same property for LTR and RTL.\r\n */\r\n function resolve( prop: string, axisOnly?: boolean ): string {\r\n const { direction } = options;\r\n const index = direction === RTL && ! axisOnly ? 1 : direction === TTB ? 0 : -1;\r\n return ORIENTATION_MAP[ prop ][ index ] || prop;\r\n }\r\n\r\n /**\r\n * Orients the value towards the current direction.\r\n *\r\n * @param value - A value to orient.\r\n *\r\n * @return The oriented value.\r\n */\r\n function orient( value: number ): number {\r\n return value * ( options.direction === RTL ? 1 : -1 );\r\n }\r\n\r\n return {\r\n resolve,\r\n orient,\r\n };\r\n}\r\n","import { PROJECT_CODE } from './project';\r\n\r\n\r\nexport const CLASS_ROOT = PROJECT_CODE;\r\nexport const CLASS_SLIDER = `${ PROJECT_CODE }__slider`;\r\nexport const CLASS_TRACK = `${ PROJECT_CODE }__track`;\r\nexport const CLASS_LIST = `${ PROJECT_CODE }__list`;\r\nexport const CLASS_SLIDE = `${ PROJECT_CODE }__slide`;\r\nexport const CLASS_CLONE = `${ CLASS_SLIDE }--clone`;\r\nexport const CLASS_CONTAINER = `${ CLASS_SLIDE }__container`;\r\nexport const CLASS_ARROWS = `${ PROJECT_CODE }__arrows`;\r\nexport const CLASS_ARROW = `${ PROJECT_CODE }__arrow`;\r\nexport const CLASS_ARROW_PREV = `${ CLASS_ARROW }--prev`;\r\nexport const CLASS_ARROW_NEXT = `${ CLASS_ARROW }--next`;\r\nexport const CLASS_PAGINATION = `${ PROJECT_CODE }__pagination`;\r\nexport const CLASS_PAGINATION_PAGE = `${ CLASS_PAGINATION }__page`;\r\nexport const CLASS_PROGRESS = `${ PROJECT_CODE }__progress`;\r\nexport const CLASS_PROGRESS_BAR = `${ CLASS_PROGRESS }__bar`;\r\nexport const CLASS_AUTOPLAY = `${ PROJECT_CODE }__autoplay`;\r\nexport const CLASS_PLAY = `${ PROJECT_CODE }__play`;\r\nexport const CLASS_PAUSE = `${ PROJECT_CODE }__pause`;\r\nexport const CLASS_SPINNER = `${ PROJECT_CODE }__spinner`;\r\nexport const CLASS_INITIALIZED = 'is-initialized';\r\nexport const CLASS_ACTIVE = 'is-active';\r\nexport const CLASS_PREV = 'is-prev';\r\nexport const CLASS_NEXT = 'is-next';\r\nexport const CLASS_VISIBLE = 'is-visible';\r\nexport const CLASS_LOADING = 'is-loading';\r\n\r\n/**\r\n * The array with all status classes.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const STATUS_CLASSES = [ CLASS_ACTIVE, CLASS_VISIBLE, CLASS_PREV, CLASS_NEXT, CLASS_LOADING ];\r\n\r\n/**\r\n * The collection of classes for elements that Splide dynamically creates.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const CLASSES = {\r\n slide : CLASS_SLIDE,\r\n clone : CLASS_CLONE,\r\n arrows : CLASS_ARROWS,\r\n arrow : CLASS_ARROW,\r\n prev : CLASS_ARROW_PREV,\r\n next : CLASS_ARROW_NEXT,\r\n pagination: CLASS_PAGINATION,\r\n page : CLASS_PAGINATION_PAGE,\r\n spinner : CLASS_SPINNER,\r\n};\r\n","import {\n CLASS_ACTIVE,\n CLASS_ARROW_NEXT,\n CLASS_ARROW_PREV,\n CLASS_ARROWS,\n CLASS_AUTOPLAY,\n CLASS_CLONE,\n CLASS_LIST,\n CLASS_PAUSE,\n CLASS_PLAY,\n CLASS_PROGRESS,\n CLASS_PROGRESS_BAR,\n CLASS_ROOT,\n CLASS_SLIDE,\n CLASS_SLIDER,\n CLASS_TRACK,\n} from '../../constants/classes';\nimport { EVENT_REFRESH, EVENT_UPDATED } from '../../constants/events';\nimport { DEFAULT_EVENT_PRIORITY } from '../../constants/priority';\nimport { PROJECT_CODE } from '../../constants/project';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { BaseComponent, Components, Options } from '../../types';\nimport {\n addClass,\n assert,\n assign,\n child,\n children,\n empty,\n push,\n query,\n removeAttribute,\n removeClass,\n uniqueId,\n} from '../../utils';\n\n\n/**\n * The interface for elements which the slider consists of.\n *\n * @since 3.0.0\n */\nexport interface ElementCollection {\n root: HTMLElement;\n slider: HTMLElement;\n track: HTMLElement;\n list: HTMLElement;\n slides: HTMLElement[];\n arrows: HTMLElement;\n prev: HTMLButtonElement;\n next: HTMLButtonElement;\n bar: HTMLElement;\n autoplay: HTMLElement;\n play: HTMLButtonElement;\n pause: HTMLButtonElement;\n}\n\n/**\n * The interface for the Elements component.\n *\n * @since 3.0.0\n */\nexport interface ElementsComponent extends BaseComponent, ElementCollection {\n}\n\n/**\n * The component that collects and handles elements which the slider consists of.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return An Elements component object.\n */\nexport function Elements( Splide: Splide, Components: Components, options: Options ): ElementsComponent {\n const { on } = EventInterface( Splide );\n const { root } = Splide;\n const elements: ElementCollection = {} as ElementCollection;\n\n /**\n * Stores all slide elements.\n */\n const slides: HTMLElement[] = [];\n\n /**\n * Stores all root classes.\n */\n let classes: string[];\n\n /**\n * The slider element that may be `undefined`.\n */\n let slider: HTMLElement;\n\n /**\n * The track element.\n */\n let track: HTMLElement;\n\n /**\n * The list element.\n */\n let list: HTMLElement;\n\n /**\n * Called when the component is constructed.\n */\n function setup(): void {\n collect();\n identify();\n addClass( root, ( classes = getClasses() ) );\n }\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n on( EVENT_REFRESH, refresh, DEFAULT_EVENT_PRIORITY - 2 );\n on( EVENT_UPDATED, update );\n }\n\n /**\n * Destroys the component.\n */\n function destroy(): void {\n [ root, track, list ].forEach( elm => {\n removeAttribute( elm, 'style' );\n } );\n\n empty( slides );\n removeClass( root, classes );\n }\n\n /**\n * Recollects slide elements.\n */\n function refresh(): void {\n destroy();\n setup();\n }\n\n /**\n * Updates the status of elements.\n */\n function update(): void {\n removeClass( root, classes );\n addClass( root, ( classes = getClasses() ) );\n }\n\n /**\n * Collects elements which the slider consists of.\n */\n function collect(): void {\n slider = child( root, `.${ CLASS_SLIDER }` );\n track = query( root, `.${ CLASS_TRACK }` );\n list = child( track, `.${ CLASS_LIST }` );\n\n assert( track && list, 'A track/list element is missing.' );\n\n push( slides, children( list, `.${ CLASS_SLIDE }:not(.${ CLASS_CLONE })` ) );\n\n const autoplay = find( `.${ CLASS_AUTOPLAY }` );\n const arrows = find( `.${ CLASS_ARROWS }` );\n\n assign( elements, {\n root,\n slider,\n track,\n list,\n slides,\n arrows,\n autoplay,\n prev : query( arrows, `.${ CLASS_ARROW_PREV }` ),\n next : query( arrows, `.${ CLASS_ARROW_NEXT }` ),\n bar : query( find( `.${ CLASS_PROGRESS }` ), `.${ CLASS_PROGRESS_BAR }` ),\n play : query( autoplay, `.${ CLASS_PLAY }` ),\n pause: query( autoplay, `.${ CLASS_PAUSE }` ),\n } );\n }\n\n /**\n * Assigns unique IDs to essential elements.\n */\n function identify(): void {\n const id = root.id || uniqueId( PROJECT_CODE );\n root.id = id;\n track.id = track.id || `${ id }-track`;\n list.id = list.id || `${ id }-list`;\n }\n\n /**\n * Finds an element only in children of the root or slider element.\n *\n * @return {Element} - A found element or undefined.\n */\n function find( selector: string ): HTMLElement {\n return child( root, selector ) || child( slider, selector );\n }\n\n /**\n * Return an array with classes for the root element.\n *\n * @return An array with classes.\n */\n function getClasses(): string[] {\n return [\n `${ CLASS_ROOT }--${ options.type }`,\n `${ CLASS_ROOT }--${ options.direction }`,\n options.drag && `${ CLASS_ROOT }--draggable`,\n options.isNavigation && `${ CLASS_ROOT }--nav`,\n CLASS_ACTIVE,\n ];\n }\n\n return assign( elements, {\n setup,\n mount,\n destroy,\n } );\n}\n","export const ROLE = 'role';\r\nexport const ARIA_CONTROLS = 'aria-controls';\r\nexport const ARIA_CURRENT = 'aria-current';\r\nexport const ARIA_LABEL = 'aria-label';\r\nexport const ARIA_HIDDEN = 'aria-hidden';\r\nexport const TAB_INDEX = 'tabindex';\r\nexport const DISABLED = 'disabled';\r\nexport const ARIA_ORIENTATION = 'aria-orientation';\r\n\r\n/**\r\n * The array with all attributes.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const ALL_ATTRIBUTES = [\r\n ROLE,\r\n ARIA_CONTROLS,\r\n ARIA_CURRENT,\r\n ARIA_LABEL,\r\n ARIA_HIDDEN,\r\n ARIA_ORIENTATION,\r\n TAB_INDEX,\r\n DISABLED,\r\n];\r\n","/**\r\n * The type for the regular slider.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const SLIDE = 'slide';\r\n\r\n/**\r\n * The type for the carousel slider.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const LOOP = 'loop';\r\n\r\n/**\r\n * The type for the fade slider that can not have multiple slides in a page.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport const FADE = 'fade';\r\n","import {\n ALL_ATTRIBUTES,\n ARIA_CONTROLS,\n ARIA_CURRENT,\n ARIA_HIDDEN,\n ARIA_LABEL,\n ROLE,\n TAB_INDEX,\n} from '../../constants/attributes';\nimport {\n CLASS_ACTIVE,\n CLASS_CONTAINER,\n CLASS_NEXT,\n CLASS_PREV,\n CLASS_VISIBLE,\n STATUS_CLASSES,\n} from '../../constants/classes';\nimport {\n EVENT_ACTIVE,\n EVENT_CLICK,\n EVENT_HIDDEN,\n EVENT_INACTIVE,\n EVENT_MOVE,\n EVENT_MOVED,\n EVENT_REFRESH,\n EVENT_REPOSITIONED,\n EVENT_SCROLLED,\n EVENT_SLIDE_KEYDOWN,\n EVENT_VISIBLE,\n} from '../../constants/events';\nimport { FADE, SLIDE } from '../../constants/types';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { BaseComponent } from '../../types';\nimport {\n abs,\n ceil,\n child,\n floor,\n format,\n getAttribute,\n hasClass,\n min,\n pad,\n queryAll,\n rect,\n removeAttribute,\n removeClass,\n setAttribute,\n style as _style,\n toggleClass,\n} from '../../utils';\n\n\n/**\n * The interface for the Slide sub component.\n *\n * @since 3.0.0\n */\nexport interface SlideComponent extends BaseComponent {\n index: number;\n slideIndex: number;\n slide: HTMLElement;\n container: HTMLElement;\n isClone: boolean;\n style( prop: string, value: string | number, useContainer?: boolean ): void\n isWithin( from: number, distance: number ): boolean;\n}\n\n/**\n * The sub component for managing each slide.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param index - A slide index.\n * @param slideIndex - A slide index for clones. This must be `-1` if the slide is not a clone.\n * @param slide - A slide element.\n *\n * @return A Slide sub component.\n */\nexport function Slide( Splide: Splide, index: number, slideIndex: number, slide: HTMLElement ): SlideComponent {\n const { on, emit, bind, destroy: destroyEvents } = EventInterface( Splide );\n const { Components, root, options } = Splide;\n const { isNavigation, updateOnMove } = options;\n const { resolve } = Components.Direction;\n const styles = getAttribute( slide, 'style' );\n const isClone = slideIndex > -1;\n const container = child( slide, `.${ CLASS_CONTAINER }` );\n const focusableNodes = options.focusableNodes && queryAll( slide, options.focusableNodes );\n\n /**\n * Turns into `true` when the component is destroyed.\n */\n let destroyed: boolean;\n\n /**\n * Called when the component is mounted.\n */\n function mount( this: SlideComponent ): void {\n init();\n\n bind( slide, 'click keydown', e => {\n emit( e.type === 'click' ? EVENT_CLICK : EVENT_SLIDE_KEYDOWN, this, e );\n } );\n\n on( [ EVENT_REFRESH, EVENT_REPOSITIONED, EVENT_MOVED, EVENT_SCROLLED ], update.bind( this ) );\n\n if ( updateOnMove ) {\n on( EVENT_MOVE, onMove.bind( this ) );\n }\n }\n\n /**\n * Initializes the component.\n */\n function init(): void {\n if ( ! isClone ) {\n slide.id = `${ root.id }-slide${ pad( index + 1 ) }`;\n }\n\n if ( isNavigation ) {\n const idx = isClone ? slideIndex : index;\n const label = format( options.i18n.slideX, idx + 1 );\n const controls = Splide.splides.map( splide => splide.root.id ).join( ' ' );\n\n setAttribute( slide, ARIA_LABEL, label );\n setAttribute( slide, ARIA_CONTROLS, controls );\n setAttribute( slide, ROLE, 'menuitem' );\n }\n }\n\n /**\n * Destroys the component.\n */\n function destroy(): void {\n destroyed = true;\n destroyEvents();\n removeClass( slide, STATUS_CLASSES );\n removeAttribute( slide, ALL_ATTRIBUTES );\n setAttribute( slide, 'style', styles );\n }\n\n /**\n * If the `updateOnMove` option is `true`, called when the slider starts moving.\n *\n * @param next - A next index.\n * @param prev - A previous index.\n * @param dest - A destination index.\n */\n function onMove( this: SlideComponent, next: number, prev: number, dest: number ): void {\n if ( ! destroyed ) {\n update.call( this );\n\n if ( dest === index ) {\n updateActivity.call( this, true );\n }\n }\n }\n\n /**\n * Updates attribute and classes of the slide.\n */\n function update( this: SlideComponent ): void {\n if ( ! destroyed ) {\n const { index: currIndex } = Splide;\n\n updateActivity.call( this, isActive() );\n updateVisibility.call( this, isVisible() );\n\n toggleClass( slide, CLASS_PREV, index === currIndex - 1 );\n toggleClass( slide, CLASS_NEXT, index === currIndex + 1 );\n }\n }\n\n /**\n * Updates the status related with activity.\n *\n * @param active - Set `true` if the slide is active.\n */\n function updateActivity( this: SlideComponent, active: boolean ): void {\n if ( active !== hasClass( slide, CLASS_ACTIVE ) ) {\n toggleClass( slide, CLASS_ACTIVE, active );\n\n if ( isNavigation ) {\n setAttribute( slide, ARIA_CURRENT, active || null );\n }\n\n emit( active ? EVENT_ACTIVE : EVENT_INACTIVE, this );\n }\n }\n\n /**\n * Updates classes and attributes related with visibility.\n *\n * @param visible - Set `true` if the slide is visible.\n */\n function updateVisibility( this: SlideComponent, visible: boolean ): void {\n const ariaHidden = ! visible && ! isActive();\n\n setAttribute( slide, ARIA_HIDDEN, ariaHidden || null );\n setAttribute( slide, TAB_INDEX, ! ariaHidden && options.slideFocus ? 0 : null );\n\n if ( focusableNodes ) {\n focusableNodes.forEach( node => {\n setAttribute( node, TAB_INDEX, ariaHidden ? -1 : null );\n } );\n }\n\n if ( visible !== hasClass( slide, CLASS_VISIBLE ) ) {\n toggleClass( slide, CLASS_VISIBLE, visible );\n emit( visible ? EVENT_VISIBLE : EVENT_HIDDEN, this );\n }\n }\n\n /**\n * Adds a CSS rule to the slider or the container.\n *\n * @param prop - A property name.\n * @param value - A CSS value to add.\n * @param useContainer - Optional. Determines whether to apply the rule to the container or not.\n */\n function style( prop: string, value: string | number, useContainer?: boolean ): void {\n _style( ( useContainer && container ) || slide, prop, value );\n }\n\n /**\n * Checks if the slide is active or not.\n *\n * @return `true` if the slide is active.\n */\n function isActive(): boolean {\n return Splide.index === index;\n }\n\n /**\n * Checks if the slide is visible or not.\n */\n function isVisible(): boolean {\n if ( Splide.is( FADE ) ) {\n return isActive();\n }\n\n const trackRect = rect( Components.Elements.track );\n const slideRect = rect( slide );\n const left = resolve( 'left' );\n const right = resolve( 'right' );\n\n return floor( trackRect[ left ] ) <= ceil( slideRect[ left ] )\n && floor( slideRect[ right ] ) <= ceil( trackRect[ right ] );\n }\n\n /**\n * Calculates how far this slide is from another slide and\n * returns `true` if the distance is within the given number.\n *\n * @param from - An index of a base slide.\n * @param distance - `true` if the slide is within this number.\n *\n * @return `true` if the slide is within the `distance` from the base slide, or otherwise `false`.\n */\n function isWithin( from: number, distance: number ): boolean {\n let diff = abs( from - index );\n\n if ( ! Splide.is( SLIDE ) && ! isClone ) {\n diff = min( diff, Splide.length - diff );\n }\n\n return diff <= distance;\n }\n\n return {\n index,\n slideIndex,\n slide,\n container,\n isClone,\n mount,\n destroy,\n style,\n isWithin,\n };\n}\n","import { EVENT_MOUNTED, EVENT_REFRESH, EVENT_RESIZE } from '../../constants/events';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { AnyFunction, BaseComponent, Components, Options } from '../../types';\nimport {\n addClass,\n append,\n before,\n between,\n empty,\n forEach as forEachItem,\n includes,\n isFunction,\n isHTMLElement,\n isString,\n matches,\n parseHtml,\n queryAll,\n remove as removeNode,\n toArray,\n} from '../../utils';\nimport { Slide, SlideComponent } from './Slide';\n\n\n/**\n * The interface for the Slides component.\n *\n * @since 3.0.0\n */\nexport interface SlidesComponent extends BaseComponent {\n register( slide: HTMLElement, index: number, slideIndex: number ): void;\n get( excludeClones?: boolean ): SlideComponent[];\n getIn( page: number ): SlideComponent[];\n getAt( index: number ): SlideComponent | undefined;\n add( slide: string | Element | Array, index?: number ): void;\n remove( selector: SlideMatcher ): void;\n forEach( iteratee: SlidesIteratee, excludeClones?: boolean ): void;\n filter( matcher: SlideMatcher ): SlideComponent[];\n style( prop: string, value: string | number, useContainer?: boolean ): void\n getLength( excludeClones?: boolean ): number;\n isEnough(): boolean;\n}\n\n/**\n * The iteratee function for Slides.\n *\n * @since 3.0.0\n */\nexport type SlidesIteratee = ( Slide: SlideComponent, index: number, Slides: SlideComponent[] ) => void\n\n/**\n * The predicate function for Slides.\n *\n * @since 3.0.0\n */\nexport type SlidesPredicate = ( Slide: SlideComponent, index: number, Slides: SlideComponent[] ) => any\n\n/**\n * The type for filtering SlideComponent objects.\n *\n * @since 3.0.0\n */\nexport type SlideMatcher = number | number[] | string | SlidesPredicate;\n\n/**\n * The component for managing all slides include clones.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return An Slides component object.\n */\nexport function Slides( Splide: Splide, Components: Components, options: Options ): SlidesComponent {\n const { on, emit, bind } = EventInterface( Splide );\n const { slides, list } = Components.Elements;\n\n /**\n * Stores all SlideComponent objects.\n */\n const Slides: SlideComponent[] = [];\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n init();\n on( EVENT_REFRESH, refresh );\n on( [ EVENT_MOUNTED, EVENT_REFRESH ], () => {\n Slides.sort( ( Slide1, Slide2 ) => Slide1.index - Slide2.index );\n } );\n }\n\n /**\n * Initializes the component.\n */\n function init(): void {\n slides.forEach( ( slide, index ) => { register( slide, index, -1 ) } );\n }\n\n /**\n * Destroys the component.\n */\n function destroy(): void {\n forEach( Slide => { Slide.destroy() } );\n empty( Slides );\n }\n\n /**\n * Discards all Slide components and regenerates them.\n */\n function refresh(): void {\n destroy();\n init();\n }\n\n /**\n * Registers a slide element and creates a Slide object.\n *\n * @param slide - A slide element to register.\n * @param index - A slide index.\n * @param slideIndex - A slide index for clones. This must be `-1` for regular slides.\n */\n function register( slide: HTMLElement, index: number, slideIndex: number ): void {\n const object = Slide( Splide, index, slideIndex, slide );\n object.mount();\n Slides.push( object );\n }\n\n /**\n * Returns all Slide objects.\n *\n * @param excludeClones - Optional. Determines whether to exclude clones or not.\n *\n * @return An array with Slide objects.\n */\n function get( excludeClones?: boolean ): SlideComponent[] {\n return excludeClones ? filter( Slide => ! Slide.isClone ) : Slides;\n }\n\n /**\n * Returns slides in the specified page.\n *\n * @param page - A page index.\n *\n * @return An array with slides that belong to the page.\n */\n function getIn( page: number ): SlideComponent[] {\n const { Controller } = Components;\n const index = Controller.toIndex( page );\n const max = Controller.hasFocus() ? 1 : options.perPage;\n return filter( Slide => between( Slide.index, index, index + max - 1 ) );\n }\n\n /**\n * Returns a Slide object at the specified index.\n *\n * @param index - A slide index.\n *\n * @return A Slide object if available, or otherwise `undefined`.\n */\n function getAt( index: number ): SlideComponent | undefined {\n return filter( index )[ 0 ];\n }\n\n /**\n * Inserts a slide or slides at a specified index.\n *\n * @param items - A slide element, an HTML string or an array with them.\n * @param index - Optional. An index to insert the slide at. If omitted, appends it to the list.\n */\n function add( items: string | Element | Array, index?: number ): void {\n forEachItem( items, slide => {\n if ( isString( slide ) ) {\n slide = parseHtml( slide );\n }\n\n if ( isHTMLElement( slide ) ) {\n const ref = slides[ index ];\n ref ? before( slide, ref ) : append( list, slide );\n addClass( slide, options.classes.slide );\n observeImages( slide, emit.bind( null, EVENT_RESIZE ) );\n }\n } );\n\n emit( EVENT_REFRESH );\n }\n\n /**\n * Removes slides that match the matcher\n * that can be an index, an array with indices, a selector, or an iteratee function.\n *\n * @param matcher - An index, an array with indices, a selector string, or an iteratee function.\n */\n function remove( matcher: SlideMatcher ): void {\n removeNode( filter( matcher ).map( Slide => Slide.slide ) );\n emit( EVENT_REFRESH );\n }\n\n /**\n * Iterates over Slide objects by the iteratee function.\n *\n * @param iteratee - An iteratee function that takes a Slide object, an index and an array with Slides.\n * @param excludeClones - Optional. Determines whether to exclude clones or not.\n */\n function forEach( iteratee: SlidesIteratee, excludeClones?: boolean ): void {\n get( excludeClones ).forEach( iteratee );\n }\n\n /**\n * Filters Slides by the matcher\n * that can be an index, an array with indices, a selector, or a predicate function.\n *\n * @param matcher - An index, an array with indices, a selector string, or a predicate function.\n *\n * @return An array with SlideComponent objects.\n */\n function filter( matcher: SlideMatcher ): SlideComponent[] {\n return Slides.filter( isFunction( matcher )\n ? matcher\n : Slide => isString( matcher )\n ? matches( Slide.slide, matcher )\n : includes( toArray( matcher ), Slide.index )\n );\n }\n\n /**\n * Adds a CSS rule to all slides or containers.\n *\n * @param prop - A property name.\n * @param value - A CSS value to add.\n * @param useContainer - Optional. Determines whether to apply the rule to the container or not.\n */\n function style( prop: string, value: string | number, useContainer?: boolean ): void {\n forEach( Slide => { Slide.style( prop, value, useContainer ) } );\n }\n\n /**\n * Invokes the callback after all images in the element are loaded.\n *\n * @param elm - An element that may contain images.\n * @param callback - A callback function.\n */\n function observeImages( elm: Element, callback: AnyFunction ): void {\n const images = queryAll( elm, 'img' );\n let { length } = images;\n\n if ( length ) {\n images.forEach( img => {\n bind( img, 'load error', () => {\n if ( ! --length ) {\n callback();\n }\n } );\n } );\n } else {\n callback();\n }\n }\n\n /**\n * Returns the length of slides.\n *\n * @param excludeClones - Optional. Determines whether to exclude clones or not.\n *\n * @return The length of slides.\n */\n function getLength( excludeClones?: boolean ): number {\n return excludeClones ? slides.length : Slides.length;\n }\n\n /**\n * Checks if the number of slides is over than the `perPage` option, including clones.\n *\n * @return `true` if there are enough slides, or otherwise `false`.\n */\n function isEnough(): boolean {\n return Slides.length > options.perPage;\n }\n\n return {\n mount,\n destroy,\n register,\n get,\n getIn,\n getAt,\n add,\n remove,\n forEach,\n filter,\n style,\n getLength,\n isEnough,\n };\n}\n","import { TTB } from '../../constants/directions';\nimport { EVENT_REFRESH, EVENT_RESIZE, EVENT_RESIZED, EVENT_UPDATED } from '../../constants/events';\nimport { EventInterface, Throttle } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { BaseComponent, Components, Options } from '../../types';\nimport { abs, assert, isObject, rect, style, unit } from '../../utils';\n\n\n/**\n * The interface for the Layout component.\n *\n * @since 3.0.0\n */\nexport interface LayoutComponent extends BaseComponent {\n listSize(): number;\n slideSize( index: number, withoutGap?: boolean ): number;\n sliderSize(): number;\n totalSize( index?: number, withoutGap?: boolean ): number;\n getPadding( right: boolean ): number;\n}\n\n/**\n * The component that layouts slider components and provides methods for dimensions.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return An Layout component object.\n */\nexport function Layout( Splide: Splide, Components: Components, options: Options ): LayoutComponent {\n const { on, bind, emit } = EventInterface( Splide );\n const { Slides } = Components;\n const { resolve } = Components.Direction;\n const { track, list } = Components.Elements;\n const { getAt } = Slides;\n\n /**\n * Indicates whether the slider direction is vertical or not.\n */\n let vertical: boolean;\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n init();\n bind( window, 'resize load', Throttle( emit.bind( this, EVENT_RESIZE ) ) );\n on( [ EVENT_UPDATED, EVENT_REFRESH ], init );\n on( EVENT_RESIZE, resize );\n }\n\n /**\n * Initializes the component on `mount` or `updated`.\n * Uses `max-width` for the root to prevent the slider from exceeding the parent element.\n */\n function init(): void {\n vertical = options.direction === TTB;\n\n style( Splide.root, 'maxWidth', unit( options.width ) );\n style( track, resolve( 'paddingLeft' ), cssPadding( false ) );\n style( track, resolve( 'paddingRight' ), cssPadding( true ) );\n\n resize();\n }\n\n /**\n * Updates dimensions of some elements when the slider is resized.\n */\n function resize(): void {\n style( track, 'height', cssTrackHeight() );\n\n Slides.style( resolve( 'marginRight' ), unit( options.gap ) );\n Slides.style( 'width', cssSlideWidth() || null );\n setSlidesHeight();\n\n emit( EVENT_RESIZED );\n }\n\n /**\n * Updates the height of slides or their container elements if available.\n */\n function setSlidesHeight(): void {\n Slides.style( 'height', cssSlideHeight() || null, true );\n }\n\n /**\n * Parses the padding option and returns the value for each side.\n * This method returns `paddingTop` or `paddingBottom` for the vertical slider.\n *\n * @param right - Determines whether to get `paddingRight/Bottom` or `paddingLeft/Top`.\n *\n * @return The padding value as a CSS string.\n */\n function cssPadding( right: boolean ): string {\n const { padding } = options;\n const prop = resolve( right ? 'right' : 'left', true );\n return padding && unit( padding[ prop ] || ( isObject( padding ) ? 0 : padding ) ) || '0px';\n }\n\n /**\n * Returns the height of the track element as a CSS string.\n *\n * @return The height of the track.\n */\n function cssTrackHeight(): string {\n let height = '';\n\n if ( vertical ) {\n height = cssHeight();\n assert( height, 'height or heightRatio is missing.' );\n height = `calc(${ height } - ${ cssPadding( false ) } - ${ cssPadding( true ) })`;\n }\n\n return height;\n }\n\n /**\n * Converts options related with height to a CSS string.\n *\n * @return The height as a CSS string if available, or otherwise an empty string.\n */\n function cssHeight(): string {\n return unit( options.height || rect( list ).width * options.heightRatio );\n }\n\n /**\n * Returns the width of the slide as a CSS string.\n *\n * @return The width of the slide.\n */\n function cssSlideWidth(): string {\n return options.autoWidth ? '' : unit( options.fixedWidth ) || ( vertical ? '' : cssSlideSize() );\n }\n\n /**\n * Returns the height of the slide as a CSS string.\n *\n * @return The height of the slide.\n */\n function cssSlideHeight(): string {\n return unit( options.fixedHeight )\n || ( vertical ? ( options.autoHeight ? '' : cssSlideSize() ) : cssHeight() );\n }\n\n /**\n * Returns the CSS string for slide width or height without gap.\n *\n * @return The CSS string for slide width or height.\n */\n function cssSlideSize(): string {\n const gap = unit( options.gap );\n return `calc((100%${ gap && ` + ${ gap }` })/${ options.perPage || 1 }${ gap && ` - ${ gap }` })`;\n }\n\n /**\n * Returns the list width for the horizontal slider, or the height for the vertical slider.\n *\n * @return The size of the track element in pixel.\n */\n function listSize(): number {\n return rect( list )[ resolve( 'width' ) ];\n }\n\n /**\n * Returns the slide width for the horizontal slider, or the height for the vertical slider.\n *\n * @param index - Optional. A slide index.\n * @param withoutGap - Optional. Determines whether to exclude the gap amount or not.\n *\n * @return The size of the specified slide element in pixel.\n */\n function slideSize( index?: number, withoutGap?: boolean ): number {\n const Slide = getAt( index || 0 );\n return Slide\n ? rect( Slide.slide )[ resolve( 'width' ) ] + ( withoutGap ? 0 : getGap() )\n : 0;\n }\n\n /**\n * Returns the total width or height of slides from the head of the slider to the specified index.\n * This includes sizes of clones before the first slide.\n *\n * @param index - A slide index. If omitted, uses the last index.\n * @param withoutGap - Optional. Determines whether to exclude the last gap or not.\n *\n * @return The total width of slides in the horizontal slider, or the height in the vertical one.\n */\n function totalSize( index: number, withoutGap?: boolean ): number {\n const Slide = getAt( index );\n\n if ( Slide ) {\n const right = rect( Slide.slide )[ resolve( 'right' ) ];\n const left = rect( list )[ resolve( 'left' ) ];\n return abs( right - left ) + ( withoutGap ? 0 : getGap() );\n }\n\n return 0;\n }\n\n /**\n * Returns the slider size without clones before the first slide.\n *\n * @return The width or height of the slider without clones.\n */\n function sliderSize(): number {\n return totalSize( Splide.length - 1, true ) - totalSize( -1, true );\n }\n\n /**\n * Returns the gap value.\n *\n *\n * @return The gap value in pixel.\n */\n function getGap(): number {\n const Slide = getAt( 0 );\n return Slide && parseFloat( style( Slide.slide, resolve( 'marginRight' ) ) ) || 0;\n }\n\n /**\n * Returns the padding value.\n *\n * @param right - Determines whether to get `paddingRight/Bottom` or `paddingLeft/Top`.\n *\n * @return The padding value in pixel.\n */\n function getPadding( right: boolean ): number {\n return parseFloat( style( track, resolve( `padding${ right ? 'Right' : 'Left' }`, true ) ) ) || 0;\n }\n\n return {\n mount,\n listSize,\n slideSize,\n sliderSize,\n totalSize,\n getPadding,\n };\n}\n","import { EVENT_REFRESH, EVENT_RESIZE, EVENT_UPDATED } from '../../constants/events';\nimport { LOOP } from '../../constants/types';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { BaseComponent, Components, Options } from '../../types';\nimport { addClass, append, before, ceil, empty, measure, pad, push, rect, remove } from '../../utils';\n\n\n/**\n * The interface for the Clone component.\n *\n * @since 3.0.0\n */\nexport interface CloneComponent extends BaseComponent {\n}\n\n/**\n * The component that generates clones for the loop slider.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return A Clones component object.\n */\nexport function Clones( Splide: Splide, Components: Components, options: Options ): CloneComponent {\n const { on, emit } = EventInterface( Splide );\n const { Elements, Slides } = Components;\n const { resolve } = Components.Direction;\n\n /**\n * Stores all cloned elements.\n */\n const clones: HTMLElement[] = [];\n\n /**\n * Keeps the current number of clones.\n */\n let cloneCount: number;\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n init();\n on( EVENT_REFRESH, refresh );\n on( [ EVENT_UPDATED, EVENT_RESIZE ], observe );\n }\n\n /**\n * Removes all clones if available, and generates new clones.\n */\n function init(): void {\n if ( ( cloneCount = computeCloneCount() ) ) {\n generate( cloneCount );\n emit( EVENT_RESIZE );\n }\n }\n\n /**\n * Destroys clones.\n */\n function destroy(): void {\n remove( clones );\n empty( clones );\n }\n\n /**\n * Discards all clones and regenerates them.\n * Must do this before the Elements component collects slide elements.\n */\n function refresh(): void {\n destroy();\n init();\n }\n\n /**\n * Observes the required clone count and refreshes the slider if necessary.\n */\n function observe(): void {\n if ( cloneCount < computeCloneCount() ) {\n emit( EVENT_REFRESH );\n }\n }\n\n /**\n * Generates the specified number of clones.\n *\n * @param count - The number of clones to generate for each side.\n */\n function generate( count: number ): void {\n const slides = Slides.get().slice();\n const { length } = slides;\n\n if ( length ) {\n while ( slides.length < count ) {\n push( slides, slides );\n }\n\n push( slides.slice( -count ), slides.slice( 0, count ) ).forEach( ( Slide, index ) => {\n const isHead = index < count;\n const clone = cloneDeep( Slide.slide, index );\n isHead ? before( clone, slides[ 0 ].slide ) : append( Elements.list, clone );\n push( clones, clone );\n Slides.register( clone, index - count + ( isHead ? 0 : length ), Slide.index );\n } );\n }\n }\n\n /**\n * Deeply clones the provided element with removing the ID attribute.\n *\n * @param elm - An element to clone.\n * @param index - An index of the clone.\n *\n * @return A cloned element.\n */\n function cloneDeep( elm: HTMLElement, index: number ): HTMLElement {\n const clone = elm.cloneNode( true ) as HTMLElement;\n addClass( clone, options.classes.clone );\n clone.id = `${ Splide.root.id }-clone${ pad( index + 1 ) }`;\n return clone;\n }\n\n /**\n * Returns the number of elements to generate.\n * This always returns 0 if the slider type is not `'loop'`.\n *\n * @return The number of clones.\n */\n function computeCloneCount(): number {\n let { clones } = options;\n\n if ( ! Splide.is( LOOP ) ) {\n clones = 0;\n } else if ( ! clones ) {\n const fixedSize = measure( Elements.list, options[ resolve( 'fixedWidth' ) ] );\n const fixedCount = fixedSize && ceil( rect( Elements.track )[ resolve( 'width' ) ] / fixedSize );\n const baseCount = fixedCount || ( options[ resolve( 'autoWidth' ) ] && Splide.length ) || options.perPage;\n\n clones = baseCount * ( options.drag ? ( options.flickMaxPages || 1 ) + 1 : 2 );\n }\n\n return clones;\n }\n\n return {\n mount,\n destroy,\n };\n}\n","import {\n EVENT_MOUNTED,\n EVENT_MOVE,\n EVENT_MOVED,\n EVENT_REFRESH,\n EVENT_REPOSITIONED,\n EVENT_RESIZED,\n EVENT_UPDATED,\n} from '../../constants/events';\nimport { IDLE, MOVING } from '../../constants/states';\nimport { FADE, LOOP, SLIDE } from '../../constants/types';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { AnyFunction, BaseComponent, Components, Options } from '../../types';\nimport { abs, ceil, clamp, isUndefined, rect, removeAttribute, sign } from '../../utils';\n\n\n/**\n * The interface for the Move component.\n *\n * @since 3.0.0\n */\nexport interface MoveComponent extends BaseComponent {\n move( dest: number, index: number, prev: number, callback?: AnyFunction ): void;\n jump( index: number ): void;\n translate( position: number, preventLoop?: boolean ): void;\n shift( position: number, backwards: boolean ): number;\n cancel(): void;\n toIndex( position: number ): number;\n toPosition( index: number, trimming?: boolean ): number;\n getPosition(): number;\n getLimit( max: boolean ): number;\n isBusy(): boolean;\n exceededLimit( max?: boolean | undefined, position?: number ): boolean;\n}\n\n/**\n * The component for moving the slider.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return A Move component object.\n */\nexport function Move( Splide: Splide, Components: Components, options: Options ): MoveComponent {\n const { on, emit } = EventInterface( Splide );\n const { slideSize, getPadding, totalSize, listSize, sliderSize } = Components.Layout;\n const { resolve, orient } = Components.Direction;\n const { list, track } = Components.Elements;\n\n /**\n * Indicates whether the component can move the slider or not.\n */\n let waiting: boolean;\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n on( [ EVENT_MOUNTED, EVENT_RESIZED, EVENT_UPDATED, EVENT_REFRESH ], reposition );\n }\n\n /**\n * Destroys the component.\n */\n function destroy(): void {\n removeAttribute( list, 'style' );\n }\n\n /**\n * Repositions the slider.\n * This must be called before the Slide component checks the visibility.\n * Do not call `cancel()` here because LazyLoad may emit resize while transitioning.\n */\n function reposition(): void {\n Components.Scroll.cancel();\n jump( Splide.index );\n emit( EVENT_REPOSITIONED );\n }\n\n /**\n * Moves the slider to the dest index with the Transition component.\n *\n * @param dest - A destination index to go to, including clones'.\n * @param index - A slide index.\n * @param prev - A previous index.\n * @param callback - Optional. A callback function invoked after transition ends.\n */\n function move( dest: number, index: number, prev: number, callback?: AnyFunction ): void {\n if ( ! isBusy() ) {\n const { set } = Splide.state;\n const position = getPosition();\n const looping = dest !== index;\n\n waiting = looping || options.waitForTransition;\n set( MOVING );\n emit( EVENT_MOVE, index, prev, dest );\n\n Components.Transition.start( dest, () => {\n looping && jump( index );\n waiting = false;\n set( IDLE );\n emit( EVENT_MOVED, index, prev, dest );\n\n if ( options.trimSpace === 'move' && dest !== prev && position === getPosition() ) {\n Components.Controller.go( dest > prev ? '>' : '<', false, callback );\n } else {\n callback && callback();\n }\n } );\n }\n }\n\n /**\n * Jumps to the slide at the specified index.\n *\n * @param index - An index to jump to.\n */\n function jump( index: number ): void {\n translate( toPosition( index, true ) );\n }\n\n /**\n * Moves the slider to the provided position.\n *\n * @param position - The position to move to.\n * @param preventLoop - Optional. If `true`, sets the provided position as is.\n */\n function translate( position: number, preventLoop?: boolean ): void {\n if ( ! Splide.is( FADE ) ) {\n list.style.transform = `translate${ resolve( 'X' ) }(${ preventLoop ? position : loop( position ) }px)`;\n }\n }\n\n /**\n * Loops the provided position if it exceeds bounds.\n *\n * @param position - A position to loop.\n */\n function loop( position: number ): number {\n if ( ! waiting && Splide.is( LOOP ) ) {\n const diff = orient( position - getPosition() );\n const exceededMin = exceededLimit( false, position ) && diff < 0;\n const exceededMax = exceededLimit( true, position ) && diff > 0;\n\n if ( exceededMin || exceededMax ) {\n position = shift( position, exceededMax );\n }\n }\n\n return position;\n }\n\n /**\n * Adds or subtracts the slider width to the provided position.\n *\n * @param position - A position to shift.\n * @param backwards - Determines whether to shift the slider backwards or forwards.\n *\n * @return The shifted position.\n */\n function shift( position: number, backwards: boolean ): number {\n const excess = position - getLimit( backwards );\n const size = sliderSize();\n position -= sign( excess ) * size * ceil( abs( excess ) / size );\n return position;\n }\n\n /**\n * Cancels transition.\n */\n function cancel(): void {\n waiting = false;\n translate( getPosition() );\n Components.Transition.cancel();\n }\n\n /**\n * Returns the closest index to the position.\n *\n * @param position - A position to convert.\n *\n * @return The closest index to the position.\n */\n function toIndex( position: number ): number {\n const Slides = Components.Slides.get();\n\n let index = 0;\n let minDistance = Infinity;\n\n for ( let i = 0; i < Slides.length; i++ ) {\n const slideIndex = Slides[ i ].index;\n const distance = abs( toPosition( slideIndex, true ) - position );\n\n if ( distance <= minDistance ) {\n minDistance = distance;\n index = slideIndex;\n } else {\n break;\n }\n }\n\n return index;\n }\n\n /**\n * Converts the slide index to the position.\n *\n * @param index - An index to convert.\n * @param trimming - Optional. Whether to trim edge spaces or not.\n *\n * @return The position corresponding with the index.\n */\n function toPosition( index: number, trimming?: boolean ): number {\n const position = orient( totalSize( index - 1 ) - offset( index ) );\n return trimming ? trim( position ) : position;\n }\n\n /**\n * Returns the current position.\n *\n * @return The position of the list element.\n */\n function getPosition(): number {\n const left = resolve( 'left' );\n return rect( list )[ left ] - rect( track )[ left ] + orient( getPadding( false ) );\n }\n\n /**\n * Trims spaces on the edge of the slider.\n *\n * @param position - A position to trim.\n *\n * @return A trimmed position.\n */\n function trim( position: number ): number {\n if ( options.trimSpace && Splide.is( SLIDE ) ) {\n position = clamp( position, 0, orient( sliderSize() - listSize() ) );\n }\n\n return position;\n }\n\n /**\n * Returns the offset amount.\n *\n * @param index - An index.\n */\n function offset( index: number ): number {\n const { focus } = options;\n return focus === 'center' ? ( listSize() - slideSize( index, true ) ) / 2 : +focus * slideSize( index ) || 0;\n }\n\n /**\n * Returns the limit number that the slider can move to.\n *\n * @param max - Determines whether to return the maximum or minimum limit.\n *\n * @return The border number.\n */\n function getLimit( max: boolean ): number {\n return toPosition( max ? Components.Controller.getEnd() : 0, !! options.trimSpace );\n }\n\n /**\n * Checks if the slider can move now or not.\n *\n * @return `true` if the slider can move, or otherwise `false`.\n */\n function isBusy(): boolean {\n return !! waiting;\n }\n\n /**\n * Checks if the provided position exceeds the minimum or maximum limit or not.\n *\n * @param max - Optional. `true` for testing max, `false` for min, and `undefined` for both.\n * @param position - Optional. A position to test. If omitted, tests the current position.\n *\n * @return `true` if the position exceeds the limit, or otherwise `false`.\n */\n function exceededLimit( max?: boolean | undefined, position?: number ): boolean {\n position = isUndefined( position ) ? getPosition() : position;\n const exceededMin = max !== true && orient( position ) < orient( getLimit( false ) );\n const exceededMax = max !== false && orient( position ) > orient( getLimit( true ) );\n return exceededMin || exceededMax;\n }\n\n return {\n mount,\n destroy,\n move,\n jump,\n translate,\n shift,\n cancel,\n toIndex,\n toPosition,\n getPosition,\n getLimit,\n isBusy,\n exceededLimit,\n };\n}\n","import { EVENT_REFRESH, EVENT_UPDATED } from '../../constants/events';\nimport { DEFAULT_EVENT_PRIORITY } from '../../constants/priority';\nimport { LOOP, SLIDE } from '../../constants/types';\nimport { EventInterface } from '../../constructors';\nimport { Splide } from '../../core/Splide/Splide';\nimport { AnyFunction, BaseComponent, Components, Options } from '../../types';\nimport { approximatelyEqual, between, clamp, floor, isString, isUndefined, max } from '../../utils';\n\n\n/**\n * The interface for the Controller component.\n *\n * @since 3.0.0\n */\nexport interface ControllerComponent extends BaseComponent {\n go( control: number | string, allowSameIndex?: boolean, callback?: AnyFunction ): void;\n scroll( destination: number, useIndex?: boolean, snap?: boolean, duration?: number, callback?: AnyFunction ): void;\n getNext( destination?: boolean ): number;\n getPrev( destination?: boolean ): number;\n getEnd(): number;\n setIndex( index: number ): void;\n getIndex( prev?: boolean ): number;\n toIndex( page: number ): number;\n toPage( index: number ): number;\n toDest( position: number ): number;\n hasFocus(): boolean;\n}\n\n/**\n * The component for controlling the slider.\n *\n * @since 3.0.0\n *\n * @param Splide - A Splide instance.\n * @param Components - A collection of components.\n * @param options - Options.\n *\n * @return A Controller component object.\n */\nexport function Controller( Splide: Splide, Components: Components, options: Options ): ControllerComponent {\n const { on } = EventInterface( Splide );\n const { Move } = Components;\n const { getPosition, getLimit } = Move;\n const { isEnough, getLength } = Components.Slides;\n const isLoop = Splide.is( LOOP );\n const isSlide = Splide.is( SLIDE );\n\n /**\n * The current index.\n */\n let currIndex = options.start || 0;\n\n /**\n * The previous index.\n */\n let prevIndex = currIndex;\n\n /**\n * The latest number of slides.\n */\n let slideCount: number;\n\n /**\n * The latest `perMove` value.\n */\n let perMove: number;\n\n /**\n * The latest `perMove` value.\n */\n let perPage: number;\n\n /**\n * Called when the component is mounted.\n */\n function mount(): void {\n init();\n on( [ EVENT_UPDATED, EVENT_REFRESH ], init, DEFAULT_EVENT_PRIORITY - 1 );\n }\n\n /**\n * Initializes some parameters.\n * Needs to check the slides length since the current index may be out of the range after refresh.\n */\n function init(): void {\n slideCount = getLength( true );\n perMove = options.perMove;\n perPage = options.perPage;\n currIndex = clamp( currIndex, 0, slideCount - 1 );\n }\n\n /**\n * Moves the slider by the control pattern.\n *\n * @see `Splide#go()`\n *\n * @param control - A control pattern.\n * @param allowSameIndex - Optional. Determines whether to allow to go to the current index or not.\n * @param callback - Optional. A callback function invoked after transition ends.\n */\n function go( control: number | string, allowSameIndex?: boolean, callback?: AnyFunction ): void {\n const dest = parse( control );\n\n if ( options.useScroll ) {\n scroll( dest, true, true, options.speed, callback );\n } else {\n const index = loop( dest );\n\n if ( index > -1 && ! Move.isBusy() && ( allowSameIndex || index !== currIndex ) ) {\n setIndex( index );\n Move.move( dest, index, prevIndex, callback );\n }\n }\n }\n\n /**\n * Scrolls the slider to the specified destination with updating indices.\n *\n * @param destination - A position or an index to scroll to.\n * @param useIndex - Optional. Whether to use an index as a destination or not.\n * @param snap - Optional. Whether to snap the closest slide or not.\n * @param duration - Optional. Specifies the scroll duration.\n * @param callback - Optional. A callback function invoked after scroll ends.\n */\n function scroll(\n destination: number,\n useIndex?: boolean,\n snap?: boolean,\n duration?: number,\n callback?: AnyFunction\n ): void {\n const dest = useIndex ? destination : toDest( destination );\n\n Components.Scroll.scroll( useIndex || snap ? Move.toPosition( dest, true ) : destination, duration, () => {\n setIndex( Move.toIndex( Move.getPosition() ) );\n callback && callback();\n } );\n }\n\n /**\n * Parses the control and returns a slide index.\n *\n * @param control - A control pattern to parse.\n */\n function parse( control: number | string ): number {\n let index = currIndex;\n\n if ( isString( control ) ) {\n const [ , indicator, number ] = control.match( /([+\\-<>])(\\d+)?/ ) || [];\n\n if ( indicator === '+' || indicator === '-' ) {\n index = computeDestIndex( currIndex + +`${ indicator }${ +number || 1 }`, currIndex, true );\n } else if ( indicator === '>' ) {\n index = number ? toIndex( +number ) : getNext( true );\n } else if ( indicator === '<' ) {\n index = getPrev( true );\n }\n } else {\n if ( isLoop ) {\n index = clamp( control, -perPage, slideCount + perPage - 1 );\n } else {\n index = clamp( control, 0, getEnd() );\n }\n }\n\n return index;\n }\n\n /**\n * Returns a next destination index.\n *\n * @param destination - Optional. Determines whether to get a destination index or a slide one.\n *\n * @return A next index if available, or otherwise `-1`.\n */\n function getNext( destination?: boolean ): number {\n return getAdjacent( false, destination );\n }\n\n /**\n * Returns a previous destination index.\n *\n * @param destination - Optional. Determines whether to get a destination index or a slide one.\n *\n * @return A previous index if available, or otherwise `-1`.\n */\n function getPrev( destination?: boolean ): number {\n return getAdjacent( true, destination );\n }\n\n /**\n * Returns an adjacent destination index.\n *\n * @param prev - Determines whether to return a previous or next index.\n * @param destination - Optional. Determines whether to get a destination index or a slide one.\n *\n * @return An adjacent index if available, or otherwise `-1`.\n */\n function getAdjacent( prev: boolean, destination?: boolean ): number {\n const number = perMove || ( hasFocus() ? 1 : perPage );\n const dest = computeDestIndex( currIndex + number * ( prev ? -1 : 1 ), currIndex );\n\n if ( dest === -1 && isSlide ) {\n if ( ! approximatelyEqual( getPosition(), getLimit( ! prev ), 1 ) ) {\n return prev ? 0 : getEnd();\n }\n }\n\n return destination ? dest : loop( dest );\n }\n\n /**\n * Converts the desired destination index to the valid one.\n * - This may return clone indices if the editor is the loop mode,\n * or `-1` if there is no slide to go.\n * - There are still slides where the slider can go if borders are between `from` and `dest`.\n *\n * @param dest - The desired destination.\n * @param from - A base index.\n * @param incremental - Optional. Whether the control is incremental or not.\n *\n * @return A converted destination index, including clones.\n */\n function computeDestIndex( dest: number, from: number, incremental?: boolean ): number {\n if ( isEnough() ) {\n const end = getEnd();\n\n // Will overrun:\n if ( dest < 0 || dest > end ) {\n if ( between( 0, dest, from, true ) || between( end, from, dest, true ) ) {\n dest = toIndex( toPage( dest ) );\n } else {\n if ( isLoop ) {\n dest = perMove\n ? dest\n : dest < 0 ? - ( slideCount % perPage || perPage ) : slideCount;\n } else if ( options.rewind ) {\n dest = dest < 0 ? end : 0;\n } else {\n dest = -1;\n }\n }\n } else {\n if ( ! isLoop && ! incremental && dest !== from ) {\n dest = perMove ? dest : toIndex( toPage( from ) + ( dest < from ? -1 : 1 ) );\n }\n }\n } else {\n dest = -1;\n }\n\n return dest;\n }\n\n /**\n * Returns the end index where the slider can go.\n * For example, if the slider has 10 slides and the `perPage` option is 3,\n * the slider can go to the slide 8 (the index is 7).\n *\n * @return An end index.\n */\n function getEnd(): number {\n let end = slideCount - perPage;\n\n if ( hasFocus() || ( isLoop && perMove ) ) {\n end = slideCount - 1;\n }\n\n return max( end, 0 );\n }\n\n /**\n * Loops the provided index only in the loop mode.\n *\n * @param index - An index to loop.\n *\n * @return A looped index.\n */\n function loop( index: number ): number {\n if ( isLoop ) {\n return isEnough() ? index % slideCount + ( index < 0 ? slideCount : 0 ) : -1;\n }\n\n return index;\n }\n\n /**\n * Converts the page index to the slide index.\n *\n * @param page - A page index to convert.\n *\n * @return A slide index.\n */\n function toIndex( page: number ): number {\n return clamp( hasFocus() ? page : perPage * page, 0, getEnd() );\n }\n\n /**\n * Converts the slide index to the page index.\n *\n * @param index - An index to convert.\n */\n function toPage( index: number ): number {\n if ( ! hasFocus() ) {\n index = between( index, slideCount - perPage, slideCount - 1 ) ? slideCount - 1 : index;\n index = floor( index / perPage );\n }\n\n return index;\n }\n\n /**\n * Converts the destination position to the dest index.\n *\n * @param destination - A position to convert.\n *\n * @return A dest index.\n */\n function toDest( destination: number ): number {\n const closest = Move.toIndex( destination );\n return isSlide ? clamp( closest, 0, getEnd() ) : closest;\n }\n\n /**\n * Sets a new index and retains old one.\n *\n * @param index - A new index to set.\n */\n function setIndex( index: number ): void {\n if ( index !== currIndex ) {\n prevIndex = currIndex;\n currIndex = index;\n }\n }\n\n /**\n * Returns the current/previous index.\n *\n * @param prev - Optional. Whether to return previous index or not.\n */\n function getIndex( prev?: boolean ): number {\n return prev ? prevIndex : currIndex;\n }\n\n /**\n * Verifies if the focus option is available or not.\n *\n * @return `true` if the slider has the focus option.\n */\n function hasFocus(): boolean {\n return ! isUndefined( options.focus ) || options.isNavigation;\n }\n\n return {\n mount,\n go,\n scroll,\n getNext,\n getPrev,\n getEnd,\n setIndex,\n getIndex,\n toIndex,\n toPage,\n toDest,\n hasFocus,\n };\n}\n","/**\r\n * The namespace for SVG elements.\r\n */\r\nexport const XML_NAME_SPACE = 'http://www.w3.org/2000/svg';\r\n\r\n/**\r\n * The arrow path.\r\n */\r\nexport const PATH = 'm15.5 0.932-4.3 4.38 14.5 14.6-14.5 14.5 4.3 4.4 14.6-14.6 4.4-4.3-4.4-4.4-14.6-14.6z';\r\n\r\n/**\r\n * SVG width and height.\r\n */\r\nexport const SIZE = 40;\r\n","import { ALL_ATTRIBUTES, ARIA_CONTROLS, ARIA_LABEL } from '../../constants/attributes';\r\nimport {\r\n EVENT_ARROWS_MOUNTED,\r\n EVENT_ARROWS_UPDATED,\r\n EVENT_MOUNTED,\r\n EVENT_MOVED,\r\n EVENT_REFRESH,\r\n EVENT_SCROLLED,\r\n EVENT_UPDATED,\r\n} from '../../constants/events';\r\nimport { EventInterface } from '../../constructors';\r\nimport { Splide } from '../../core/Splide/Splide';\r\nimport { BaseComponent, Components, Options } from '../../types';\r\nimport { append, before, child, create, display, parseHtml, remove, removeAttribute, setAttribute } from '../../utils';\r\nimport { PATH, SIZE, XML_NAME_SPACE } from './path';\r\n\r\n\r\n/**\r\n * The interface for the Arrows component.\r\n *\r\n * @since 3.0.0\r\n */\r\nexport interface ArrowsComponent extends BaseComponent {\r\n arrows: { prev?: HTMLButtonElement, next?: HTMLButtonElement };\r\n}\r\n\r\n/**\r\n * The component for handling previous and next arrows.\r\n *\r\n * @since 3.0.0\r\n *\r\n * @param Splide - A Splide instance.\r\n * @param Components - A collection of components.\r\n * @param options - Options.\r\n *\r\n * @return An Arrows component object.\r\n */\r\nexport function Arrows( Splide: Splide, Components: Components, options: Options ): ArrowsComponent {\r\n const { on, bind, emit } = EventInterface( Splide );\r\n const { classes, i18n } = options;\r\n const { Elements, Controller } = Components;\r\n\r\n /**\r\n * The wrapper element.\r\n */\r\n let wrapper = Elements.arrows;\r\n\r\n /**\r\n * The previous arrow element.\r\n */\r\n let prev = Elements.prev;\r\n\r\n /**\r\n * The next arrow element.\r\n */\r\n let next = Elements.next;\r\n\r\n /**\r\n * Indicates whether the component creates arrows or retrieved from the DOM.\r\n */\r\n let created: boolean;\r\n\r\n /**\r\n * An object with previous and next arrows.\r\n */\r\n const arrows: ArrowsComponent[ 'arrows' ] = {};\r\n\r\n /**\r\n * Called when the component is mounted.\r\n */\r\n function mount(): void {\r\n init();\r\n on( EVENT_UPDATED, init );\r\n }\r\n\r\n /**\r\n * Initializes the component.\r\n */\r\n function init(): void {\r\n if ( options.arrows ) {\r\n if ( ! prev || ! next ) {\r\n createArrows();\r\n }\r\n }\r\n\r\n if ( prev && next ) {\r\n if ( ! arrows.prev ) {\r\n const { id } = Elements.track;\r\n\r\n setAttribute( prev, ARIA_CONTROLS, id );\r\n setAttribute( next, ARIA_CONTROLS, id );\r\n\r\n arrows.prev = prev;\r\n arrows.next = next;\r\n\r\n listen();\r\n\r\n emit( EVENT_ARROWS_MOUNTED, prev, next );\r\n } else {\r\n display( wrapper, options.arrows === false ? 'none' : '' );\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Destroys the component.\r\n */\r\n function destroy(): void {\r\n if ( created ) {\r\n remove( wrapper );\r\n } else {\r\n removeAttribute( prev, ALL_ATTRIBUTES );\r\n removeAttribute( next, ALL_ATTRIBUTES );\r\n }\r\n }\r\n\r\n /**\r\n * Listens to some events.\r\n */\r\n function listen(): void {\r\n const { go } = Controller;\r\n on( [ EVENT_MOUNTED, EVENT_MOVED, EVENT_UPDATED, EVENT_REFRESH, EVENT_SCROLLED ], update );\r\n bind( next, 'click', () => { go( '>', true ) } );\r\n bind( prev, 'click', () => { go( '<', true ) } );\r\n }\r\n\r\n /**\r\n * Create arrows and append them to the slider.\r\n */\r\n function createArrows(): void {\r\n wrapper = create( 'div', classes.arrows );\r\n prev = createArrow( true );\r\n next = createArrow( false );\r\n created = true;\r\n\r\n append( wrapper, [ prev, next ] );\r\n before( wrapper, child( options.arrows === 'slider' && Elements.slider || Splide.root ) );\r\n }\r\n\r\n /**\r\n * Creates an arrow button.\r\n *\r\n * @param prev - Determines whether to create a previous or next arrow.\r\n *\r\n * @return A created button element.\r\n */\r\n function createArrow( prev: boolean ): HTMLButtonElement {\r\n const arrow = `