@gem-sdk/swiper 0.0.15-dev.1 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/swiper-element.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Swiper Custom Element 0.0.15-dev.1
2
+ * Swiper Custom Element 0.0.15
3
3
  * Gem SDK - Swiper, Customized of swiper
4
4
  * https://swiperjs.com
5
5
  *
@@ -841,6 +841,9 @@
841
841
 
842
842
  function updateSlides() {
843
843
  const swiper = this;
844
+ function getDirectionPropertyValue(node, label) {
845
+ return parseFloat(node.getPropertyValue(swiper.getDirectionLabel(label)) || 0);
846
+ }
844
847
  const params = swiper.params;
845
848
  const {
846
849
  wrapperEl,
@@ -849,8 +852,10 @@
849
852
  rtlTranslate: rtl,
850
853
  wrongRTL
851
854
  } = swiper;
855
+ const isVirtual = swiper.virtual && params.virtual.enabled;
856
+ const previousSlidesLength = isVirtual ? swiper.virtual.slides.length : swiper.slides.length;
852
857
  const slides = elementChildren(slidesEl, `.${swiper.params.slideClass}, swiper-slide`);
853
- const slidesLength = slides.length;
858
+ const slidesLength = isVirtual ? swiper.virtual.slides.length : slides.length;
854
859
  let snapGrid = [];
855
860
  const slidesGrid = [];
856
861
  const slidesSizesGrid = [];
@@ -868,15 +873,15 @@
868
873
  let slidePosition = -offsetBefore;
869
874
  let prevSlideSize = 0;
870
875
  let index = 0;
871
- if (typeof swiperSize === 'undefined') return;
876
+ if (typeof swiperSize === 'undefined') {
877
+ return;
878
+ }
872
879
  if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) {
873
880
  spaceBetween = parseFloat(spaceBetween.replace('%', '')) / 100 * swiperSize;
874
881
  } else if (typeof spaceBetween === 'string') {
875
882
  spaceBetween = parseFloat(spaceBetween);
876
883
  }
877
-
878
- // core-lite: compute total slides size without optional modules
879
- swiper.slidesTotalSize = -spaceBetween;
884
+ swiper.virtualSize = -spaceBetween;
880
885
 
881
886
  // reset margins
