PageSourceSearch

https://learningbird.com/wp-content/plugins/elementor-map-testimonial-carousel/assets/emtc-script.js?ver=1.1.1

js learningbird.com collected 2026-09-24 10:40:35 UTC 9,379 bytes, 231 lines download raw bytes

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.