(() => { 'use strict'; const galleries = Array.from(document.querySelectorAll('[data-v311-gallery]')); if (!galleries.length) return; const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)'); const controllers = []; const isEnglish = () => ( document.documentElement.dataset.lang === 'en' || document.documentElement.lang.toLowerCase().startsWith('en') ); galleries.forEach((gallery) => { const slides = Array.from(gallery.querySelectorAll('[data-v311-slide]')); const captions = Array.from(gallery.querySelectorAll('[data-v311-caption]')); const interval = Math.max(5000, Number(gallery.dataset.v311Interval) || 6800); if (slides.length < 2) return; let currentIndex = 0; let timeoutId = 0; let isVisible = false; let destroyed = false; gallery.dataset.v452Autoplay = 'ready'; gallery.setAttribute('aria-live', 'off'); const validIndexes = () => slides .map((slide, index) => ({ slide, index })) .filter(({ slide }) => slide.dataset.v311Failed !== 'true') .map(({ index }) => index); const clearTimer = () => { if (!timeoutId) return; window.clearTimeout(timeoutId); timeoutId = 0; }; const shouldRun = () => ( !destroyed && isVisible && document.visibilityState === 'visible' && !reduceMotion.matches && validIndexes().length > 1 ); const setSlide = (nextIndex) => { const indexes = validIndexes(); if (!indexes.length) return; currentIndex = indexes.includes(nextIndex) ? nextIndex : indexes[0]; slides.forEach((slide, index) => { const active = index === currentIndex; slide.classList.toggle('is-active', active); slide.setAttribute('aria-hidden', active ? 'false' : 'true'); }); }; const nextIndex = () => { const indexes = validIndexes(); const position = Math.max(0, indexes.indexOf(currentIndex)); return indexes[(position + 1) % indexes.length]; }; const schedule = () => { clearTimer(); if (!shouldRun()) return; timeoutId = window.setTimeout(() => { setSlide(nextIndex()); schedule(); }, interval); }; const updateLanguage = () => { const english = isEnglish(); slides.forEach((slide, index) => { const caption = captions[index]; const label = caption ? caption.dataset[english ? 'navEn' : 'navCn'] || caption.textContent.trim() : ''; slide.setAttribute( 'aria-label', english ? `Slide ${index + 1} of ${slides.length}: ${label}` : `第 ${index + 1} 张,共 ${slides.length} 张:${label}` ); }); }; slides.forEach((slide, index) => { const image = slide.querySelector('img'); if (!image) return; image.addEventListener('error', () => { slide.dataset.v311Failed = 'true'; slide.hidden = true; if (index === currentIndex) setSlide(validIndexes()[0]); schedule(); }, { once: true }); }); const observer = 'IntersectionObserver' in window ? new IntersectionObserver((entries) => { isVisible = entries.some((entry) => entry.isIntersecting && entry.intersectionRatio >= .3); if (isVisible) schedule(); else clearTimer(); }, { threshold: [0, .3, .65] }) : null; if (observer) observer.observe(gallery); else isVisible = true; const onVisibilityChange = () => { if (document.visibilityState === 'visible') schedule(); else clearTimer(); }; const onMotionPreferenceChange = () => { if (reduceMotion.matches) clearTimer(); else schedule(); }; const onLanguageChange = () => window.requestAnimationFrame(updateLanguage); const languageObserver = new MutationObserver(onLanguageChange); languageObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['lang', 'data-lang'] }); document.addEventListener('visibilitychange', onVisibilityChange); document.addEventListener('dbc:languagechange', onLanguageChange); reduceMotion.addEventListener?.('change', onMotionPreferenceChange); setSlide(0); updateLanguage(); schedule(); controllers.push({ destroy() { destroyed = true; clearTimer(); observer?.disconnect(); languageObserver.disconnect(); document.removeEventListener('visibilitychange', onVisibilityChange); document.removeEventListener('dbc:languagechange', onLanguageChange); reduceMotion.removeEventListener?.('change', onMotionPreferenceChange); } }); }); window.addEventListener('pagehide', () => { controllers.forEach((controller) => controller.destroy()); }, { once: true }); })();