PageSourceSearch

https://www.lacomarcahotel.com.ar/neo_modules/standard_lp_motor/includes/carousel.js?filev=1778144039

js lacomarcahotel.com.ar collected 2026-10-05 07:07:28 UTC 23,932 bytes, 836 lines download raw bytes

1/**
2 * Carrusel Moderno Custom - Standard LP Motor
3 * @author Pxsol Development Team
4 */
5
6class PxGallery {
7  constructor(items) {
8    this.items = items; // Array of objects {src, thumb, alt}
9    this.currentIndex = 0;
10    this.overlay = null;
11    this.isZoomed = false;
12    this.scale = 1;
13    this.lastScale = 1;
14    this.panning = false;
15    this.pointX = 0;
16    this.pointY = 0;
17    this.startPointX = 0;
18    this.startPointY = 0;
19    this.lastPointX = 0;
20    this.lastPointY = 0;
21
22    this.init();
23  }
24
25  init() {
26    this.createOverlay();
27    this.bindEvents();
28  }
29
30  createOverlay() {
31    // Prevent duplicate overlays
32    if (document.querySelector(".px-gallery-overlay")) return;
33
34    const div = document.createElement("div");
35    div.className = "px-gallery-overlay";
36    div.innerHTML = `
37            <div class="px-gallery-header">
38                <div class="px-gallery-counter"></div>
39                <button class="px-gallery-close" aria-label="Cerrar">&times;</button>
40            </div>
41            <div class="px-gallery-main">
42                <div class="px-gallery-slides"></div>
43                <button class="px-gallery-nav prev" aria-label="Anterior">&#10094;</button>
44                <button class="px-gallery-nav next" aria-label="Siguiente">&#10095;</button>
45                <div class="px-gallery-loader"></div>
46            </div>
47            <div class="px-gallery-thumbnails"></div>
48            <div class="px-gallery-zoom-hint">Haga doble clic para hacer zoom</div>
49        `;
50    document.body.appendChild(div);
51    this.overlay = div;
52
53    this.slidesContainer = div.querySelector(".px-gallery-slides");
54    this.thumbsContainer = div.querySelector(".px-gallery-thumbnails");
55    this.counter = div.querySelector(".px-gallery-counter");
56    this.loader = div.querySelector(".px-gallery-loader");
57
58    // Create thumbnails
59    this.renderThumbnails();
60  }
61
62  renderThumbnails() {
63    if (!this.thumbsContainer) return;
64    this.thumbsContainer.innerHTML = "";
65    this.items.forEach((item, idx) => {
66      const thumb = document.createElement("div");
67      thumb.className = "px-gallery-thumb";
68      const image = document.createElement("img");
69      image.src = item.thumb;
70      image.alt = item.alt || "";
71      thumb.appendChild(image);
72      thumb.onclick = (e) => {
73        e.stopPropagation();
74        this.goTo(idx);
75      };
76      this.thumbsContainer.appendChild(thumb);
77    });
78  }
79
80  bindEvents() {
81    if (!this.overlay) return;
82    this.overlay.querySelector(".px-gallery-close").onclick = () =>
83      this.close();
84    this.overlay.querySelector(".px-gallery-nav.prev").onclick = (e) => {
85      e.stopPropagation();
86      this.prev();
87    };
88    this.overlay.querySelector(".px-gallery-nav.next").onclick = (e) => {
89      e.stopPropagation();
90      this.next();
91    };
92
93    // Keyboard nav
94    document.addEventListener("keydown", (e) => {
95      if (!this.overlay.classList.contains("active")) return;
96      if (e.key === "Escape") this.close();
97      if (e.key === "ArrowLeft") this.prev();
98      if (e.key === "ArrowRight") this.next();
99    });
100
101    // Touch/Swipe/Zoom logic
102    const main = this.overlay.querySelector(".px-gallery-main");
103
104    main.addEventListener("touchstart", (e) => this.handleTouchStart(e), {
105      passive: false,
106    });
107    main.addEventListener("touchmove", (e) => this.handleTouchMove(e), {
108      passive: false,
109    });
110    main.addEventListener("touchend", (e) => this.handleTouchEnd(e));
111
112    // Mouse drag only when zoomed
113    main.addEventListener("mousedown", (e) => this.handleMouseDown(e));
114    main.addEventListener("mousemove", (e) => this.handleMouseMove(e));
115    main.addEventListener("mouseup", (e) => this.handleMouseUp(e));
116
117    // Double tap/click to zoom
118    let lastTap = 0;
119    main.addEventListener("click", (e) => {
120      // If clicking navigating arrows, ignore
121      if (e.target.closest(".px-gallery-nav")) return;
122
123      const curTime = new Date().getTime();
124      const tapLen = curTime - lastTap;
125      if (tapLen < 300 && tapLen > 0) {
126        // Double tap
127        this.toggleZoom(e);
128        e.preventDefault();
129      }
130      lastTap = curTime;
131    });
132
133    // Wheel to zoom
134    main.addEventListener(
135      "wheel",
136      (e) => {
137        if (e.ctrlKey) {
138          e.preventDefault();
139          const delta = e.deltaY > 0 ? 0.9 : 1.1;
140          this.updateZoom(this.scale * delta);
141        }
142      },
143      { passive: false }
144    );
145  }
146
147  open(index = 0) {
148    if (!this.overlay) this.init(); // Re-init if needed?
149
150    this.overlay.classList.add("active");
151    document.body.style.overflow = "hidden";
152    this.goTo(index);
153
154    // Show hint momentarily
155    const hint = this.overlay.querySelector(".px-gallery-zoom-hint");
156    hint.classList.add("visible");
157    setTimeout(() => hint.classList.remove("visible"), 3000);
158  }
159
160  close() {
161    this.overlay.classList.remove("active");
162    document.body.style.overflow = "";
163    this.slidesContainer.innerHTML = "";
164    this.isZoomed = false;
165    this.scale = 1;
166  }
167
168  goTo(index) {
169    if (index < 0) index = this.items.length - 1;
170    if (index >= this.items.length) index = 0;
171
172    this.currentIndex = index;
173    this.updateCounter();
174    this.updateThumbnails();
175    this.renderSlide();
176
177    // Reset zoom
178    this.scale = 1;
179    this.pointX = 0;
180    this.pointY = 0;
181    this.isZoomed = false;
182  }
183
184  prev() {
185    this.goTo(this.currentIndex - 1);
186  }
187
188  next() {
189    this.goTo(this.currentIndex + 1);
190  }
191
192  updateCounter() {
193    this.counter.textContent = `${this.currentIndex + 1} / ${
194      this.items.length
195    }`;
196  }
197
198  updateThumbnails() {
199    const thumbs = this.thumbsContainer.querySelectorAll(".px-gallery-thumb");
200    thumbs.forEach((t, i) => {
201      if (i === this.currentIndex) {
202        t.classList.add("active");
203        t.scrollIntoView({
204          behavior: "smooth",
205          inline: "center",
206          block: "nearest",
207        });
208      } else {
209        t.classList.remove("active");
210      }
211    });
212  }
213
214  renderSlide() {
215    const item = this.items[this.currentIndex];
216
217    // Clear current
218    this.slidesContainer.innerHTML = "";
219    this.loader.style.display = "block";
220
221    const wrapper = document.createElement("div");
222    wrapper.className = "px-gallery-image-wrapper active-slide";
223
224    const img = new Image();
225    img.className = "px-gallery-image";
226    img.alt = item.alt || "";
227    img.onload = () => {
228      this.loader.style.display = "none";
229      img.classList.remove("loading");
230    };
231    img.src = item.src;
232
233    wrapper.appendChild(img);
234    this.slidesContainer.appendChild(wrapper);
235    this.currentWrapper = wrapper;
236    this.currentImage = img;
237  }
238
239  // Touch/Zoom Logic
240  handleTouchStart(e) {
241    if (e.touches.length === 2) {
242      // Pinch start
243      this.isZooming = true;
244      this.startDist = Math.hypot(
245        e.touches[0].pageX - e.touches[1].pageX,
246        e.touches[0].pageY - e.touches[1].pageY
247      );
248      this.startScale = this.scale;
249    } else if (e.touches.length === 1) {
250      // Drag start
251      this.isDragging = true;
252      this.startX = e.touches[0].clientX;
253      this.startY = e.touches[0].clientY;
254      this.lastPointX = this.pointX;
255      this.lastPointY = this.pointY;
256    }
257  }
258
259  handleTouchMove(e) {
260    if (this.isZooming && e.touches.length === 2) {
261      e.preventDefault();
262      const dist = Math.hypot(
263        e.touches[0].pageX - e.touches[1].pageX,
264        e.touches[0].pageY - e.touches[1].pageY
265      );
266      const delta = dist / this.startDist;
267      this.updateZoom(this.startScale * delta);
268    } else if (this.isDragging && e.touches.length === 1) {
269      if (this.isZoomed) {
270        // Pan
271        e.preventDefault();
272        const deltaX = e.touches[0].clientX - this.startX;
273        const deltaY = e.touches[0].clientY - this.startY;
274        this.pointX = this.lastPointX + deltaX;
275        this.pointY = this.lastPointY + deltaY;
276        this.applyTransform();
277      } else {
278        // Prevent default only if moving significantly to stop scrolling page (though overflow hidden handles most)
279        // But here we might want to swipe
280      }
281    }
282  }
283
284  handleTouchEnd(e) {
285    this.isZooming = false;
286
287    if (this.isDragging && !this.isZoomed) {
288      // Check for swipe
289      const diffX = e.changedTouches[0].clientX - this.startX;
290      if (Math.abs(diffX) > 50) {
291        if (diffX > 0) this.prev();
292        else this.next();
293      }
294    }
295
296    this.isDragging = false;
297  }
298
299  handleMouseDown(e) {
300    if (!this.isZoomed) return;
301    this.isDragging = true;
302    this.startX = e.clientX;
303    this.startY = e.clientY;
304    this.lastPointX = this.pointX;
305    this.lastPointY = this.pointY;
306    this.currentWrapper.style.cursor = "grabbing";
307    e.preventDefault();
308  }
309
310  handleMouseMove(e) {
311    if (this.isDragging && this.isZoomed) {
312      const deltaX = e.clientX - this.startX;
313      const deltaY = e.clientY - this.startY;
314      this.pointX = this.lastPointX + deltaX;
315      this.pointY = this.lastPointY + deltaY;
316      this.applyTransform();
317      e.preventDefault();
318    }
319  }
320
321  handleMouseUp(e) {
322    this.isDragging = false;
323    if (this.currentWrapper) this.currentWrapper.style.cursor = "grab";
324  }
325
326  toggleZoom(e) {
327    if (this.isZoomed) {
328      this.scale = 1;
329      this.pointX = 0;
330      this.pointY = 0;
331      this.isZoomed = false;
332      this.currentWrapper.classList.remove("zoomed");
333    } else {
334      this.scale = 2.5;
335      this.isZoomed = true;
336      this.currentWrapper.classList.add("zoomed");
337    }
338    this.applyTransform();
339  }
340
341  updateZoom(newScale) {
342    this.scale = Math.max(1, Math.min(newScale, 5));
343    if (this.scale < 1.1) {
344      this.scale = 1;
345      this.isZoomed = false;
346      this.pointX = 0;
347      this.pointY = 0;
348      if (this.currentWrapper) this.currentWrapper.classList.remove("zoomed");
349    } else {
350      this.isZoomed = true;
351      if (this.currentWrapper) this.currentWrapper.classList.add("zoomed");
352    }
353    this.applyTransform();
354  }
355
356  applyTransform() {
357    if (this.currentImage) {
358      this.currentImage.style.transform = `translate(${this.pointX}px, ${this.pointY}px) scale(${this.scale})`;
359    }
360  }
361}
362
363class PxCustomCarousel {
364  constructor(element, options = {}) {
365    this.element = element;
366    this.options = {
367      autoplay: false,
368      autoplayDelay: 4000,
369      enableTouch: true,
370      enableKeyboard: true,
371      enableNavigation: true,
372      loadingClass: "loading",
373      minQueueItems: 10,
374      ...options,
375    };
376
377    const sourceItems = Array.from(
378      this.element.querySelectorAll(".px_carousel_item")
379    );
380    this.baseItems = sourceItems.map((el, index) => {
381      const img = el.querySelector("img");
382      return {
383        index,
384        href: el.getAttribute("href") || "#",
385        target: el.getAttribute("target") || "_blank",
386        thumb: img?.getAttribute("data-lazy") || img?.getAttribute("src") || "",
387        placeholder: img?.getAttribute("src") || "",
388        alt: img?.getAttribute("alt") || "",
389      };
390    });
391    this.baseTotalItems = this.baseItems.length;
392    this.queueStartIndex = 0;
393    this.renderCount = 0;
394    this.renderItems = [];
395    this.loopEnabled = this.baseTotalItems > 1;
396    this.suppressClickUntil = 0;
397    this.pendingSteps = 0;
398
399    // Parse items for gallery
400    this.galleryItems = this.baseItems.map((item) => ({
401      src: item.href,
402      thumb: item.thumb,
403      alt: item.alt,
404    }));
405
406    // Initialize one valid global gallery instance
407    if (!window.pxGalleryInstance && this.galleryItems.length > 0) {
408      window.pxGalleryInstance = new PxGallery(this.galleryItems);
409    } else if (window.pxGalleryInstance) {
410      // Only update items if we found new ones (to support multiple carousels scenario loosely)
411      if (this.galleryItems.length > 0) {
412        window.pxGalleryInstance.items = this.galleryItems;
413        window.pxGalleryInstance.renderThumbnails();
414      }
415    }
416    this.isAnimating = false;
417    this.transitionFallbackTimer = null;
418    this.resizeTimer = null;
419    this.transitionCallback = null;
420    this.autoplayTimer = null;
421    this.touchStartX = 0;
422    this.touchCurrentX = 0;
423    this.isDragging = false;
424    this.hasMoved = false;
425    this.startTime = null;
426
427    this.init();
428  }
429
430  init() {
431    if (this.baseTotalItems === 0) return;
432
433    this.setupCarousel();
434    this.setupNavigation();
435    this.setupEvents();
436    this.buildQueue();
437
438    if (this.options.autoplay) {
439      this.startAutoplay();
440    }
441  }
442
443  setupCarousel() {
444    this.container = this.element.querySelector(".px_carousel_container");
445    this.element.classList.toggle("px_loop_enabled", this.loopEnabled);
446    if (this.options.enableTouch) {
447      this.element.classList.add("touch_enabled");
448    }
449  }
450
451  setupNavigation() {
452    if (!this.options.enableNavigation) return;
453
454    this.prevBtn = document.createElement("button");
455    this.prevBtn.className = "px_carousel_nav prev";
456    this.prevBtn.innerHTML = '<i class="mdi mdi-chevron-left"></i>';
457    this.prevBtn.setAttribute("aria-label", "Anterior");
458
459    this.nextBtn = document.createElement("button");
460    this.nextBtn.className = "px_carousel_nav next";
461    this.nextBtn.innerHTML = '<i class="mdi mdi-chevron-right"></i>';
462    this.nextBtn.setAttribute("aria-label", "Siguiente");
463
464    this.element.appendChild(this.prevBtn);
465    this.element.appendChild(this.nextBtn);
466
467    this.prevBtn.addEventListener("click", (e) => {
468      e.stopPropagation();
469      this.prev();
470    });
471    this.nextBtn.addEventListener("click", (e) => {
472      e.stopPropagation();
473      this.next();
474    });
475
476    if (!this.loopEnabled) {
477      this.prevBtn.style.display = "none";
478      this.nextBtn.style.display = "none";
479    }
480  }
481
482  setupEvents() {
483    this.element.addEventListener("click", (e) =>
483 this.handleItemClick(e));
484
485    if (this.options.enableTouch) {
486      this.element.addEventListener(
487        "touchstart",
488        (e) => this.handleTouchStart(e),
489        { passive: true }
490      );
491      this.element.addEventListener(
492        "touchmove",
493        (e) => this.handleTouchMove(e),
494        { passive: false }
495      );
496      this.element.addEventListener("touchend", (e) => this.handleTouchEnd(e), {
497        passive: true,
498      });
499    }
500
501    this.element.addEventListener("mousedown", (e) => this.handleMouseDown(e));
502    this.element.addEventListener("mousemove", (e) => this.handleMouseMove(e));
503    this.element.addEventListener("mouseup", (e) => this.handleMouseUp(e));
504    this.element.addEventListener("mouseleave", (e) => this.handleMouseUp(e));
505
506    this.element.addEventListener("mouseenter", () => this.pauseAutoplay());
507    this.element.addEventListener("mouseleave", () => this.resumeAutoplay());
508
509    window.addEventListener("resize", () => this.handleResize());
510
511    this.container.addEventListener("transitionend", (e) => {
512      if (e.target === this.container && e.propertyName === "transform") {
513        this.handleTransitionEnd();
514      }
515    });
516  }
517
518  next() {
519    if (!this.loopEnabled) return;
520    if (this.isAnimating) {
521      this.pendingSteps++;
522      return;
523    }
524
525    const item = this.createItem(this.queueStartIndex + this.renderCount);
526    this.container.appendChild(item);
527    this.renderItems.push(item);
528    this.animateTo(-this.getItemStep(), () => {
529      this.renderItems.shift()?.remove();
530      this.queueStartIndex = this.normalizeBaseIndex(this.queueStartIndex + 1);
531      this.renderItems = Array.from(this.container.querySelectorAll(".px_carousel_item"));
532      this.updateSlidePosition(false);
533      this.processPendingSteps();
534    });
535  }
536
537  prev() {
538    if (!this.loopEnabled) return;
539    if (this.isAnimating) {
540      this.pendingSteps--;
541      return;
542    }
543
544    const item = this.createItem(this.queueStartIndex - 1);
545    this.container.insertBefore(item, this.container.firstChild);
546    this.renderItems.unshift(item);
547    this.updateSlidePosition(false, -this.getItemStep());
548    this.animateTo(0, () => {
549      this.renderItems.pop()?.remove();
550      this.queueStartIndex = this.normalizeBaseIndex(this.queueStartIndex - 1);
551      this.renderItems = Array.from(this.container.querySelectorAll(".px_carousel_item"));
552      this.updateSlidePosition(false);
553      this.processPendingSteps();
554    });
555  }
556
557  handleTransitionEnd() {
558    if (!this.loopEnabled) return;
559
560    this.clearTransitionFallback();
561    const callback = this.transitionCallback;
562    this.transitionCallback = null;
563    this.isAnimating = false;
564    if (callback) callback();
565  }
566
567  handleTouchStart(e) {
568    if (!this.loopEnabled) return;
569    if (this.isAnimating) return;
570    this.touchStartX = e.touches[0].clientX;
571    this.startTime = Date.now();
572    this.hasMoved = false;
573    this.isDragging = false;
574    this.pauseAutoplay();
575  }
576
577  handleTouchMove(e) {
578    if (!this.loopEnabled || !this.touchStartX) return;
579    this.touchCurrentX = e.touches[0].clientX;
580    const diff = Math.abs(this.touchStartX - this.touchCurrentX);
581    if (diff > 10 && !this.isDragging) {
582      this.isDragging = true;
583      this.hasMoved = true;
584      this.element.classList.add("dragging");
585      e.preventDefault();
586    }
587    if (this.isDragging) {
588      e.preventDefault();
589      this.container.style.transform = `translateX(${-(this.touchStartX - this.touchCurrentX)}px)`;
590    }
591  }
592
593  handleTouchEnd(e) {
594    if (!this.loopEnabled) return;
595    const wasDragging = this.isDragging;
596    this.isDragging = false;
597    this.element.classList.remove("dragging");
598
599    if (wasDragging && this.hasMoved) {
600      const diff = this.touchStartX - this.touchCurrentX;
601      if (Math.abs(diff) > 50) {
602        if (diff > 0) this.next();
603        else this.prev();
604      } else {
605        this.animateTo(0);
606      }
607      this.suppressClickUntil = Date.now() + 250;
608    }
609    this.touchStartX = 0;
610    this.touchCurrentX = 0;
611    this.hasMoved = false;
612    this.resumeAutoplay();
613  }
614
615  handleMouseDown(e) {
616    if (!this.loopEnabled) return;
617    if (this.isAnimating) return;
618    this.touchStartX = e.clientX;
619    this.startTime = Date.now();
620    this.hasMoved = false;
621    this.isDragging = false;
622    this.pauseAutoplay();
623  }
624
625  handleMouseMove(e) {
626    if (!this.loopEnabled || !this.touchStartX) return;
627    this.touchCurrentX = e.clientX;
628    const diff = Math.abs(this.touchStartX - this.touchCurrentX);
629    if (diff > 5 && !this.isDragging) {
630      this.isDragging = true;
631      this.hasMoved = true;
632      this.element.classList.add("dragging");
633    }
634    if (this.isDragging) {
635      this.container.style.transform = `translateX(${-(this.touchStartX - this.touchCurrentX)}px)`;
636    }
637  }
638
639  handleMouseUp(e) {
640    if (!this.loopEnabled) return;
641    const wasDragging = this.isDragging;
642    this.isDragging = false;
643    this.element.classList.remove("dragging");
644
645    if (wasDragging && this.hasMoved) {
646      const diff = this.touchStartX - this.touchCurrentX;
647      if (Math.abs(diff) > 50) {
648        if (diff > 0) this.next();
649        else this.prev();
650      } else {
651        this.animateTo(0);
652      }
653      this.suppressClickUntil = Date.now() + 250;
654    }
655    this.touchStartX = 0;
656    this.touchCurrentX = 0;
657    this.hasMoved = false;
658    this.resumeAutoplay();
659  }
660
661  handleResize() {
662    if (!this.loopEnabled) return;
663    window.clearTimeout(this.resizeTimer);
664    this.resizeTimer = window.setTimeout(() => {
665      this.clearTransitionFallback();
666      this.isAnimating = false;
667      this.transitionCallback = null;
668      this.pendingSteps = 0;
669      this.buildQueue();
670    }, 120);
671  }
672
673  startAutoplay() {
674    if (!this.loopEnabled) return;
675    this.autoplayTimer = setInterval(() => {
676      this.next();
677    }, this.options.autoplayDelay);
678  }
679
680  pauseAutoplay() {
681    if (this.autoplayTimer) {
682      clearInterval(this.autoplayTimer);
683      this.autoplayTimer = null;
684    }
685  }
686
687  resumeAutoplay() {
688    if (this.options.autoplay && this.loopEnabled) {
689      this.startAutoplay();
690    }
691  }
692
693  handleItemClick(e) {
694    const item = e.target.closest(".px_carousel_item");
695    if (!item || !this.element.contains(item)) return;
696
697    e.preventDefault();
698
699    if (Date.now() < this.suppressClickUntil) {
700      return;
701    }
702
703    const index = this.getBaseIndexFromItem(item);
704    if (window.pxGalleryInstance) {
705      window.pxGalleryInstance.open(index);
706    }
707  }
708
709  getBaseIndexFromItem(item) {
710    if (!item) return 0;
711    return this.normalizeBaseIndex(parseInt(item.dataset.baseIndex || "0", 10));
712  }
713
714  normalizeBaseIndex(index) {
715    if (this.baseTotalItems === 0) return 0;
716    return ((index % this.baseTotalItems) + this.baseTotalItems) % this.baseTotalItems;
717  }
718
719  buildQueue() {
720    if (!this.container) return;
721
722    this.renderCount = this.getRenderCount();
723    this.container.innerHTML = "";
724
725    for (let i = 0; i < this.renderCount; i++) {
726      this.container.appendChild(this.createItem(this.queueStartIndex + i));
727    }
728
729    this.renderItems = Array.from(this.container.querySelectorAll(".px_carousel_item"));
730    this.updateSlidePosition(false);
731  }
732
733  createItem(index) {
734    const baseIndex = this.normalizeBaseIndex(index);
735    const item = this.baseItems[baseIndex];
736    const link = document.createElement("a");
737    const img = document.createElement("img");
738
739    link.className = "px_carousel_item";
740    link.href = item.href;
741    link.target = item.target;
742    link.dataset.baseIndex = baseIndex;
743
744    img.src = item.thumb || item.placeholder;
745    img.alt = item.alt;
746    img.onerror = function () {
747      this.style.display = "none";
748    };
749
750    link.appendChild(img);
751    return link;
752  }
753
754  getRenderCount() {
755    if (!this.loopEnabled) return this.baseTotalItems;
756
757    const itemStep = this.getItemStep();
758    const containerWidth = this.element.clientWidth || this.container.clientWidth;
759    const minQueueItems = Math.max(this.baseTotalItems, this.options.minQueueItems);
760
761    if (!itemStep || !containerWidth) {
762      return minQueueItems;
763    }
764
765    return Math.max(minQueueItems, Math.ceil(containerWidth / itemStep) + 2);
766  }
767
768  getItemStep() {
769    const firstItem = this.container.querySelector(".px_carousel_item") || this.element.querySelector(".px_carousel_item");
770    const width = firstItem?.getBoundingClientRect().width || this.getFallbackItemWidth();
771    return width + this.getGapSize();
772  }
773
774  getFallbackItemWidth() {
775    if (window.matchMedia("(max-width: 480px)").matches) return 140;
776    if (window.matchMedia("(max-width: 768px)").matches) return 160;
777    return 260;
778  }
779
780  getGapSize() {
781    const styles = window.getComputedStyle(this.container);
782    return parseFloat(styles.columnGap || styles.gap || 0) || 0;
783  }
784
785  updateSlidePosition(animate = true, position = 0) {
786    this.container.style.transition = animate ? "" : "none";
787    this.container.style.transform = `translateX(${position}px)`;
788
789    if (!animate) {
790      this.container.offsetHeight;
791      this.container.style.transition = "";
792    }
793  }
794
795  animateTo(position, callback = null) {
796    this.isAnimating = true;
797    this.transitionCallback = callback;
798    this.updateSlidePosition(true, position);
799    this.queueTransitionFallback();
800  }
801
802  processPendingSteps() {
803    if (this.pendingSteps > 0) {
804      this.pendingSteps--;
805      this.next();
806    } else if (this.pendingSteps < 0) {
807      this.pendingSteps++;
808      this.prev();
809    }
810  }
811
812  queueTransitionFallback() {
813    this.clearTransitionFallback();
814    this.transitionFallbackTimer = window.setTimeout(() => {
815      this.handleTransitionEnd();
816    }, 450);
817  }
818
819  clearTransitionFallback() {
820    if (this.transitionFallbackTimer) {
821      window.clearTimeout(this.transitionFallbackTimer);
822      this.transitionFallbackTimer = null;
823    }
824  }
825}
826
827document.addEventListener("DOMContentLoaded", function () {
828  const carousels = document.querySelectorAll(".px_custom_carousel");
829  carousels.forEach((carousel) => {
830    new PxCustomCarousel(carousel);
831  });
832});
833
834// Export manual
835window.PxCustomCarousel = PxCustomCarousel;
836window.PxGallery = PxGallery;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.