882
887
  slides.forEach(slideEl => {
@@ -894,34 +899,49 @@
894
899
  setCSSProperty(wrapperEl, '--swiper-centered-offset-before', '');
895
900
  setCSSProperty(wrapperEl, '--swiper-centered-offset-after', '');
896
901
  }
902
+ const gridEnabled = params.grid && params.grid.rows > 1 && swiper.grid;
903
+ if (gridEnabled) {
904
+ swiper.grid.initSlides(slides);
905
+ } else if (swiper.grid) {
906
+ swiper.grid.unsetSlides();
907
+ }
897
908
 
898
909
  // Calc slides
899
910
  let slideSize;
900
- const shouldResetSlideSize = params.slidesPerView === 'auto' && params.breakpoints && Object.keys(params.breakpoints).filter(key => typeof params.breakpoints[key].slidesPerView !== 'undefined').length > 0;
911
+ const shouldResetSlideSize = params.slidesPerView === 'auto' && params.breakpoints && Object.keys(params.breakpoints).filter(key => {
912
+ return typeof params.breakpoints[key].slidesPerView !== 'undefined';
913
+ }).length > 0;
901
914
  for (let i = 0; i < slidesLength; i += 1) {
902
915
  slideSize = 0;
903
916
  let slide;
904
917
  if (slides[i]) slide = slides[i];
918
+ if (gridEnabled) {
919
+ swiper.grid.updateSlide(i, slide, slides);
920
+ }
905
921
  if (slides[i] && elementStyle(slide, 'display') === 'none') continue; // eslint-disable-line
906
922
 
907
923
  if (params.slidesPerView === 'auto') {
908
- if (shouldResetSlideSize && slides[i]) {
924
+ if (shouldResetSlideSize) {
909
925
  slides[i].style[swiper.getDirectionLabel('width')] = ``;
910
926
  }
911
927
  const slideStyles = getComputedStyle(slide);
912
928
  const currentTransform = slide.style.transform;
913
929
  const currentWebKitTransform = slide.style.webkitTransform;
914
- if (currentTransform) slide.style.transform = 'none';
915
- if (currentWebKitTransform) slide.style.webkitTransform = 'none';
930
+ if (currentTransform) {
931
+ slide.style.transform = 'none';
932
+ }
933
+ if (currentWebKitTransform) {
934
+ slide.style.webkitTransform = 'none';
935
+ }
916
936
  if (params.roundLengths) {
917
937
  slideSize = swiper.isHorizontal() ? elementOuterSize(slide, 'width', true) : elementOuterSize(slide, 'height', true);
918
938
  } else {
919
939
  // eslint-disable-next-line
920
- const width = parseFloat(slideStyles.getPropertyValue('width')) || slide.offsetWidth;
921
- const paddingLeft = parseFloat(slideStyles.getPropertyValue('padding-left')) || 0;
922
- const paddingRight = parseFloat(slideStyles.getPropertyValue('padding-right')) || 0;
923
- const marginLeft = parseFloat(slideStyles.getPropertyValue('margin-left')) || 0;
924
- const marginRight = parseFloat(slideStyles.getPropertyValue('margin-right')) || 0;
940
+ const width = getDirectionPropertyValue(slideStyles, 'width');
941
+ const paddingLeft = getDirectionPropertyValue(slideStyles, 'padding-left');
942
+ const paddingRight = getDirectionPropertyValue(slideStyles, 'padding-right');
943
+ const marginLeft = getDirectionPropertyValue(slideStyles, 'margin-left');
944
+ const marginRight = getDirectionPropertyValue(slideStyles, 'margin-right');
925
945
  const boxSizing = slideStyles.getPropertyValue('box-sizing');
926
946
  if (boxSizing && boxSizing === 'border-box') {
927
947
  slideSize = width + marginLeft + marginRight;
@@ -933,8 +953,12 @@
933
953
  slideSize = width + paddingLeft + paddingRight + marginLeft + marginRight + (offsetWidth - clientWidth);
934
954
  }
935
955
  }
936
- if (currentTransform) slide.style.transform = currentTransform;
937
- if (currentWebKitTransform) slide.style.webkitTransform = currentWebKitTransform;
956
+ if (currentTransform) {
957
+ slide.style.transform = currentTransform;
958
+ }
959
+ if (currentWebKitTransform) {
960
+ slide.style.webkitTransform = currentWebKitTransform;
961
+ }
938
962
  if (params.roundLengths) slideSize = Math.floor(slideSize);
939
963
  } else {
940
964
  slideSize = (swiperSize - (params.slidesPerView - 1) * spaceBetween) / params.slidesPerView;
@@ -943,13 +967,13 @@
943
967
  slides[i].style[swiper.getDirectionLabel('width')] = `${slideSize}px`;
944
968
  }
945
969
  }
946
- if (slides[i]) slides[i].swiperSlideSize = slideSize;
970
+ if (slides[i]) {
971
+ slides[i].swiperSlideSize = slideSize;
972
+ }
947
973
  slidesSizesGrid.push(slideSize);
948
974
  if (params.centeredSlides) {
949
975
  slidePosition = slidePosition + slideSize / 2 + prevSlideSize / 2 + spaceBetween;
950
- if (prevSlideSize === 0 && i !== 0) {
951
- slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
952
- }
976
+ if (prevSlideSize === 0 && i !== 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
953
977
  if (i === 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
954
978
  if (Math.abs(slidePosition) < 1 / 1000) slidePosition = 0;
955
979
  if (params.roundLengths) slidePosition = Math.floor(slidePosition);
@@ -957,37 +981,55 @@
957
981
  slidesGrid.push(slidePosition);
958
982
  } else {
959
983
  if (params.roundLengths) slidePosition = Math.floor(slidePosition);
960
- if ((index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0) {
961
- snapGrid.push(slidePosition);
962
- }
984
+ if ((index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0) snapGrid.push(slidePosition);
963
985
  slidesGrid.push(slidePosition);
964
986
  slidePosition = slidePosition + slideSize + spaceBetween;
965
987
  }
966
- swiper.slidesTotalSize += slideSize + spaceBetween;
988
+ swiper.virtualSize += slideSize + spaceBetween;
967
989
  prevSlideSize = slideSize;
968
990
  index += 1;
969
991
  }
970
- swiper.slidesTotalSize = Math.max(swiper.slidesTotalSize, swiperSize) + offsetAfter;
971
- if (rtl && wrongRTL && params.effect === 'slide') {
972
- wrapperEl.style.width = `${swiper.slidesTotalSize + spaceBetween}px`;
992
+ swiper.virtualSize = Math.max(swiper.virtualSize, swiperSize) + offsetAfter;
993
+ if (rtl && wrongRTL && (params.effect === 'slide' || params.effect === 'coverflow')) {
994
+ wrapperEl.style.width = `${swiper.virtualSize + spaceBetween}px`;
973
995
  }
974
996
  if (params.setWrapperSize) {
975
- wrapperEl.style[swiper.getDirectionLabel('width')] = `${swiper.slidesTotalSize + spaceBetween}px`;
997
+ wrapperEl.style[swiper.getDirectionLabel('width')] = `${swiper.virtualSize + spaceBetween}px`;
998
+ }
999
+ if (gridEnabled) {
1000
+ swiper.grid.updateWrapperSize(slideSize, snapGrid);
976
1001
  }
977
1002
 
978
- // Remove last snap points depending on width (non-centered)
1003
+ // Remove last grid elements depending on width
979
1004
  if (!params.centeredSlides) {
980
1005
  const newSlidesGrid = [];
981
1006
  for (let i = 0; i < snapGrid.length; i += 1) {
982
1007
  let slidesGridItem = snapGrid[i];
983
1008
  if (params.roundLengths) slidesGridItem = Math.floor(slidesGridItem);
984
- if (snapGrid[i] <= swiper.slidesTotalSize - swiperSize) {
1009
+ if (snapGrid[i] <= swiper.virtualSize - swiperSize) {
985
1010
  newSlidesGrid.push(slidesGridItem);
986
1011
  }
987
1012
  }
988
1013
  snapGrid = newSlidesGrid;
989
- if (Math.floor(swiper.slidesTotalSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1) {
990
- snapGrid.push(swiper.slidesTotalSize - swiperSize);
1014
+ if (Math.floor(swiper.virtualSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1) {
1015
+ snapGrid.push(swiper.virtualSize - swiperSize);
1016
+ }
1017
+ }
1018
+ if (isVirtual && params.loop) {
1019
+ const size = slidesSizesGrid[0] + spaceBetween;
1020
+ if (params.slidesPerGroup > 1) {
1021
+ const groups = Math.ceil((swiper.virtual.slidesBefore + swiper.virtual.slidesAfter) / params.slidesPerGroup);
1022
+ const groupSize = size * params.slidesPerGroup;
1023
+ for (let i = 0; i < groups; i += 1) {
1024
+ snapGrid.push(snapGrid[snapGrid.length - 1] + groupSize);
1025
+ }
1026
+ }
1027
+ for (let i = 0; i < swiper.virtual.slidesBefore + swiper.virtual.slidesAfter; i += 1) {
1028
+ if (params.slidesPerGroup === 1) {
1029
+ snapGrid.push(snapGrid[snapGrid.length - 1] + size);
1030
+ }
1031
+ slidesGrid.push(slidesGrid[slidesGrid.length - 1] + size);
1032
+ swiper.virtualSize += size;
991
1033
  }
992
1034
  }
993
1035
  if (snapGrid.length === 0) snapGrid = [0];
@@ -995,7 +1037,9 @@
995
1037
  const key = swiper.isHorizontal() && rtl ? 'marginLeft' : swiper.getDirectionLabel('marginRight');
996
1038
  slides.filter((_, slideIndex) => {
997
1039
  if (!params.cssMode || params.loop) return true;
998
- if (slideIndex === slides.length - 1) return false;
1040
+ if (slideIndex === slides.length - 1) {
1041
+ return false;
1042
+ }
999
1043
  return true;
1000
1044
  }).forEach(slideEl => {
1001
1045
  slideEl.style[key] = `${spaceBetween}px`;
@@ -1045,9 +1089,7 @@
1045
1089
  swiper.snapGrid = swiper.snapGrid.map(v => v + addToSnapGrid);
1046
1090
  swiper.slidesGrid = swiper.slidesGrid.map(v => v + addToSlidesGrid);
1047
1091
  }
1048
-
1049
- // Emit changes
1050
- if (slidesLength !== (previousSlidesGridLength ? slides.length : slides.length)) {
1092
+ if (slidesLength !== previousSlidesLength) {
1051
1093
  swiper.emit('slidesLengthChange');
1052
1094
  }
1053
1095
  if (snapGrid.length !== previousSnapGridLength) {
@@ -1061,7 +1103,7 @@
1061
1103
  swiper.updateSlidesOffset();
1062
1104
  }
1063
1105
  swiper.emit('slidesUpdated');
1064
- if (!params.cssMode && params.effect === 'slide') {
1106
+ if (!isVirtual && !params.cssMode && (params.effect === 'slide' || params.effect === 'fade')) {
1065
1107
  const backFaceHiddenClass = `${params.containerModifierClass}backface-hidden`;
1066
1108
  const hasClassBackfaceClassAdded = swiper.el.classList.contains(backFaceHiddenClass);
1067
1109
  if (slidesLength <= params.maxBackfaceHiddenSlides) {
@@ -1075,13 +1117,20 @@
1075
1117
  function updateAutoHeight(speed) {
1076
1118
  const swiper = this;
1077
1119
  const activeSlides = [];
1120
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
1121
+ let newHeight = 0;
1122
+ let i;
1078
1123
  if (typeof speed === 'number') {
1079
1124
  swiper.setTransition(speed);
1080
1125
  } else if (speed === true) {
1081
1126
  swiper.setTransition(swiper.params.speed);
1082
1127
  }
1083
- const getSlideByIndex = index => swiper.slides[index];
1084
-
1128
+ const getSlideByIndex = index => {
1129
+ if (isVirtual) {
1130
+ return swiper.slides[swiper.getSlideIndexByData(index)];
1131
+ }
1132
+ return swiper.slides[index];
1133
+ };
1085
1134
  // Find slides currently in view
1086
1135
  if (swiper.params.slidesPerView !== 'auto' && swiper.params.slidesPerView > 1) {
1087
1136
  if (swiper.params.centeredSlides) {
@@ -1089,9 +1138,9 @@
1089
1138
  activeSlides.push(slide);
1090
1139
  });
1091
1140
  } else {
1092
- for (let i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
1141
+ for (i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
1093
1142
  const index = swiper.activeIndex + i;
1094
- if (index > swiper.slides.length) break;
1143
+ if (index > swiper.slides.length && !isVirtual) break;
1095
1144
  activeSlides.push(getSlideByIndex(index));
1096
1145
  }
1097
1146
  }
@@ -1100,13 +1149,14 @@
1100
1149
  }
1101
1150
 
1102
1151
  // Find new height from highest slide in view
1103
- let newHeight = 0;
1104
- for (let i = 0; i < activeSlides.length; i += 1) {
1152
+ for (i = 0; i < activeSlides.length; i += 1) {
1105
1153
  if (typeof activeSlides[i] !== 'undefined') {
1106
1154
  const height = activeSlides[i].offsetHeight;
1107
1155
  newHeight = height > newHeight ? height : newHeight;
1108
1156
  }
1109
1157
  }
1158
+
1159
+ // Update Height
1110
1160
  if (newHeight || newHeight === 0) swiper.wrapperEl.style.height = `${newHeight}px`;
1111
1161
  }
1112
1162
 
@@ -1251,16 +1301,46 @@
1251
1301
  slidesEl,
1252
1302
  activeIndex
1253
1303
  } = swiper;
1254
- const getNextSlide = slideEl => elementNextAll(slideEl, `.${params.slideClass}, swiper-slide`)[0];
1255
- const getPrevSlide = slideEl => elementPrevAll(slideEl, `.${params.slideClass}, swiper-slide`)[0];
1256
- const activeSlide = slides[activeIndex];
1257
- let nextSlide;
1304
+ const isVirtual = swiper.virtual && params.virtual.enabled;
1305
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
1306
+ const getFilteredSlide = selector => {
1307
+ return elementChildren(slidesEl, `.${params.slideClass}${selector}, swiper-slide${selector}`)[0];
1308
+ };
1309
+ let activeSlide;
1258
1310
  let prevSlide;
1311
+ let nextSlide;
1312
+ if (isVirtual) {
1313
+ if (params.loop) {
1314
+ let slideIndex = activeIndex - swiper.virtual.slidesBefore;
1315
+ if (slideIndex < 0) slideIndex = swiper.virtual.slides.length + slideIndex;
1316
+ if (slideIndex >= swiper.virtual.slides.length) slideIndex -= swiper.virtual.slides.length;
1317
+ activeSlide = getFilteredSlide(`[data-swiper-slide-index="${slideIndex}"]`);
1318
+ } else {
1319
+ activeSlide = getFilteredSlide(`[data-swiper-slide-index="${activeIndex}"]`);
1320
+ }
1321
+ } else {
1322
+ if (gridEnabled) {
1323
+ activeSlide = slides.find(slideEl => slideEl.column === activeIndex);
1324
+ nextSlide = slides.find(slideEl => slideEl.column === activeIndex + 1);
1325
+ prevSlide = slides.find(slideEl => slideEl.column === activeIndex - 1);
1326
+ } else {
1327
+ activeSlide = slides[activeIndex];
1328
+ }
1329
+ }
1259
1330
  if (activeSlide) {
1260
- nextSlide = getNextSlide(activeSlide);
1261
- prevSlide = getPrevSlide(activeSlide);
1262
- if (params.loop && !nextSlide) nextSlide = slides[0];
1263
- if (params.loop && !prevSlide) prevSlide = slides[slides.length - 1];
1331
+ if (!gridEnabled) {
1332
+ // Next Slide
1333
+ nextSlide = elementNextAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0];
1334
+ if (params.loop && !nextSlide) {
1335
+ nextSlide = slides[0];
1336
+ }
1337
+
1338
+ // Prev Slide
1339
+ prevSlide = elementPrevAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0];
1340
+ if (params.loop && !prevSlide === 0) {
1341
+ prevSlide = slides[slides.length - 1];
1342
+ }
1343
+ }
1264
1344
  }
1265
1345
  slides.forEach(slideEl => {
1266
1346
  toggleSlideClasses(slideEl, slideEl === activeSlide, params.slideActiveClass);
@@ -1351,6 +1431,7 @@
1351
1431
  activeIndex = i;
1352
1432
  }
1353
1433
  }
1434
+ // Normalize slideIndex
1354
1435
  if (params.normalizeSlideIndex) {
1355
1436
  if (activeIndex < 0 || typeof activeIndex === 'undefined') activeIndex = 0;
1356
1437
  }
@@ -1368,6 +1449,16 @@
1368
1449
  } = swiper;
1369
1450
  let activeIndex = newActiveIndex;
1370
1451
  let snapIndex;
1452
+ const getVirtualRealIndex = aIndex => {
1453
+ let realIndex = aIndex - swiper.virtual.slidesBefore;
1454
+ if (realIndex < 0) {
1455
+ realIndex = swiper.virtual.slides.length + realIndex;
1456
+ }
1457
+ if (realIndex >= swiper.virtual.slides.length) {
1458
+ realIndex -= swiper.virtual.slides.length;
1459
+ }
1460
+ return realIndex;
1461
+ };
1371
1462
  if (typeof activeIndex === 'undefined') {
1372
1463
  activeIndex = getActiveIndexByTranslate(swiper);
1373
1464
  }
@@ -1385,12 +1476,32 @@
1385
1476
  }
1386
1477
  return;
1387
1478
  }
1388
- let realIndex = activeIndex;
1389
- if (swiper.slides[activeIndex]) {
1479
+ if (activeIndex === previousIndex && swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) {
1480
+ swiper.realIndex = getVirtualRealIndex(activeIndex);
1481
+ return;
1482
+ }
1483
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
1484
+
1485
+ // Get real index
1486
+ let realIndex;
1487
+ if (swiper.virtual && params.virtual.enabled && params.loop) {
1488
+ realIndex = getVirtualRealIndex(activeIndex);
1489
+ } else if (gridEnabled) {
1490
+ const firstSlideInColumn = swiper.slides.find(slideEl => slideEl.column === activeIndex);
1491
+ let activeSlideIndex = parseInt(firstSlideInColumn.getAttribute('data-swiper-slide-index'), 10);
1492
+ if (Number.isNaN(activeSlideIndex)) {
1493
+ activeSlideIndex = Math.max(swiper.slides.indexOf(firstSlideInColumn), 0);
1494
+ }
1495
+ realIndex = Math.floor(activeSlideIndex / params.grid.rows);
1496
+ } else if (swiper.slides[activeIndex]) {
1390
1497
  const slideIndex = swiper.slides[activeIndex].getAttribute('data-swiper-slide-index');
1391
1498
  if (slideIndex) {
1392
1499
  realIndex = parseInt(slideIndex, 10);
1500
+ } else {
1501
+ realIndex = activeIndex;
1393
1502
  }
1503
+ } else {
1504
+ realIndex = activeIndex;
1394
1505
  }
1395
1506
  Object.assign(swiper, {
1396
1507
  previousSnapIndex,
@@ -1437,7 +1548,11 @@
1437
1548
  }
1438
1549
  if (slide && slideFound) {
1439
1550
  swiper.clickedSlide = slide;
1440
- swiper.clickedIndex = slideIndex;
1551
+ if (swiper.virtual && swiper.params.virtual.enabled) {
1552
+ swiper.clickedIndex = parseInt(slide.getAttribute('data-swiper-slide-index'), 10);
1553
+ } else {
1554
+ swiper.clickedIndex = slideIndex;
1555
+ }
1441
1556
  } else {
1442
1557
  swiper.clickedSlide = undefined;
1443
1558
  swiper.clickedIndex = undefined;
@@ -1471,6 +1586,9 @@
1471
1586
  translate,
1472
1587
  wrapperEl
1473
1588
  } = swiper;
1589
+ if (params.virtualTranslate) {
1590
+ return rtl ? -translate : translate;
1591
+ }
1474
1592
  if (params.cssMode) {
1475
1593
  return translate;
1476
1594
  }
@@ -1504,7 +1622,7 @@
1504
1622
  swiper.translate = swiper.isHorizontal() ? x : y;
1505
1623
  if (params.cssMode) {
1506
1624
  wrapperEl[swiper.isHorizontal() ? 'scrollLeft' : 'scrollTop'] = swiper.isHorizontal() ? -x : -y;
1507
- } else {
1625
+ } else if (!params.virtualTranslate) {
1508
1626
  if (swiper.isHorizontal()) {
1509
1627
  x -= swiper.cssOverflowAdjustment();
1510
1628
  } else {
@@ -1741,6 +1859,7 @@
1741
1859
  let snapIndex = skip + Math.floor((slideIndex - skip) / swiper.params.slidesPerGroup);
1742
1860
  if (snapIndex >= snapGrid.length) snapIndex = snapGrid.length - 1;
1743
1861
  const translate = -snapGrid[snapIndex];
1862
+ // Normalize slideIndex
1744
1863
  if (params.normalizeSlideIndex) {
1745
1864
  for (let i = 0; i < slidesGrid.length; i += 1) {
1746
1865
  const normalizedTranslate = -Math.floor(translate * 100);
@@ -1757,24 +1876,33 @@
1757
1876
  }
1758
1877
  }
1759
1878
  }
1760
-
1761
1879
  // Directions locks
1762
1880
  if (swiper.initialized && slideIndex !== activeIndex) {
1763
1881
  if (!swiper.allowSlideNext && (rtl ? translate > swiper.translate && translate > swiper.minTranslate() : translate < swiper.translate && translate < swiper.minTranslate())) {
1764
1882
  return false;
1765
1883
  }
1766
1884
  if (!swiper.allowSlidePrev && translate > swiper.translate && translate > swiper.maxTranslate()) {
1767
- if ((activeIndex || 0) !== slideIndex) return false;
1885
+ if ((activeIndex || 0) !== slideIndex) {
1886
+ return false;
1887
+ }
1768
1888
  }
1769
1889
  }
1770
1890
  if (slideIndex !== (previousIndex || 0) && runCallbacks) {
1771
1891
  swiper.emit('beforeSlideChangeStart');
1772
1892
  }
1893
+
1894
+ // Update progress
1773
1895
  swiper.updateProgress(translate);
1774
1896
  let direction;
1775
1897
  if (slideIndex > activeIndex) direction = 'next';else if (slideIndex < activeIndex) direction = 'prev';else direction = 'reset';
1776
- if (rtl && -translate === swiper.translate || !rtl && translate === swiper.translate) {
1898
+
1899
+ // initial virtual
1900
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
1901
+ const isInitialVirtual = isVirtual && initial;
1902
+ // Update Index
1903
+ if (!isInitialVirtual && (rtl && -translate === swiper.translate || !rtl && translate === swiper.translate)) {
1777
1904
  swiper.updateActiveIndex(slideIndex);
1905
+ // Update Height
1778
1906
  if (params.autoHeight) {
1779
1907
  swiper.updateAutoHeight();
1780
1908
  }
@@ -1792,7 +1920,24 @@
1792
1920
  const isH = swiper.isHorizontal();
1793
1921
  const t = rtl ? translate : -translate;
1794
1922
  if (speed === 0) {
1795
- wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1923
+ if (isVirtual) {
1924
+ swiper.wrapperEl.style.scrollSnapType = 'none';
1925
+ swiper._immediateVirtual = true;
1926
+ }
1927
+ if (isVirtual && !swiper._cssModeVirtualInitialSet && swiper.params.initialSlide > 0) {
1928
+ swiper._cssModeVirtualInitialSet = true;
1929
+ requestAnimationFrame(() => {
1930
+ wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1931
+ });
1932
+ } else {
1933
+ wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1934
+ }
1935
+ if (isVirtual) {
1936
+ requestAnimationFrame(() => {
1937
+ swiper.wrapperEl.style.scrollSnapType = '';
1938
+ swiper._immediateVirtual = false;
1939
+ });
1940
+ }
1796
1941
  } else {
1797
1942
  if (!swiper.support.smoothScroll) {
1798
1943
  animateCSSModeScroll({
@@ -1810,7 +1955,10 @@
1810
1955
  return true;
1811
1956
  }
1812
1957
  const browser = getBrowser();
1813
- browser.isSafari;
1958
+ const isSafari = browser.isSafari;
1959
+ if (isVirtual && !initial && isSafari && swiper.isElement) {
1960
+ swiper.virtual.update(false, false, slideIndex);
1961
+ }
1814
1962
  swiper.setTransition(speed);
1815
1963
  swiper.setTranslate(translate);
1816
1964
  swiper.updateActiveIndex(slideIndex);
@@ -1981,6 +2129,8 @@
1981
2129
  if (swiper.params?.isSneakPeekCenter && slides.length > 1 && swiper.activeIndex === 0) {
1982
2130
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
1983
2131
  const swiperTranslate = JSON.parse(JSON.stringify(swiper.snapGrid[1]));
2132
+
2133
+ // Move last item to first position only if active slide is the first slide
1984
2134
  const lastSlide = slides[slides.length - 1];
1985
2135
  lastSlide.swiperLoopMoveDOM = true;
1986
2136
  swiper.slidesEl.prepend(lastSlide);
@@ -2015,8 +2165,11 @@
2015
2165
  perGroup = Math.max(swiper.slidesPerViewDynamic('current', true), 1);
2016
2166
  }
2017
2167
  const increment = swiper.activeIndex < params.slidesPerGroupSkip ? 1 : perGroup;
2168
+ const isVirtual = swiper.virtual && params.virtual.enabled;
2018
2169
  if (params.loop) {
2019
- if (animating && params.loopPreventsSliding) return false;
2170
+ if (animating && !isVirtual && params.loopPreventsSliding) return false;
2171
+
2172
+ // Kiểm tra xem loop có bị disable không
2020
2173
  const currentSlidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(params.slidesPerView, 10));
2021
2174
  if (swiper.slides.length >= currentSlidesPerView) {
2022
2175
  swiper.loopFix({
@@ -2043,6 +2196,7 @@
2043
2196
  const gap = Math.abs(swiper.snapGrid[lastSnapGridIndex] - swiper.snapGrid[lastSnapGridIndex - 1]);
2044
2197
  const swiperTranslate = structuredClone(swiper.snapGrid[lastSnapGridIndex - 1]);
2045
2198
  if (!swiper.params.loop) return;
2199
+ // Move first item to last position only if active slide is the last slide
2046
2200
  const firstSlide = slides[0];
2047
2201
  firstSlide.swiperLoopMoveDOM = true;
2048
2202
  swiper.slidesEl.append(firstSlide);
@@ -2067,14 +2221,17 @@
2067
2221
  params,
2068
2222
  snapGrid,
2069
2223
  slidesGrid,
2070
- rtlTranslate: rtlTranslate,
2071
- enabled
2224
+ rtlTranslate,
2225
+ enabled,
2226
+ animating
2072
2227
  } = swiper;
2073
2228
  if (!enabled || swiper.destroyed) return swiper;
2074
2229
  if (typeof speed === 'undefined') {
2075
2230
  speed = swiper.params.speed;
2076
2231
  }
2232
+ swiper.virtual && params.virtual.enabled;
2077
2233
  if (params.loop) {
2234
+ // Kiểm tra xem loop có bị disable không
2078
2235
  const currentSlidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(params.slidesPerView, 10));
2079
2236
  if (swiper.slides.length >= currentSlidesPerView) {
2080
2237
  swiper.loopFix({
@@ -2091,16 +2248,18 @@
2091
2248
  }
2092
2249
  const normalizedTranslate = normalize(translate);
2093
2250
  const normalizedSnapGrid = snapGrid.map(val => normalize(val));
2251
+ const isFreeMode = params.freeMode && params.freeMode.enabled;
2094
2252
  let prevSnap = snapGrid[normalizedSnapGrid.indexOf(normalizedTranslate) - 1];
2095
- if (typeof prevSnap === 'undefined' && params.cssMode) {
2253
+ if (typeof prevSnap === 'undefined' && (params.cssMode || isFreeMode)) {
2096
2254
  let prevSnapIndex;
2097
2255
  snapGrid.forEach((snap, snapIndex) => {
2098
2256
  if (normalizedTranslate >= snap) {
2257
+ // prevSnap = snap;
2099
2258
  prevSnapIndex = snapIndex;
2100
2259
  }
2101
2260
  });
2102
2261
  if (typeof prevSnapIndex !== 'undefined') {
2103
- prevSnap = snapGrid[prevSnapIndex > 0 ? prevSnapIndex - 1 : prevSnapIndex];
2262
+ prevSnap = isFreeMode ? snapGrid[prevSnapIndex] : snapGrid[prevSnapIndex > 0 ? prevSnapIndex - 1 : prevSnapIndex];
2104
2263
  }
2105
2264
  }
2106
2265
  let prevIndex = 0;
@@ -2113,7 +2272,7 @@
2113
2272
  }
2114
2273
  }
2115
2274
  if (params.rewind && swiper.isBeginning) {
2116
- const lastIndex = swiper.slides.length - 1;
2275
+ const lastIndex = swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1;
2117
2276
  return swiper.slideTo(lastIndex, speed, runCallbacks, internal);
2118
2277
  } else if (params.loop && swiper.activeIndex === 0 && params.cssMode) {
2119
2278
  requestAnimationFrame(() => {
@@ -2127,6 +2286,8 @@
2127
2286
  if (swiper.params?.isSneakPeekCenter && slides.length > 1 && swiper.activeIndex === 0) {
2128
2287
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
2129
2288
  const swiperTranslate = JSON.parse(JSON.stringify(swiper.snapGrid[1]));
2289
+
2290
+ // Move last item to first position only if active slide is the first slide
2130
2291
  if (!swiper.params.loop) return;
2131
2292
  const lastSlide = slides[slides.length - 1];
2132
2293
  lastSlide.swiperLoopMoveDOM = true;
@@ -2203,19 +2364,20 @@
2203
2364
  slidesEl
2204
2365
  } = swiper;
2205
2366
  const slidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : params.slidesPerView;
2206
- const slideToIndex = swiper.clickedIndex;
2367
+ let slideToIndex = swiper.getSlideIndexWhenGrid(swiper.clickedIndex);
2207
2368
  let realIndex;
2208
2369
  const slideSelector = swiper.isElement ? `swiper-slide` : `.${params.slideClass}`;
2370
+ const isGrid = swiper.grid && swiper.params.grid && swiper.params.grid.rows > 1;
2209
2371
  if (params.loop) {
2210
2372
  if (swiper.animating) return;
2211
2373
  realIndex = parseInt(swiper.clickedSlide.getAttribute('data-swiper-slide-index'), 10);
2212
2374
  if (params.centeredSlides) {
2213
2375
  swiper.slideToLoop(realIndex);
2214
- } else if (slideToIndex > swiper.slides.length - slidesPerView) {
2376
+ } else if (slideToIndex > (isGrid ? (swiper.slides.length - slidesPerView) / 2 - (swiper.params.grid.rows - 1) : swiper.slides.length - slidesPerView)) {
2215
2377
  swiper.loopFix();
2216
- const clickedEl = elementChildren(slidesEl, `${slideSelector}[data-swiper-slide-index="${realIndex}"]`)[0];
2378
+ slideToIndex = swiper.getSlideIndex(elementChildren(slidesEl, `${slideSelector}[data-swiper-slide-index="${realIndex}"]`)[0]);
2217
2379
  nextTick(() => {
2218
- if (clickedEl) swiper.slideTo(swiper.getSlideIndex(clickedEl));
2380
+ swiper.slideTo(slideToIndex);
2219
2381
  });
2220
2382
  } else {
2221
2383
  swiper.slideTo(slideToIndex);
@@ -2242,7 +2404,7 @@
2242
2404
  params,
2243
2405
  slidesEl
2244
2406
  } = swiper;
2245
- if (!params.loop) return;
2407
+ if (!params.loop || swiper.virtual && swiper.params.virtual.enabled) return;
2246
2408
  const initSlides = () => {
2247
2409
  const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
2248
2410
  slides.forEach((el, index) => {
@@ -2251,17 +2413,21 @@
2251
2413
  };
2252
2414
  const clearBlankSlides = () => {
2253
2415
  const slides = elementChildren(slidesEl, `.${params.slideBlankClass}`);
2254
- slides.forEach(el => el.remove());
2416
+ slides.forEach(el => {
2417
+ el.remove();
2418
+ });
2255
2419
  if (slides.length > 0) {
2256
2420
  swiper.recalcSlides();
2257
2421
  swiper.updateSlides();
2258
2422
  }
2259
2423
  };
2260
- if (params.loopAddBlankSlides && params.slidesPerGroup > 1) {
2424
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
2425
+ if (params.loopAddBlankSlides && (params.slidesPerGroup > 1 || gridEnabled)) {
2261
2426
  clearBlankSlides();
2262
2427
  }
2263
- const slidesPerGroup = params.slidesPerGroup;
2428
+ const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1);
2264
2429
  const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0;
2430
+ const shouldFillGrid = gridEnabled && swiper.slides.length % params.grid.rows !== 0;
2265
2431
  const addBlankSlides = amountOfSlides => {
2266
2432
  for (let i = 0; i < amountOfSlides; i += 1) {
2267
2433
  const slideEl = swiper.isElement ? createElement('swiper-slide', [params.slideBlankClass]) : createElement('div', [params.slideClass, params.slideBlankClass]);
@@ -2278,6 +2444,16 @@
2278
2444
  showWarning('Swiper Loop Warning: The number of slides is not even to slidesPerGroup, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)');
2279
2445
  }
2280
2446
  initSlides();
2447
+ } else if (shouldFillGrid) {
2448
+ if (params.loopAddBlankSlides) {
2449
+ const slidesToAdd = params.grid.rows - swiper.slides.length % params.grid.rows;
2450
+ addBlankSlides(slidesToAdd);
2451
+ swiper.recalcSlides();
2452
+ swiper.updateSlides();
2453
+ } else {
2454
+ showWarning('Swiper Loop Warning: The number of slides is not even to grid.rows, loop mode may not function properly. You need to add more slides (or make duplicates, or empty slides)');
2455
+ }
2456
+ initSlides();
2281
2457
  } else {
2282
2458
  initSlides();
2283
2459
  }
@@ -2296,12 +2472,13 @@
2296
2472
  setTranslate,
2297
2473
  activeSlideIndex,
2298
2474
  initial,
2475
+ byController,
2299
2476
  byMousewheel
2300
2477
  } = _temp === void 0 ? {} : _temp;
2301
2478
  const swiper = this;
2302
2479
  if (!swiper.params.loop) return;
2303
2480
 
2304
- // Disable loop mode if number of slides is smaller than slidesPerView
2481
+ // Disable loop mode nếu số slides ít hơn slidesPerView
2305
2482
  const currentSlidesPerView = swiper.params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(swiper.params.slidesPerView, 10));
2306
2483
  if (swiper.slides.length < currentSlidesPerView) {
2307
2484
  console.warn('Swiper: Loop mode disabled - slides.length < slidesPerView');
@@ -2321,6 +2498,21 @@
2321
2498
  } = params;
2322
2499
  swiper.allowSlidePrev = true;
2323
2500
  swiper.allowSlideNext = true;
2501
+ if (swiper.virtual && params.virtual.enabled) {
2502
+ if (slideTo) {
2503
+ if (!params.centeredSlides && swiper.snapIndex === 0) {
2504
+ swiper.slideTo(swiper.virtual.slides.length, 0, false, true);
2505
+ } else if (params.centeredSlides && swiper.snapIndex < params.slidesPerView) {
2506
+ swiper.slideTo(swiper.virtual.slides.length + swiper.snapIndex, 0, false, true);
2507
+ } else if (swiper.snapIndex === swiper.snapGrid.length - 1) {
2508
+ swiper.slideTo(swiper.virtual.slidesBefore, 0, false, true);
2509
+ }
2510
+ }
2511
+ swiper.allowSlidePrev = allowSlidePrev;
2512
+ swiper.allowSlideNext = allowSlideNext;
2513
+ swiper.emit('loopFix');
2514
+ return;
2515
+ }
2324
2516
  let slidesPerView = params.slidesPerView;
2325
2517
  if (slidesPerView === 'auto') {
2326
2518
  slidesPerView = swiper.slidesPerViewDynamic();
@@ -2330,19 +2522,22 @@
2330
2522
  slidesPerView = slidesPerView + 1;
2331
2523
  }
2332
2524
  }
2333
- const slidesPerGroup = params.slidesPerGroup;
2525
+ const slidesPerGroup = params.slidesPerGroupAuto ? slidesPerView : params.slidesPerGroup;
2334
2526
  let loopedSlides = centeredSlides ? Math.max(slidesPerGroup, Math.ceil(slidesPerView / 2)) : slidesPerGroup;
2335
2527
  if (loopedSlides % slidesPerGroup !== 0) {
2336
2528
  loopedSlides += slidesPerGroup - loopedSlides % slidesPerGroup;
2337
2529
  }
2338
2530
  loopedSlides += params.loopAdditionalSlides;
2339
2531
  swiper.loopedSlides = loopedSlides;
2340
- if (slides.length < slidesPerView + loopedSlides) {
2532
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
2533
+ if (slides.length < slidesPerView + loopedSlides || swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
2341
2534
  showWarning('Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled or not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters');
2535
+ } else if (gridEnabled && params.grid.fill === 'row') {
2536
+ showWarning('Swiper Loop Warning: Loop mode is not compatible with grid.fill = `row`');
2342
2537
  }
2343
2538
  const prependSlidesIndexes = [];
2344
2539
  const appendSlidesIndexes = [];
2345
- const cols = slides.length;
2540
+ const cols = gridEnabled ? Math.ceil(slides.length / params.grid.rows) : slides.length;
2346
2541
  const isInitialOverflow = initial && cols - initialSlide < slidesPerView && !centeredSlides;
2347
2542
  let activeIndex = isInitialOverflow ? initialSlide : swiper.activeIndex;
2348
2543
  if (typeof activeSlideIndex === 'undefined') {
@@ -2354,15 +2549,24 @@
2354
2549
  const isPrev = direction === 'prev' || !direction;
2355
2550
  let slidesPrepended = 0;
2356
2551
  let slidesAppended = 0;
2357
- const activeColIndex = activeSlideIndex;
2552
+ const activeColIndex = gridEnabled ? slides[activeSlideIndex].column : activeSlideIndex;
2358
2553
  const activeColIndexWithShift = activeColIndex + (centeredSlides && typeof setTranslate === 'undefined' ? -slidesPerView / 2 + 0.5 : 0);
2359
-
2360
2554
  // prepend last slides before start
2361
2555
  if (activeColIndexWithShift < loopedSlides) {
2362
2556
  slidesPrepended = Math.max(loopedSlides - activeColIndexWithShift, slidesPerGroup);
2363
2557
  for (let i = 0; i < loopedSlides - activeColIndexWithShift; i += 1) {
2364
2558
  const index = i - Math.floor(i / cols) * cols;
2365
- prependSlidesIndexes.push(cols - index - 1);
2559
+ if (gridEnabled) {
2560
+ const colIndexToPrepend = cols - index - 1;
2561
+ for (let i = slides.length - 1; i >= 0; i -= 1) {
2562
+ if (slides[i].column === colIndexToPrepend) prependSlidesIndexes.push(i);
2563
+ }
2564
+ // slides.forEach((slide, slideIndex) => {
2565
+ // if (slide.column === colIndexToPrepend) prependSlidesIndexes.push(slideIndex);
2566
+ // });
2567
+ } else {
2568
+ prependSlidesIndexes.push(cols - index - 1);
2569
+ }
2366
2570
  }
2367
2571
  } else if (activeColIndexWithShift + slidesPerView > cols - loopedSlides) {
2368
2572
  slidesAppended = Math.max(activeColIndexWithShift - (cols - loopedSlides * 2), slidesPerGroup);
@@ -2371,13 +2575,27 @@
2371
2575
  }
2372
2576
  for (let i = 0; i < slidesAppended; i += 1) {
2373
2577
  const index = i - Math.floor(i / cols) * cols;
2374
- appendSlidesIndexes.push(index);
2578
+ if (gridEnabled) {
2579
+ slides.forEach((slide, slideIndex) => {
2580
+ if (slide.column === index) appendSlidesIndexes.push(slideIndex);
2581
+ });
2582
+ } else {
2583
+ appendSlidesIndexes.push(index);
2584
+ }
2375
2585
  }
2376
2586
  }
2377
2587
  swiper.__preventObserver__ = true;
2378
2588
  requestAnimationFrame(() => {
2379
2589
  swiper.__preventObserver__ = false;
2380
2590
  });
2591
+ if (swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
2592
+ if (appendSlidesIndexes.includes(activeSlideIndex)) {
2593
+ appendSlidesIndexes.splice(appendSlidesIndexes.indexOf(activeSlideIndex), 1);
2594
+ }
2595
+ if (prependSlidesIndexes.includes(activeSlideIndex)) {
2596
+ prependSlidesIndexes.splice(prependSlidesIndexes.indexOf(activeSlideIndex), 1);
2597
+ }
2598
+ }
2381
2599
  if (isPrev) {
2382
2600
  prependSlidesIndexes.forEach(index => {
2383
2601
  slides[index].swiperLoopMoveDOM = true;
@@ -2395,6 +2613,10 @@
2395
2613
  swiper.recalcSlides();
2396
2614
  if (params.slidesPerView === 'auto') {
2397
2615
  swiper.updateSlides();
2616
+ } else if (gridEnabled && (prependSlidesIndexes.length > 0 && isPrev || appendSlidesIndexes.length > 0 && isNext)) {
2617
+ swiper.slides.forEach((slide, slideIndex) => {
2618
+ swiper.grid.updateSlide(slideIndex, slide, swiper.slides);
2619
+ });
2398
2620
  }
2399
2621
  if (params.watchSlidesProgress) {
2400
2622
  swiper.updateSlidesOffset();
@@ -2414,10 +2636,12 @@
2414
2636
  swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff;
2415
2637
  }
2416
2638
  }
2417
- } else if (setTranslate) {
2418
- const shift = prependSlidesIndexes.length;
2419
- swiper.slideTo(swiper.activeIndex + shift, 0, false, true);
2420
- swiper.touchEventsData.currentTranslate = swiper.translate;
2639
+ } else {
2640
+ if (setTranslate) {
2641
+ const shift = gridEnabled ? prependSlidesIndexes.length / params.grid.rows : prependSlidesIndexes.length;
2642
+ swiper.slideTo(swiper.activeIndex + shift, 0, false, true);
2643
+ swiper.touchEventsData.currentTranslate = swiper.translate;
2644
+ }
2421
2645
  }
2422
2646
  } else if (appendSlidesIndexes.length > 0 && isNext) {
2423
2647
  if (typeof slideRealIndex === 'undefined') {
@@ -2433,14 +2657,36 @@
2433
2657
  swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff;
2434
2658
  }
2435
2659
  }
2436
- } else if (setTranslate) {
2437
- const shift = appendSlidesIndexes.length;
2660
+ } else {
2661
+ const shift = gridEnabled ? appendSlidesIndexes.length / params.grid.rows : appendSlidesIndexes.length;
2438
2662
  swiper.slideTo(swiper.activeIndex - shift, 0, false, true);
2439
2663
  }
2440
2664
  }
2441
2665
  }
2442
2666
  swiper.allowSlidePrev = allowSlidePrev;
2443
2667
  swiper.allowSlideNext = allowSlideNext;
2668
+ if (swiper.controller && swiper.controller.control && !byController) {
2669
+ const loopParams = {
2670
+ slideRealIndex,
2671
+ direction,
2672
+ setTranslate,
2673
+ activeSlideIndex,
2674
+ byController: true
2675
+ };
2676
+ if (Array.isArray(swiper.controller.control)) {
2677
+ swiper.controller.control.forEach(c => {
2678
+ if (!c.destroyed && c.params.loop) c.loopFix({
2679
+ ...loopParams,
2680
+ slideTo: c.params.slidesPerView === params.slidesPerView ? slideTo : false
2681
+ });
2682
+ });
2683
+ } else if (swiper.controller.control instanceof swiper.constructor && swiper.controller.control.params.loop) {
2684
+ swiper.controller.control.loopFix({
2685
+ ...loopParams,
2686
+ slideTo: swiper.controller.control.params.slidesPerView === params.slidesPerView ? slideTo : false
2687
+ });
2688
+ }
2689
+ }
2444
2690
  swiper.emit('loopFix');
2445
2691
  }
2446
2692
 
@@ -2457,7 +2703,7 @@
2457
2703
  const swiper = this;
2458
2704
  if (!swiper.params.loop) return;
2459
2705
 
2460
- // Disable loop mode if number of slides is smaller than slidesPerView
2706
+ // Disable loop mode nếu số slides ít hơn slidesPerView
2461
2707
  const currentSlidesPerView = swiper.params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(swiper.params.slidesPerView, 10));
2462
2708
  if (swiper.slides.length < currentSlidesPerView) {
2463
2709
  console.warn('Swiper: Loop mode disabled - slides.length < slidesPerView');
@@ -2496,7 +2742,7 @@
2496
2742
  }
2497
2743
  loopedSlides += params.loopAdditionalSlides;
2498
2744
  swiper.loopedSlides = loopedSlides;
2499
- if (slides.length < slidesPerView + loopedSlides) {
2745
+ if (slides.length < slidesPerView + loopedSlides || swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
2500
2746
  showWarning('Swiper Loop Warning: The number of slides is not enough for loop mode, it will be disabled or not function properly. You need to add more slides (or make duplicates) or lower the values of slidesPerView and slidesPerGroup parameters');
2501
2747
  }
2502
2748
  const isNext = direction === 'next' || !direction;
@@ -2522,16 +2768,20 @@
2522
2768
  activeSlideIndex = swiper.getSlideIndex(slides.find(el => el.classList.contains(params.slideActiveClass)));
2523
2769
  }
2524
2770
 
2525
- // DocumentFragment to hold slide clones
2771
+ // Tạo DocumentFragment để chứa các slide clone
2526
2772
  const cloneFragment = document.createDocumentFragment();
2527
2773
 
2528
- // Clone slides according to numberOfSlidesNeedClone and append to fragment
2774
+ // Clone các slide theo numberOfSlidesNeedClone (đã đảo ngược) và thêm vào fragment
2529
2775
  (isNext ? numberOfSlidesNeedClone : numberOfSlidesNeedClone.reverse()).forEach(index => {
2530
2776
  if (slides[index]) {
2531
2777
  const originalSlide = slides[index];
2532
2778
  const clonedSlide = originalSlide.cloneNode(true);
2779
+
2780
+ // Đánh dấu slide clone
2533
2781
  clonedSlide.setAttribute('data-swiper-clone', 'true');
2534
2782
  clonedSlide.classList.add('swiper-slide-clone');
2783
+
2784
+ // Thêm clone vào fragment
2535
2785
  cloneFragment.appendChild(clonedSlide);
2536
2786
  }
2537
2787
  });
@@ -2552,28 +2802,32 @@
2552
2802
  });
2553
2803
  }
2554
2804
 
2555
- // Sort cloned slides by data-swiper-slide-index
2805
+ // Sắp xếp cloneFragment theo data-swiper-slide-index tăng dần
2556
2806
  const clonedSlides = Array.from(cloneFragment.children);
2557
2807
  clonedSlides.sort((a, b) => {
2558
2808
  const indexA = parseInt(a.getAttribute('data-swiper-slide-index')) || 0;
2559
2809
  const indexB = parseInt(b.getAttribute('data-swiper-slide-index')) || 0;
2560
2810
  return indexA - indexB;
2561
2811
  });
2812
+
2813
+ // Xóa tất cả children cũ và thêm lại theo thứ tự đã sắp xếp
2562
2814
  cloneFragment.innerHTML = '';
2563
2815
  clonedSlides.forEach(slide => {
2564
2816
  cloneFragment.appendChild(slide);
2565
2817
  });
2566
2818
 
2567
- // Place fragment into the right position
2819
+ // Thêm fragment vào vị trí phù hợp
2568
2820
  if (isPrev) {
2821
+ // Nếu là prev, thêm fragment vào cuối slidesEl
2569
2822
  slidesEl.appendChild(cloneFragment);
2570
2823
  } else if (isNext) {
2824
+ // Nếu là next, thêm fragment vào đầu slidesEl
2571
2825
  slidesEl.insertBefore(cloneFragment, slidesEl.firstChild);
2572
2826
  }
2573
2827
  swiper.recalcSlides();
2574
2828
  swiper.updateSlides();
2575
2829
 
2576
- // Find old active slide index after recalculation
2830
+ // Tìm slide data-swiper-slide-index tương ứng
2577
2831
  let oldActiveIndex = null;
2578
2832
  for (let i = 0; i < slidesEl.children.length; i++) {
2579
2833
  const child = slidesEl.children[i];
@@ -2586,7 +2840,7 @@
2586
2840
  swiper.slideTo(oldActiveIndex, 0);
2587
2841
  }
2588
2842
 
2589
- // Update translate after removing clones for animation
2843
+ // Tìm vị trí slide cũ sau khi remove clone để set lại translate tạo hiệu ứng animate
2590
2844
  const skip = Math.min(swiper.params.slidesPerGroupSkip, newIndex);
2591
2845
  let snapIndex = skip + Math.floor((newIndex - skip) / swiper.params.slidesPerGroup);
2592
2846
  if (snapIndex >= swiper.snapGrid.length) snapIndex = swiper.snapGrid.length - 1;
@@ -2612,8 +2866,7 @@
2612
2866
  }
2613
2867
  swiper.setTranslate(updateTranslate);
2614
2868
  }
2615
-
2616
- // Remove clones
2869
+ // Remove slide clone ra khỏi slidesEl sau khi slideTo hoàn thành
2617
2870
  const cloneSlides = slidesEl.querySelectorAll('[data-swiper-clone="true"]');
2618
2871
  cloneSlides.forEach(cloneSlide => {
2619
2872
  if (cloneSlide.parentNode) {
@@ -2643,7 +2896,7 @@
2643
2896
  params,
2644
2897
  slidesEl
2645
2898
  } = swiper;
2646
- if (!params.loop || !slidesEl) return;
2899
+ if (!params.loop || !slidesEl || swiper.virtual && swiper.params.virtual.enabled) return;
2647
2900
  swiper.recalcSlides();
2648
2901
  const newSlidesOrder = [];
2649
2902
  swiper.slides.forEach(slideEl => {
@@ -2704,6 +2957,7 @@
2704
2957
  unsetGrabCursor
2705
2958
  };
2706
2959
 
2960
+ // Modified from https://stackoverflow.com/questions/54520554/custom-element-getrootnode-closest-function-crossing-multiple-parent-shadowd
2707
2961
  function closestElement(selector, base) {
2708
2962
  if (base === void 0) {
2709
2963
  base = this;
@@ -2712,7 +2966,9 @@
2712
2966
  if (!el || el === getDocument() || el === getWindow()) return null;
2713
2967
  if (el.assignedSlot) el = el.assignedSlot;
2714
2968
  const found = el.closest(selector);
2715
- if (!found && !el.getRootNode) return null;
2969
+ if (!found && !el.getRootNode) {
2970
+ return null;
2971
+ }
2716
2972
  return found || __closestFrom(el.getRootNode().host);
2717
2973
  }
2718
2974
  return __closestFrom(base);
@@ -2740,7 +2996,9 @@
2740
2996
  if (e.originalEvent) e = e.originalEvent;
2741
2997
  const data = swiper.touchEventsData;
2742
2998
  if (e.type === 'pointerdown') {
2743
- if (data.pointerId !== null && data.pointerId !== e.pointerId) return;
2999
+ if (data.pointerId !== null && data.pointerId !== e.pointerId) {
3000
+ return;
3001
+ }
2744
3002
  data.pointerId = e.pointerId;
2745
3003
  } else if (e.type === 'touchstart' && e.targetTouches.length === 1) {
2746
3004
  data.touchId = e.targetTouches[0].identifier;
@@ -2757,7 +3015,9 @@
2757
3015
  } = swiper;
2758
3016
  if (!enabled) return;
2759
3017
  if (!params.simulateTouch && e.pointerType === 'mouse') return;
2760
- if (swiper.animating && params.preventInteractionOnTransition) return;
3018
+ if (swiper.animating && params.preventInteractionOnTransition) {
3019
+ return;
3020
+ }
2761
3021
  if (!swiper.animating && params.cssMode && params.loop) {
2762
3022
  swiper.loopFix();
2763
3023
  }
@@ -2768,6 +3028,8 @@
2768
3028
  if ('which' in e && e.which === 3) return;
2769
3029
  if ('button' in e && e.button > 0) return;
2770
3030
  if (data.isTouched && data.isMoved) return;
3031
+
3032
+ // change target el for shadow root component
2771
3033
  const swipingClassHasValue = !!params.noSwipingClass && params.noSwipingClass !== '';
2772
3034
  // eslint-disable-next-line
2773
3035
  const eventPath = e.composedPath ? e.composedPath() : e.path;
@@ -2776,6 +3038,8 @@
2776
3038
  }
2777
3039
  const noSwipingSelector = params.noSwipingSelector ? params.noSwipingSelector : `.${params.noSwipingClass}`;
2778
3040
  const isTargetShadow = !!(e.target && e.target.shadowRoot);
3041
+
3042
+ // use closestElement for shadow root element to get the actual closest for nested shadow root element
2779
3043
  if (params.noSwiping && (isTargetShadow ? closestElement(noSwipingSelector, targetEl) : targetEl.closest(noSwipingSelector))) {
2780
3044
  swiper.allowClick = true;
2781
3045
  return;
@@ -2787,7 +3051,12 @@
2787
3051
  touches.currentY = e.pageY;
2788
3052
  const startX = touches.currentX;
2789
3053
  const startY = touches.currentY;
2790
- if (!preventEdgeSwipe(swiper, e, startX)) return;
3054
+
3055
+ // Do NOT start if iOS edge swipe is detected. Otherwise iOS app cannot swipe-to-go-back anymore
3056
+
3057
+ if (!preventEdgeSwipe(swiper, e, startX)) {
3058
+ return;
3059
+ }
2791
3060
  Object.assign(data, {
2792
3061
  isTouched: true,
2793
3062
  isMoved: false,
@@ -2816,6 +3085,9 @@
2816
3085
  if ((params.touchStartForcePreventDefault || shouldPreventDefault) && !targetEl.isContentEditable) {
2817
3086
  e.preventDefault();
2818
3087
  }
3088
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode && swiper.animating && !params.cssMode) {
3089
+ swiper.freeMode.onTouchStart();
3090
+ }
2819
3091
  swiper.emit('touchStart', e);
2820
3092
  }
2821
3093
 
@@ -2834,7 +3106,7 @@
2834
3106
  let e = event;
2835
3107
  if (e.originalEvent) e = e.originalEvent;
2836
3108
  if (e.type === 'pointermove') {
2837
- if (data.touchId !== null) return;
3109
+ if (data.touchId !== null) return; // return from pointer if we use touch
2838
3110
  const id = e.pointerId;
2839
3111
  if (id !== data.pointerId) return;
2840
3112
  }
@@ -2875,6 +3147,7 @@
2875
3147
  }
2876
3148
  if (params.touchReleaseOnEdges && !params.loop) {
2877
3149
  if (swiper.isVertical()) {
3150
+ // Vertical
2878
3151
  if (pageY < touches.startY && swiper.translate <= swiper.maxTranslate() || pageY > touches.startY && swiper.translate >= swiper.minTranslate()) {
2879
3152
  data.isTouched = false;
2880
3153
  data.isMoved = false;
@@ -2976,6 +3249,7 @@
2976
3249
  swiper.wrapperEl.dispatchEvent(evt);
2977
3250
  }
2978
3251
  data.allowMomentumBounce = false;
3252
+ // Grab Cursor
2979
3253
  if (params.grabCursor && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
2980
3254
  swiper.setGrabCursor(true);
2981
3255
  }
@@ -3046,6 +3320,8 @@
3046
3320
  if (!swiper.allowSlidePrev && !swiper.allowSlideNext) {
3047
3321
  data.currentTranslate = data.startTranslate;
3048
3322
  }
3323
+
3324
+ // Threshold
3049
3325
  if (params.threshold > 0) {
3050
3326
  if (Math.abs(diff) > params.threshold || data.allowThresholdMove) {
3051
3327
  if (!data.allowThresholdMove) {
@@ -3063,12 +3339,17 @@
3063
3339
  }
3064
3340
  if (!params.followFinger || params.cssMode) return;
3065
3341
 
3066
- // core-lite: no optional feature updates; only watchSlidesProgress
3067
- if (params.watchSlidesProgress) {
3342
+ // Update active index in free mode
3343
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode || params.watchSlidesProgress) {
3068
3344
  swiper.updateActiveIndex();
3069
3345
  swiper.updateSlidesClasses();
3070
3346
  }
3347
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode) {
3348
+ swiper.freeMode.onTouchMove();
3349
+ }
3350
+ // Update progress
3071
3351
  swiper.updateProgress(data.currentTranslate);
3352
+ // Update translate
3072
3353
  swiper.setTranslate(data.currentTranslate);
3073
3354
  }
3074
3355
 
@@ -3080,7 +3361,7 @@
3080
3361
  let targetTouch;
3081
3362
  const isTouchEvent = e.type === 'touchend' || e.type === 'touchcancel';
3082
3363
  if (!isTouchEvent) {
3083
- if (data.touchId !== null) return;
3364
+ if (data.touchId !== null) return; // return from pointer if we use touch
3084
3365
  if (e.pointerId !== data.pointerId) return;
3085
3366
  targetTouch = e;
3086
3367
  } else {
@@ -3089,7 +3370,9 @@
3089
3370
  }
3090
3371
  if (['pointercancel', 'pointerout', 'pointerleave', 'contextmenu'].includes(e.type)) {
3091
3372
  const proceed = ['pointercancel', 'contextmenu'].includes(e.type) && (swiper.browser.isSafari || swiper.browser.isWebView);
3092
- if (!proceed) return;
3373
+ if (!proceed) {
3374
+ return;
3375
+ }
3093
3376
  }
3094
3377
  data.pointerId = null;
3095
3378
  data.touchId = null;
@@ -3107,7 +3390,9 @@
3107
3390
  }
3108
3391
  data.allowTouchCallbacks = false;
3109
3392
  if (!data.isTouched) {
3110
- if (data.isMoved && params.grabCursor) swiper.setGrabCursor(false);
3393
+ if (data.isMoved && params.grabCursor) {
3394
+ swiper.setGrabCursor(false);
3395
+ }
3111
3396
  data.isMoved = false;
3112
3397
  data.startMoving = false;
3113
3398
  return;
@@ -3117,6 +3402,8 @@
3117
3402
  if (params.grabCursor && data.isMoved && data.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
3118
3403
  swiper.setGrabCursor(false);
3119
3404
  }
3405
+
3406
+ // Time diff
3120
3407
  const touchEndTime = now();
3121
3408
  const timeDiff = touchEndTime - data.touchStartTime;
3122
3409
 
@@ -3143,8 +3430,20 @@
3143
3430
  data.isMoved = false;
3144
3431
  data.startMoving = false;
3145
3432
  let currentPos;
3146
- if (params.followFinger) currentPos = rtl ? swiper.translate : -swiper.translate;else currentPos = -data.currentTranslate;
3147
- if (params.cssMode) return;
3433
+ if (params.followFinger) {
3434
+ currentPos = rtl ? swiper.translate : -swiper.translate;
3435
+ } else {
3436
+ currentPos = -data.currentTranslate;
3437
+ }
3438
+ if (params.cssMode) {
3439
+ return;
3440
+ }
3441
+ if (params.freeMode && params.freeMode.enabled) {
3442
+ swiper.freeMode.onTouchEnd({
3443
+ currentPos
3444
+ });
3445
+ return;
3446
+ }
3148
3447
 
3149
3448
  // Find current slide
3150
3449
  const swipeToLast = currentPos >= -swiper.maxTranslate() && !swiper.params.loop;
@@ -3166,22 +3465,22 @@
3166
3465
  let rewindLastIndex = null;
3167
3466
  if (params.rewind) {
3168
3467
  if (swiper.isBeginning) {
3169
- rewindLastIndex = swiper.slides.length - 1;
3468
+ rewindLastIndex = params.virtual && params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1;
3170
3469
  } else if (swiper.isEnd) {
3171
3470
  rewindFirstIndex = 0;
3172
3471
  }
3173
3472
  }
3473
+ // Find current slide size
3174
3474
  const ratio = (currentPos - slidesGrid[stopIndex]) / groupSize;
3175
3475
  const increment = stopIndex < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup;
3176
3476
  if (timeDiff > params.longSwipesMs) {
3477
+ // Long touches
3177
3478
  if (!params.longSwipes) {
3178
3479
  swiper.slideTo(swiper.activeIndex);
3179
3480
  return;
3180
3481
  }
3181
3482
  if (swiper.swipeDirection === 'next') {
3182
- if (ratio >= params.longSwipesRatio) {
3183
- swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment);
3184
- } else swiper.slideTo(stopIndex);
3483
+ if (ratio >= params.longSwipesRatio) swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment);else swiper.slideTo(stopIndex);
3185
3484
  }
3186
3485
  if (swiper.swipeDirection === 'prev') {
3187
3486
  if (ratio > 1 - params.longSwipesRatio) {
@@ -3233,6 +3532,7 @@
3233
3532
  allowSlidePrev,
3234
3533
  snapGrid
3235
3534
  } = swiper;
3535
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
3236
3536
 
3237
3537
  // Disable locks on resize
3238
3538
  swiper.allowSlideNext = true;
@@ -3240,12 +3540,15 @@
3240
3540
  swiper.updateSize();
3241
3541
  swiper.updateSlides();
3242
3542
  swiper.updateSlidesClasses();
3243
- if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && !swiper.params.centeredSlides) {
3543
+ const isVirtualLoop = isVirtual && params.loop;
3544
+ if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && !swiper.params.centeredSlides && !isVirtualLoop) {
3244
3545
  swiper.slideTo(swiper.slides.length - 1, 0, false, true);
3245
- } else if (swiper.params.loop) {
3246
- swiper.slideToLoop(swiper.realIndex, 0, false, true);
3247
3546
  } else {
3248
- swiper.slideTo(swiper.activeIndex, 0, false, true);
3547
+ if (swiper.params.loop && !isVirtual) {
3548
+ swiper.slideToLoop(swiper.realIndex, 0, false, true);
3549
+ } else {
3550
+ swiper.slideTo(swiper.activeIndex, 0, false, true);
3551
+ }
3249
3552
  }
3250
3553
  if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) {
3251
3554
  clearTimeout(swiper.autoplay.resizeTimeout);
@@ -3255,11 +3558,10 @@
3255
3558
  }
3256
3559
  }, 500);
3257
3560
  }
3258
-
3259
3561
  // Return locks after resize
3260
3562
  swiper.allowSlidePrev = allowSlidePrev;
3261
3563
  swiper.allowSlideNext = allowSlideNext;
3262
- if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
3564
+ if (swiper.params.watchOverflow && snapGrid !== swiper.snapGrid) {
3263
3565
  swiper.checkOverflow();
3264
3566
  }
3265
3567
  }
@@ -3313,10 +3615,6 @@
3313
3615
  if (swiper.params.cssMode || swiper.params.slidesPerView !== 'auto' && !swiper.params.autoHeight) {
3314
3616
  return;
3315
3617
  }
3316
- const el = e?.target;
3317
- // IMG, IFRAME, EMBED with width/height attributes don't change slide size — no need to call swiper.update()
3318
- if (!el || !['IMG', 'IFRAME', 'EMBED'].includes(el.tagName)) return;
3319
- if (el.hasAttribute('width') && el.hasAttribute('height')) return;
3320
3618
  swiper.update();
3321
3619
  }
3322
3620
 
@@ -3428,15 +3726,8 @@
3428
3726
  detachEvents
3429
3727
  };
3430
3728
 
3431
- const toggleModule = (swiper, params, breakpointParams, prop) => {
3432
- const wasModuleEnabled = params[prop] && params[prop].enabled;
3433
- const isModuleEnabled = breakpointParams[prop] && breakpointParams[prop].enabled;
3434
- if (wasModuleEnabled && !isModuleEnabled) {
3435
- swiper[prop].disable();
3436
- }
3437
- if (!wasModuleEnabled && isModuleEnabled) {
3438
- swiper[prop].enable();
3439
- }
3729
+ const isGridEnabled = (swiper, params) => {
3730
+ return swiper.grid && params.grid && params.grid.rows > 1;
3440
3731
  };
3441
3732
  function setBreakpoint() {
3442
3733
  const swiper = this;
@@ -3457,20 +3748,38 @@
3457
3748
  if (!breakpoint || swiper.currentBreakpoint === breakpoint) return;
3458
3749
  const breakpointOnlyParams = breakpoint in breakpoints ? breakpoints[breakpoint] : undefined;
3459
3750
  const breakpointParams = breakpointOnlyParams || swiper.originalParams;
3751
+ const wasMultiRow = isGridEnabled(swiper, params);
3752
+ const isMultiRow = isGridEnabled(swiper, breakpointParams);
3460
3753
  const wasGrabCursor = swiper.params.grabCursor;
3461
3754
  const isGrabCursor = breakpointParams.grabCursor;
3462
3755
  const wasEnabled = params.enabled;
3756
+ if (wasMultiRow && !isMultiRow) {
3757
+ el.classList.remove(`${params.containerModifierClass}grid`, `${params.containerModifierClass}grid-column`);
3758
+ swiper.emitContainerClasses();
3759
+ } else if (!wasMultiRow && isMultiRow) {
3760
+ el.classList.add(`${params.containerModifierClass}grid`);
3761
+ if (breakpointParams.grid.fill && breakpointParams.grid.fill === 'column' || !breakpointParams.grid.fill && params.grid.fill === 'column') {
3762
+ el.classList.add(`${params.containerModifierClass}grid-column`);
3763
+ }
3764
+ swiper.emitContainerClasses();
3765
+ }
3463
3766
  if (wasGrabCursor && !isGrabCursor) {
3464
3767
  swiper.unsetGrabCursor();
3465
3768
  } else if (!wasGrabCursor && isGrabCursor) {
3466
3769
  swiper.setGrabCursor();
3467
3770
  }
3468
3771
 
3469
- // Core-lite: toggle navigation & pagination only.
3470
- const modules = ['navigation', 'pagination'];
3471
- modules.forEach(prop => {
3772
+ // Toggle navigation, pagination, scrollbar
3773
+ ['navigation', 'pagination', 'scrollbar'].forEach(prop => {
3472
3774
  if (typeof breakpointParams[prop] === 'undefined') return;
3473
- toggleModule(swiper, params, breakpointParams, prop);
3775
+ const wasModuleEnabled = params[prop] && params[prop].enabled;
3776
+ const isModuleEnabled = breakpointParams[prop] && breakpointParams[prop].enabled;
3777
+ if (wasModuleEnabled && !isModuleEnabled) {
3778
+ swiper[prop].disable();
3779
+ }
3780
+ if (!wasModuleEnabled && isModuleEnabled) {
3781
+ swiper[prop].enable();
3782
+ }
3474
3783
  });
3475
3784
  const directionChanged = breakpointParams.direction && breakpointParams.direction !== params.direction;
3476
3785
  const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged);
@@ -3552,7 +3861,7 @@
3552
3861
  getBreakpoint
3553
3862
  };
3554
3863
 
3555
- const prepareClasses = (entries, prefix) => {
3864
+ function prepareClasses(entries, prefix) {
3556
3865
  const resultClasses = [];
3557
3866
  entries.forEach(item => {
3558
3867
  if (typeof item === 'object') {
@@ -3566,7 +3875,7 @@
3566
3875
  }
3567
3876
  });
3568
3877
  return resultClasses;
3569
- };
3878
+ }
3570
3879
  function addClasses() {
3571
3880
  const swiper = this;
3572
3881
  const {
@@ -3576,12 +3885,17 @@
3576
3885
  el,
3577
3886
  device
3578
3887
  } = swiper;
3579
- // core-lite: removed module-specific classes
3580
3888
  // prettier-ignore
3581
3889
  const suffixes = prepareClasses(['initialized', params.direction, {
3890
+ 'free-mode': swiper.params.freeMode && params.freeMode.enabled
3891
+ }, {
3582
3892
  'autoheight': params.autoHeight
3583
3893
  }, {
3584
3894
  'rtl': rtl
3895
+ }, {
3896
+ 'grid': params.grid && params.grid.rows > 1
3897
+ }, {
3898
+ 'grid-column': params.grid && params.grid.rows > 1 && params.grid.fill === 'column'
3585
3899
  }, {
3586
3900
  'android': device.android
3587
3901
  }, {
@@ -3678,12 +3992,16 @@
3678
3992
  autoHeight: false,
3679
3993
  // Set wrapper width
3680
3994
  setWrapperSize: false,
3681
- // Effects (core-lite only supports `slide`)
3995
+ // Virtual Translate
3996
+ virtualTranslate: false,
3997
+ // Effects
3682
3998
  effect: 'slide',
3999
+ // 'slide' or 'fade' or 'cube' or 'coverflow' or 'flip'
4000
+
3683
4001
  // Breakpoints
3684
4002
  breakpoints: undefined,
3685
4003
  breakpointsBase: 'window',
3686
- // Slides
4004
+ // Slides grid
3687
4005
  spaceBetween: 0,
3688
4006
  slidesPerView: 1,
3689
4007
  slidesPerGroup: 1,
@@ -3786,9 +4104,7 @@
3786
4104
  if (moduleParamName === 'navigation' && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].prevEl && !params[moduleParamName].nextEl) {
3787
4105
  params[moduleParamName].auto = true;
3788
4106
  }
3789
-
3790
- // Core-lite: keep only pagination auto-init.
3791
- if (moduleParamName === 'pagination' && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].el) {
4107
+ if (['pagination', 'scrollbar'].indexOf(moduleParamName) >= 0 && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].el) {
3792
4108
  params[moduleParamName].auto = true;
3793
4109
  }
3794
4110
  if (!(moduleParamName in params && 'enabled' in moduleParams)) {
@@ -3914,6 +4230,7 @@
3914
4230
  // Indexes
3915
4231
  activeIndex: 0,
3916
4232
  realIndex: 0,
4233
+ //
3917
4234
  isBeginning: true,
3918
4235
  isEnd: false,
3919
4236
  // Props
@@ -3940,9 +4257,12 @@
3940
4257
  currentTranslate: undefined,
3941
4258
  startTranslate: undefined,
3942
4259
  allowThresholdMove: undefined,
4260
+ // Form elements to match
3943
4261
  focusableElements: swiper.params.focusableElements,
4262
+ // Last click time
3944
4263
  lastClickTime: 0,
3945
4264
  clickTimeout: undefined,
4265
+ // Velocities
3946
4266
  velocities: [],
3947
4267
  allowMomentumBounce: undefined,
3948
4268
  startMoving: undefined,
@@ -3971,6 +4291,7 @@
3971
4291
  swiper.init();
3972
4292
  }
3973
4293
 
4294
+ // Return app instance
3974
4295
  // eslint-disable-next-line no-constructor-return
3975
4296
  return swiper;
3976
4297
  }
@@ -4002,6 +4323,16 @@
4002
4323
  getSlideIndexByData(index) {
4003
4324
  return this.getSlideIndex(this.slides.find(slideEl => slideEl.getAttribute('data-swiper-slide-index') * 1 === index));
4004
4325
  }
4326
+ getSlideIndexWhenGrid(index) {
4327
+ if (this.grid && this.params.grid && this.params.grid.rows > 1) {
4328
+ if (this.params.grid.fill === 'column') {
4329
+ index = Math.floor(index / this.params.grid.rows);
4330
+ } else if (this.params.grid.fill === 'row') {
4331
+ index = index % Math.ceil(this.slides.length / this.params.grid.rows);
4332
+ }
4333
+ }
4334
+ return index;
4335
+ }
4005
4336
  recalcSlides() {
4006
4337
  const swiper = this;
4007
4338
  const {
@@ -4103,15 +4434,21 @@
4103
4434
  }
4104
4435
  }
4105
4436
  } else {
4437
+ // eslint-disable-next-line
4106
4438
  if (view === 'current') {
4107
4439
  for (let i = activeIndex + 1; i < slides.length; i += 1) {
4108
4440
  const slideInView = exact ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize;
4109
- if (slideInView) spv += 1;
4441
+ if (slideInView) {
4442
+ spv += 1;
4443
+ }
4110
4444
  }
4111
4445
  } else {
4446
+ // previous
4112
4447
  for (let i = activeIndex - 1; i >= 0; i -= 1) {
4113
4448
  const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize;
4114
- if (slideInView) spv += 1;
4449
+ if (slideInView) {
4450
+ spv += 1;
4451
+ }
4115
4452
  }
4116
4453
  }
4117
4454
  }
@@ -4124,11 +4461,14 @@
4124
4461
  snapGrid,
4125
4462
  params
4126
4463
  } = swiper;
4464
+ // Breakpoints
4127
4465
  if (params.breakpoints) {
4128
4466
  swiper.setBreakpoint();
4129
4467
  }
4130
4468
  [...swiper.el.querySelectorAll('[loading="lazy"]')].forEach(imageEl => {
4131
- if (imageEl.complete) processLazyPreloader(swiper, imageEl);
4469
+ if (imageEl.complete) {
4470
+ processLazyPreloader(swiper, imageEl);
4471
+ }
4132
4472
  });
4133
4473
  swiper.updateSize();
4134
4474
  swiper.updateSlides();
@@ -4142,12 +4482,22 @@
4142
4482
  swiper.updateSlidesClasses();
4143
4483
  }
4144
4484
  let translated;
4145
- if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !params.centeredSlides) {
4146
- translated = swiper.slideTo(swiper.slides.length - 1, 0, false, true);
4485
+ if (params.freeMode && params.freeMode.enabled && !params.cssMode) {
4486
+ setTranslate();
4487
+ if (params.autoHeight) {
4488
+ swiper.updateAutoHeight();
4489
+ }
4147
4490
  } else {
4148
- translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
4491
+ if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !params.centeredSlides) {
4492
+ const slides = swiper.virtual && params.virtual.enabled ? swiper.virtual.slides : swiper.slides;
4493
+ translated = swiper.slideTo(slides.length - 1, 0, false, true);
4494
+ } else {
4495
+ translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
4496
+ }
4497
+ if (!translated) {
4498
+ setTranslate();
4499
+ }
4149
4500
  }
4150
- if (!translated) setTranslate();
4151
4501
  if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
4152
4502
  swiper.checkOverflow();
4153
4503
  }
@@ -4160,6 +4510,7 @@
4160
4510
  const swiper = this;
4161
4511
  const currentDirection = swiper.params.direction;
4162
4512
  if (!newDirection) {
4513
+ // eslint-disable-next-line
4163
4514
  newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
4164
4515
  }
4165
4516
  if (newDirection === currentDirection || newDirection !== 'horizontal' && newDirection !== 'vertical') {
@@ -4197,11 +4548,15 @@
4197
4548
  mount(element) {
4198
4549
  const swiper = this;
4199
4550
  if (swiper.mounted) return true;
4551
+
4552
+ // Find el
4200
4553
  let el = element || swiper.params.el;
4201
4554
  if (typeof el === 'string') {
4202
4555
  el = document.querySelector(el);
4203
4556
  }
4204
- if (!el) return false;
4557
+ if (!el) {
4558
+ return false;
4559
+ }
4205
4560
  el.swiper = swiper;
4206
4561
  if (el.parentNode && el.parentNode.host && el.parentNode.host.nodeName === swiper.params.swiperElementNodeName.toUpperCase()) {
4207
4562
  swiper.isElement = true;
@@ -4212,10 +4567,12 @@
4212
4567
  const getWrapper = () => {
4213
4568
  if (el && el.shadowRoot && el.shadowRoot.querySelector) {
4214
4569
  const res = el.shadowRoot.querySelector(getWrapperSelector());
4570
+ // Children needs to return slot items
4215
4571
  return res;
4216
4572
  }
4217
4573
  return elementChildren(el, getWrapperSelector())[0];
4218
4574
  };
4575
+ // Find Wrapper
4219
4576
  let wrapperEl = getWrapper();
4220
4577
  if (!wrapperEl && swiper.params.createElements) {
4221
4578
  wrapperEl = createElement('div', swiper.params.wrapperClass);
@@ -4230,6 +4587,7 @@
4230
4587
  slidesEl: swiper.isElement && !el.parentNode.host.slideSlots ? el.parentNode.host : wrapperEl,
4231
4588
  hostEl: swiper.isElement ? el.parentNode.host : el,
4232
4589
  mounted: true,
4590
+ // RTL
4233
4591
  rtl: el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl',
4234
4592
  rtlTranslate: swiper.params.direction === 'horizontal' && (el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl'),
4235
4593
  wrongRTL: elementStyle(wrapperEl, 'display') === '-webkit-box'
@@ -4249,6 +4607,7 @@
4249
4607
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
4250
4608
  const swiperTranslate = structuredClone(swiper.snapGrid[1]);
4251
4609
  if (isFirstSlide) {
4610
+ // Move last item to first position when at first slide
4252
4611
  const lastSlide = slides.at(-1);
4253
4612
  lastSlide.swiperLoopMoveDOM = true;
4254
4613
  swiper.slidesEl.prepend(lastSlide);
@@ -4260,6 +4619,7 @@
4260
4619
  swiper.setTransition(speed);
4261
4620
  swiper.setTranslate(-swiperTranslate);
4262
4621
  } else if (isLastSlide) {
4622
+ // Move first item to last position when at last slide
4263
4623
  const firstSlide = slides[0];
4264
4624
  firstSlide.swiperLoopMoveDOM = true;
4265
4625
  swiper.slidesEl.append(firstSlide);
@@ -4280,24 +4640,42 @@
4280
4640
  const mounted = swiper.mount(el);
4281
4641
  if (mounted === false) return swiper;
4282
4642
  swiper.emit('beforeInit');
4643
+
4644
+ // Set breakpoint
4283
4645
  if (swiper.params.breakpoints) {
4284
4646
  swiper.setBreakpoint();
4285
4647
  }
4648
+
4649
+ // Add Classes
4286
4650
  swiper.addClasses();
4651
+
4652
+ // Update size
4287
4653
  swiper.updateSize();
4654
+
4655
+ // Update slides
4288
4656
  swiper.updateSlides();
4289
4657
  if (swiper.params.watchOverflow) {
4290
4658
  swiper.checkOverflow();
4291
4659
  }
4660
+
4661
+ // Set Grab Cursor
4292
4662
  if (swiper.params.grabCursor && swiper.enabled) {
4293
4663
  swiper.setGrabCursor();
4294
4664
  }
4295
4665
 
4296
- // Slide to initial slide (core-lite: no optional feature initial offsets)
4297
- swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
4666
+ // Slide To Initial Slide
4667
+ if (swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) {
4668
+ swiper.slideTo(swiper.params.initialSlide + swiper.virtual.slidesBefore, 0, swiper.params.runCallbacksOnInit, false, true);
4669
+ } else {
4670
+ swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
4671
+ }
4672
+
4673
+ // Create loop
4298
4674
  if (swiper.params.loop) {
4299
4675
  swiper.loopCreate(undefined, true);
4300
4676
  }
4677
+
4678
+ // Attach events
4301
4679
  swiper.attachEvents();
4302
4680
  const lazyElements = [...swiper.el.querySelectorAll('[loading="lazy"]')];
4303
4681
  if (swiper.isElement) {
@@ -4307,12 +4685,18 @@
4307
4685
  if (imageEl.complete) {
4308
4686
  processLazyPreloader(swiper, imageEl);
4309
4687
  } else {
4310
- imageEl.addEventListener('load', e => processLazyPreloader(swiper, e.target));
4688
+ imageEl.addEventListener('load', e => {
4689
+ processLazyPreloader(swiper, e.target);
4690
+ });
4311
4691
  }
4312
4692
  });
4313
4693
  preload(swiper);
4694
+
4695
+ // Init Flag
4314
4696
  swiper.initialized = true;
4315
4697
  preload(swiper);
4698
+
4699
+ // Emit
4316
4700
  swiper.emit('init');
4317
4701
  swiper.emit('afterInit');
4318
4702
  return swiper;
@@ -4335,15 +4719,27 @@
4335
4719
  return null;
4336
4720
  }
4337
4721
  swiper.emit('beforeDestroy');
4722
+
4723
+ // Init Flag
4338
4724
  swiper.initialized = false;
4725
+
4726
+ // Detach events
4339
4727
  swiper.detachEvents();
4728
+
4729
+ // Destroy loop
4340
4730
  if (params.loop) {
4341
4731
  swiper.loopDestroy();
4342
4732
  }
4733
+
4734
+ // Cleanup styles
4343
4735
  if (cleanStyles) {
4344
4736
  swiper.removeClasses();
4345
- if (el && typeof el !== 'string') el.removeAttribute('style');
4346
- if (wrapperEl) wrapperEl.removeAttribute('style');
4737
+ if (el && typeof el !== 'string') {
4738
+ el.removeAttribute('style');
4739
+ }
4740
+ if (wrapperEl) {
4741
+ wrapperEl.removeAttribute('style');
4742
+ }
4347
4743
  if (slides && slides.length) {
4348
4744
  slides.forEach(slideEl => {
4349
4745
  slideEl.classList.remove(params.slideVisibleClass, params.slideFullyVisibleClass, params.slideActiveClass, params.slideNextClass, params.slidePrevClass);
@@ -4353,6 +4749,8 @@
4353
4749
  }
4354
4750
  }
4355
4751
  swiper.emit('destroy');
4752
+
4753
+ // Detach emitter events
4356
4754
  Object.keys(swiper.eventsListeners).forEach(eventName => {
4357
4755
  swiper.off(eventName);
4358
4756
  });
@@ -4723,7 +5121,7 @@
4723
5121
  }
4724
5122
 
4725
5123
  /**
4726
- * Swiper Custom Element 0.0.15-dev.1
5124
+ * Swiper Custom Element 0.0.15
4727
5125
  * Gem SDK - Swiper, Customized of swiper
4728
5126
  * https://swiperjs.com
4729
5127
  *