swiper
Version:
Most modern mobile touch slider and framework with hardware accelerated transitions
1,312 lines (1,288 loc) • 213 kB
JavaScript
/**
* Swiper Custom Element 14.0.0
* Most modern mobile touch slider and framework with hardware accelerated transitions
* https://swiperjs.com
*
* Copyright 2014-2026 Vladimir Kharlampidi
*
* Released under the MIT License
*
* Released on: June 26, 2026
*/
(function () {
'use strict';
function classesToTokens(classes = '') {
return classes
.trim()
.split(' ')
.filter((c) => !!c.trim());
}
function deleteProps(obj) {
Object.keys(obj).forEach((key) => {
try {
obj[key] = null;
}
catch {
// no getter for object
}
try {
delete obj[key];
}
catch {
// something got wrong
}
});
}
function nextTick(callback, delay = 0) {
return setTimeout(callback, delay);
}
function now() {
return Date.now();
}
function getComputedStyle$1(el) {
return window.getComputedStyle(el, null);
}
function getTranslate(el, axis = 'x') {
const style = getComputedStyle$1(el);
const transform = style.transform || style.webkitTransform;
if (!transform || transform === 'none')
return 0;
const matrix = new DOMMatrixReadOnly(transform);
return axis === 'x' ? matrix.m41 : matrix.m42;
}
function isObject$1(o) {
return (typeof o === 'object' &&
o !== null &&
o.constructor === Object &&
Object.prototype.toString.call(o).slice(8, -1) === 'Object');
}
function isNode(node) {
if (typeof HTMLElement !== 'undefined' && node instanceof HTMLElement)
return true;
return (!!node &&
typeof node === 'object' &&
(node.nodeType === 1 || node.nodeType === 11));
}
function extend$1(target, ...sources) {
const to = Object(target);
for (let i = 0; i < sources.length; i += 1) {
const nextSource = sources[i];
if (nextSource === undefined || nextSource === null || isNode(nextSource))
continue;
const sourceObj = nextSource;
const keysArray = Object.keys(Object(sourceObj)).filter((key) => key !== '__proto__' && key !== 'constructor' && key !== 'prototype');
for (const nextKey of keysArray) {
const desc = Object.getOwnPropertyDescriptor(sourceObj, nextKey);
if (!desc || !desc.enumerable)
continue;
const sourceVal = sourceObj[nextKey];
if (isObject$1(to[nextKey]) && isObject$1(sourceVal)) {
if (sourceVal.__swiper__) {
to[nextKey] = sourceVal;
}
else {
extend$1(to[nextKey], sourceVal);
}
}
else if (!isObject$1(to[nextKey]) && isObject$1(sourceVal)) {
to[nextKey] = {};
if (sourceVal.__swiper__) {
to[nextKey] = sourceVal;
}
else {
extend$1(to[nextKey], sourceVal);
}
}
else {
to[nextKey] = sourceVal;
}
}
}
return to;
}
function setCSSProperty(el, varName, varValue) {
el.style.setProperty(varName, varValue);
}
function elementChildren(element, selector = '') {
const children = [...element.children];
if (element instanceof HTMLSlotElement) {
children.push(...element.assignedElements());
}
return selector ? children.filter((el) => el.matches(selector)) : children;
}
function elementIsChildOfSlot(el, slot) {
const queue = [slot];
while (queue.length > 0) {
const cur = queue.shift();
if (el === cur)
return true;
queue.push(...cur.children, ...(cur.shadowRoot ? cur.shadowRoot.children : []), ...(cur.assignedElements
? cur.assignedElements()
: []));
}
return false;
}
function elementIsChildOf(el, parent) {
let isChild = parent.contains(el);
if (!isChild && parent instanceof HTMLSlotElement) {
const children = [...parent.assignedElements()];
isChild = children.includes(el);
if (!isChild)
isChild = elementIsChildOfSlot(el, parent);
}
return isChild;
}
function showWarning(text) {
try {
console.warn(text);
}
catch {
// err
}
}
function createElement(tag, classes = []) {
const el = document.createElement(tag);
el.classList.add(...(Array.isArray(classes) ? classes : classesToTokens(classes)));
return el;
}
function elementPrevAll(el, selector) {
const prevEls = [];
let prev = el.previousElementSibling;
while (prev) {
if (!selector || prev.matches(selector))
prevEls.push(prev);
prev = prev.previousElementSibling;
}
return prevEls;
}
function elementNextAll(el, selector) {
const nextEls = [];
let next = el.nextElementSibling;
while (next) {
if (!selector || next.matches(selector))
nextEls.push(next);
next = next.nextElementSibling;
}
return nextEls;
}
function elementStyle(el, prop) {
return window.getComputedStyle(el, null).getPropertyValue(prop);
}
function elementIndex(el) {
if (!el || !el.parentNode)
return undefined;
return [...el.parentNode.children].indexOf(el);
}
function elementParents(el, selector) {
const parents = [];
let parent = el.parentElement;
while (parent) {
parents.push(parent);
parent = parent.parentElement;
}
return parents;
}
function elementOuterSize(el, size, includeMargins) {
{
const style = window.getComputedStyle(el, null);
return (el[size === 'width' ? 'offsetWidth' : 'offsetHeight'] +
parseFloat(style.getPropertyValue(size === 'width' ? 'margin-right' : 'margin-top')) +
parseFloat(style.getPropertyValue(size === 'width' ? 'margin-left' : 'margin-bottom')));
}
}
function setInnerHTML(el, html = '') {
const tt = globalThis.trustedTypes;
if (typeof tt !== 'undefined') {
el.innerHTML = tt.createPolicy('html', { createHTML: (s) => s }).createHTML(html);
}
else {
el.innerHTML = html;
}
}
let supportCached;
function calcSupport() {
if (typeof window === 'undefined')
return { touch: false };
return {
touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0,
};
}
function getSupport() {
if (!supportCached)
supportCached = calcSupport();
return supportCached;
}
let deviceCached;
function calcDevice({ userAgent } = {}) {
if (typeof window === 'undefined')
return { ios: false, android: false };
const support = getSupport();
const platform = navigator.platform;
const ua = userAgent || navigator.userAgent;
const device = { ios: false, android: false };
const isAndroid = /(Android);?[\s/]+([\d.]+)?/.test(ua);
const isIPhoneOrIPod = /(iPhone\sOS|iOS|iPod)/.test(ua);
const isIPadDirect = /iPad/.test(ua);
// iPad on iPadOS 13+ reports as MacIntel; distinguish from a real Mac by touch capability.
const isIPadMasquerade = platform === 'MacIntel' && support.touch && navigator.maxTouchPoints > 1;
const isIPad = isIPadDirect || isIPadMasquerade;
const isWindows = platform === 'Win32';
if (isAndroid && !isWindows) {
device.os = 'android';
device.android = true;
}
if (isIPad || isIPhoneOrIPod) {
device.os = 'ios';
device.ios = true;
}
return device;
}
function getDevice(overrides = {}) {
if (!deviceCached)
deviceCached = calcDevice(overrides);
return deviceCached;
}
let browserCached;
function calcBrowser() {
if (typeof window === 'undefined') {
return { isSafari: false, isWebView: false, need3dFix: false };
}
const device = getDevice();
const ua = navigator.userAgent;
const uaLower = ua.toLowerCase();
const isSafari = uaLower.includes('safari') && !uaLower.includes('chrome') && !uaLower.includes('android');
const isWebView = /(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(ua);
// 3D transform glitches still affect iOS WebView and Safari at the baseline (16.4+).
const need3dFix = isSafari || (isWebView && device.ios);
return { isSafari, isWebView, need3dFix };
}
function getBrowser() {
if (!browserCached)
browserCached = calcBrowser();
return browserCached;
}
const processLazyPreloader = (swiper, imageEl) => {
if (!swiper || swiper.destroyed || !swiper.params)
return;
const slideSelector = () => (swiper.isElement ? 'swiper-slide' : `.${swiper.params.slideClass}`);
const slideEl = imageEl.closest(slideSelector());
if (slideEl) {
let lazyEl = slideEl.querySelector(`.${swiper.params.lazyPreloaderClass}`);
if (!lazyEl && swiper.isElement) {
if (slideEl.shadowRoot) {
lazyEl = slideEl.shadowRoot.querySelector(`.${swiper.params.lazyPreloaderClass}`);
}
else {
requestAnimationFrame(() => {
if (slideEl.shadowRoot) {
const innerLazy = slideEl.shadowRoot.querySelector(`.${swiper.params.lazyPreloaderClass}`);
if (innerLazy && !innerLazy.lazyPreloaderManaged)
innerLazy.remove();
}
});
}
}
if (lazyEl && !lazyEl.lazyPreloaderManaged)
lazyEl.remove();
}
};
const unlazy = (swiper, index) => {
if (!swiper.slides[index])
return;
const imageEl = swiper.slides[index].querySelector('[loading="lazy"]');
if (imageEl)
imageEl.removeAttribute('loading');
};
const preload = (swiper) => {
if (!swiper || swiper.destroyed || !swiper.params)
return;
let amount = swiper.params.lazyPreloadPrevNext;
const len = swiper.slides.length;
if (!len || !amount || amount < 0)
return;
amount = Math.min(amount, len);
const slidesPerView = swiper.params.slidesPerView === 'auto'
? swiper.slidesPerViewDynamic()
: Math.ceil(swiper.params.slidesPerView);
const activeIndex = swiper.activeIndex;
if (swiper.params.grid && (swiper.params.grid.rows ?? 1) > 1) {
const activeColumn = activeIndex;
const preloadColumns = [activeColumn - amount];
preloadColumns.push(...Array.from({ length: amount }).map((_, i) => activeColumn + slidesPerView + i));
swiper.slides.forEach((slideEl, i) => {
if (slideEl.column !== undefined && preloadColumns.includes(slideEl.column))
unlazy(swiper, i);
});
return;
}
const slideIndexLastInView = activeIndex + slidesPerView - 1;
if (swiper.params.rewind || swiper.params.loop) {
for (let i = activeIndex - amount; i <= slideIndexLastInView + amount; i += 1) {
const realIndex = ((i % len) + len) % len;
if (realIndex < activeIndex || realIndex > slideIndexLastInView)
unlazy(swiper, realIndex);
}
}
else {
for (let i = Math.max(activeIndex - amount, 0); i <= Math.min(slideIndexLastInView + amount, len - 1); i += 1) {
if (i !== activeIndex && (i > slideIndexLastInView || i < activeIndex)) {
unlazy(swiper, i);
}
}
}
};
function getBreakpoint(breakpoints, base = 'window', containerEl) {
if (!breakpoints || (base === 'container' && !containerEl))
return undefined;
let breakpoint = false;
const currentHeight = base === 'window' ? window.innerHeight : containerEl.clientHeight;
const points = Object.keys(breakpoints).map((point) => {
if (typeof point === 'string' && point.indexOf('@') === 0) {
const minRatio = parseFloat(point.substr(1));
const value = currentHeight * minRatio;
return { value, point };
}
return { value: point, point };
});
points.sort((a, b) => parseInt(String(a.value), 10) - parseInt(String(b.value), 10));
for (let i = 0; i < points.length; i += 1) {
const { point, value } = points[i];
if (base === 'window') {
if (window.matchMedia(`(min-width: ${value}px)`).matches) {
breakpoint = point;
}
}
else if (value <= containerEl.clientWidth) {
breakpoint = point;
}
}
return breakpoint || 'max';
}
const isGridEnabled = (swiper, params) => {
return !!(swiper.grid && params.grid && params.grid.rows > 1);
};
function setBreakpoint() {
const swiper = this;
const { realIndex, initialized, params, el } = swiper;
const breakpoints = params.breakpoints;
if (!breakpoints || (breakpoints && Object.keys(breakpoints).length === 0))
return;
// Get breakpoint for window/container width and update parameters
const breakpointsBase = params.breakpointsBase === 'window' || !params.breakpointsBase
? params.breakpointsBase
: 'container';
const breakpointContainer = ['window', 'container'].includes(params.breakpointsBase) || !params.breakpointsBase
? swiper.el
: document.querySelector(params.breakpointsBase);
const breakpoint = swiper.getBreakpoint(breakpoints, breakpointsBase, breakpointContainer);
if (!breakpoint || swiper.currentBreakpoint === breakpoint)
return;
const breakpointsRecord = breakpoints;
const breakpointOnlyParams = breakpoint in breakpointsRecord ? breakpointsRecord[breakpoint] : undefined;
const breakpointParams = breakpointOnlyParams || swiper.originalParams;
const wasMultiRow = isGridEnabled(swiper, params);
const isMultiRow = isGridEnabled(swiper, breakpointParams);
const wasGrabCursor = swiper.params.grabCursor;
const isGrabCursor = breakpointParams.grabCursor;
const wasEnabled = params.enabled;
if (wasMultiRow && !isMultiRow) {
el.classList.remove(`${params.containerModifierClass}grid`, `${params.containerModifierClass}grid-column`);
swiper.emitContainerClasses();
}
else if (!wasMultiRow && isMultiRow) {
el.classList.add(`${params.containerModifierClass}grid`);
if ((breakpointParams.grid.fill && breakpointParams.grid.fill === 'column') ||
(!breakpointParams.grid.fill && params.grid.fill === 'column')) {
el.classList.add(`${params.containerModifierClass}grid-column`);
}
swiper.emitContainerClasses();
}
if (wasGrabCursor && !isGrabCursor) {
swiper.unsetGrabCursor();
}
else if (!wasGrabCursor && isGrabCursor) {
swiper.setGrabCursor();
}
const moduleOpt = (opts, prop) => opts[prop];
['navigation', 'pagination', 'scrollbar'].forEach((prop) => {
const bpOpts = moduleOpt(breakpointParams, prop);
if (typeof bpOpts === 'undefined')
return;
const paramsOpts = moduleOpt(params, prop);
const wasModuleEnabled = typeof paramsOpts === 'object' && paramsOpts !== null && paramsOpts.enabled;
const isModuleEnabled = typeof bpOpts === 'object' && bpOpts !== null && bpOpts.enabled;
const moduleApi = swiper[prop];
if (wasModuleEnabled && !isModuleEnabled)
moduleApi?.disable?.();
if (!wasModuleEnabled && isModuleEnabled)
moduleApi?.enable?.();
});
const directionChanged = breakpointParams.direction && breakpointParams.direction !== params.direction;
const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged);
const wasLoop = params.loop;
if (directionChanged && initialized) {
swiper.changeDirection();
}
extend$1(swiper.params, breakpointParams);
const isEnabled = swiper.params.enabled;
const hasLoop = swiper.params.loop;
Object.assign(swiper, {
allowTouchMove: swiper.params.allowTouchMove,
allowSlideNext: swiper.params.allowSlideNext,
allowSlidePrev: swiper.params.allowSlidePrev,
});
if (wasEnabled && !isEnabled) {
swiper.disable();
}
else if (!wasEnabled && isEnabled) {
swiper.enable();
}
swiper.currentBreakpoint = breakpoint;
swiper.emit('_beforeBreakpoint', breakpointParams);
if (initialized) {
if (needsReLoop) {
swiper.loopDestroy();
swiper.loopCreate(realIndex);
swiper.updateSlides();
}
else if (!wasLoop && hasLoop) {
swiper.loopCreate(realIndex);
swiper.updateSlides();
}
else if (wasLoop && !hasLoop) {
swiper.loopDestroy();
}
}
swiper.emit('breakpoint', breakpointParams);
}
var breakpoints = { setBreakpoint, getBreakpoint };
function checkOverflow() {
const swiper = this;
const { isLocked: wasLocked, params } = swiper;
const { slidesOffsetBefore } = params;
if (slidesOffsetBefore) {
const lastSlideIndex = swiper.slides.length - 1;
const lastSlideRightEdge = swiper.slidesGrid[lastSlideIndex] +
swiper.slidesSizesGrid[lastSlideIndex] +
slidesOffsetBefore * 2;
swiper.isLocked = swiper.size > lastSlideRightEdge;
}
else {
swiper.isLocked = swiper.snapGrid.length === 1;
}
if (params.allowSlideNext === true) {
swiper.allowSlideNext = !swiper.isLocked;
}
if (params.allowSlidePrev === true) {
swiper.allowSlidePrev = !swiper.isLocked;
}
if (wasLocked && wasLocked !== swiper.isLocked) {
swiper.isEnd = false;
}
if (wasLocked !== swiper.isLocked) {
swiper.emit(swiper.isLocked ? 'lock' : 'unlock');
}
}
var checkOverflow$1 = { checkOverflow };
function prepareClasses(entries, prefix) {
const resultClasses = [];
entries.forEach((item) => {
if (typeof item === 'object') {
Object.keys(item).forEach((classNames) => {
if (item[classNames]) {
resultClasses.push(prefix + classNames);
}
});
}
else if (typeof item === 'string') {
resultClasses.push(prefix + item);
}
});
return resultClasses;
}
function addClasses() {
const swiper = this;
const { classNames, params, rtl, el, device } = swiper;
// oxfmt-ignore
const suffixes = prepareClasses([
'initialized',
params.direction,
{ 'free-mode': swiper.params.freeMode && params.freeMode.enabled },
{ 'autoheight': params.autoHeight },
{ 'rtl': rtl },
{ 'grid': params.grid && params.grid.rows > 1 },
{ 'grid-column': params.grid && params.grid.rows > 1 && params.grid.fill === 'column' },
{ 'android': device.android },
{ 'ios': device.ios },
{ 'css-mode': params.cssMode },
{ 'centered': params.cssMode && params.centeredSlides },
{ 'watch-progress': params.watchSlidesProgress },
], params.containerModifierClass);
classNames.push(...suffixes);
el.classList.add(...classNames);
swiper.emitContainerClasses();
}
function removeClasses() {
const swiper = this;
const { el, classNames } = swiper;
if (!el || typeof el === 'string')
return;
el.classList.remove(...classNames);
swiper.emitContainerClasses();
}
var classes = { addClasses, removeClasses };
const defaults = {
init: true,
direction: 'horizontal',
oneWayMovement: false,
swiperElementNodeName: 'SWIPER-CONTAINER',
touchEventsTarget: 'wrapper',
initialSlide: 0,
speed: 300,
cssMode: false,
updateOnWindowResize: true,
resizeObserver: true,
nested: false,
createElements: false,
eventsPrefix: 'swiper',
enabled: true,
focusableElements: 'input, select, option, textarea, button, video, label',
// Overrides
width: null,
height: null,
//
preventInteractionOnTransition: false,
// ssr
userAgent: null,
url: null,
// To support iOS's swipe-to-go-back gesture (when being used in-app).
edgeSwipeDetection: false,
edgeSwipeThreshold: 20,
// Autoheight
autoHeight: false,
// Set wrapper width
setWrapperSize: false,
// Virtual Translate
virtualTranslate: false,
// Effects
effect: 'slide',
// Breakpoints
breakpoints: undefined,
breakpointsBase: 'window',
// Slides grid
spaceBetween: 0,
slidesPerView: 1,
slidesPerGroup: 1,
slidesPerGroupSkip: 0,
slidesPerGroupAuto: false,
centeredSlides: false,
centeredSlidesBounds: false,
slidesOffsetBefore: 0,
slidesOffsetAfter: 0,
normalizeSlideIndex: true,
centerInsufficientSlides: false,
snapToSlideEdge: false,
// Disable swiper and hide navigation when container not overflow
watchOverflow: true,
// Round length
roundLengths: false,
// Touches
touchRatio: 1,
touchAngle: 45,
simulateTouch: true,
shortSwipes: true,
longSwipes: true,
longSwipesRatio: 0.5,
longSwipesMs: 300,
followFinger: true,
allowTouchMove: true,
threshold: 5,
touchMoveStopPropagation: false,
touchStartPreventDefault: true,
touchStartForcePreventDefault: false,
touchReleaseOnEdges: false,
// Unique Navigation Elements
uniqueNavElements: true,
// Resistance
resistance: true,
resistanceRatio: 0.85,
// Progress
watchSlidesProgress: false,
// Cursor
grabCursor: false,
// Clicks
preventClicks: true,
preventClicksPropagation: true,
slideToClickedSlide: false,
// loop
loop: false,
loopAddBlankSlides: true,
loopAdditionalSlides: 0,
loopPreventsSliding: true,
// rewind
rewind: false,
// Swiping/no swiping
allowSlidePrev: true,
allowSlideNext: true,
swipeHandler: null,
noSwiping: true,
noSwipingClass: 'swiper-no-swiping',
noSwipingSelector: null,
// Passive Listeners
passiveListeners: true,
maxBackfaceHiddenSlides: 10,
// NS
containerModifierClass: 'swiper-',
slideClass: 'swiper-slide',
slideBlankClass: 'swiper-slide-blank',
slideActiveClass: 'swiper-slide-active',
slideVisibleClass: 'swiper-slide-visible',
slideFullyVisibleClass: 'swiper-slide-fully-visible',
slideNextClass: 'swiper-slide-next',
slidePrevClass: 'swiper-slide-prev',
wrapperClass: 'swiper-wrapper',
lazyPreloaderClass: 'swiper-lazy-preloader',
lazyPreloadPrevNext: 0,
// Callbacks
runCallbacksOnInit: true,
// Internals
_emitClasses: false,
};
var eventsEmitter = {
on(events, handler, priority) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (typeof handler !== 'function')
return self;
const method = priority ? 'unshift' : 'push';
events.split(' ').forEach((event) => {
if (!self.eventsListeners[event])
self.eventsListeners[event] = [];
self.eventsListeners[event][method](handler);
});
return self;
},
once(events, handler, priority) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (typeof handler !== 'function')
return self;
const onceHandler = function onceHandlerFn(...args) {
self.off(events, onceHandler);
if (onceHandler.__emitterProxy) {
delete onceHandler.__emitterProxy;
}
handler.apply(self, args);
};
onceHandler.__emitterProxy = handler;
return self.on(events, onceHandler, priority);
},
onAny(handler, priority) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (typeof handler !== 'function')
return self;
const method = priority ? 'unshift' : 'push';
if (self.eventsAnyListeners.indexOf(handler) < 0) {
self.eventsAnyListeners[method](handler);
}
return self;
},
offAny(handler) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (!self.eventsAnyListeners)
return self;
const index = self.eventsAnyListeners.indexOf(handler);
if (index >= 0) {
self.eventsAnyListeners.splice(index, 1);
}
return self;
},
off(events, handler) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (!self.eventsListeners)
return self;
events.split(' ').forEach((event) => {
if (typeof handler === 'undefined') {
self.eventsListeners[event] = [];
}
else if (self.eventsListeners[event]) {
self.eventsListeners[event].forEach((eventHandler, index) => {
if (eventHandler === handler ||
(eventHandler.__emitterProxy && eventHandler.__emitterProxy === handler)) {
self.eventsListeners[event].splice(index, 1);
}
});
}
});
return self;
},
emit(...args) {
const self = this;
if (!self.eventsListeners || self.destroyed)
return self;
if (!self.eventsListeners)
return self;
let events;
let data;
let context;
if (typeof args[0] === 'string' || Array.isArray(args[0])) {
events = args[0];
data = args.slice(1, args.length);
context = self;
}
else {
const opts = args[0];
events = opts.events;
data = opts.data ?? [];
context = opts.context || self;
}
data.unshift(context);
const eventsArray = Array.isArray(events) ? events : events.split(' ');
eventsArray.forEach((event) => {
if (self.eventsAnyListeners && self.eventsAnyListeners.length) {
self.eventsAnyListeners.forEach((eventHandler) => {
eventHandler.apply(context, [event, ...data]);
});
}
if (self.eventsListeners && self.eventsListeners[event]) {
self.eventsListeners[event].forEach((eventHandler) => {
eventHandler.apply(context, data);
});
}
});
return self;
},
};
function onClick(e) {
const swiper = this;
if (swiper.destroyed)
return;
if (!swiper.enabled)
return;
if (!swiper.allowClick) {
if (swiper.params.preventClicks)
e.preventDefault();
if (swiper.params.preventClicksPropagation && swiper.animating) {
e.stopPropagation();
e.stopImmediatePropagation();
}
}
}
function onDocumentTouchStart() {
const swiper = this;
if (swiper.destroyed)
return;
if (swiper.documentTouchHandlerProceeded)
return;
swiper.documentTouchHandlerProceeded = true;
if (swiper.params.touchReleaseOnEdges) {
swiper.el.style.touchAction = 'auto';
}
}
function onLoad(e) {
const swiper = this;
if (swiper.destroyed)
return;
processLazyPreloader(swiper, e.target);
if (swiper.params.cssMode ||
(swiper.params.slidesPerView !== 'auto' && !swiper.params.autoHeight)) {
return;
}
swiper.update();
}
function onResize() {
const swiper = this;
const { params, el } = swiper;
if (el && el.offsetWidth === 0)
return;
// Breakpoints
if (params.breakpoints) {
swiper.setBreakpoint();
}
// Save locks
const { allowSlideNext, allowSlidePrev, snapGrid } = swiper;
const isVirtual = swiper.virtual && swiper.params.virtual?.enabled;
// Disable locks on resize
swiper.allowSlideNext = true;
swiper.allowSlidePrev = true;
swiper.updateSize();
swiper.updateSlides();
swiper.updateSlidesClasses();
const isVirtualLoop = isVirtual && params.loop;
if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) &&
swiper.isEnd &&
!swiper.isBeginning &&
!swiper.params.centeredSlides &&
!isVirtualLoop) {
const slidesLength = isVirtual ? swiper.virtual.slides.length : swiper.slides.length;
swiper.slideTo(slidesLength - 1, 0, false, true);
}
else {
if (swiper.params.loop && !isVirtual) {
swiper.slideToLoop(swiper.realIndex, 0, false, true);
}
else {
swiper.slideTo(swiper.activeIndex, 0, false, true);
}
}
if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) {
const autoplay = swiper.autoplay;
clearTimeout(autoplay.resizeTimeout);
autoplay.resizeTimeout = setTimeout(() => {
if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) {
swiper.autoplay.resume();
}
}, 500);
}
// Return locks after resize
swiper.allowSlidePrev = allowSlidePrev;
swiper.allowSlideNext = allowSlideNext;
if (swiper.params.watchOverflow && snapGrid !== swiper.snapGrid) {
swiper.checkOverflow();
}
}
function onScroll() {
const swiper = this;
if (swiper.destroyed)
return;
const { wrapperEl, rtlTranslate, enabled } = swiper;
if (!enabled)
return;
swiper.previousTranslate = swiper.translate;
if (swiper.isHorizontal()) {
swiper.translate = -wrapperEl.scrollLeft;
}
else {
swiper.translate = -wrapperEl.scrollTop;
}
if (swiper.translate === 0)
swiper.translate = 0;
swiper.updateActiveIndex();
swiper.updateSlidesClasses();
let newProgress;
const translatesDiff = swiper.maxTranslate() - swiper.minTranslate();
if (translatesDiff === 0) {
newProgress = 0;
}
else {
newProgress = (swiper.translate - swiper.minTranslate()) / translatesDiff;
}
if (newProgress !== swiper.progress) {
swiper.updateProgress(rtlTranslate ? -swiper.translate : swiper.translate);
}
swiper.emit('setTranslate', swiper.translate, false);
}
function onTouchEnd(event) {
const swiper = this;
if (swiper.destroyed)
return;
const data = swiper.touchEventsData;
let e = event.originalEvent ?? event;
const isTouchEvent = e.type === 'touchend' || e.type === 'touchcancel';
if (!isTouchEvent) {
if (data.touchId !== null)
return; // return from pointer if we use touch
const pe = e;
if (pe.pointerId !== data.pointerId)
return;
}
else {
const te = e;
const found = [...te.changedTouches].find((t) => t.identifier === data.touchId);
if (!found || found.identifier !== data.touchId)
return;
}
if (['pointercancel', 'pointerout', 'pointerleave', 'contextmenu'].includes(e.type)) {
const proceed = ['pointercancel', 'contextmenu'].includes(e.type) &&
(swiper.browser.isSafari || swiper.browser.isWebView);
if (!proceed) {
return;
}
}
data.pointerId = null;
data.touchId = null;
const { params, touches, rtlTranslate: rtl, slidesGrid, enabled } = swiper;
if (!enabled)
return;
if (!params.simulateTouch && e.pointerType === 'mouse')
return;
if (data.allowTouchCallbacks) {
swiper.emit('touchEnd', e);
}
data.allowTouchCallbacks = false;
if (!data.isTouched) {
if (data.isMoved && params.grabCursor) {
swiper.setGrabCursor(false);
}
data.isMoved = false;
data.startMoving = false;
return;
}
// Return Grab Cursor
if (params.grabCursor &&
data.isMoved &&
data.isTouched &&
(swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
swiper.setGrabCursor(false);
}
// Time diff
const touchEndTime = now();
const timeDiff = touchEndTime - data.touchStartTime;
// Tap, doubleTap, Click
if (swiper.allowClick) {
// Legacy `e.path` was a non-standard Chrome extension; `composedPath()` is the modern API.
const pathTree = e.path ?? (e.composedPath && e.composedPath());
swiper.updateClickedSlide((pathTree && pathTree[0]), pathTree);
swiper.emit('tap click', e);
if (timeDiff < 300 && touchEndTime - data.lastClickTime < 300) {
swiper.emit('doubleTap doubleClick', e);
}
}
data.lastClickTime = now();
nextTick(() => {
if (!swiper.destroyed)
swiper.allowClick = true;
});
if (!data.isTouched ||
!data.isMoved ||
!swiper.swipeDirection ||
(touches.diff === 0 && !data.loopSwapReset) ||
(data.currentTranslate === data.startTranslate && !data.loopSwapReset)) {
data.isTouched = false;
data.isMoved = false;
data.startMoving = false;
return;
}
data.isTouched = false;
data.isMoved = false;
data.startMoving = false;
let currentPos;
if (params.followFinger) {
currentPos = rtl ? swiper.translate : -swiper.translate;
}
else {
currentPos = -(data.currentTranslate ?? 0);
}
if (params.cssMode) {
return;
}
if (params.freeMode && params.freeMode.enabled) {
swiper.freeMode.onTouchEnd({ currentPos });
return;
}
// Find current slide
const swipeToLast = currentPos >= -swiper.maxTranslate() && !swiper.params.loop;
let stopIndex = 0;
let groupSize = swiper.slidesSizesGrid[0];
for (let i = 0; i < slidesGrid.length; i += i < params.slidesPerGroupSkip ? 1 : params.slidesPerGroup) {
const increment = i < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup;
if (typeof slidesGrid[i + increment] !== 'undefined') {
if (swipeToLast ||
(currentPos >= slidesGrid[i] && currentPos < slidesGrid[i + increment])) {
stopIndex = i;
groupSize = slidesGrid[i + increment] - slidesGrid[i];
}
}
else if (swipeToLast || currentPos >= slidesGrid[i]) {
stopIndex = i;
groupSize = slidesGrid[slidesGrid.length - 1] - slidesGrid[slidesGrid.length - 2];
}
}
let rewindFirstIndex = null;
let rewindLastIndex = null;
if (params.rewind) {
if (swiper.isBeginning) {
rewindLastIndex =
params.virtual?.enabled && swiper.virtual
? swiper.virtual.slides.length - 1
: swiper.slides.length - 1;
}
else if (swiper.isEnd) {
rewindFirstIndex = 0;
}
}
// Find current slide size
const ratio = (currentPos - slidesGrid[stopIndex]) / groupSize;
const increment = stopIndex < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup;
if (timeDiff > params.longSwipesMs) {
// Long touches
if (!params.longSwipes) {
swiper.slideTo(swiper.activeIndex);
return;
}
if (swiper.swipeDirection === 'next') {
if (ratio >= params.longSwipesRatio)
swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment);
else
swiper.slideTo(stopIndex);
}
if (swiper.swipeDirection === 'prev') {
if (ratio > 1 - params.longSwipesRatio) {
swiper.slideTo(stopIndex + increment);
}
else if (rewindLastIndex !== null &&
ratio < 0 &&
Math.abs(ratio) > params.longSwipesRatio) {
swiper.slideTo(rewindLastIndex);
}
else {
swiper.slideTo(stopIndex);
}
}
}
else {
// Short swipes
if (!params.shortSwipes) {
swiper.slideTo(swiper.activeIndex);
return;
}
const isNavButtonTarget = swiper.navigation &&
(e.target === swiper.navigation.nextEl || e.target === swiper.navigation.prevEl);
if (!isNavButtonTarget) {
if (swiper.swipeDirection === 'next') {
swiper.slideTo(rewindFirstIndex !== null ? rewindFirstIndex : stopIndex + increment);
}
if (swiper.swipeDirection === 'prev') {
swiper.slideTo(rewindLastIndex !== null ? rewindLastIndex : stopIndex);
}
}
else if (e.target === swiper.navigation.nextEl) {
swiper.slideTo(stopIndex + increment);
}
else {
swiper.slideTo(stopIndex);
}
}
}
function onTouchMove(event) {
const swiper = this;
if (swiper.destroyed)
return;
const data = swiper.touchEventsData;
const { params, touches, rtlTranslate: rtl, enabled } = swiper;
if (!enabled)
return;
if (!params.simulateTouch && event.pointerType === 'mouse')
return;
// Legacy event wrappers nest the native event under .originalEvent.
const wrapped = event;
const e = wrapped.originalEvent ?? wrapped;
if (e.type === 'pointermove') {
if (data.touchId !== null)
return; // return from pointer if we use touch
const pe = e;
if (pe.pointerId !== data.pointerId)
return;
}
let targetTouch;
if (e.type === 'touchmove') {
const te = e;
const found = [...te.changedTouches].find((t) => t.identifier === data.touchId);
if (!found || found.identifier !== data.touchId)
return;
targetTouch = found;
}
else {
targetTouch = e;
}
if (!data.isTouched) {
if (data.startMoving && data.isScrolling) {
swiper.emit('touchMoveOpposite', e);
}
return;
}
const pageX = targetTouch.pageX;
const pageY = targetTouch.pageY;
if (e.preventedByNestedSwiper) {
touches.startX = pageX;
touches.startY = pageY;
return;
}
if (!swiper.allowTouchMove) {
if (!e.target.matches(data.focusableElements)) {
swiper.allowClick = false;
}
if (data.isTouched) {
Object.assign(touches, {
startX: pageX,
startY: pageY,
currentX: pageX,
currentY: pageY,
});
data.touchStartTime = now();
}
return;
}
if (params.touchReleaseOnEdges && !params.loop) {
if (swiper.isVertical()) {
// Vertical
if ((pageY < touches.startY && swiper.translate <= swiper.maxTranslate()) ||
(pageY > touches.startY && swiper.translate >= swiper.minTranslate())) {
data.isTouched = false;
data.isMoved = false;
return;
}
}
else if (rtl &&
((pageX > touches.startX && -swiper.translate <= swiper.maxTranslate()) ||
(pageX < touches.startX && -swiper.translate >= swiper.minTranslate()))) {
return;
}
else if (!rtl &&
((pageX < touches.startX && swiper.translate <= swiper.maxTranslate()) ||
(pageX > touches.startX && swiper.translate >= swiper.minTranslate()))) {
return;
}
}
if (document.activeElement &&
document.activeElement.matches(data.focusableElements) &&
document.activeElement !== e.target &&
e.pointerType !== 'mouse') {
document.activeElement.blur();
}
if (document.activeElement) {
if (e.target === document.activeElement &&
e.target.matches(data.focusableElements)) {
data.isMoved = true;
swiper.allowClick = false;
return;
}
}
if (data.allowTouchCallbacks) {
swiper.emit('touchMove', e);
}
touches.previousX = touches.currentX;
touches.previousY = touches.currentY;
touches.currentX = pageX;
touches.currentY = pageY;
const diffX = touches.currentX - touches.startX;
const diffY = touches.currentY - touches.startY;
if (swiper.params.threshold && Math.sqrt(diffX ** 2 + diffY ** 2) < swiper.params.threshold)
return;
if (typeof data.isScrolling === 'undefined') {
let touchAngle;
if ((swiper.isHorizontal() && touches.currentY === touches.startY) ||
(swiper.isVertical() && touches.currentX === touches.startX)) {
data.isScrolling = false;
}
else {
if (diffX * diffX + diffY * diffY >= 25) {
touchAngle = (Math.atan2(Math.abs(diffY), Math.abs(diffX)) * 180) / Math.PI;
data.isScrolling = swiper.isHorizontal()
? touchAngle > params.touchAngle
: 90 - touchAngle > params.touchAngle;
}
}
}
if (data.isScrolling) {
swiper.emit('touchMoveOpposite', e);
}
if (typeof data.startMoving === 'undefined') {
if (touches.currentX !== touches.startX || touches.currentY !== touches.startY) {
data.startMoving = true;
}
}
if (data.isScrolling || (e.type === 'touchmove' && data.preventTouchMoveFromPointerMove)) {
data.isTouched = false;
return;
}
if (!data.startMoving) {
return;
}
swiper.allowClick = false;
if (!params.cssMode && e.cancelable) {
e.preventDefault();
}
if (params.touchMoveStopPropagation && !params.nested) {
e.stopPropagation();
}
let diff = swiper.isHorizontal() ? diffX : diffY;
let touchesDiff = swiper.isHorizontal()
? touches.currentX - touches.previousX
: touches.currentY - touches.previousY;
if (params.oneWayMovement) {
diff = Math.abs(diff) * (rtl ? 1 : -1);
touchesDiff = Math.abs(touchesDiff) * (rtl ? 1 : -1);
}
touches.diff = diff;
diff *= params.touchRatio;
if (rtl) {
diff = -diff;
touchesDiff = -touchesDiff;
}
const prevTouchesDirection = swiper.touchesDirection;
swiper.swipeDirection = diff > 0 ? 'prev' : 'next';
swiper.touchesDirection = touchesDiff > 0 ? 'prev' : 'next';
const isLoop = swiper.params.loop && !params.cssMode;
const allowLoopFix = (swiper.touchesDirection === 'next' && swiper.allowSlideNext) ||
(swiper.touchesDirection === 'prev' && swiper.allowSlidePrev);
if (!data.isMoved) {
if (isLoop && allowLoopFix) {
swiper.loopFix({ direction: swiper.swipeDirection });
}
data.startTranslate = swiper.getTranslate();
swiper.setTransition(0);
if (swiper.animating) {
const evt = new window.CustomEvent('transitionend', {
bubbles: true,
cancelable: true,
detail: {
bySwiperTouchMove: true,
},
});
swiper.wrapperEl.dispatchEvent(evt);
}
data.allowMomentumBounce = false;
// Grab Cursor
if (params.grabCursor && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
swiper.setGrabCursor(true);
}
swiper.emit('sliderFirstMove', e);
}
new Date().getTime();
if (params._loopSwapReset !== false &&
data.isMoved &&
data.allowThresholdMove &&
prevTouchesDirection !== swiper.touchesDirection &&
isLoop &&
allowLoopFix &&
Math.abs(diff) >= 1) {
Object.assign(touches, {
startX: pageX,
startY: pageY,
currentX: pageX,
currentY: pageY,
startTranslate: data.currentTranslate,
});
data.loopSwapReset = true;
data.startTranslate = data.currentTranslate;
return;
}
swiper.emit('sliderMove', e);
data.isMoved = true;
// startTranslate is guaranteed to be set by this point (set in onTouchStart-side init).
const startTranslate = data.startTranslate ?? 0;
data.currentTranslate = diff + startTranslate;
let disableParentSwiper = true;
let resistanceRatio = params.resistanceRatio;
if (params.touchReleaseOnEdges) {
resistanceRatio = 0;
}
if (diff > 0) {
if (isLoop &&
allowLoopFix &&
true &&
data.allowThresholdMove &&
data.currentTranslate >
(params.centeredSlides
? swiper.minTranslate() -
swiper.slidesSizesGrid[swiper.activeIndex + 1] -
(params.slidesPerView !== 'auto' &&