(function () { 'use strict'; if (window.__BIOTEN_PACKAGING_FAMILY_V7140__) return; window.__BIOTEN_PACKAGING_FAMILY_V7140__ = true; const root = document.documentElement; const body = document.body; const languageKey = 'dbc-site-language'; const pageIsEnglishReady = root.dataset.enReady === 'true' || (body && body.dataset.enReady === 'true'); function getLanguage() { try { const query = new URLSearchParams(window.location.search).get('lang'); const preference = query === 'zh' || query === 'en' ? query : (root.dataset.languagePreference === 'en' || root.dataset.lang === 'en' || localStorage.getItem(languageKey) === 'en' ? 'en' : 'zh'); return pageIsEnglishReady ? preference : 'zh'; } catch (error) { return pageIsEnglishReady && root.dataset.lang === 'en' ? 'en' : 'zh'; } } function setText(element, language) { const htmlKey = language === 'en' ? 'htmlEn' : 'htmlZh'; const textKey = language === 'en' ? 'en' : 'zh'; if (element.dataset[htmlKey] !== undefined) { element.innerHTML = element.dataset[htmlKey]; } else if (element.dataset[textKey] !== undefined) { element.textContent = element.dataset[textKey]; } } function applyLanguage(language) { const next = language === 'en' ? 'en' : 'zh'; // Do not rewrite the observed attribute when the value is unchanged. // MutationObserver fires for attribute writes, so an unconditional write // here can create a self-sustaining microtask loop and freeze the page. if (root.dataset.lang !== next) root.dataset.lang = next; root.lang = next === 'en' ? 'en' : 'zh-CN'; // A Chinese-authoritative page may render a Chinese fallback while the // visitor's global English preference remains intact for the next fully // bilingual route. Never persist the fallback as a new preference. if (pageIsEnglishReady) { try { localStorage.setItem(languageKey, next); } catch (error) { /* no-op */ } } document.querySelectorAll('[data-zh], [data-html-zh]').forEach(function (element) { setText(element, next); }); document.querySelectorAll('[data-alt-zh]').forEach(function (element) { element.alt = next === 'en' ? (element.dataset.altEn || '') : (element.dataset.altZh || ''); }); document.querySelectorAll('[data-label-zh]').forEach(function (element) { element.setAttribute('aria-label', next === 'en' ? (element.dataset.labelEn || '') : (element.dataset.labelZh || '')); }); if (body) { const title = next === 'en' ? body.dataset.pageTitleEn : body.dataset.pageTitleZh; const description = next === 'en' ? body.dataset.pageDescriptionEn : body.dataset.pageDescriptionZh; if (title) document.title = title; const descriptionTag = document.querySelector('meta[name="description"]'); if (descriptionTag && description) descriptionTag.content = description; } updateResultCount(next); } function updateResultCount(language) { const cards = Array.from(document.querySelectorAll('[data-bp7140-release-card]')).filter(function (card) { return !card.hidden; }); const target = document.querySelector('[data-bp7140-result-count]'); if (!target) return; target.textContent = language === 'en' ? cards.length + (cards.length === 1 ? ' release' : ' releases') : cards.length + ' 篇发布'; } function initFilters() { const filters = Array.from(document.querySelectorAll('[data-bp7140-filter]')); const cards = Array.from(document.querySelectorAll('[data-bp7140-release-card]')); if (!filters.length || !cards.length) return; filters.forEach(function (button) { button.addEventListener('click', function () { const value = button.dataset.bp7140Filter || 'all'; filters.forEach(function (item) { item.setAttribute('aria-pressed', String(item === button)); }); cards.forEach(function (card) { card.hidden = value !== 'all' && card.dataset.bp7140Category !== value; }); updateResultCount(getLanguage()); }); }); } function initReveal() { const targets = Array.from(document.querySelectorAll('[data-bp7140-reveal]')); if (!targets.length) return; // Progressive enhancement: content is visible by default in CSS and is // hidden for animation only after this initializer is known to be alive. root.classList.add('bp7140-motion-ready'); if (!('IntersectionObserver' in window) || window.matchMedia('(prefers-reduced-motion: reduce)').matches) { targets.forEach(function (target) { target.classList.add('is-visible'); }); return; } const observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (!entry.isIntersecting) return; entry.target.classList.add('is-visible'); observer.unobserve(entry.target); }); }, { rootMargin: '0px 0px -8% 0px', threshold: .08 }); targets.forEach(function (target) { observer.observe(target); }); // Final safety net: an observer implementation must never be able to leave // readable content transparent indefinitely. window.setTimeout(function () { targets.forEach(function (target) { target.classList.add('is-visible'); }); }, 2400); } function initToc() { const links = Array.from(document.querySelectorAll('.bp7140-toc a[href^="#"]')); if (!links.length || !('IntersectionObserver' in window)) return; const pairs = links.map(function (link) { return { link: link, section: document.querySelector(link.getAttribute('href')) }; }).filter(function (pair) { return Boolean(pair.section); }); if (!pairs.length) return; const observer = new IntersectionObserver(function (entries) { const visible = entries.filter(function (entry) { return entry.isIntersecting; }) .sort(function (a, b) { return a.boundingClientRect.top - b.boundingClientRect.top; }); if (!visible.length) return; const id = visible[0].target.id; pairs.forEach(function (pair) { if (pair.section.id === id) pair.link.setAttribute('aria-current', 'true'); else pair.link.removeAttribute('aria-current'); }); }, { rootMargin: '-22% 0px -62% 0px', threshold: 0 }); pairs.forEach(function (pair) { observer.observe(pair.section); }); } function initContextNavigation() { const navigation = document.querySelector('[data-bp7160-context-route]'); if (!navigation) return; const links = Array.from(navigation.querySelectorAll('a')); const overview = links.find(function (link) { return !link.hash; }); const sections = links.map(function (link) { if (!link.hash) return null; const section = document.querySelector(link.hash); return section ? { link: link, section: section } : null; }).filter(Boolean); if (!overview || !sections.length) return; let scheduled = false; function setCurrent(link) { links.forEach(function (item) { item.removeAttribute('aria-current'); }); link.setAttribute('aria-current', link === overview ? 'page' : 'location'); } function updateCurrent() { scheduled = false; const navigationRect = navigation.getBoundingClientRect(); if (navigationRect.top >= window.innerHeight) { setCurrent(overview); return; } const navigationBottom = Math.min(navigationRect.bottom, 132); const threshold = navigationBottom + Math.min(110, window.innerHeight * .14); let current = overview; sections.forEach(function (pair) { if (pair.section.getBoundingClientRect().top <= threshold) current = pair.link; }); setCurrent(current); } function scheduleUpdate() { if (scheduled) return; scheduled = true; window.requestAnimationFrame(updateCurrent); } links.forEach(function (link) { link.addEventListener('click', function () { window.setTimeout(scheduleUpdate, 80); }); }); window.addEventListener('scroll', scheduleUpdate, { passive: true }); window.addEventListener('resize', scheduleUpdate); window.addEventListener('hashchange', scheduleUpdate); scheduleUpdate(); } function initParticles() { const field = document.querySelector('[data-bp7140-material-field]'); if (!field || window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; const count = window.innerWidth < 680 ? 18 : 34; for (let index = 0; index < count; index += 1) { const particle = document.createElement('span'); particle.className = 'bp7140-particle'; particle.style.setProperty('--top', (9 + Math.random() * 72).toFixed(2) + '%'); particle.style.setProperty('--right', (4 + Math.random() * 42).toFixed(2) + '%'); particle.style.setProperty('--size', (2 + Math.random() * 6).toFixed(2) + 'px'); particle.style.setProperty('--duration', (4.8 + Math.random() * 5.4).toFixed(2) + 's'); particle.style.setProperty('--delay', (-Math.random() * 6).toFixed(2) + 's'); particle.style.setProperty('--dx', (-30 + Math.random() * 60).toFixed(2) + 'px'); particle.style.setProperty('--dy', (-48 + Math.random() * 96).toFixed(2) + 'px'); field.appendChild(particle); } const line = document.createElement('span'); line.className = 'bp7140-film-line'; field.appendChild(line); } function initIntakeContext() { if (!body.classList.contains('bp7140-intake-page')) return; const params = new URLSearchParams(window.location.search); const keys = ['domain', 'topic', 'route', 'source']; const values = {}; const labels = { 'packaging': { zh: '博碳包装项目', en: 'BIOTEN Packaging project' }, 'food-beverage': { zh: '食品与饮料包装', en: 'Food & beverage packaging' }, 'beauty-care': { zh: '美妆与个护包装', en: 'Beauty & personal care packaging' }, 'medical-outer': { zh: '医药与医疗外包装', en: 'Pharma & medical outer packaging' }, 'special-performance': { zh: '特种包装', en: 'Specialty packaging' }, 'pha-barrier-emulsion': { zh: '博碳阻隔乳液', en: 'BIOTEN barrier emulsion' }, 'waterborne-coated-paper': { zh: '博碳水涂纸', en: 'BIOTEN waterborne-coated paper' }, 'bioten-foodserviceware': { zh: '博碳餐饮具', en: 'BIOTEN foodserviceware' }, 'paper-cup-sample': { zh: '纸杯', en: 'Paper cups' }, 'paper-box-sample': { zh: '纸盒', en: 'Paper cartons' }, 'paper-bag-sample': { zh: '纸袋等制品', en: 'Paper bags and related articles' }, 'other-sample': { zh: '其他纸基包装样品', en: 'Other fiber-based packaging samples' }, 'packaging-home-sample': { zh: '首页样品申请', en: 'Homepage sample request' }, 'coating-converting-partnership': { zh: '涂布与加工成型合作', en: 'Coating and converting partnership' }, 'packaging-product-release': { zh: '新应用发布与合作申请', en: 'New application release and partnership application' } }; const requestedSampleLabels = { 'pha-barrier-emulsion': '博碳阻隔乳液 / BIOTEN barrier emulsion', 'waterborne-coated-paper': '博碳水涂纸 / BIOTEN waterborne-coated paper', 'bioten-foodserviceware': '博碳餐饮具 / BIOTEN foodserviceware', 'paper-cup-sample': '纸杯 / Paper cups', 'paper-box-sample': '纸盒 / Paper cartons', 'paper-bag-sample': '纸袋等制品 / Paper bags and related articles', 'other-sample': '其他纸基包装样品 / Other fiber-based packaging samples' }; keys.forEach(function (key) { values[key] = params.get(key) || ''; }); const sourceInput = document.querySelector('input[name="source"]'); if (sourceInput) sourceInput.value = values.source || 'packaging-direct'; keys.forEach(function (key) { let input = document.querySelector('input[name="' + key + '"]'); if (!input && values[key]) { input = document.createElement('input'); input.type = 'hidden'; input.name = key; const form = document.querySelector('form'); if (form) form.appendChild(input); } if (input && values[key]) input.value = values[key]; }); const context = document.querySelector('[data-bp7140-intake-context]'); if (context && (values.domain || values.topic || values.route || values.source)) { context.hidden = false; const rawValues = [values.domain, values.topic, values.route, values.source].filter(Boolean); const summaryZh = rawValues.map(function (value) { return labels[value] ? labels[value].zh : value; }).join(' · '); const summaryEn = rawValues.map(function (value) { return labels[value] ? labels[value].en : value; }).join(' · '); const output = context.querySelector('[data-bp7140-context-value]'); if (output) { output.dataset.zh = summaryZh; output.dataset.en = summaryEn; output.textContent = getLanguage() === 'en' ? summaryEn : summaryZh; } } if (values.source === 'packaging-home-sample') { const sampleNeed = document.querySelector('input[name="request_need"][value="样品/纸样测试"]'); if (sampleNeed) sampleNeed.checked = true; const sampleSelection = document.querySelector('input[name="sample_selection"]'); if (sampleSelection && requestedSampleLabels[values.topic] && !sampleSelection.value) { sampleSelection.value = requestedSampleLabels[values.topic]; } } if (values.topic === 'coating-converting-partnership' || values.source === 'packaging-product-release') { const partnershipNeed = document.querySelector('input[name="request_need"][value="商务合作/报价咨询"]'); if (partnershipNeed) partnershipNeed.checked = true; const application = document.querySelector('input[name="application"]'); if (application && !application.value) { application.value = '涂布、印刷、模切、制盒或加工成型合作 / Coating, printing, die cutting, carton making or converting partnership'; } const message = document.querySelector('textarea[name="message"]'); if (message && !message.value) { message.value = '希望围绕博碳新应用开展打样、加工服务或市场协同 / I would like to discuss prototyping, converting services or market collaboration for a BIOTEN application'; } } } document.addEventListener('dbc:languagechange', function (event) { applyLanguage(event.detail && event.detail.language === 'en' ? 'en' : 'zh'); }); const languageObserver = new MutationObserver(function () { applyLanguage(root.dataset.lang === 'en' ? 'en' : 'zh'); }); languageObserver.observe(root, { attributes: true, attributeFilter: ['data-lang'] }); initFilters(); initReveal(); initToc(); initContextNavigation(); initParticles(); initIntakeContext(); applyLanguage(getLanguage()); }());