/** * session_state.js * 放在 assets/ 目錄,Dash 自動載入。 */ (function () { "use strict"; var STORE_KEY = "dash_math_page_state"; function loadState() { try { return JSON.parse(sessionStorage.getItem(STORE_KEY) || "{}"); } catch (e) { return {}; } } function saveState(state) { try { sessionStorage.setItem(STORE_KEY, JSON.stringify(state)); } catch (e) {} } function getPathname() { return window.location.pathname || "/"; } function getPageState() { var s = loadState(); var p = getPathname(); if (!s[p]) s[p] = {}; return { state: s, page: p, ps: s[p] }; } function save(key, value) { var r = getPageState(); r.ps[key] = value; r.state[r.page] = r.ps; saveState(r.state); } // ── 從 id 字串取得 key ──────────────────────────────────────────────── function parseId(idStr) { try { return JSON.parse(idStr); } catch (e) { return null; } } // ── 快照目前頁面所有狀態 ────────────────────────────────────────────── function snapshotState() { var r = getPageState(); var ps = r.ps; // 1. 填空題輸入值 if (!ps.quiz_inputs) ps.quiz_inputs = {}; document.querySelectorAll("[id]").forEach(function (el) { var parsed = parseId(el.id); if (!parsed || parsed.type !== "quiz-input") return; var inp = el.querySelector("input"); if (inp) ps.quiz_inputs[parsed.qid] = inp.value || ""; }); // 2. 選擇題已選中的按鈕(找 data-active 或 aria-pressed 的按鈕) if (!ps.quiz_choice) ps.quiz_choice = {}; document.querySelectorAll("[id]").forEach(function (el) { var parsed = parseId(el.id); if (!parsed || parsed.type !== "quiz-feedback-choice") return; // feedback 有內容代表已作答 if (el.textContent && el.textContent.trim()) { ps.quiz_choice[parsed.qid] = { children: el.textContent.trim(), color: el.getAttribute("data-color") || "" }; } }); // 3. 解法展開狀態 if (!ps.solutions) ps.solutions = {}; document.querySelectorAll("button[id]").forEach(function (btn) { var parsed = parseId(btn.id); if (!parsed || parsed.type !== "solution-toggle") return; var key = parsed.key; // 找對應的 collapse 元素,判斷是否展開 var collapseEl = document.querySelector( '[id=\'{"key":"' + key + '","type":"solution-collapse"}\'],' + '[id=\'{"type":"solution-collapse","key":"' + key + '"}\']' ); if (collapseEl) { ps.solutions[key] = collapseEl.offsetHeight > 10; } }); r.state[r.page] = ps; saveState(r.state); } // ── 恢復狀態 ────────────────────────────────────────────────────────── var _restoring = false; function restoreState() { var r = getPageState(); var ps = r.ps; _restoring = true; // 1. 恢復 section if (ps.section) { var secBtn = document.querySelector('[data-sid="' + ps.section + '"]'); if (secBtn) secBtn.click(); } // 2. 恢復填空題輸入值 var inputs = ps.quiz_inputs || {}; Object.keys(inputs).forEach(function (qid) { var val = inputs[qid]; if (!val) return; var el = document.querySelector('[id=\'{"qid":"' + qid + '","type":"quiz-input"}\'],' + '[id=\'{"type":"quiz-input","qid":"' + qid + '"}\']'); if (!el) return; var inp = el.querySelector("input"); if (!inp) return; var setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; setter.call(inp, val); inp.dispatchEvent(new Event("input", { bubbles: true })); }); // 3. 恢復解法展開(點擊 toggle 按鈕) var solutions = ps.solutions || {}; Object.keys(solutions).forEach(function (key) { if (!solutions[key]) return; var btn = document.querySelector( '[id=\'{"key":"' + key + '","type":"solution-toggle"}\'],' + '[id=\'{"type":"solution-toggle","key":"' + key + '"}\']' ); if (btn) btn.click(); }); setTimeout(function () { _restoring = false; }, 600); } // ── 監聽 section 按鈕點擊 ──────────────────────────────────────────── var _secListened = new Set(); function attachSectionListeners() { document.querySelectorAll("[data-sid]").forEach(function (btn) { var sid = btn.getAttribute("data-sid"); if (_secListened.has(sid)) return; _secListened.add(sid); btn.addEventListener("click", function () { if (_restoring) return; save("section", sid); }); }); } // ── 定時快照(每 2 秒存一次目前狀態)──────────────────────────────── function startAutoSnapshot() { setInterval(function () { if (!_restoring) snapshotState(); }, 2000); } // ── 監聽 URL 變化 ───────────────────────────────────────────────────── var _lastPathname = null; var _restoreTimer = null; function checkPathname() { var cur = getPathname(); if (cur !== _lastPathname) { _lastPathname = cur; _secListened.clear(); clearTimeout(_restoreTimer); _restoreTimer = setTimeout(function () { attachSectionListeners(); restoreState(); }, 1000); } else { attachSectionListeners(); } } // ── 初始化 ──────────────────────────────────────────────────────────── var _mutTimer = null; var observer = new MutationObserver(function () { clearTimeout(_mutTimer); _mutTimer = setTimeout(checkPathname, 150); }); function init() { _lastPathname = getPathname(); observer.observe(document.body, { childList: true, subtree: true }); startAutoSnapshot(); setTimeout(function () { attachSectionListeners(); restoreState(); }, 1200); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();