Dash-math_Database / session_state.js
Mochi0622's picture
initial deploy
e66cfb4
Raw
History Blame Contribute Delete
7.39 kB
/**
* 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();
}
})();