PageSourceSearch

https://www.broadwayfurniture.net/js/rodeo/main.js

js broadwayfurniture.net collected 2026-09-26 14:19:52 UTC 11,392 bytes, 310 lines download raw bytes

1/**
2*
3* Rodeo Utils
4*
5**/
6
7document.addEventListener("DOMContentLoaded", () => {
8    "use strict";
9
10    // Initializes slideshow for specific block
11    function initializeSlideshow(slideshowBlock) {
12        let columns = slideshowBlock.querySelectorAll(":scope > div:not(.arrow)");
13
14        if (columns.length === 0) {
15            columns = slideshowBlock.querySelectorAll(":scope > a:not(.arrow)");
16        }
17
18        const prevButton = slideshowBlock.querySelector(".arrow-prev");
19        const nextButton = slideshowBlock.querySelector(".arrow-next");
20
21        let currentIndex = 0;
22        let interval = null; // Stores autoplay interval
23
24        // Add __active class to first column
25        if (columns.length > 0) {
26            columns[0].classList.add("__active");
27        }
28
29        function updateSlideshow() {
30            columns.forEach((col, index) => {
31                if (index === currentIndex) {
32                    col.classList.add("__active", "__fade");
33                    col.addEventListener("animationend", () => {
34                        col.classList.remove("__fade");
35                    }, { once: true });
36                } else {
37                    col.classList.remove("__active");
38                }
39            });
40        }
41
42        function nextColumn() {
43            currentIndex = (currentIndex + 1) % columns.length;
44            updateSlideshow();
45        }
46
47        function prevColumn() {
48            currentIndex = (currentIndex - 1 + columns.length) % columns.length;
49            updateSlideshow();
50        }
51
52        function startAutoplay() {
53            if (slideshowBlock.classList.contains("rodeo-autoplay")) {
54                interval = setInterval(() => {
55                    nextColumn();
56                }, 6000); // Autoplay every 6 seconds
57            }
58        }
59
60        function stopAutoplay() {
61            clearInterval(interval);
62            interval = null;
63        }
64
65        // Event listeners for navigation via arrows
66        if (prevButton) prevButton.addEventListener("click", () => {
67            stopAutoplay();
68            prevColumn();
69        });
70
71        if (nextButton) nextButton.addEventListener("click", () => {
72            stopAutoplay();
73            nextColumn();
74        });
75
76        // Event listener to stop autoplay on user interaction with column
77        columns.forEach((col) => {
78            col.addEventListener("click", stopAutoplay);
79        });
80
81        // Start autoplay if class is present
82        startAutoplay();
83    }
84
85    // Find all slideshow blocks and initialize them
86    const slideshowBlocks = document.querySelectorAll(".rodeo-slideshow");
87    slideshowBlocks.forEach(initializeSlideshow);
88
89    // Accordion
90    document.querySelectorAll('.rodeo-accordion').forEach(accordion => {
91        const allowMultiple = accordion.dataset.multiple === "true";
92        const defaultState = accordion.dataset.state;
93
94        const items = accordion.querySelectorAll('.rodeo-accordion-item');
95
96        // Function to apply content's styles to the header
97        const applyContentStyleToHeader = (headerEl, contentEl) => {
98            const radius = contentEl.style.borderRadius || ''; // fallback if not set inline
99            const width = contentEl.style.borderBottomWidth || '';
100            const color = contentEl.style.borderBottomColor || '';
101
102            if (radius) {
103                headerEl.style.borderRadius = radius;
104            }
105
106            if (width && color) {
107                headerEl.style.borderBottom = `${width} solid ${color}`;
108            }
109        };
110
111        // Function to restore the header's default styling
112        const restoreHeaderDefaults = (headerEl) => {
113            headerEl.style.borderRadius = headerEl.dataset.defaultRadius;
114            headerEl.style.borderBottom = headerEl.dataset.defaultBorderBottom;
115        };
116
117        items.forEach((item, index) => {
118            const header = item.querySelector('.rodeo-accordion-header');
119            const content = item.querySelector('.rodeo-accordion-content');
120            const contentInner = item.querySelector('.rodeo-accordion-content-inner');
121            const isLast = index === items.length - 1;
122
123            if (isLast) {
124                // Save current header border styles
125                header.dataset.defaultRadius = header.style.borderRadius || '';
126                header.dataset.defaultBorderBottom = header.style.borderBottom || '';
127
128                applyContentStyleToHeader(header, contentInner);
129            }
130
131            // Open the first item by default if specified
132            if (defaultState === 'FirstExpanded' && index === 0) {
133                header.classList.add('active');
134                content.style.maxHeight = content.scrollHeight + 'px';
135            }
136
137            header.addEventListener('click', () => {
138                const isOpen = header.classList.contains('active');
139
140                if (!allowMultiple) {
141                    items.forEach((i, idx) => {
142                        const h = i.querySelector('.rodeo-accordion-header');
143                        const c = i.querySelector('.rodeo-accordion-content');
144                        const ci = i.querySelector('.rodeo-accordion-content-inner');
145
146                        h.classList.remove('active');
147                        c.style.maxHeight = null;
148
149                        if (idx == items.length - 1) { 
150                            applyContentStyleToHeader(h, ci);
151                        }
152                    });
153                }
154
155                if (!isOpen || allowMultiple) {
156                    header.classList.toggle('active');
157                    content.style.maxHeight = isOpen ? null : content.scrollHeight + 'px';
158
159                    if (isLast) {
160                        if (isOpen) {
161                            setTimeout(() => {
162                                applyContentStyleToHeader(header, contentInner); // closing
163                            }, 300); 
164                        } else {
165                            restoreHeaderDefaults(header); // opening
166                        }
167                    }
168                }
169            });
170        });
171    });
172
173    // Image Zoom on Click
174    const zoomableImages = document.querySelectorAll(".img-zoom-trigger");
175
176    if (zoomableImages.length > 0) {
177        let lastFocusedTrigger = null;
178        const overlay = document.createElement("div");
179        overlay.className = "rodeo-zoom-overlay";
180        document.body.appendChild(overlay);
181
182        let zoomedImg = null;
183        let spinner = null;
184
185        zoomableImages.forEach((img) => {
186            img.addEventListener("click", (e) => {
187                e.preventDefault();
188                e.stopPropagation();
189
190                lastFocusedTrigger = img;
191                const fullSrc = img.getAttribute("data-fullsrc");
192                const alt = img.getAttribute("alt") || "";
193
194                overlay.innerHTML = ""; // Clear old content
195
196                // Create and show spinner
197                spinner = document.createElement("div");
198                spinner.className = "rodeo-zoom-spinner";
199                spinner.setAttribute("role", "status");
200                spinner.setAttribute("aria-label", "Loading full-size image");
201                overlay.appendChild(spinner);
202
203                // Preload full image
204                zoomedImg = document.createElement("img");
205                zoomedImg.src = fullSrc;
206                zoomedImg.alt = alt;
207
208                zoomedImg.onload = () => {
209                    if (spinner && spinner.parentNode) {
210                        spinner.remove();
211                    }
212                    overlay.appendChild(zoomedImg);
213                    zoomedImg.classList.add("zoom-in");
214                };
215
216                zoomedImg.onerror = () => {
217                    if (spinner && spinner.parentNode) {
218                        spinner.remove();
219                    }
220                    const errorMsg = document.createElement("div");
221                    errorMsg.textContent = "Image failed to load.";
222                    errorMsg.style.color = "#fff";
223                    overlay.appendChild(errorMsg);
224                };
225
226                overlay.classList.add("active");
227            });
228        });
229
230        function closeOverlay() {
231            if (!zoomedImg) return;
232
233            // Animate out
234            zoomedImg.classList.remove("zoom-in");
235
236            zoomedImg.addEventListener("transitionend", function handleTransitionEnd(e) {
237                if (e.propertyName !== "transform") return;
238
239                zoomedImg.removeEventListener("transitionend", handleTransitionEnd);
240                overlay.classList.remove("active");
241
242                setTimeout(() => {
243                    overlay.innerHTML = "";
244                    zoomedImg = null;
245                    if (lastFocusedTrigger) lastFocusedTrigger.focus();
246                }, 300);
247            });
248        }
249
250        overlay.addEventListener("click", closeOverlay);
251
252        document.addEventListener("keydown", (e) => {
253            // Handle Enter or Space on zoom-trigger
254            if ((e.key === "Enter" || e.key === " ") && e.target.classList.contains("img-zoom-trigger")) {
255                e.preventDefault();
256                e.target.click();
257                return;
258            }
259
260            // Always allow Escape to close the overlay
261            if (e.key === "Escape") {
262                closeOverlay();
263            }
264        });
265    }
266
267    (() => {
268        // Check if IntersectionObserver is supported
269        if (!('IntersectionObserver' in window)) {
270            console.warn('IntersectionObserver is not supported in this browser');
271            document.querySelectorAll('.animate-on-scroll').forEach(el => el.classList.add('visible'));
272            return;
273        }
274
275        // Create the observer instance with rootMargin option
276        const observer = new IntersectionObserver((entries) => {
277            entries.forEach(entry => {
278                if (entry.isIntersecting) {
279                    entry.target.classList.add('visible');
280                    observer.unobserve(entry.target);
281                }
282            });
283        }, {
284            rootMargin: '0px 0px -100px 0px' // This creates a 100px margin from the bottom
285        });
286
287        // Function to start observing elements
288        const observeElements = () => {
289            const elements = document.querySelectorAll('.animate-on-scroll');
290            elements.forEach(element => {
291                observer.observe(element);
292            });
293        };
294
295        // Initialize when DOM is ready
296        if (document.readyState === 'loading') {
297            document.addEventListener('DOMContentLoaded', observeElements);
298        } else {
299            observeElements();
300        }
301
302        // Cleanup function (useful for SPAs or when script needs to be destroyed)
303        const cleanup = () => {
304            observer.disconnect();
305        };
306
307        // Optional: Expose cleanup method to window (if needed)
308        window.cleanupScrollAnimations = cleanup;
309    })();
310});

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.