(function () { const data = window.CASE_DATA; const timeline = document.querySelector("#timeline"); const filters = document.querySelector("#filters"); let activeFilter = "all"; let timelineView = "compact"; let editing = false; const editStoreKey = "saigon-leverage-edits-v1"; const themeStoreKey = "saigon-leverage-theme"; document.querySelector("#episodeLabel").textContent = `CASE TIMELINE · ${data.episode}`; document.querySelector("#heroEyebrow").textContent = data.eyebrow; document.querySelector("#caseTitle").innerHTML = `${data.title.split(":")[0]}:
${data.title.split(":").slice(1).join(":")}`; document.querySelector("#heroDeck").textContent = data.deck; document.querySelector("#heroDeck").dataset.edit = "hero-deck"; document.querySelector("#heroMetrics").innerHTML = data.metrics.map((metric) => `
${metric.value}${metric.label}
`).join(""); document.querySelector("#participantsGrid").innerHTML = data.participants.map((party) => `

${party.name}

${party.role}

${party.stance}

${party.asset}
`).join(""); function applyTimelineView() { timeline.classList.toggle("compact", timelineView === "compact"); document.querySelectorAll("[data-view]").forEach((button) => { const active = button.dataset.view === timelineView; button.classList.toggle("active", active); button.setAttribute("aria-pressed", String(active)); }); } function renderFilters() { filters.innerHTML = data.categories.map((item) => ` `).join(""); } function renderTimeline() { const visible = activeFilter === "all" ? data.events : data.events.filter((event) => event.category === activeFilter); timeline.innerHTML = visible.map((event, position) => `
${event.date}${event.index}
`).join(""); bindCards(); applyTimelineView(); restoreEdits(); if (editing) setEditing(true); } function categoryLabel(id) { return data.categories.find((item) => item.id === id)?.label || id; } function bindCards() { document.querySelectorAll(".event-card").forEach((card) => { card.addEventListener("click", () => { if (editing) return; const expanded = card.getAttribute("aria-expanded") === "true"; card.setAttribute("aria-expanded", String(!expanded)); card.closest(".event").classList.toggle("open", !expanded); }); }); } filters.addEventListener("click", (event) => { const button = event.target.closest("[data-filter]"); if (!button) return; activeFilter = button.dataset.filter; renderFilters(); renderTimeline(); }); document.querySelector(".view-switch").addEventListener("click", (event) => { const button = event.target.closest("[data-view]"); if (!button) return; timelineView = button.dataset.view; applyTimelineView(); }); document.querySelector("#chapterNav").innerHTML = data.chapters.map((chapter) => { const target = data.events.find((event) => event.chapter === chapter.id)?.id; return `${chapter.label.slice(0, 2)}${chapter.label.slice(3)}`; }).join(""); document.querySelector("#modelGrid").innerHTML = data.model.map((item) => `
${item.num}

${item.title}

${item.subtitle}

${item.body}

`).join(""); function storedEdits() { try { return JSON.parse(localStorage.getItem(editStoreKey)) || {}; } catch (_) { return {}; } } function restoreEdits() { const saved = storedEdits(); document.querySelectorAll("[data-edit]").forEach((element) => { if (saved[element.dataset.edit] !== undefined) element.innerHTML = saved[element.dataset.edit]; }); } function saveEdit(element) { const saved = storedEdits(); saved[element.dataset.edit] = element.innerHTML; localStorage.setItem(editStoreKey, JSON.stringify(saved)); } function setEditing(next) { editing = next; document.body.classList.toggle("is-editing", editing); const editButton = document.querySelector("#editToggle"); editButton.querySelector(".icon-glyph").textContent = editing ? "✓" : "✎"; editButton.setAttribute("aria-label", editing ? "完成编辑" : "编辑页面内容"); editButton.title = editing ? "完成编辑" : "编辑页面内容"; document.querySelectorAll("[data-edit]").forEach((element) => { element.contentEditable = editing ? "true" : "false"; element.spellcheck = editing; }); } document.addEventListener("input", (event) => { const editable = event.target.closest("[data-edit]"); if (editing && editable) saveEdit(editable); }); document.querySelector("#editToggle").addEventListener("click", () => setEditing(!editing)); document.querySelector("#resetEdits").addEventListener("click", () => { if (!confirm("确定恢复全部原稿吗?当前编辑内容将被清除。")) return; localStorage.removeItem(editStoreKey); location.reload(); }); function setTheme(theme) { document.documentElement.dataset.theme = theme; const themeButton = document.querySelector("#themeToggle"); const nextTheme = theme === "dark" ? "浅色" : "深色"; themeButton.querySelector(".icon-glyph").textContent = theme === "dark" ? "☼" : "☾"; themeButton.setAttribute("aria-label", `切换至${nextTheme}模式`); themeButton.title = `切换至${nextTheme}模式`; document.querySelectorAll("img[data-dark-src]").forEach((image) => { image.src = theme === "dark" ? image.dataset.darkSrc : image.dataset.lightSrc; }); localStorage.setItem(themeStoreKey, theme); } document.querySelector("#themeToggle").addEventListener("click", () => { setTheme(document.documentElement.dataset.theme === "dark" ? "light" : "dark"); }); document.querySelector("#copyLink").addEventListener("click", async (event) => { const button = event.currentTarget; try { await navigator.clipboard.writeText(location.href); button.classList.add("is-copied"); button.querySelector(".icon-glyph").textContent = "✓"; button.setAttribute("aria-label", "链接已复制"); button.title = "链接已复制"; setTimeout(() => { button.classList.remove("is-copied"); button.querySelector(".icon-glyph").textContent = "⤴"; button.setAttribute("aria-label", "复制当前页面链接"); button.title = "复制当前页面链接"; }, 1600); } catch (_) { button.querySelector(".icon-glyph").textContent = "!"; button.setAttribute("aria-label", "复制失败"); button.title = "复制失败"; } }); function updateProgress() { const story = document.querySelector("#story"); const start = story.offsetTop; const distance = Math.max(story.offsetHeight - innerHeight, 1); const percent = Math.max(0, Math.min(100, ((scrollY - start) / distance) * 100)); document.querySelector("#progressBar").style.width = `${percent}%`; document.querySelector("#progressText").textContent = `${Math.round(percent)}%`; } addEventListener("scroll", updateProgress, { passive: true }); renderFilters(); renderTimeline(); restoreEdits(); setTheme(localStorage.getItem(themeStoreKey) || "dark"); updateProgress(); })();