(function () { 'use strict'; document.documentElement.classList.add('js'); var state = { language: 'zh', releases: [], filter: 'all' }; var languageToggle = document.querySelector('[data-language-toggle]'); var languageCurrent = document.querySelector('[data-language-current]'); var languageNext = document.querySelector('[data-language-next]'); var navToggle = document.querySelector('[data-nav-toggle]'); var primaryNav = document.querySelector('[data-primary-nav]'); var releaseGrid = document.querySelector('[data-release-grid]'); var releaseEmpty = document.querySelector('[data-release-empty]'); var releaseFilters = document.querySelector('[data-release-filters]'); function readSavedLanguage() { try { return localStorage.getItem('dbc-site-language') === 'en' ? 'en' : 'zh'; } catch (error) { return 'zh'; } } function saveLanguage(language) { try { localStorage.setItem('dbc-site-language', language); } catch (error) { return; } } function translated(entry, key) { var suffix = state.language === 'en' ? 'En' : 'Zh'; return entry[key + suffix] || entry[key + 'Zh'] || ''; } function applyLanguage(language) { state.language = language === 'en' ? 'en' : 'zh'; document.documentElement.lang = state.language === 'en' ? 'en' : 'zh-CN'; document.documentElement.dataset.lang = state.language; document.querySelectorAll('[data-zh][data-en]').forEach(function (node) { node.textContent = node.getAttribute(state.language === 'en' ? 'data-en' : 'data-zh'); }); document.querySelectorAll('[data-label-zh][data-label-en]').forEach(function (node) { node.setAttribute('aria-label', node.getAttribute(state.language === 'en' ? 'data-label-en' : 'data-label-zh')); }); document.querySelectorAll('[data-alt-zh][data-alt-en]').forEach(function (node) { node.setAttribute('alt', node.getAttribute(state.language === 'en' ? 'data-alt-en' : 'data-alt-zh')); }); if (languageCurrent && languageNext) { languageCurrent.textContent = state.language === 'en' ? 'EN' : '中'; languageNext.textContent = state.language === 'en' ? '中' : 'EN'; } if (languageToggle) { languageToggle.setAttribute('aria-label', state.language === 'en' ? '切换到中文' : 'Switch to English'); } document.title = state.language === 'en' ? 'BIOTEN PHA Waterborne Varnish | Paper Packaging Surface Engineering' : '博碳PHA水性光油|纸包装表面工程产品系统'; renderReleases(); saveLanguage(state.language); } function closePrimaryNav() { if (!primaryNav || !navToggle) return; primaryNav.classList.remove('is-open'); navToggle.setAttribute('aria-expanded', 'false'); document.body.classList.remove('nav-open'); } function togglePrimaryNav() { if (!primaryNav || !navToggle) return; var open = !primaryNav.classList.contains('is-open'); primaryNav.classList.toggle('is-open', open); navToggle.setAttribute('aria-expanded', String(open)); document.body.classList.toggle('nav-open', open); } function escapeText(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatDate(value) { if (!/^\d{4}-\d{2}-\d{2}$/.test(value || '')) return ''; if (state.language === 'en') { var date = new Date(value + 'T00:00:00'); return new Intl.DateTimeFormat('en', { year: 'numeric', month: 'short', day: '2-digit' }).format(date); } return value.replace(/-/g, '.'); } function releaseCard(entry) { var featured = entry.featured ? ' is-featured' : ''; var title = escapeText(translated(entry, 'title')); var summary = escapeText(translated(entry, 'summary')); var label = escapeText(translated(entry, 'label')); var action = state.language === 'en' ? 'Open record' : '查看记录'; var imageAlt = title; return [ '', ' ' + imageAlt + '', ' ', ' ' + label + '', '

' + title + '

', '

' + summary + '

', ' ' + action + '', '
', '
' ].join(''); } function renderReleases() { if (!releaseGrid || !state.releases.length) return; var entries = state.releases.filter(function (entry) { return state.filter === 'all' || entry.type === state.filter; }); releaseGrid.innerHTML = entries.map(releaseCard).join(''); releaseGrid.hidden = entries.length === 0; if (releaseEmpty) releaseEmpty.hidden = entries.length !== 0; } function fallbackReleases() { var source = document.getElementById('release-fallback'); if (!source) return []; try { var parsed = JSON.parse(source.textContent); return Array.isArray(parsed.items) ? parsed.items : []; } catch (error) { return []; } } function loadReleases() { var fallback = fallbackReleases(); state.releases = fallback; renderReleases(); if (window.location.protocol === 'file:') return Promise.resolve(fallback); return fetch('bioten-pha-waterborne-varnish/data/releases.json', { cache: 'no-store' }) .then(function (response) { if (!response.ok) throw new Error('release-data-unavailable'); return response.json(); }) .then(function (payload) { if (payload && Array.isArray(payload.items) && payload.items.length) { state.releases = payload.items; renderReleases(); } return state.releases; }) .catch(function () { return fallback; }); } function setupReleaseFilters() { if (!releaseFilters) return; releaseFilters.addEventListener('click', function (event) { var button = event.target.closest('button[data-filter]'); if (!button) return; state.filter = button.getAttribute('data-filter') || 'all'; releaseFilters.querySelectorAll('button').forEach(function (item) { item.classList.toggle('is-active', item === button); }); renderReleases(); }); } function setupReveal() { var items = Array.prototype.slice.call(document.querySelectorAll('[data-reveal]')); if (!items.length) return; if (!('IntersectionObserver' in window) || window.matchMedia('(prefers-reduced-motion: reduce)').matches) { items.forEach(function (item) { item.classList.add('is-visible'); }); return; } var observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; entry.target.classList.add('is-visible'); observer.unobserve(entry.target); }); }, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' }); items.forEach(function (item) { observer.observe(item); }); } function setupSectionNavigation() { var sections = Array.prototype.slice.call(document.querySelectorAll('[data-section]')); var links = Array.prototype.slice.call(document.querySelectorAll('[data-section-link]')); var manualNavigationUntil = 0; if (!sections.length || !links.length) return; function setActiveLink(id) { links.forEach(function (link) { link.classList.toggle('is-active', link.getAttribute('data-section-link') === id); }); } links.forEach(function (link) { link.addEventListener('click', function () { manualNavigationUntil = Date.now() + 900; setActiveLink(link.getAttribute('data-section-link')); }); }); if (!('IntersectionObserver' in window)) return; var sectionObserver = new IntersectionObserver(function (entries) { if (Date.now() < manualNavigationUntil) return; var visible = entries .filter(function (entry) { return entry.isIntersecting; }) .sort(function (a, b) { return b.intersectionRatio - a.intersectionRatio; })[0]; if (!visible) return; var id = visible.target.getAttribute('data-section'); setActiveLink(id); }, { threshold: [0.18, 0.35, 0.55], rootMargin: '-20% 0px -58% 0px' }); sections.forEach(function (section) { sectionObserver.observe(section); }); } if (languageToggle && !languageToggle.dataset.dbcNavigationLanguageBound) { languageToggle.addEventListener('click', function () { applyLanguage(state.language === 'zh' ? 'en' : 'zh'); }); } document.addEventListener('dbc:languagechange', function (event) { applyLanguage(event.detail && event.detail.language === 'en' ? 'en' : 'zh'); }); if (navToggle) navToggle.addEventListener('click', togglePrimaryNav); if (primaryNav) { primaryNav.addEventListener('click', function (event) { if (event.target.closest('a')) closePrimaryNav(); }); } window.addEventListener('resize', function () { if (window.innerWidth > 900) closePrimaryNav(); }); setupReleaseFilters(); setupReveal(); setupSectionNavigation(); applyLanguage(readSavedLanguage()); loadReleases(); })();