1(function() { 2 function initCarousels() { 3 document.querySelectorAll('.emtc-widget').forEach(function(widget) { 4 if (widget.dataset.emtcInitialized) return; 5 widget.dataset.emtcInitialized = 'true'; 6 7 let leftImages; 8 try { 9 leftImages = JSON.parse(widget.dataset.leftImages); 10 } catch(e) { return; } 11 12 const loop = widget.dataset.loop === 'true'; 13 const autoplay = widget.dataset.autoplay === 'true'; 14 const autoplaySpeed = parseInt(widget.dataset.autoplaySpeed, 10) || 5000; 15 const pauseOnHover = widget.dataset.pauseOnHover === 'true'; 16 const transitionEffect = widget.dataset.transitionEffect || 'slide'; 17 18 const track = widget.querySelector('.emtc-carousel-track'); 19 const slides = widget.querySelectorAll('.emtc-testimonial-slide'); 20 const mapImage = widget.querySelector('.emtc-map-image'); 21 const mapImageNext = widget.querySelector('.emtc-map-image-next'); 22 23 if (!track || !slides.length) return; 24 25 let currentIndex = 0; 26 let nextIndex = 0; 27 const totalSlides = slides.length; 28 let autoplayInterval = null; 29 let isTransitioning = false; 30 31 function setContainerHeight(slide) { 32 if (!slide) return; 33 if (transitionEffect === 'fade') { 34 track.style.minHeight = slide.offsetHeight + 'px'; 35 } 36 } 37 38 function updateFadeVisibility(activeIndex) { 39 slides.forEach((slide, idx) => { 40 if (idx === activeIndex) { 41 slide.style.opacity = '1'; 42 slide.style.pointerEvents = 'auto'; 43 slide.style.zIndex = '3'; 44 } else { 45 slide.style.opacity = '0'; 46 slide.style.pointerEvents = 'none'; 47 slide.style.zIndex = '1'; 48 } 49 }); 50 setContainerHeight(slides[activeIndex]); 51 } 52 53 // Setup initial state 54 if (transitionEffect === 'fade') { 55 track.style.position = 'relative'; 56 track.style.display = 'block'; 57 slides.forEach((slide, idx) => { 58 slide.style.position = 'absolute'; 59 slide.style.top = '0'; 60 slide.style.left = '0'; 61 slide.style.width = '100%'; 62 slide.style.transition = 'opacity 0.4s ease-in-out'; 63 slide.style.boxSizing = 'border-box'; 64 }); 65 updateFadeVisibility(currentIndex); 66 if (mapImage && leftImages && leftImages[currentIndex]) { 67 mapImage.src = leftImages[currentIndex]; 68 } 69 window.addEventListener('resize', function() { 70 if (slides[currentIndex]) { 71 track.style.minHeight = slides[currentIndex].offsetHeight + 'px'; 72 } 73 }); 74 const resizeObserver = new ResizeObserver(function() { 75 if (slides[currentIndex]) { 76 track.style.minHeight = slides[currentIndex].offsetHeight + 'px'; 77 } 78 });
79 slides.forEach(slide => resizeObserver.observe(slide)); 80 } else { 81 track.style.display = 'flex'; 82 slides.forEach(slide => { 83 slide.style.position = ''; 84 slide.style.opacity = ''; 85 slide.style.zIndex = ''; 86 }); 87 const translateX = -currentIndex * 100; 88 track.style.transform = 'translateX(' + translateX + '%)'; 89 if (mapImage && leftImages && leftImages[currentIndex]) { 90 mapImage.src = leftImages[currentIndex]; 91 } 92 } 93 94 // Helper to fade map image 95 function updateMapImage(newSrc) { 96 if (!mapImage || !mapImageNext) return; 97 // Preload next image 98 mapImageNext.src = newSrc; 99 mapImageNext.style.display = 'block'; 100 // Fade out current, fade in next 101 mapImage.classList.add('fading'); 102 mapImageNext.classList.add('active'); 103 setTimeout(function() { 104 mapImage.src = newSrc; 105 mapImage.classList.remove('fading'); 106 mapImageNext.classList.remove('active'); 107 mapImageNext.style.display = 'none'; 108 }, 400); 109 } 110 111 function updateArrows() { 112 const prevBtns = widget.querySelectorAll('.emtc-prev-arrow'); 113 const nextBtns = widget.querySelectorAll('.emtc-next-arrow'); 114 115 if (!loop) { 116 prevBtns.forEach(btn => btn.disabled = (currentIndex === 0)); 117 nextBtns.forEach(btn => btn.disabled = (currentIndex === totalSlides - 1)); 118 } else { 119 prevBtns.forEach(btn => btn.disabled = false); 120 nextBtns.forEach(btn => btn.disabled = false); 121 } 122 } 123 124 function goToIndex(newIndex) { 125 if (isTransitioning) return; 126 if (newIndex === currentIndex) return; 127 if (!loop && (newIndex < 0 || newIndex >= totalSlides)) return; 128 129 isTransitioning = true; 130 nextIndex = newIndex; 131 132 // Update map with cross-fade 133 if (leftImages && leftImages[nextIndex]) { 134 updateMapImage(leftImages[nextIndex]); 135 } 136 137 if (transitionEffect === 'fade') { 138 slides[currentIndex].style.opacity = '0'; 139 slides[currentIndex].style.pointerEvents = 'none'; 140 setTimeout(function() { 141 currentIndex = nextIndex; 142 slides[currentIndex].style.opacity = '1'; 143 slides[currentIndex].style.pointerEvents = 'auto'; 144 setContainerHeight(slides[currentIndex]); 145 updateArrows(); 146 isTransitioning = false; 147 }, 400); 148 } else { 149 currentIndex = newIndex; 150 const translateX = -currentIndex * 100; 151 track.style.transform = 'translateX(' + translateX + '%)'; 152 setTimeout(function() { 153 updateArrows(); 154 isTransitioning = false; 155 }, 400); 156 } 157 resetAutoplay(); 158 } 159 160 function goNext() { 161 let newIndex = currentIndex + 1; 162 if (loop) newIndex = newIndex % totalSlides; 163 goToIndex(newIndex); 164 } 165 166 function goPrev() { 167 let newIndex = currentIndex - 1; 168 if (loop) newIndex = (newIndex + totalSlides) % totalSlides; 169 goToIndex(newIndex); 170 } 171 172 function startAutoplay() { 173 if (!autoplay) return; 174 if (autoplayInterval) clearInterval(autoplayInterval); 175 autoplayInterval = setInterval(goNext, autoplaySpeed); 176 } 177 178 function stopAutoplay() { 179 if (autoplayInterval) { 180 clearInterval(autoplayInterval); 181 autoplayInterval = null; 182 } 183 } 184 185 function resetAutoplay() { 186 if (!autoplay) return; 187 stopAutoplay(); 188 startAutoplay(); 189 } 190 191 if (autoplay && pauseOnHover) { 192 widget.addEventListener('mouseenter', stopAutoplay); 193 widget.addEventListener('mouseleave', startAutoplay); 194 } 195 196 // Event Delegation for dynamic/multiple arrow sets 197 widget.addEventListener('click', function(e) { 198 const prevBtnClicked = e.target.closest('.emtc-prev-arrow'); 199 const nextBtnClicked = e.target.closest('.emtc-next-arrow'); 200 201 if (prevBtnClicked && !prevBtnClicked.disabled) { 202 e.preventDefault(); 203 goPrev(); 204 } 205 206 if (nextBtnClicked && !nextBtnClicked.disabled) { 207 e.preventDefault(); 208 goNext(); 209 } 210 }); 211 212 // Initialize first map image (if any) 213 if (mapImage && leftImages && leftImages[0]) { 214 mapImage.src = leftImages[0]; 215 } 216 217 updateArrows(); 218 if (autoplay) startAutoplay(); 219 }); 220 } 221 222 if (document.readyState === 'loading') { 223 document.addEventListener('DOMContentLoaded', initCarousels); 224 } else { 225 initCarousels(); 226 } 227 228 if (typeof window.elementorFrontend !== 'undefined' && window.elementorFrontend.hooks) { 229 window.elementorFrontend.hooks.addAction('frontend/element_ready/map_testimonial_carousel.default', initCarousels); 230 } 231})();
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.