@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.
@@ -382,6 +382,9 @@ function updateSize() {
382
382
 
383
383
  function updateSlides() {
384
384
  const swiper = this;
385
+ function getDirectionPropertyValue(node, label) {
386
+ return parseFloat(node.getPropertyValue(swiper.getDirectionLabel(label)) || 0);
387
+ }
385
388
  const params = swiper.params;
386
389
  const {
387
390
  wrapperEl,
@@ -390,8 +393,10 @@ function updateSlides() {
390
393
  rtlTranslate: rtl,
391
394
  wrongRTL
392
395
  } = swiper;
396
+ const isVirtual = swiper.virtual && params.virtual.enabled;
397
+ const previousSlidesLength = isVirtual ? swiper.virtual.slides.length : swiper.slides.length;
393
398
  const slides = elementChildren(slidesEl, `.${swiper.params.slideClass}, swiper-slide`);
394
- const slidesLength = slides.length;
399
+ const slidesLength = isVirtual ? swiper.virtual.slides.length : slides.length;
395
400
  let snapGrid = [];
396
401
  const slidesGrid = [];
397
402
  const slidesSizesGrid = [];
@@ -409,15 +414,15 @@ function updateSlides() {
409
414
  let slidePosition = -offsetBefore;
410
415
  let prevSlideSize = 0;
411
416
  let index = 0;
412
- if (typeof swiperSize === 'undefined') return;
417
+ if (typeof swiperSize === 'undefined') {
418
+ return;
419
+ }
413
420
  if (typeof spaceBetween === 'string' && spaceBetween.indexOf('%') >= 0) {
414
421
  spaceBetween = parseFloat(spaceBetween.replace('%', '')) / 100 * swiperSize;
415
422
  } else if (typeof spaceBetween === 'string') {
416
423
  spaceBetween = parseFloat(spaceBetween);
417
424
  }
418
-
419
- // core-lite: compute total slides size without optional modules
420
- swiper.slidesTotalSize = -spaceBetween;
425
+ swiper.virtualSize = -spaceBetween;
421
426
 
422
427
  // reset margins
423
428
  slides.forEach(slideEl => {
@@ -435,34 +440,49 @@ function updateSlides() {
435
440
  setCSSProperty(wrapperEl, '--swiper-centered-offset-before', '');
436
441
  setCSSProperty(wrapperEl, '--swiper-centered-offset-after', '');
437
442
  }
443
+ const gridEnabled = params.grid && params.grid.rows > 1 && swiper.grid;
444
+ if (gridEnabled) {
445
+ swiper.grid.initSlides(slides);
446
+ } else if (swiper.grid) {
447
+ swiper.grid.unsetSlides();
448
+ }
438
449
 
439
450
  // Calc slides
440
451
  let slideSize;
441
- const shouldResetSlideSize = params.slidesPerView === 'auto' && params.breakpoints && Object.keys(params.breakpoints).filter(key => typeof params.breakpoints[key].slidesPerView !== 'undefined').length > 0;
452
+ const shouldResetSlideSize = params.slidesPerView === 'auto' && params.breakpoints && Object.keys(params.breakpoints).filter(key => {
453
+ return typeof params.breakpoints[key].slidesPerView !== 'undefined';
454
+ }).length > 0;
442
455
  for (let i = 0; i < slidesLength; i += 1) {
443
456
  slideSize = 0;
444
457
  let slide;
445
458
  if (slides[i]) slide = slides[i];
459
+ if (gridEnabled) {
460
+ swiper.grid.updateSlide(i, slide, slides);
461
+ }
446
462
  if (slides[i] && elementStyle(slide, 'display') === 'none') continue; // eslint-disable-line
447
463
 
448
464
  if (params.slidesPerView === 'auto') {
449
- if (shouldResetSlideSize && slides[i]) {
465
+ if (shouldResetSlideSize) {
450
466
  slides[i].style[swiper.getDirectionLabel('width')] = ``;
451
467
  }
452
468
  const slideStyles = getComputedStyle(slide);
453
469
  const currentTransform = slide.style.transform;
454
470
  const currentWebKitTransform = slide.style.webkitTransform;
455
- if (currentTransform) slide.style.transform = 'none';
456
- if (currentWebKitTransform) slide.style.webkitTransform = 'none';
471
+ if (currentTransform) {
472
+ slide.style.transform = 'none';
473
+ }
474
+ if (currentWebKitTransform) {
475
+ slide.style.webkitTransform = 'none';
476
+ }
457
477
  if (params.roundLengths) {
458
478
  slideSize = swiper.isHorizontal() ? elementOuterSize(slide, 'width', true) : elementOuterSize(slide, 'height', true);
459
479
  } else {
460
480
  // eslint-disable-next-line
461
- const width = parseFloat(slideStyles.getPropertyValue('width')) || slide.offsetWidth;
462
- const paddingLeft = parseFloat(slideStyles.getPropertyValue('padding-left')) || 0;
463
- const paddingRight = parseFloat(slideStyles.getPropertyValue('padding-right')) || 0;
464
- const marginLeft = parseFloat(slideStyles.getPropertyValue('margin-left')) || 0;
465
- const marginRight = parseFloat(slideStyles.getPropertyValue('margin-right')) || 0;
481
+ const width = getDirectionPropertyValue(slideStyles, 'width');
482
+ const paddingLeft = getDirectionPropertyValue(slideStyles, 'padding-left');
483
+ const paddingRight = getDirectionPropertyValue(slideStyles, 'padding-right');
484
+ const marginLeft = getDirectionPropertyValue(slideStyles, 'margin-left');
485
+ const marginRight = getDirectionPropertyValue(slideStyles, 'margin-right');
466
486
  const boxSizing = slideStyles.getPropertyValue('box-sizing');
467
487
  if (boxSizing && boxSizing === 'border-box') {
468
488
  slideSize = width + marginLeft + marginRight;
@@ -474,8 +494,12 @@ function updateSlides() {
474
494
  slideSize = width + paddingLeft + paddingRight + marginLeft + marginRight + (offsetWidth - clientWidth);
475
495
  }
476
496
  }
477
- if (currentTransform) slide.style.transform = currentTransform;
478
- if (currentWebKitTransform) slide.style.webkitTransform = currentWebKitTransform;
497
+ if (currentTransform) {
498
+ slide.style.transform = currentTransform;
499
+ }
500
+ if (currentWebKitTransform) {
501
+ slide.style.webkitTransform = currentWebKitTransform;
502
+ }
479
503
  if (params.roundLengths) slideSize = Math.floor(slideSize);
480
504
  } else {
481
505
  slideSize = (swiperSize - (params.slidesPerView - 1) * spaceBetween) / params.slidesPerView;
@@ -484,13 +508,13 @@ function updateSlides() {
484
508
  slides[i].style[swiper.getDirectionLabel('width')] = `${slideSize}px`;
485
509
  }
486
510
  }
487
- if (slides[i]) slides[i].swiperSlideSize = slideSize;
511
+ if (slides[i]) {
512
+ slides[i].swiperSlideSize = slideSize;
513
+ }
488
514
  slidesSizesGrid.push(slideSize);
489
515
  if (params.centeredSlides) {
490
516
  slidePosition = slidePosition + slideSize / 2 + prevSlideSize / 2 + spaceBetween;
491
- if (prevSlideSize === 0 && i !== 0) {
492
- slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
493
- }
517
+ if (prevSlideSize === 0 && i !== 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
494
518
  if (i === 0) slidePosition = slidePosition - swiperSize / 2 - spaceBetween;
495
519
  if (Math.abs(slidePosition) < 1 / 1000) slidePosition = 0;
496
520
  if (params.roundLengths) slidePosition = Math.floor(slidePosition);
@@ -498,37 +522,55 @@ function updateSlides() {
498
522
  slidesGrid.push(slidePosition);
499
523
  } else {
500
524
  if (params.roundLengths) slidePosition = Math.floor(slidePosition);
501
- if ((index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0) {
502
- snapGrid.push(slidePosition);
503
- }
525
+ if ((index - Math.min(swiper.params.slidesPerGroupSkip, index)) % swiper.params.slidesPerGroup === 0) snapGrid.push(slidePosition);
504
526
  slidesGrid.push(slidePosition);
505
527
  slidePosition = slidePosition + slideSize + spaceBetween;
506
528
  }
507
- swiper.slidesTotalSize += slideSize + spaceBetween;
529
+ swiper.virtualSize += slideSize + spaceBetween;
508
530
  prevSlideSize = slideSize;
509
531
  index += 1;
510
532
  }
511
- swiper.slidesTotalSize = Math.max(swiper.slidesTotalSize, swiperSize) + offsetAfter;
512
- if (rtl && wrongRTL && params.effect === 'slide') {
513
- wrapperEl.style.width = `${swiper.slidesTotalSize + spaceBetween}px`;
533
+ swiper.virtualSize = Math.max(swiper.virtualSize, swiperSize) + offsetAfter;
534
+ if (rtl && wrongRTL && (params.effect === 'slide' || params.effect === 'coverflow')) {
535
+ wrapperEl.style.width = `${swiper.virtualSize + spaceBetween}px`;
514
536
  }
515
537
  if (params.setWrapperSize) {
516
- wrapperEl.style[swiper.getDirectionLabel('width')] = `${swiper.slidesTotalSize + spaceBetween}px`;
538
+ wrapperEl.style[swiper.getDirectionLabel('width')] = `${swiper.virtualSize + spaceBetween}px`;
539
+ }
540
+ if (gridEnabled) {
541
+ swiper.grid.updateWrapperSize(slideSize, snapGrid);
517
542
  }
518
543
 
519
- // Remove last snap points depending on width (non-centered)
544
+ // Remove last grid elements depending on width
520
545
  if (!params.centeredSlides) {
521
546
  const newSlidesGrid = [];
522
547
  for (let i = 0; i < snapGrid.length; i += 1) {
523
548
  let slidesGridItem = snapGrid[i];
524
549
  if (params.roundLengths) slidesGridItem = Math.floor(slidesGridItem);
525
- if (snapGrid[i] <= swiper.slidesTotalSize - swiperSize) {
550
+ if (snapGrid[i] <= swiper.virtualSize - swiperSize) {
526
551
  newSlidesGrid.push(slidesGridItem);
527
552
  }
528
553
  }
529
554
  snapGrid = newSlidesGrid;
530
- if (Math.floor(swiper.slidesTotalSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1) {
531
- snapGrid.push(swiper.slidesTotalSize - swiperSize);
555
+ if (Math.floor(swiper.virtualSize - swiperSize) - Math.floor(snapGrid[snapGrid.length - 1]) > 1) {
556
+ snapGrid.push(swiper.virtualSize - swiperSize);
557
+ }
558
+ }
559
+ if (isVirtual && params.loop) {
560
+ const size = slidesSizesGrid[0] + spaceBetween;
561
+ if (params.slidesPerGroup > 1) {
562
+ const groups = Math.ceil((swiper.virtual.slidesBefore + swiper.virtual.slidesAfter) / params.slidesPerGroup);
563
+ const groupSize = size * params.slidesPerGroup;
564
+ for (let i = 0; i < groups; i += 1) {
565
+ snapGrid.push(snapGrid[snapGrid.length - 1] + groupSize);
566
+ }
567
+ }
568
+ for (let i = 0; i < swiper.virtual.slidesBefore + swiper.virtual.slidesAfter; i += 1) {
569
+ if (params.slidesPerGroup === 1) {
570
+ snapGrid.push(snapGrid[snapGrid.length - 1] + size);
571
+ }
572
+ slidesGrid.push(slidesGrid[slidesGrid.length - 1] + size);
573
+ swiper.virtualSize += size;
532
574
  }
533
575
  }
534
576
  if (snapGrid.length === 0) snapGrid = [0];
@@ -536,7 +578,9 @@ function updateSlides() {
536
578
  const key = swiper.isHorizontal() && rtl ? 'marginLeft' : swiper.getDirectionLabel('marginRight');
537
579
  slides.filter((_, slideIndex) => {
538
580
  if (!params.cssMode || params.loop) return true;
539
- if (slideIndex === slides.length - 1) return false;
581
+ if (slideIndex === slides.length - 1) {
582
+ return false;
583
+ }
540
584
  return true;
541
585
  }).forEach(slideEl => {
542
586
  slideEl.style[key] = `${spaceBetween}px`;
@@ -586,9 +630,7 @@ function updateSlides() {
586
630
  swiper.snapGrid = swiper.snapGrid.map(v => v + addToSnapGrid);
587
631
  swiper.slidesGrid = swiper.slidesGrid.map(v => v + addToSlidesGrid);
588
632
  }
589
-
590
- // Emit changes
591
- if (slidesLength !== (previousSlidesGridLength ? slides.length : slides.length)) {
633
+ if (slidesLength !== previousSlidesLength) {
592
634
  swiper.emit('slidesLengthChange');
593
635
  }
594
636
  if (snapGrid.length !== previousSnapGridLength) {
@@ -602,7 +644,7 @@ function updateSlides() {
602
644
  swiper.updateSlidesOffset();
603
645
  }
604
646
  swiper.emit('slidesUpdated');
605
- if (!params.cssMode && params.effect === 'slide') {
647
+ if (!isVirtual && !params.cssMode && (params.effect === 'slide' || params.effect === 'fade')) {
606
648
  const backFaceHiddenClass = `${params.containerModifierClass}backface-hidden`;
607
649
  const hasClassBackfaceClassAdded = swiper.el.classList.contains(backFaceHiddenClass);
608
650
  if (slidesLength <= params.maxBackfaceHiddenSlides) {
@@ -616,13 +658,20 @@ function updateSlides() {
616
658
  function updateAutoHeight(speed) {
617
659
  const swiper = this;
618
660
  const activeSlides = [];
661
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
662
+ let newHeight = 0;
663
+ let i;
619
664
  if (typeof speed === 'number') {
620
665
  swiper.setTransition(speed);
621
666
  } else if (speed === true) {
622
667
  swiper.setTransition(swiper.params.speed);
623
668
  }
624
- const getSlideByIndex = index => swiper.slides[index];
625
-
669
+ const getSlideByIndex = index => {
670
+ if (isVirtual) {
671
+ return swiper.slides[swiper.getSlideIndexByData(index)];
672
+ }
673
+ return swiper.slides[index];
674
+ };
626
675
  // Find slides currently in view
627
676
  if (swiper.params.slidesPerView !== 'auto' && swiper.params.slidesPerView > 1) {
628
677
  if (swiper.params.centeredSlides) {
@@ -630,9 +679,9 @@ function updateAutoHeight(speed) {
630
679
  activeSlides.push(slide);
631
680
  });
632
681
  } else {
633
- for (let i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
682
+ for (i = 0; i < Math.ceil(swiper.params.slidesPerView); i += 1) {
634
683
  const index = swiper.activeIndex + i;
635
- if (index > swiper.slides.length) break;
684
+ if (index > swiper.slides.length && !isVirtual) break;
636
685
  activeSlides.push(getSlideByIndex(index));
637
686
  }
638
687
  }
@@ -641,13 +690,14 @@ function updateAutoHeight(speed) {
641
690
  }
642
691
 
643
692
  // Find new height from highest slide in view
644
- let newHeight = 0;
645
- for (let i = 0; i < activeSlides.length; i += 1) {
693
+ for (i = 0; i < activeSlides.length; i += 1) {
646
694
  if (typeof activeSlides[i] !== 'undefined') {
647
695
  const height = activeSlides[i].offsetHeight;
648
696
  newHeight = height > newHeight ? height : newHeight;
649
697
  }
650
698
  }
699
+
700
+ // Update Height
651
701
  if (newHeight || newHeight === 0) swiper.wrapperEl.style.height = `${newHeight}px`;
652
702
  }
653
703
 
@@ -792,16 +842,46 @@ function updateSlidesClasses() {
792
842
  slidesEl,
793
843
  activeIndex
794
844
  } = swiper;
795
- const getNextSlide = slideEl => elementNextAll(slideEl, `.${params.slideClass}, swiper-slide`)[0];
796
- const getPrevSlide = slideEl => elementPrevAll(slideEl, `.${params.slideClass}, swiper-slide`)[0];
797
- const activeSlide = slides[activeIndex];
798
- let nextSlide;
845
+ const isVirtual = swiper.virtual && params.virtual.enabled;
846
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
847
+ const getFilteredSlide = selector => {
848
+ return elementChildren(slidesEl, `.${params.slideClass}${selector}, swiper-slide${selector}`)[0];
849
+ };
850
+ let activeSlide;
799
851
  let prevSlide;
852
+ let nextSlide;
853
+ if (isVirtual) {
854
+ if (params.loop) {
855
+ let slideIndex = activeIndex - swiper.virtual.slidesBefore;
856
+ if (slideIndex < 0) slideIndex = swiper.virtual.slides.length + slideIndex;
857
+ if (slideIndex >= swiper.virtual.slides.length) slideIndex -= swiper.virtual.slides.length;
858
+ activeSlide = getFilteredSlide(`[data-swiper-slide-index="${slideIndex}"]`);
859
+ } else {
860
+ activeSlide = getFilteredSlide(`[data-swiper-slide-index="${activeIndex}"]`);
861
+ }
862
+ } else {
863
+ if (gridEnabled) {
864
+ activeSlide = slides.find(slideEl => slideEl.column === activeIndex);
865
+ nextSlide = slides.find(slideEl => slideEl.column === activeIndex + 1);
866
+ prevSlide = slides.find(slideEl => slideEl.column === activeIndex - 1);
867
+ } else {
868
+ activeSlide = slides[activeIndex];
869
+ }
870
+ }
800
871
  if (activeSlide) {
801
- nextSlide = getNextSlide(activeSlide);
802
- prevSlide = getPrevSlide(activeSlide);
803
- if (params.loop && !nextSlide) nextSlide = slides[0];
804
- if (params.loop && !prevSlide) prevSlide = slides[slides.length - 1];
872
+ if (!gridEnabled) {
873
+ // Next Slide
874
+ nextSlide = elementNextAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0];
875
+ if (params.loop && !nextSlide) {
876
+ nextSlide = slides[0];
877
+ }
878
+
879
+ // Prev Slide
880
+ prevSlide = elementPrevAll(activeSlide, `.${params.slideClass}, swiper-slide`)[0];
881
+ if (params.loop && !prevSlide === 0) {
882
+ prevSlide = slides[slides.length - 1];
883
+ }
884
+ }
805
885
  }
806
886
  slides.forEach(slideEl => {
807
887
  toggleSlideClasses(slideEl, slideEl === activeSlide, params.slideActiveClass);
@@ -892,6 +972,7 @@ function getActiveIndexByTranslate(swiper) {
892
972
  activeIndex = i;
893
973
  }
894
974
  }
975
+ // Normalize slideIndex
895
976
  if (params.normalizeSlideIndex) {
896
977
  if (activeIndex < 0 || typeof activeIndex === 'undefined') activeIndex = 0;
897
978
  }
@@ -909,6 +990,16 @@ function updateActiveIndex(newActiveIndex) {
909
990
  } = swiper;
910
991
  let activeIndex = newActiveIndex;
911
992
  let snapIndex;
993
+ const getVirtualRealIndex = aIndex => {
994
+ let realIndex = aIndex - swiper.virtual.slidesBefore;
995
+ if (realIndex < 0) {
996
+ realIndex = swiper.virtual.slides.length + realIndex;
997
+ }
998
+ if (realIndex >= swiper.virtual.slides.length) {
999
+ realIndex -= swiper.virtual.slides.length;
1000
+ }
1001
+ return realIndex;
1002
+ };
912
1003
  if (typeof activeIndex === 'undefined') {
913
1004
  activeIndex = getActiveIndexByTranslate(swiper);
914
1005
  }
@@ -926,12 +1017,32 @@ function updateActiveIndex(newActiveIndex) {
926
1017
  }
927
1018
  return;
928
1019
  }
929
- let realIndex = activeIndex;
930
- if (swiper.slides[activeIndex]) {
1020
+ if (activeIndex === previousIndex && swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) {
1021
+ swiper.realIndex = getVirtualRealIndex(activeIndex);
1022
+ return;
1023
+ }
1024
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
1025
+
1026
+ // Get real index
1027
+ let realIndex;
1028
+ if (swiper.virtual && params.virtual.enabled && params.loop) {
1029
+ realIndex = getVirtualRealIndex(activeIndex);
1030
+ } else if (gridEnabled) {
1031
+ const firstSlideInColumn = swiper.slides.find(slideEl => slideEl.column === activeIndex);
1032
+ let activeSlideIndex = parseInt(firstSlideInColumn.getAttribute('data-swiper-slide-index'), 10);
1033
+ if (Number.isNaN(activeSlideIndex)) {
1034
+ activeSlideIndex = Math.max(swiper.slides.indexOf(firstSlideInColumn), 0);
1035
+ }
1036
+ realIndex = Math.floor(activeSlideIndex / params.grid.rows);
1037
+ } else if (swiper.slides[activeIndex]) {
931
1038
  const slideIndex = swiper.slides[activeIndex].getAttribute('data-swiper-slide-index');
932
1039
  if (slideIndex) {
933
1040
  realIndex = parseInt(slideIndex, 10);
1041
+ } else {
1042
+ realIndex = activeIndex;
934
1043
  }
1044
+ } else {
1045
+ realIndex = activeIndex;
935
1046
  }
936
1047
  Object.assign(swiper, {
937
1048
  previousSnapIndex,
@@ -978,7 +1089,11 @@ function updateClickedSlide(el, path) {
978
1089
  }
979
1090
  if (slide && slideFound) {
980
1091
  swiper.clickedSlide = slide;
981
- swiper.clickedIndex = slideIndex;
1092
+ if (swiper.virtual && swiper.params.virtual.enabled) {
1093
+ swiper.clickedIndex = parseInt(slide.getAttribute('data-swiper-slide-index'), 10);
1094
+ } else {
1095
+ swiper.clickedIndex = slideIndex;
1096
+ }
982
1097
  } else {
983
1098
  swiper.clickedSlide = undefined;
984
1099
  swiper.clickedIndex = undefined;
@@ -1012,6 +1127,9 @@ function getSwiperTranslate(axis) {
1012
1127
  translate,
1013
1128
  wrapperEl
1014
1129
  } = swiper;
1130
+ if (params.virtualTranslate) {
1131
+ return rtl ? -translate : translate;
1132
+ }
1015
1133
  if (params.cssMode) {
1016
1134
  return translate;
1017
1135
  }
@@ -1045,7 +1163,7 @@ function setTranslate(translate, byController) {
1045
1163
  swiper.translate = swiper.isHorizontal() ? x : y;
1046
1164
  if (params.cssMode) {
1047
1165
  wrapperEl[swiper.isHorizontal() ? 'scrollLeft' : 'scrollTop'] = swiper.isHorizontal() ? -x : -y;
1048
- } else {
1166
+ } else if (!params.virtualTranslate) {
1049
1167
  if (swiper.isHorizontal()) {
1050
1168
  x -= swiper.cssOverflowAdjustment();
1051
1169
  } else {
@@ -1282,6 +1400,7 @@ function slideTo(index, speed, runCallbacks, internal, initial) {
1282
1400
  let snapIndex = skip + Math.floor((slideIndex - skip) / swiper.params.slidesPerGroup);
1283
1401
  if (snapIndex >= snapGrid.length) snapIndex = snapGrid.length - 1;
1284
1402
  const translate = -snapGrid[snapIndex];
1403
+ // Normalize slideIndex
1285
1404
  if (params.normalizeSlideIndex) {
1286
1405
  for (let i = 0; i < slidesGrid.length; i += 1) {
1287
1406
  const normalizedTranslate = -Math.floor(translate * 100);
@@ -1298,24 +1417,33 @@ function slideTo(index, speed, runCallbacks, internal, initial) {
1298
1417
  }
1299
1418
  }
1300
1419
  }
1301
-
1302
1420
  // Directions locks
1303
1421
  if (swiper.initialized && slideIndex !== activeIndex) {
1304
1422
  if (!swiper.allowSlideNext && (rtl ? translate > swiper.translate && translate > swiper.minTranslate() : translate < swiper.translate && translate < swiper.minTranslate())) {
1305
1423
  return false;
1306
1424
  }
1307
1425
  if (!swiper.allowSlidePrev && translate > swiper.translate && translate > swiper.maxTranslate()) {
1308
- if ((activeIndex || 0) !== slideIndex) return false;
1426
+ if ((activeIndex || 0) !== slideIndex) {
1427
+ return false;
1428
+ }
1309
1429
  }
1310
1430
  }
1311
1431
  if (slideIndex !== (previousIndex || 0) && runCallbacks) {
1312
1432
  swiper.emit('beforeSlideChangeStart');
1313
1433
  }
1434
+
1435
+ // Update progress
1314
1436
  swiper.updateProgress(translate);
1315
1437
  let direction;
1316
1438
  if (slideIndex > activeIndex) direction = 'next';else if (slideIndex < activeIndex) direction = 'prev';else direction = 'reset';
1317
- if (rtl && -translate === swiper.translate || !rtl && translate === swiper.translate) {
1439
+
1440
+ // initial virtual
1441
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
1442
+ const isInitialVirtual = isVirtual && initial;
1443
+ // Update Index
1444
+ if (!isInitialVirtual && (rtl && -translate === swiper.translate || !rtl && translate === swiper.translate)) {
1318
1445
  swiper.updateActiveIndex(slideIndex);
1446
+ // Update Height
1319
1447
  if (params.autoHeight) {
1320
1448
  swiper.updateAutoHeight();
1321
1449
  }
@@ -1333,7 +1461,24 @@ function slideTo(index, speed, runCallbacks, internal, initial) {
1333
1461
  const isH = swiper.isHorizontal();
1334
1462
  const t = rtl ? translate : -translate;
1335
1463
  if (speed === 0) {
1336
- wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1464
+ if (isVirtual) {
1465
+ swiper.wrapperEl.style.scrollSnapType = 'none';
1466
+ swiper._immediateVirtual = true;
1467
+ }
1468
+ if (isVirtual && !swiper._cssModeVirtualInitialSet && swiper.params.initialSlide > 0) {
1469
+ swiper._cssModeVirtualInitialSet = true;
1470
+ requestAnimationFrame(() => {
1471
+ wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1472
+ });
1473
+ } else {
1474
+ wrapperEl[isH ? 'scrollLeft' : 'scrollTop'] = t;
1475
+ }
1476
+ if (isVirtual) {
1477
+ requestAnimationFrame(() => {
1478
+ swiper.wrapperEl.style.scrollSnapType = '';
1479
+ swiper._immediateVirtual = false;
1480
+ });
1481
+ }
1337
1482
  } else {
1338
1483
  if (!swiper.support.smoothScroll) {
1339
1484
  animateCSSModeScroll({
@@ -1351,7 +1496,10 @@ function slideTo(index, speed, runCallbacks, internal, initial) {
1351
1496
  return true;
1352
1497
  }
1353
1498
  const browser = getBrowser();
1354
- browser.isSafari;
1499
+ const isSafari = browser.isSafari;
1500
+ if (isVirtual && !initial && isSafari && swiper.isElement) {
1501
+ swiper.virtual.update(false, false, slideIndex);
1502
+ }
1355
1503
  swiper.setTransition(speed);
1356
1504
  swiper.setTranslate(translate);
1357
1505
  swiper.updateActiveIndex(slideIndex);
@@ -1522,6 +1670,8 @@ function slideToLoopCenterSneakPeek(index, speed, runCallbacks, internal) {
1522
1670
  if (swiper.params?.isSneakPeekCenter && slides.length > 1 && swiper.activeIndex === 0) {
1523
1671
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
1524
1672
  const swiperTranslate = JSON.parse(JSON.stringify(swiper.snapGrid[1]));
1673
+
1674
+ // Move last item to first position only if active slide is the first slide
1525
1675
  const lastSlide = slides[slides.length - 1];
1526
1676
  lastSlide.swiperLoopMoveDOM = true;
1527
1677
  swiper.slidesEl.prepend(lastSlide);
@@ -1556,8 +1706,11 @@ function slideNext(speed, runCallbacks, internal) {
1556
1706
  perGroup = Math.max(swiper.slidesPerViewDynamic('current', true), 1);
1557
1707
  }
1558
1708
  const increment = swiper.activeIndex < params.slidesPerGroupSkip ? 1 : perGroup;
1709
+ const isVirtual = swiper.virtual && params.virtual.enabled;
1559
1710
  if (params.loop) {
1560
- if (animating && params.loopPreventsSliding) return false;
1711
+ if (animating && !isVirtual && params.loopPreventsSliding) return false;
1712
+
1713
+ // Kiểm tra xem loop có bị disable không
1561
1714
  const currentSlidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(params.slidesPerView, 10));
1562
1715
  if (swiper.slides.length >= currentSlidesPerView) {
1563
1716
  swiper.loopFix({
@@ -1584,6 +1737,7 @@ function slideNext(speed, runCallbacks, internal) {
1584
1737
  const gap = Math.abs(swiper.snapGrid[lastSnapGridIndex] - swiper.snapGrid[lastSnapGridIndex - 1]);
1585
1738
  const swiperTranslate = structuredClone(swiper.snapGrid[lastSnapGridIndex - 1]);
1586
1739
  if (!swiper.params.loop) return;
1740
+ // Move first item to last position only if active slide is the last slide
1587
1741
  const firstSlide = slides[0];
1588
1742
  firstSlide.swiperLoopMoveDOM = true;
1589
1743
  swiper.slidesEl.append(firstSlide);
@@ -1608,14 +1762,17 @@ function slidePrev(speed, runCallbacks, internal) {
1608
1762
  params,
1609
1763
  snapGrid,
1610
1764
  slidesGrid,
1611
- rtlTranslate: rtlTranslate,
1612
- enabled
1765
+ rtlTranslate,
1766
+ enabled,
1767
+ animating
1613
1768
  } = swiper;
1614
1769
  if (!enabled || swiper.destroyed) return swiper;
1615
1770
  if (typeof speed === 'undefined') {
1616
1771
  speed = swiper.params.speed;
1617
1772
  }
1773
+ swiper.virtual && params.virtual.enabled;
1618
1774
  if (params.loop) {
1775
+ // Kiểm tra xem loop có bị disable không
1619
1776
  const currentSlidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(params.slidesPerView, 10));
1620
1777
  if (swiper.slides.length >= currentSlidesPerView) {
1621
1778
  swiper.loopFix({
@@ -1632,16 +1789,18 @@ function slidePrev(speed, runCallbacks, internal) {
1632
1789
  }
1633
1790
  const normalizedTranslate = normalize(translate);
1634
1791
  const normalizedSnapGrid = snapGrid.map(val => normalize(val));
1792
+ const isFreeMode = params.freeMode && params.freeMode.enabled;
1635
1793
  let prevSnap = snapGrid[normalizedSnapGrid.indexOf(normalizedTranslate) - 1];
1636
- if (typeof prevSnap === 'undefined' && params.cssMode) {
1794
+ if (typeof prevSnap === 'undefined' && (params.cssMode || isFreeMode)) {
1637
1795
  let prevSnapIndex;
1638
1796
  snapGrid.forEach((snap, snapIndex) => {
1639
1797
  if (normalizedTranslate >= snap) {
1798
+ // prevSnap = snap;
1640
1799
  prevSnapIndex = snapIndex;
1641
1800
  }
1642
1801
  });
1643
1802
  if (typeof prevSnapIndex !== 'undefined') {
1644
- prevSnap = snapGrid[prevSnapIndex > 0 ? prevSnapIndex - 1 : prevSnapIndex];
1803
+ prevSnap = isFreeMode ? snapGrid[prevSnapIndex] : snapGrid[prevSnapIndex > 0 ? prevSnapIndex - 1 : prevSnapIndex];
1645
1804
  }
1646
1805
  }
1647
1806
  let prevIndex = 0;
@@ -1654,7 +1813,7 @@ function slidePrev(speed, runCallbacks, internal) {
1654
1813
  }
1655
1814
  }
1656
1815
  if (params.rewind && swiper.isBeginning) {
1657
- const lastIndex = swiper.slides.length - 1;
1816
+ const lastIndex = swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1;
1658
1817
  return swiper.slideTo(lastIndex, speed, runCallbacks, internal);
1659
1818
  } else if (params.loop && swiper.activeIndex === 0 && params.cssMode) {
1660
1819
  requestAnimationFrame(() => {
@@ -1668,6 +1827,8 @@ function slidePrev(speed, runCallbacks, internal) {
1668
1827
  if (swiper.params?.isSneakPeekCenter && slides.length > 1 && swiper.activeIndex === 0) {
1669
1828
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
1670
1829
  const swiperTranslate = JSON.parse(JSON.stringify(swiper.snapGrid[1]));
1830
+
1831
+ // Move last item to first position only if active slide is the first slide
1671
1832
  if (!swiper.params.loop) return;
1672
1833
  const lastSlide = slides[slides.length - 1];
1673
1834
  lastSlide.swiperLoopMoveDOM = true;
@@ -1744,19 +1905,20 @@ function slideToClickedSlide() {
1744
1905
  slidesEl
1745
1906
  } = swiper;
1746
1907
  const slidesPerView = params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : params.slidesPerView;
1747
- const slideToIndex = swiper.clickedIndex;
1908
+ let slideToIndex = swiper.getSlideIndexWhenGrid(swiper.clickedIndex);
1748
1909
  let realIndex;
1749
1910
  const slideSelector = swiper.isElement ? `swiper-slide` : `.${params.slideClass}`;
1911
+ const isGrid = swiper.grid && swiper.params.grid && swiper.params.grid.rows > 1;
1750
1912
  if (params.loop) {
1751
1913
  if (swiper.animating) return;
1752
1914
  realIndex = parseInt(swiper.clickedSlide.getAttribute('data-swiper-slide-index'), 10);
1753
1915
  if (params.centeredSlides) {
1754
1916
  swiper.slideToLoop(realIndex);
1755
- } else if (slideToIndex > swiper.slides.length - slidesPerView) {
1917
+ } else if (slideToIndex > (isGrid ? (swiper.slides.length - slidesPerView) / 2 - (swiper.params.grid.rows - 1) : swiper.slides.length - slidesPerView)) {
1756
1918
  swiper.loopFix();
1757
- const clickedEl = elementChildren(slidesEl, `${slideSelector}[data-swiper-slide-index="${realIndex}"]`)[0];
1919
+ slideToIndex = swiper.getSlideIndex(elementChildren(slidesEl, `${slideSelector}[data-swiper-slide-index="${realIndex}"]`)[0]);
1758
1920
  nextTick(() => {
1759
- if (clickedEl) swiper.slideTo(swiper.getSlideIndex(clickedEl));
1921
+ swiper.slideTo(slideToIndex);
1760
1922
  });
1761
1923
  } else {
1762
1924
  swiper.slideTo(slideToIndex);
@@ -1783,7 +1945,7 @@ function loopCreate(slideRealIndex, initial) {
1783
1945
  params,
1784
1946
  slidesEl
1785
1947
  } = swiper;
1786
- if (!params.loop) return;
1948
+ if (!params.loop || swiper.virtual && swiper.params.virtual.enabled) return;
1787
1949
  const initSlides = () => {
1788
1950
  const slides = elementChildren(slidesEl, `.${params.slideClass}, swiper-slide`);
1789
1951
  slides.forEach((el, index) => {
@@ -1792,17 +1954,21 @@ function loopCreate(slideRealIndex, initial) {
1792
1954
  };
1793
1955
  const clearBlankSlides = () => {
1794
1956
  const slides = elementChildren(slidesEl, `.${params.slideBlankClass}`);
1795
- slides.forEach(el => el.remove());
1957
+ slides.forEach(el => {
1958
+ el.remove();
1959
+ });
1796
1960
  if (slides.length > 0) {
1797
1961
  swiper.recalcSlides();
1798
1962
  swiper.updateSlides();
1799
1963
  }
1800
1964
  };
1801
- if (params.loopAddBlankSlides && params.slidesPerGroup > 1) {
1965
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
1966
+ if (params.loopAddBlankSlides && (params.slidesPerGroup > 1 || gridEnabled)) {
1802
1967
  clearBlankSlides();
1803
1968
  }
1804
- const slidesPerGroup = params.slidesPerGroup;
1969
+ const slidesPerGroup = params.slidesPerGroup * (gridEnabled ? params.grid.rows : 1);
1805
1970
  const shouldFillGroup = swiper.slides.length % slidesPerGroup !== 0;
1971
+ const shouldFillGrid = gridEnabled && swiper.slides.length % params.grid.rows !== 0;
1806
1972
  const addBlankSlides = amountOfSlides => {
1807
1973
  for (let i = 0; i < amountOfSlides; i += 1) {
1808
1974
  const slideEl = swiper.isElement ? createElement('swiper-slide', [params.slideBlankClass]) : createElement('div', [params.slideClass, params.slideBlankClass]);
@@ -1819,6 +1985,16 @@ function loopCreate(slideRealIndex, initial) {
1819
1985
  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)');
1820
1986
  }
1821
1987
  initSlides();
1988
+ } else if (shouldFillGrid) {
1989
+ if (params.loopAddBlankSlides) {
1990
+ const slidesToAdd = params.grid.rows - swiper.slides.length % params.grid.rows;
1991
+ addBlankSlides(slidesToAdd);
1992
+ swiper.recalcSlides();
1993
+ swiper.updateSlides();
1994
+ } else {
1995
+ 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)');
1996
+ }
1997
+ initSlides();
1822
1998
  } else {
1823
1999
  initSlides();
1824
2000
  }
@@ -1837,12 +2013,13 @@ function loopFix(_temp) {
1837
2013
  setTranslate,
1838
2014
  activeSlideIndex,
1839
2015
  initial,
2016
+ byController,
1840
2017
  byMousewheel
1841
2018
  } = _temp === void 0 ? {} : _temp;
1842
2019
  const swiper = this;
1843
2020
  if (!swiper.params.loop) return;
1844
2021
 
1845
- // Disable loop mode if number of slides is smaller than slidesPerView
2022
+ // Disable loop mode nếu số slides ít hơn slidesPerView
1846
2023
  const currentSlidesPerView = swiper.params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(swiper.params.slidesPerView, 10));
1847
2024
  if (swiper.slides.length < currentSlidesPerView) {
1848
2025
  console.warn('Swiper: Loop mode disabled - slides.length < slidesPerView');
@@ -1862,6 +2039,21 @@ function loopFix(_temp) {
1862
2039
  } = params;
1863
2040
  swiper.allowSlidePrev = true;
1864
2041
  swiper.allowSlideNext = true;
2042
+ if (swiper.virtual && params.virtual.enabled) {
2043
+ if (slideTo) {
2044
+ if (!params.centeredSlides && swiper.snapIndex === 0) {
2045
+ swiper.slideTo(swiper.virtual.slides.length, 0, false, true);
2046
+ } else if (params.centeredSlides && swiper.snapIndex < params.slidesPerView) {
2047
+ swiper.slideTo(swiper.virtual.slides.length + swiper.snapIndex, 0, false, true);
2048
+ } else if (swiper.snapIndex === swiper.snapGrid.length - 1) {
2049
+ swiper.slideTo(swiper.virtual.slidesBefore, 0, false, true);
2050
+ }
2051
+ }
2052
+ swiper.allowSlidePrev = allowSlidePrev;
2053
+ swiper.allowSlideNext = allowSlideNext;
2054
+ swiper.emit('loopFix');
2055
+ return;
2056
+ }
1865
2057
  let slidesPerView = params.slidesPerView;
1866
2058
  if (slidesPerView === 'auto') {
1867
2059
  slidesPerView = swiper.slidesPerViewDynamic();
@@ -1871,19 +2063,22 @@ function loopFix(_temp) {
1871
2063
  slidesPerView = slidesPerView + 1;
1872
2064
  }
1873
2065
  }
1874
- const slidesPerGroup = params.slidesPerGroup;
2066
+ const slidesPerGroup = params.slidesPerGroupAuto ? slidesPerView : params.slidesPerGroup;
1875
2067
  let loopedSlides = centeredSlides ? Math.max(slidesPerGroup, Math.ceil(slidesPerView / 2)) : slidesPerGroup;
1876
2068
  if (loopedSlides % slidesPerGroup !== 0) {
1877
2069
  loopedSlides += slidesPerGroup - loopedSlides % slidesPerGroup;
1878
2070
  }
1879
2071
  loopedSlides += params.loopAdditionalSlides;
1880
2072
  swiper.loopedSlides = loopedSlides;
1881
- if (slides.length < slidesPerView + loopedSlides) {
2073
+ const gridEnabled = swiper.grid && params.grid && params.grid.rows > 1;
2074
+ if (slides.length < slidesPerView + loopedSlides || swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
1882
2075
  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');
2076
+ } else if (gridEnabled && params.grid.fill === 'row') {
2077
+ showWarning('Swiper Loop Warning: Loop mode is not compatible with grid.fill = `row`');
1883
2078
  }
1884
2079
  const prependSlidesIndexes = [];
1885
2080
  const appendSlidesIndexes = [];
1886
- const cols = slides.length;
2081
+ const cols = gridEnabled ? Math.ceil(slides.length / params.grid.rows) : slides.length;
1887
2082
  const isInitialOverflow = initial && cols - initialSlide < slidesPerView && !centeredSlides;
1888
2083
  let activeIndex = isInitialOverflow ? initialSlide : swiper.activeIndex;
1889
2084
  if (typeof activeSlideIndex === 'undefined') {
@@ -1895,15 +2090,24 @@ function loopFix(_temp) {
1895
2090
  const isPrev = direction === 'prev' || !direction;
1896
2091
  let slidesPrepended = 0;
1897
2092
  let slidesAppended = 0;
1898
- const activeColIndex = activeSlideIndex;
2093
+ const activeColIndex = gridEnabled ? slides[activeSlideIndex].column : activeSlideIndex;
1899
2094
  const activeColIndexWithShift = activeColIndex + (centeredSlides && typeof setTranslate === 'undefined' ? -slidesPerView / 2 + 0.5 : 0);
1900
-
1901
2095
  // prepend last slides before start
1902
2096
  if (activeColIndexWithShift < loopedSlides) {
1903
2097
  slidesPrepended = Math.max(loopedSlides - activeColIndexWithShift, slidesPerGroup);
1904
2098
  for (let i = 0; i < loopedSlides - activeColIndexWithShift; i += 1) {
1905
2099
  const index = i - Math.floor(i / cols) * cols;
1906
- prependSlidesIndexes.push(cols - index - 1);
2100
+ if (gridEnabled) {
2101
+ const colIndexToPrepend = cols - index - 1;
2102
+ for (let i = slides.length - 1; i >= 0; i -= 1) {
2103
+ if (slides[i].column === colIndexToPrepend) prependSlidesIndexes.push(i);
2104
+ }
2105
+ // slides.forEach((slide, slideIndex) => {
2106
+ // if (slide.column === colIndexToPrepend) prependSlidesIndexes.push(slideIndex);
2107
+ // });
2108
+ } else {
2109
+ prependSlidesIndexes.push(cols - index - 1);
2110
+ }
1907
2111
  }
1908
2112
  } else if (activeColIndexWithShift + slidesPerView > cols - loopedSlides) {
1909
2113
  slidesAppended = Math.max(activeColIndexWithShift - (cols - loopedSlides * 2), slidesPerGroup);
@@ -1912,13 +2116,27 @@ function loopFix(_temp) {
1912
2116
  }
1913
2117
  for (let i = 0; i < slidesAppended; i += 1) {
1914
2118
  const index = i - Math.floor(i / cols) * cols;
1915
- appendSlidesIndexes.push(index);
2119
+ if (gridEnabled) {
2120
+ slides.forEach((slide, slideIndex) => {
2121
+ if (slide.column === index) appendSlidesIndexes.push(slideIndex);
2122
+ });
2123
+ } else {
2124
+ appendSlidesIndexes.push(index);
2125
+ }
1916
2126
  }
1917
2127
  }
1918
2128
  swiper.__preventObserver__ = true;
1919
2129
  requestAnimationFrame(() => {
1920
2130
  swiper.__preventObserver__ = false;
1921
2131
  });
2132
+ if (swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
2133
+ if (appendSlidesIndexes.includes(activeSlideIndex)) {
2134
+ appendSlidesIndexes.splice(appendSlidesIndexes.indexOf(activeSlideIndex), 1);
2135
+ }
2136
+ if (prependSlidesIndexes.includes(activeSlideIndex)) {
2137
+ prependSlidesIndexes.splice(prependSlidesIndexes.indexOf(activeSlideIndex), 1);
2138
+ }
2139
+ }
1922
2140
  if (isPrev) {
1923
2141
  prependSlidesIndexes.forEach(index => {
1924
2142
  slides[index].swiperLoopMoveDOM = true;
@@ -1936,6 +2154,10 @@ function loopFix(_temp) {
1936
2154
  swiper.recalcSlides();
1937
2155
  if (params.slidesPerView === 'auto') {
1938
2156
  swiper.updateSlides();
2157
+ } else if (gridEnabled && (prependSlidesIndexes.length > 0 && isPrev || appendSlidesIndexes.length > 0 && isNext)) {
2158
+ swiper.slides.forEach((slide, slideIndex) => {
2159
+ swiper.grid.updateSlide(slideIndex, slide, swiper.slides);
2160
+ });
1939
2161
  }
1940
2162
  if (params.watchSlidesProgress) {
1941
2163
  swiper.updateSlidesOffset();
@@ -1955,10 +2177,12 @@ function loopFix(_temp) {
1955
2177
  swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff;
1956
2178
  }
1957
2179
  }
1958
- } else if (setTranslate) {
1959
- const shift = prependSlidesIndexes.length;
1960
- swiper.slideTo(swiper.activeIndex + shift, 0, false, true);
1961
- swiper.touchEventsData.currentTranslate = swiper.translate;
2180
+ } else {
2181
+ if (setTranslate) {
2182
+ const shift = gridEnabled ? prependSlidesIndexes.length / params.grid.rows : prependSlidesIndexes.length;
2183
+ swiper.slideTo(swiper.activeIndex + shift, 0, false, true);
2184
+ swiper.touchEventsData.currentTranslate = swiper.translate;
2185
+ }
1962
2186
  }
1963
2187
  } else if (appendSlidesIndexes.length > 0 && isNext) {
1964
2188
  if (typeof slideRealIndex === 'undefined') {
@@ -1974,14 +2198,36 @@ function loopFix(_temp) {
1974
2198
  swiper.touchEventsData.currentTranslate = swiper.touchEventsData.currentTranslate - diff;
1975
2199
  }
1976
2200
  }
1977
- } else if (setTranslate) {
1978
- const shift = appendSlidesIndexes.length;
2201
+ } else {
2202
+ const shift = gridEnabled ? appendSlidesIndexes.length / params.grid.rows : appendSlidesIndexes.length;
1979
2203
  swiper.slideTo(swiper.activeIndex - shift, 0, false, true);
1980
2204
  }
1981
2205
  }
1982
2206
  }
1983
2207
  swiper.allowSlidePrev = allowSlidePrev;
1984
2208
  swiper.allowSlideNext = allowSlideNext;
2209
+ if (swiper.controller && swiper.controller.control && !byController) {
2210
+ const loopParams = {
2211
+ slideRealIndex,
2212
+ direction,
2213
+ setTranslate,
2214
+ activeSlideIndex,
2215
+ byController: true
2216
+ };
2217
+ if (Array.isArray(swiper.controller.control)) {
2218
+ swiper.controller.control.forEach(c => {
2219
+ if (!c.destroyed && c.params.loop) c.loopFix({
2220
+ ...loopParams,
2221
+ slideTo: c.params.slidesPerView === params.slidesPerView ? slideTo : false
2222
+ });
2223
+ });
2224
+ } else if (swiper.controller.control instanceof swiper.constructor && swiper.controller.control.params.loop) {
2225
+ swiper.controller.control.loopFix({
2226
+ ...loopParams,
2227
+ slideTo: swiper.controller.control.params.slidesPerView === params.slidesPerView ? slideTo : false
2228
+ });
2229
+ }
2230
+ }
1985
2231
  swiper.emit('loopFix');
1986
2232
  }
1987
2233
 
@@ -1998,7 +2244,7 @@ function loopFixDot(_temp) {
1998
2244
  const swiper = this;
1999
2245
  if (!swiper.params.loop) return;
2000
2246
 
2001
- // Disable loop mode if number of slides is smaller than slidesPerView
2247
+ // Disable loop mode nếu số slides ít hơn slidesPerView
2002
2248
  const currentSlidesPerView = swiper.params.slidesPerView === 'auto' ? swiper.slidesPerViewDynamic() : Math.ceil(parseFloat(swiper.params.slidesPerView, 10));
2003
2249
  if (swiper.slides.length < currentSlidesPerView) {
2004
2250
  console.warn('Swiper: Loop mode disabled - slides.length < slidesPerView');
@@ -2037,7 +2283,7 @@ function loopFixDot(_temp) {
2037
2283
  }
2038
2284
  loopedSlides += params.loopAdditionalSlides;
2039
2285
  swiper.loopedSlides = loopedSlides;
2040
- if (slides.length < slidesPerView + loopedSlides) {
2286
+ if (slides.length < slidesPerView + loopedSlides || swiper.params.effect === 'cards' && slides.length < slidesPerView + loopedSlides * 2) {
2041
2287
  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');
2042
2288
  }
2043
2289
  const isNext = direction === 'next' || !direction;
@@ -2063,16 +2309,20 @@ function loopFixDot(_temp) {
2063
2309
  activeSlideIndex = swiper.getSlideIndex(slides.find(el => el.classList.contains(params.slideActiveClass)));
2064
2310
  }
2065
2311
 
2066
- // DocumentFragment to hold slide clones
2312
+ // Tạo DocumentFragment để chứa các slide clone
2067
2313
  const cloneFragment = document.createDocumentFragment();
2068
2314
 
2069
- // Clone slides according to numberOfSlidesNeedClone and append to fragment
2315
+ // Clone các slide theo numberOfSlidesNeedClone (đã đảo ngược) và thêm vào fragment
2070
2316
  (isNext ? numberOfSlidesNeedClone : numberOfSlidesNeedClone.reverse()).forEach(index => {
2071
2317
  if (slides[index]) {
2072
2318
  const originalSlide = slides[index];
2073
2319
  const clonedSlide = originalSlide.cloneNode(true);
2320
+
2321
+ // Đánh dấu slide clone
2074
2322
  clonedSlide.setAttribute('data-swiper-clone', 'true');
2075
2323
  clonedSlide.classList.add('swiper-slide-clone');
2324
+
2325
+ // Thêm clone vào fragment
2076
2326
  cloneFragment.appendChild(clonedSlide);
2077
2327
  }
2078
2328
  });
@@ -2093,28 +2343,32 @@ function loopFixDot(_temp) {
2093
2343
  });
2094
2344
  }
2095
2345
 
2096
- // Sort cloned slides by data-swiper-slide-index
2346
+ // Sắp xếp cloneFragment theo data-swiper-slide-index tăng dần
2097
2347
  const clonedSlides = Array.from(cloneFragment.children);
2098
2348
  clonedSlides.sort((a, b) => {
2099
2349
  const indexA = parseInt(a.getAttribute('data-swiper-slide-index')) || 0;
2100
2350
  const indexB = parseInt(b.getAttribute('data-swiper-slide-index')) || 0;
2101
2351
  return indexA - indexB;
2102
2352
  });
2353
+
2354
+ // Xóa tất cả children cũ và thêm lại theo thứ tự đã sắp xếp
2103
2355
  cloneFragment.innerHTML = '';
2104
2356
  clonedSlides.forEach(slide => {
2105
2357
  cloneFragment.appendChild(slide);
2106
2358
  });
2107
2359
 
2108
- // Place fragment into the right position
2360
+ // Thêm fragment vào vị trí phù hợp
2109
2361
  if (isPrev) {
2362
+ // Nếu là prev, thêm fragment vào cuối slidesEl
2110
2363
  slidesEl.appendChild(cloneFragment);
2111
2364
  } else if (isNext) {
2365
+ // Nếu là next, thêm fragment vào đầu slidesEl
2112
2366
  slidesEl.insertBefore(cloneFragment, slidesEl.firstChild);
2113
2367
  }
2114
2368
  swiper.recalcSlides();
2115
2369
  swiper.updateSlides();
2116
2370
 
2117
- // Find old active slide index after recalculation
2371
+ // Tìm slide data-swiper-slide-index tương ứng
2118
2372
  let oldActiveIndex = null;
2119
2373
  for (let i = 0; i < slidesEl.children.length; i++) {
2120
2374
  const child = slidesEl.children[i];
@@ -2127,7 +2381,7 @@ function loopFixDot(_temp) {
2127
2381
  swiper.slideTo(oldActiveIndex, 0);
2128
2382
  }
2129
2383
 
2130
- // Update translate after removing clones for animation
2384
+ // Tìm vị trí slide cũ sau khi remove clone để set lại translate tạo hiệu ứng animate
2131
2385
  const skip = Math.min(swiper.params.slidesPerGroupSkip, newIndex);
2132
2386
  let snapIndex = skip + Math.floor((newIndex - skip) / swiper.params.slidesPerGroup);
2133
2387
  if (snapIndex >= swiper.snapGrid.length) snapIndex = swiper.snapGrid.length - 1;
@@ -2153,8 +2407,7 @@ function loopFixDot(_temp) {
2153
2407
  }
2154
2408
  swiper.setTranslate(updateTranslate);
2155
2409
  }
2156
-
2157
- // Remove clones
2410
+ // Remove slide clone ra khỏi slidesEl sau khi slideTo hoàn thành
2158
2411
  const cloneSlides = slidesEl.querySelectorAll('[data-swiper-clone="true"]');
2159
2412
  cloneSlides.forEach(cloneSlide => {
2160
2413
  if (cloneSlide.parentNode) {
@@ -2184,7 +2437,7 @@ function loopDestroy() {
2184
2437
  params,
2185
2438
  slidesEl
2186
2439
  } = swiper;
2187
- if (!params.loop || !slidesEl) return;
2440
+ if (!params.loop || !slidesEl || swiper.virtual && swiper.params.virtual.enabled) return;
2188
2441
  swiper.recalcSlides();
2189
2442
  const newSlidesOrder = [];
2190
2443
  swiper.slides.forEach(slideEl => {
@@ -2245,6 +2498,7 @@ var grabCursor = {
2245
2498
  unsetGrabCursor
2246
2499
  };
2247
2500
 
2501
+ // Modified from https://stackoverflow.com/questions/54520554/custom-element-getrootnode-closest-function-crossing-multiple-parent-shadowd
2248
2502
  function closestElement(selector, base) {
2249
2503
  if (base === void 0) {
2250
2504
  base = this;
@@ -2253,7 +2507,9 @@ function closestElement(selector, base) {
2253
2507
  if (!el || el === getDocument() || el === getWindow()) return null;
2254
2508
  if (el.assignedSlot) el = el.assignedSlot;
2255
2509
  const found = el.closest(selector);
2256
- if (!found && !el.getRootNode) return null;
2510
+ if (!found && !el.getRootNode) {
2511
+ return null;
2512
+ }
2257
2513
  return found || __closestFrom(el.getRootNode().host);
2258
2514
  }
2259
2515
  return __closestFrom(base);
@@ -2281,7 +2537,9 @@ function onTouchStart(event) {
2281
2537
  if (e.originalEvent) e = e.originalEvent;
2282
2538
  const data = swiper.touchEventsData;
2283
2539
  if (e.type === 'pointerdown') {
2284
- if (data.pointerId !== null && data.pointerId !== e.pointerId) return;
2540
+ if (data.pointerId !== null && data.pointerId !== e.pointerId) {
2541
+ return;
2542
+ }
2285
2543
  data.pointerId = e.pointerId;
2286
2544
  } else if (e.type === 'touchstart' && e.targetTouches.length === 1) {
2287
2545
  data.touchId = e.targetTouches[0].identifier;
@@ -2298,7 +2556,9 @@ function onTouchStart(event) {
2298
2556
  } = swiper;
2299
2557
  if (!enabled) return;
2300
2558
  if (!params.simulateTouch && e.pointerType === 'mouse') return;
2301
- if (swiper.animating && params.preventInteractionOnTransition) return;
2559
+ if (swiper.animating && params.preventInteractionOnTransition) {
2560
+ return;
2561
+ }
2302
2562
  if (!swiper.animating && params.cssMode && params.loop) {
2303
2563
  swiper.loopFix();
2304
2564
  }
@@ -2309,6 +2569,8 @@ function onTouchStart(event) {
2309
2569
  if ('which' in e && e.which === 3) return;
2310
2570
  if ('button' in e && e.button > 0) return;
2311
2571
  if (data.isTouched && data.isMoved) return;
2572
+
2573
+ // change target el for shadow root component
2312
2574
  const swipingClassHasValue = !!params.noSwipingClass && params.noSwipingClass !== '';
2313
2575
  // eslint-disable-next-line
2314
2576
  const eventPath = e.composedPath ? e.composedPath() : e.path;
@@ -2317,6 +2579,8 @@ function onTouchStart(event) {
2317
2579
  }
2318
2580
  const noSwipingSelector = params.noSwipingSelector ? params.noSwipingSelector : `.${params.noSwipingClass}`;
2319
2581
  const isTargetShadow = !!(e.target && e.target.shadowRoot);
2582
+
2583
+ // use closestElement for shadow root element to get the actual closest for nested shadow root element
2320
2584
  if (params.noSwiping && (isTargetShadow ? closestElement(noSwipingSelector, targetEl) : targetEl.closest(noSwipingSelector))) {
2321
2585
  swiper.allowClick = true;
2322
2586
  return;
@@ -2328,7 +2592,12 @@ function onTouchStart(event) {
2328
2592
  touches.currentY = e.pageY;
2329
2593
  const startX = touches.currentX;
2330
2594
  const startY = touches.currentY;
2331
- if (!preventEdgeSwipe(swiper, e, startX)) return;
2595
+
2596
+ // Do NOT start if iOS edge swipe is detected. Otherwise iOS app cannot swipe-to-go-back anymore
2597
+
2598
+ if (!preventEdgeSwipe(swiper, e, startX)) {
2599
+ return;
2600
+ }
2332
2601
  Object.assign(data, {
2333
2602
  isTouched: true,
2334
2603
  isMoved: false,
@@ -2357,6 +2626,9 @@ function onTouchStart(event) {
2357
2626
  if ((params.touchStartForcePreventDefault || shouldPreventDefault) && !targetEl.isContentEditable) {
2358
2627
  e.preventDefault();
2359
2628
  }
2629
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode && swiper.animating && !params.cssMode) {
2630
+ swiper.freeMode.onTouchStart();
2631
+ }
2360
2632
  swiper.emit('touchStart', e);
2361
2633
  }
2362
2634
 
@@ -2375,7 +2647,7 @@ function onTouchMove(event) {
2375
2647
  let e = event;
2376
2648
  if (e.originalEvent) e = e.originalEvent;
2377
2649
  if (e.type === 'pointermove') {
2378
- if (data.touchId !== null) return;
2650
+ if (data.touchId !== null) return; // return from pointer if we use touch
2379
2651
  const id = e.pointerId;
2380
2652
  if (id !== data.pointerId) return;
2381
2653
  }
@@ -2416,6 +2688,7 @@ function onTouchMove(event) {
2416
2688
  }
2417
2689
  if (params.touchReleaseOnEdges && !params.loop) {
2418
2690
  if (swiper.isVertical()) {
2691
+ // Vertical
2419
2692
  if (pageY < touches.startY && swiper.translate <= swiper.maxTranslate() || pageY > touches.startY && swiper.translate >= swiper.minTranslate()) {
2420
2693
  data.isTouched = false;
2421
2694
  data.isMoved = false;
@@ -2517,6 +2790,7 @@ function onTouchMove(event) {
2517
2790
  swiper.wrapperEl.dispatchEvent(evt);
2518
2791
  }
2519
2792
  data.allowMomentumBounce = false;
2793
+ // Grab Cursor
2520
2794
  if (params.grabCursor && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
2521
2795
  swiper.setGrabCursor(true);
2522
2796
  }
@@ -2587,6 +2861,8 @@ function onTouchMove(event) {
2587
2861
  if (!swiper.allowSlidePrev && !swiper.allowSlideNext) {
2588
2862
  data.currentTranslate = data.startTranslate;
2589
2863
  }
2864
+
2865
+ // Threshold
2590
2866
  if (params.threshold > 0) {
2591
2867
  if (Math.abs(diff) > params.threshold || data.allowThresholdMove) {
2592
2868
  if (!data.allowThresholdMove) {
@@ -2604,12 +2880,17 @@ function onTouchMove(event) {
2604
2880
  }
2605
2881
  if (!params.followFinger || params.cssMode) return;
2606
2882
 
2607
- // core-lite: no optional feature updates; only watchSlidesProgress
2608
- if (params.watchSlidesProgress) {
2883
+ // Update active index in free mode
2884
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode || params.watchSlidesProgress) {
2609
2885
  swiper.updateActiveIndex();
2610
2886
  swiper.updateSlidesClasses();
2611
2887
  }
2888
+ if (params.freeMode && params.freeMode.enabled && swiper.freeMode) {
2889
+ swiper.freeMode.onTouchMove();
2890
+ }
2891
+ // Update progress
2612
2892
  swiper.updateProgress(data.currentTranslate);
2893
+ // Update translate
2613
2894
  swiper.setTranslate(data.currentTranslate);
2614
2895
  }
2615
2896
 
@@ -2621,7 +2902,7 @@ function onTouchEnd(event) {
2621
2902
  let targetTouch;
2622
2903
  const isTouchEvent = e.type === 'touchend' || e.type === 'touchcancel';
2623
2904
  if (!isTouchEvent) {
2624
- if (data.touchId !== null) return;
2905
+ if (data.touchId !== null) return; // return from pointer if we use touch
2625
2906
  if (e.pointerId !== data.pointerId) return;
2626
2907
  targetTouch = e;
2627
2908
  } else {
@@ -2630,7 +2911,9 @@ function onTouchEnd(event) {
2630
2911
  }
2631
2912
  if (['pointercancel', 'pointerout', 'pointerleave', 'contextmenu'].includes(e.type)) {
2632
2913
  const proceed = ['pointercancel', 'contextmenu'].includes(e.type) && (swiper.browser.isSafari || swiper.browser.isWebView);
2633
- if (!proceed) return;
2914
+ if (!proceed) {
2915
+ return;
2916
+ }
2634
2917
  }
2635
2918
  data.pointerId = null;
2636
2919
  data.touchId = null;
@@ -2648,7 +2931,9 @@ function onTouchEnd(event) {
2648
2931
  }
2649
2932
  data.allowTouchCallbacks = false;
2650
2933
  if (!data.isTouched) {
2651
- if (data.isMoved && params.grabCursor) swiper.setGrabCursor(false);
2934
+ if (data.isMoved && params.grabCursor) {
2935
+ swiper.setGrabCursor(false);
2936
+ }
2652
2937
  data.isMoved = false;
2653
2938
  data.startMoving = false;
2654
2939
  return;
@@ -2658,6 +2943,8 @@ function onTouchEnd(event) {
2658
2943
  if (params.grabCursor && data.isMoved && data.isTouched && (swiper.allowSlideNext === true || swiper.allowSlidePrev === true)) {
2659
2944
  swiper.setGrabCursor(false);
2660
2945
  }
2946
+
2947
+ // Time diff
2661
2948
  const touchEndTime = now();
2662
2949
  const timeDiff = touchEndTime - data.touchStartTime;
2663
2950
 
@@ -2684,8 +2971,20 @@ function onTouchEnd(event) {
2684
2971
  data.isMoved = false;
2685
2972
  data.startMoving = false;
2686
2973
  let currentPos;
2687
- if (params.followFinger) currentPos = rtl ? swiper.translate : -swiper.translate;else currentPos = -data.currentTranslate;
2688
- if (params.cssMode) return;
2974
+ if (params.followFinger) {
2975
+ currentPos = rtl ? swiper.translate : -swiper.translate;
2976
+ } else {
2977
+ currentPos = -data.currentTranslate;
2978
+ }
2979
+ if (params.cssMode) {
2980
+ return;
2981
+ }
2982
+ if (params.freeMode && params.freeMode.enabled) {
2983
+ swiper.freeMode.onTouchEnd({
2984
+ currentPos
2985
+ });
2986
+ return;
2987
+ }
2689
2988
 
2690
2989
  // Find current slide
2691
2990
  const swipeToLast = currentPos >= -swiper.maxTranslate() && !swiper.params.loop;
@@ -2707,22 +3006,22 @@ function onTouchEnd(event) {
2707
3006
  let rewindLastIndex = null;
2708
3007
  if (params.rewind) {
2709
3008
  if (swiper.isBeginning) {
2710
- rewindLastIndex = swiper.slides.length - 1;
3009
+ rewindLastIndex = params.virtual && params.virtual.enabled && swiper.virtual ? swiper.virtual.slides.length - 1 : swiper.slides.length - 1;
2711
3010
  } else if (swiper.isEnd) {
2712
3011
  rewindFirstIndex = 0;
2713
3012
  }
2714
3013
  }
3014
+ // Find current slide size
2715
3015
  const ratio = (currentPos - slidesGrid[stopIndex]) / groupSize;
2716
3016
  const increment = stopIndex < params.slidesPerGroupSkip - 1 ? 1 : params.slidesPerGroup;
2717
3017
  if (timeDiff > params.longSwipesMs) {
3018
+ // Long touches
2718
3019
  if (!params.longSwipes) {
2719
3020
  swiper.slideTo(swiper.activeIndex);
2720
3021
  return;
2721
3022
  }
2722
3023
  if (swiper.swipeDirection === 'next') {
2723
- if (ratio >= params.longSwipesRatio) {
2724
- swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment);
2725
- } else swiper.slideTo(stopIndex);
3024
+ if (ratio >= params.longSwipesRatio) swiper.slideTo(params.rewind && swiper.isEnd ? rewindFirstIndex : stopIndex + increment);else swiper.slideTo(stopIndex);
2726
3025
  }
2727
3026
  if (swiper.swipeDirection === 'prev') {
2728
3027
  if (ratio > 1 - params.longSwipesRatio) {
@@ -2774,6 +3073,7 @@ function onResize() {
2774
3073
  allowSlidePrev,
2775
3074
  snapGrid
2776
3075
  } = swiper;
3076
+ const isVirtual = swiper.virtual && swiper.params.virtual.enabled;
2777
3077
 
2778
3078
  // Disable locks on resize
2779
3079
  swiper.allowSlideNext = true;
@@ -2781,12 +3081,15 @@ function onResize() {
2781
3081
  swiper.updateSize();
2782
3082
  swiper.updateSlides();
2783
3083
  swiper.updateSlidesClasses();
2784
- if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && !swiper.params.centeredSlides) {
3084
+ const isVirtualLoop = isVirtual && params.loop;
3085
+ if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !swiper.isBeginning && !swiper.params.centeredSlides && !isVirtualLoop) {
2785
3086
  swiper.slideTo(swiper.slides.length - 1, 0, false, true);
2786
- } else if (swiper.params.loop) {
2787
- swiper.slideToLoop(swiper.realIndex, 0, false, true);
2788
3087
  } else {
2789
- swiper.slideTo(swiper.activeIndex, 0, false, true);
3088
+ if (swiper.params.loop && !isVirtual) {
3089
+ swiper.slideToLoop(swiper.realIndex, 0, false, true);
3090
+ } else {
3091
+ swiper.slideTo(swiper.activeIndex, 0, false, true);
3092
+ }
2790
3093
  }
2791
3094
  if (swiper.autoplay && swiper.autoplay.running && swiper.autoplay.paused) {
2792
3095
  clearTimeout(swiper.autoplay.resizeTimeout);
@@ -2796,11 +3099,10 @@ function onResize() {
2796
3099
  }
2797
3100
  }, 500);
2798
3101
  }
2799
-
2800
3102
  // Return locks after resize
2801
3103
  swiper.allowSlidePrev = allowSlidePrev;
2802
3104
  swiper.allowSlideNext = allowSlideNext;
2803
- if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
3105
+ if (swiper.params.watchOverflow && snapGrid !== swiper.snapGrid) {
2804
3106
  swiper.checkOverflow();
2805
3107
  }
2806
3108
  }
@@ -2854,10 +3156,6 @@ function onLoad(e) {
2854
3156
  if (swiper.params.cssMode || swiper.params.slidesPerView !== 'auto' && !swiper.params.autoHeight) {
2855
3157
  return;
2856
3158
  }
2857
- const el = e?.target;
2858
- // IMG, IFRAME, EMBED with width/height attributes don't change slide size — no need to call swiper.update()
2859
- if (!el || !['IMG', 'IFRAME', 'EMBED'].includes(el.tagName)) return;
2860
- if (el.hasAttribute('width') && el.hasAttribute('height')) return;
2861
3159
  swiper.update();
2862
3160
  }
2863
3161
 
@@ -2969,15 +3267,8 @@ var events$1 = {
2969
3267
  detachEvents
2970
3268
  };
2971
3269
 
2972
- const toggleModule = (swiper, params, breakpointParams, prop) => {
2973
- const wasModuleEnabled = params[prop] && params[prop].enabled;
2974
- const isModuleEnabled = breakpointParams[prop] && breakpointParams[prop].enabled;
2975
- if (wasModuleEnabled && !isModuleEnabled) {
2976
- swiper[prop].disable();
2977
- }
2978
- if (!wasModuleEnabled && isModuleEnabled) {
2979
- swiper[prop].enable();
2980
- }
3270
+ const isGridEnabled = (swiper, params) => {
3271
+ return swiper.grid && params.grid && params.grid.rows > 1;
2981
3272
  };
2982
3273
  function setBreakpoint() {
2983
3274
  const swiper = this;
@@ -2998,20 +3289,38 @@ function setBreakpoint() {
2998
3289
  if (!breakpoint || swiper.currentBreakpoint === breakpoint) return;
2999
3290
  const breakpointOnlyParams = breakpoint in breakpoints ? breakpoints[breakpoint] : undefined;
3000
3291
  const breakpointParams = breakpointOnlyParams || swiper.originalParams;
3292
+ const wasMultiRow = isGridEnabled(swiper, params);
3293
+ const isMultiRow = isGridEnabled(swiper, breakpointParams);
3001
3294
  const wasGrabCursor = swiper.params.grabCursor;
3002
3295
  const isGrabCursor = breakpointParams.grabCursor;
3003
3296
  const wasEnabled = params.enabled;
3297
+ if (wasMultiRow && !isMultiRow) {
3298
+ el.classList.remove(`${params.containerModifierClass}grid`, `${params.containerModifierClass}grid-column`);
3299
+ swiper.emitContainerClasses();
3300
+ } else if (!wasMultiRow && isMultiRow) {
3301
+ el.classList.add(`${params.containerModifierClass}grid`);
3302
+ if (breakpointParams.grid.fill && breakpointParams.grid.fill === 'column' || !breakpointParams.grid.fill && params.grid.fill === 'column') {
3303
+ el.classList.add(`${params.containerModifierClass}grid-column`);
3304
+ }
3305
+ swiper.emitContainerClasses();
3306
+ }
3004
3307
  if (wasGrabCursor && !isGrabCursor) {
3005
3308
  swiper.unsetGrabCursor();
3006
3309
  } else if (!wasGrabCursor && isGrabCursor) {
3007
3310
  swiper.setGrabCursor();
3008
3311
  }
3009
3312
 
3010
- // Core-lite: toggle navigation & pagination only.
3011
- const modules = ['navigation', 'pagination'];
3012
- modules.forEach(prop => {
3313
+ // Toggle navigation, pagination, scrollbar
3314
+ ['navigation', 'pagination', 'scrollbar'].forEach(prop => {
3013
3315
  if (typeof breakpointParams[prop] === 'undefined') return;
3014
- toggleModule(swiper, params, breakpointParams, prop);
3316
+ const wasModuleEnabled = params[prop] && params[prop].enabled;
3317
+ const isModuleEnabled = breakpointParams[prop] && breakpointParams[prop].enabled;
3318
+ if (wasModuleEnabled && !isModuleEnabled) {
3319
+ swiper[prop].disable();
3320
+ }
3321
+ if (!wasModuleEnabled && isModuleEnabled) {
3322
+ swiper[prop].enable();
3323
+ }
3015
3324
  });
3016
3325
  const directionChanged = breakpointParams.direction && breakpointParams.direction !== params.direction;
3017
3326
  const needsReLoop = params.loop && (breakpointParams.slidesPerView !== params.slidesPerView || directionChanged);
@@ -3093,7 +3402,7 @@ var breakpoints = {
3093
3402
  getBreakpoint
3094
3403
  };
3095
3404
 
3096
- const prepareClasses = (entries, prefix) => {
3405
+ function prepareClasses(entries, prefix) {
3097
3406
  const resultClasses = [];
3098
3407
  entries.forEach(item => {
3099
3408
  if (typeof item === 'object') {
@@ -3107,7 +3416,7 @@ const prepareClasses = (entries, prefix) => {
3107
3416
  }
3108
3417
  });
3109
3418
  return resultClasses;
3110
- };
3419
+ }
3111
3420
  function addClasses() {
3112
3421
  const swiper = this;
3113
3422
  const {
@@ -3117,12 +3426,17 @@ function addClasses() {
3117
3426
  el,
3118
3427
  device
3119
3428
  } = swiper;
3120
- // core-lite: removed module-specific classes
3121
3429
  // prettier-ignore
3122
3430
  const suffixes = prepareClasses(['initialized', params.direction, {
3431
+ 'free-mode': swiper.params.freeMode && params.freeMode.enabled
3432
+ }, {
3123
3433
  'autoheight': params.autoHeight
3124
3434
  }, {
3125
3435
  'rtl': rtl
3436
+ }, {
3437
+ 'grid': params.grid && params.grid.rows > 1
3438
+ }, {
3439
+ 'grid-column': params.grid && params.grid.rows > 1 && params.grid.fill === 'column'
3126
3440
  }, {
3127
3441
  'android': device.android
3128
3442
  }, {
@@ -3219,12 +3533,16 @@ var defaults = {
3219
3533
  autoHeight: false,
3220
3534
  // Set wrapper width
3221
3535
  setWrapperSize: false,
3222
- // Effects (core-lite only supports `slide`)
3536
+ // Virtual Translate
3537
+ virtualTranslate: false,
3538
+ // Effects
3223
3539
  effect: 'slide',
3540
+ // 'slide' or 'fade' or 'cube' or 'coverflow' or 'flip'
3541
+
3224
3542
  // Breakpoints
3225
3543
  breakpoints: undefined,
3226
3544
  breakpointsBase: 'window',
3227
- // Slides
3545
+ // Slides grid
3228
3546
  spaceBetween: 0,
3229
3547
  slidesPerView: 1,
3230
3548
  slidesPerGroup: 1,
@@ -3327,9 +3645,7 @@ function moduleExtendParams(params, allModulesParams) {
3327
3645
  if (moduleParamName === 'navigation' && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].prevEl && !params[moduleParamName].nextEl) {
3328
3646
  params[moduleParamName].auto = true;
3329
3647
  }
3330
-
3331
- // Core-lite: keep only pagination auto-init.
3332
- if (moduleParamName === 'pagination' && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].el) {
3648
+ if (['pagination', 'scrollbar'].indexOf(moduleParamName) >= 0 && params[moduleParamName] && params[moduleParamName].enabled && !params[moduleParamName].el) {
3333
3649
  params[moduleParamName].auto = true;
3334
3650
  }
3335
3651
  if (!(moduleParamName in params && 'enabled' in moduleParams)) {
@@ -3455,6 +3771,7 @@ class Swiper {
3455
3771
  // Indexes
3456
3772
  activeIndex: 0,
3457
3773
  realIndex: 0,
3774
+ //
3458
3775
  isBeginning: true,
3459
3776
  isEnd: false,
3460
3777
  // Props
@@ -3481,9 +3798,12 @@ class Swiper {
3481
3798
  currentTranslate: undefined,
3482
3799
  startTranslate: undefined,
3483
3800
  allowThresholdMove: undefined,
3801
+ // Form elements to match
3484
3802
  focusableElements: swiper.params.focusableElements,
3803
+ // Last click time
3485
3804
  lastClickTime: 0,
3486
3805
  clickTimeout: undefined,
3806
+ // Velocities
3487
3807
  velocities: [],
3488
3808
  allowMomentumBounce: undefined,
3489
3809
  startMoving: undefined,
@@ -3512,6 +3832,7 @@ class Swiper {
3512
3832
  swiper.init();
3513
3833
  }
3514
3834
 
3835
+ // Return app instance
3515
3836
  // eslint-disable-next-line no-constructor-return
3516
3837
  return swiper;
3517
3838
  }
@@ -3543,6 +3864,16 @@ class Swiper {
3543
3864
  getSlideIndexByData(index) {
3544
3865
  return this.getSlideIndex(this.slides.find(slideEl => slideEl.getAttribute('data-swiper-slide-index') * 1 === index));
3545
3866
  }
3867
+ getSlideIndexWhenGrid(index) {
3868
+ if (this.grid && this.params.grid && this.params.grid.rows > 1) {
3869
+ if (this.params.grid.fill === 'column') {
3870
+ index = Math.floor(index / this.params.grid.rows);
3871
+ } else if (this.params.grid.fill === 'row') {
3872
+ index = index % Math.ceil(this.slides.length / this.params.grid.rows);
3873
+ }
3874
+ }
3875
+ return index;
3876
+ }
3546
3877
  recalcSlides() {
3547
3878
  const swiper = this;
3548
3879
  const {
@@ -3644,15 +3975,21 @@ class Swiper {
3644
3975
  }
3645
3976
  }
3646
3977
  } else {
3978
+ // eslint-disable-next-line
3647
3979
  if (view === 'current') {
3648
3980
  for (let i = activeIndex + 1; i < slides.length; i += 1) {
3649
3981
  const slideInView = exact ? slidesGrid[i] + slidesSizesGrid[i] - slidesGrid[activeIndex] < swiperSize : slidesGrid[i] - slidesGrid[activeIndex] < swiperSize;
3650
- if (slideInView) spv += 1;
3982
+ if (slideInView) {
3983
+ spv += 1;
3984
+ }
3651
3985
  }
3652
3986
  } else {
3987
+ // previous
3653
3988
  for (let i = activeIndex - 1; i >= 0; i -= 1) {
3654
3989
  const slideInView = slidesGrid[activeIndex] - slidesGrid[i] < swiperSize;
3655
- if (slideInView) spv += 1;
3990
+ if (slideInView) {
3991
+ spv += 1;
3992
+ }
3656
3993
  }
3657
3994
  }
3658
3995
  }
@@ -3665,11 +4002,14 @@ class Swiper {
3665
4002
  snapGrid,
3666
4003
  params
3667
4004
  } = swiper;
4005
+ // Breakpoints
3668
4006
  if (params.breakpoints) {
3669
4007
  swiper.setBreakpoint();
3670
4008
  }
3671
4009
  [...swiper.el.querySelectorAll('[loading="lazy"]')].forEach(imageEl => {
3672
- if (imageEl.complete) processLazyPreloader(swiper, imageEl);
4010
+ if (imageEl.complete) {
4011
+ processLazyPreloader(swiper, imageEl);
4012
+ }
3673
4013
  });
3674
4014
  swiper.updateSize();
3675
4015
  swiper.updateSlides();
@@ -3683,12 +4023,22 @@ class Swiper {
3683
4023
  swiper.updateSlidesClasses();
3684
4024
  }
3685
4025
  let translated;
3686
- if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !params.centeredSlides) {
3687
- translated = swiper.slideTo(swiper.slides.length - 1, 0, false, true);
4026
+ if (params.freeMode && params.freeMode.enabled && !params.cssMode) {
4027
+ setTranslate();
4028
+ if (params.autoHeight) {
4029
+ swiper.updateAutoHeight();
4030
+ }
3688
4031
  } else {
3689
- translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
4032
+ if ((params.slidesPerView === 'auto' || params.slidesPerView > 1) && swiper.isEnd && !params.centeredSlides) {
4033
+ const slides = swiper.virtual && params.virtual.enabled ? swiper.virtual.slides : swiper.slides;
4034
+ translated = swiper.slideTo(slides.length - 1, 0, false, true);
4035
+ } else {
4036
+ translated = swiper.slideTo(swiper.activeIndex, 0, false, true);
4037
+ }
4038
+ if (!translated) {
4039
+ setTranslate();
4040
+ }
3690
4041
  }
3691
- if (!translated) setTranslate();
3692
4042
  if (params.watchOverflow && snapGrid !== swiper.snapGrid) {
3693
4043
  swiper.checkOverflow();
3694
4044
  }
@@ -3701,6 +4051,7 @@ class Swiper {
3701
4051
  const swiper = this;
3702
4052
  const currentDirection = swiper.params.direction;
3703
4053
  if (!newDirection) {
4054
+ // eslint-disable-next-line
3704
4055
  newDirection = currentDirection === 'horizontal' ? 'vertical' : 'horizontal';
3705
4056
  }
3706
4057
  if (newDirection === currentDirection || newDirection !== 'horizontal' && newDirection !== 'vertical') {
@@ -3738,11 +4089,15 @@ class Swiper {
3738
4089
  mount(element) {
3739
4090
  const swiper = this;
3740
4091
  if (swiper.mounted) return true;
4092
+
4093
+ // Find el
3741
4094
  let el = element || swiper.params.el;
3742
4095
  if (typeof el === 'string') {
3743
4096
  el = document.querySelector(el);
3744
4097
  }
3745
- if (!el) return false;
4098
+ if (!el) {
4099
+ return false;
4100
+ }
3746
4101
  el.swiper = swiper;
3747
4102
  if (el.parentNode && el.parentNode.host && el.parentNode.host.nodeName === swiper.params.swiperElementNodeName.toUpperCase()) {
3748
4103
  swiper.isElement = true;
@@ -3753,10 +4108,12 @@ class Swiper {
3753
4108
  const getWrapper = () => {
3754
4109
  if (el && el.shadowRoot && el.shadowRoot.querySelector) {
3755
4110
  const res = el.shadowRoot.querySelector(getWrapperSelector());
4111
+ // Children needs to return slot items
3756
4112
  return res;
3757
4113
  }
3758
4114
  return elementChildren(el, getWrapperSelector())[0];
3759
4115
  };
4116
+ // Find Wrapper
3760
4117
  let wrapperEl = getWrapper();
3761
4118
  if (!wrapperEl && swiper.params.createElements) {
3762
4119
  wrapperEl = createElement('div', swiper.params.wrapperClass);
@@ -3771,6 +4128,7 @@ class Swiper {
3771
4128
  slidesEl: swiper.isElement && !el.parentNode.host.slideSlots ? el.parentNode.host : wrapperEl,
3772
4129
  hostEl: swiper.isElement ? el.parentNode.host : el,
3773
4130
  mounted: true,
4131
+ // RTL
3774
4132
  rtl: el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl',
3775
4133
  rtlTranslate: swiper.params.direction === 'horizontal' && (el.dir.toLowerCase() === 'rtl' || elementStyle(el, 'direction') === 'rtl'),
3776
4134
  wrongRTL: elementStyle(wrapperEl, 'display') === '-webkit-box'
@@ -3790,6 +4148,7 @@ class Swiper {
3790
4148
  const gap = Math.abs(swiper.snapGrid[1] - swiper.snapGrid[0]);
3791
4149
  const swiperTranslate = structuredClone(swiper.snapGrid[1]);
3792
4150
  if (isFirstSlide) {
4151
+ // Move last item to first position when at first slide
3793
4152
  const lastSlide = slides.at(-1);
3794
4153
  lastSlide.swiperLoopMoveDOM = true;
3795
4154
  swiper.slidesEl.prepend(lastSlide);
@@ -3801,6 +4160,7 @@ class Swiper {
3801
4160
  swiper.setTransition(speed);
3802
4161
  swiper.setTranslate(-swiperTranslate);
3803
4162
  } else if (isLastSlide) {
4163
+ // Move first item to last position when at last slide
3804
4164
  const firstSlide = slides[0];
3805
4165
  firstSlide.swiperLoopMoveDOM = true;
3806
4166
  swiper.slidesEl.append(firstSlide);
@@ -3821,24 +4181,42 @@ class Swiper {
3821
4181
  const mounted = swiper.mount(el);
3822
4182
  if (mounted === false) return swiper;
3823
4183
  swiper.emit('beforeInit');
4184
+
4185
+ // Set breakpoint
3824
4186
  if (swiper.params.breakpoints) {
3825
4187
  swiper.setBreakpoint();
3826
4188
  }
4189
+
4190
+ // Add Classes
3827
4191
  swiper.addClasses();
4192
+
4193
+ // Update size
3828
4194
  swiper.updateSize();
4195
+
4196
+ // Update slides
3829
4197
  swiper.updateSlides();
3830
4198
  if (swiper.params.watchOverflow) {
3831
4199
  swiper.checkOverflow();
3832
4200
  }
4201
+
4202
+ // Set Grab Cursor
3833
4203
  if (swiper.params.grabCursor && swiper.enabled) {
3834
4204
  swiper.setGrabCursor();
3835
4205
  }
3836
4206
 
3837
- // Slide to initial slide (core-lite: no optional feature initial offsets)
3838
- swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
4207
+ // Slide To Initial Slide
4208
+ if (swiper.params.loop && swiper.virtual && swiper.params.virtual.enabled) {
4209
+ swiper.slideTo(swiper.params.initialSlide + swiper.virtual.slidesBefore, 0, swiper.params.runCallbacksOnInit, false, true);
4210
+ } else {
4211
+ swiper.slideTo(swiper.params.initialSlide, 0, swiper.params.runCallbacksOnInit, false, true);
4212
+ }
4213
+
4214
+ // Create loop
3839
4215
  if (swiper.params.loop) {
3840
4216
  swiper.loopCreate(undefined, true);
3841
4217
  }
4218
+
4219
+ // Attach events
3842
4220
  swiper.attachEvents();
3843
4221
  const lazyElements = [...swiper.el.querySelectorAll('[loading="lazy"]')];
3844
4222
  if (swiper.isElement) {
@@ -3848,12 +4226,18 @@ class Swiper {
3848
4226
  if (imageEl.complete) {
3849
4227
  processLazyPreloader(swiper, imageEl);
3850
4228
  } else {
3851
- imageEl.addEventListener('load', e => processLazyPreloader(swiper, e.target));
4229
+ imageEl.addEventListener('load', e => {
4230
+ processLazyPreloader(swiper, e.target);
4231
+ });
3852
4232
  }
3853
4233
  });
3854
4234
  preload(swiper);
4235
+
4236
+ // Init Flag
3855
4237
  swiper.initialized = true;
3856
4238
  preload(swiper);
4239
+
4240
+ // Emit
3857
4241
  swiper.emit('init');
3858
4242
  swiper.emit('afterInit');
3859
4243
  return swiper;
@@ -3876,15 +4260,27 @@ class Swiper {
3876
4260
  return null;
3877
4261
  }
3878
4262
  swiper.emit('beforeDestroy');
4263
+
4264
+ // Init Flag
3879
4265
  swiper.initialized = false;
4266
+
4267
+ // Detach events
3880
4268
  swiper.detachEvents();
4269
+
4270
+ // Destroy loop
3881
4271
  if (params.loop) {
3882
4272
  swiper.loopDestroy();
3883
4273
  }
4274
+
4275
+ // Cleanup styles
3884
4276
  if (cleanStyles) {
3885
4277
  swiper.removeClasses();
3886
- if (el && typeof el !== 'string') el.removeAttribute('style');
3887
- if (wrapperEl) wrapperEl.removeAttribute('style');
4278
+ if (el && typeof el !== 'string') {
4279
+ el.removeAttribute('style');
4280
+ }
4281
+ if (wrapperEl) {
4282
+ wrapperEl.removeAttribute('style');
4283
+ }
3888
4284
  if (slides && slides.length) {
3889
4285
  slides.forEach(slideEl => {
3890
4286
  slideEl.classList.remove(params.slideVisibleClass, params.slideFullyVisibleClass, params.slideActiveClass, params.slideNextClass, params.slidePrevClass);
@@ -3894,6 +4290,8 @@ class Swiper {
3894
4290
  }
3895
4291
  }
3896
4292
  swiper.emit('destroy');
4293
+
4294
+ // Detach emitter events
3897
4295
  Object.keys(swiper.eventsListeners).forEach(eventName => {
3898
4296
  swiper.off(eventName);
3899
4297
  });