/** * AKSRT Article Tools — password toggles, search clear, article actions, interaction bar */ (function () { "use strict"; function initPasswordToggles(root) { (root || document).querySelectorAll(".toggle-pwd").forEach(function (btn) { if (btn.dataset.pwdToggleBound) return; btn.dataset.pwdToggleBound = "1"; btn.addEventListener("click", function () { var id = btn.getAttribute("data-target"); var input = id ? document.getElementById(id) : null; if (!input) return; var show = input.getAttribute("type") === "password"; input.setAttribute("type", show ? "text" : "password"); btn.setAttribute("aria-label", show ? "隐藏密码" : "显示密码"); }); }); } function initSearchClear() { var btn = document.getElementById("search-clear-btn"); var input = document.getElementById("search-page-input"); if (btn && input && !btn.dataset.aksrtSearchClearBound) { btn.dataset.aksrtSearchClearBound = "1"; var syncClearButton = function () { var hasValue = input.value.trim().length > 0; btn.classList.toggle("is-visible", hasValue); btn.hidden = !hasValue; }; btn.addEventListener("click", function () { input.value = ""; input.focus(); syncClearButton(); }); input.addEventListener("input", syncClearButton); syncClearButton(); } var categorySelect = document.getElementById("search-category-filter"); if (categorySelect) { categorySelect.addEventListener("change", function () { var url = new URL(window.location.href); if (this.value) { url.searchParams.set("aksrt_category", this.value); } else { url.searchParams.delete("aksrt_category"); } if (url.searchParams.get("aksrt_search_type") && url.searchParams.get("aksrt_search_type") !== "post") { url.searchParams.delete("aksrt_category"); } url.searchParams.delete("paged"); window.location.href = url.toString(); }); } } function initArticleTools() { var buttons = document.querySelectorAll("[data-article-tool]"); if (!buttons.length) return; buttons.forEach(function (button) { if (button.dataset.aksrtArticleToolBound) return; button.dataset.aksrtArticleToolBound = "1"; button.addEventListener("click", function () { var tool = button.getAttribute("data-article-tool"); if (tool === "print") { window.print(); return; } if (tool === "pdf") { window.print(); notyf("请在打印对话框中选择“另存为 PDF”", "info"); } }); }); } var postMetaDocumentEventsBound = false; function setPostMetaMenuState(menuRoot, open) { var toggle = menuRoot.querySelector(".post-drop-meta__toggle"); var menu = menuRoot.querySelector(".post-drop-meta__menu"); if (!toggle || !menu) return; menuRoot.classList.toggle("is-open", open); toggle.setAttribute("aria-expanded", open ? "true" : "false"); menu.setAttribute("aria-hidden", open ? "false" : "true"); } function closePostMetaMenus(except, restoreFocus) { document.querySelectorAll(".post-drop-meta.is-open").forEach(function (menuRoot) { if (menuRoot === except) return; var toggle = menuRoot.querySelector(".post-drop-meta__toggle"); setPostMetaMenuState(menuRoot, false); if (restoreFocus && toggle) toggle.focus(); }); } function initPostMetaMenus(root) { var scope = root || document; var menuRoots = []; if (scope.nodeType === 1 && scope.matches(".post-drop-meta")) { menuRoots.push(scope); } scope.querySelectorAll(".post-drop-meta").forEach(function (menuRoot) { menuRoots.push(menuRoot); }); menuRoots.forEach(function (menuRoot) { var toggle = menuRoot.querySelector(".post-drop-meta__toggle"); var menu = menuRoot.querySelector(".post-drop-meta__menu"); if (!toggle || !menu || toggle.dataset.postMetaMenuBound) return; toggle.dataset.postMetaMenuBound = "1"; setPostMetaMenuState(menuRoot, false); toggle.addEventListener("click", function () { var open = !menuRoot.classList.contains("is-open"); closePostMetaMenus(menuRoot, false); setPostMetaMenuState(menuRoot, open); }); menu.addEventListener("click", function (event) { if (event.target.closest(".post-drop-meta__item")) { setPostMetaMenuState(menuRoot, false); } }); }); if (menuRoots.length && !postMetaDocumentEventsBound) { postMetaDocumentEventsBound = true; document.addEventListener("click", function (event) { if (!event.target.closest(".post-drop-meta")) { closePostMetaMenus(null, false); } }); document.addEventListener("keydown", function (event) { if (event.key === "Escape") { closePostMetaMenus(null, true); } }); } } function getBaseStatCount(statName) { var statEl = document.querySelector('[data-stat-count="' + statName + '"][data-base-count]'); return statEl ? (parseInt(statEl.dataset.baseCount, 10) || 0) : 0; } function updateStatCounts(statName, count) { var safeCount = Math.max(0, count); document.querySelectorAll('[data-stat-count="' + statName + '"]').forEach(function (statEl) { statEl.textContent = String(safeCount); }); } function setBaseStatCount(statName, count) { var safeCount = Math.max(0, count); document.querySelectorAll('[data-stat-count="' + statName + '"][data-base-count]').forEach(function (statEl) { statEl.dataset.baseCount = String(safeCount); }); updateStatCounts(statName, safeCount); } function requestInteraction(actionName, postId, active) { if (!window.aksrtData || !aksrtData.ajaxUrl || !aksrtData.nonce) { return Promise.reject(new Error("互动服务未配置")); } var body = new URLSearchParams(); body.set("action", actionName); body.set("nonce", aksrtData.nonce); body.set("postId", String(postId)); body.set("active", active ? "1" : "0"); return fetch(aksrtData.ajaxUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" }, credentials: "same-origin", body: body.toString() }).then(function (response) { return response.json().catch(function () { throw new Error("服务器返回无效响应"); }); }).then(function (payload) { if (!payload || !payload.success) { var message = payload && payload.data && payload.data.message ? payload.data.message : "请求失败"; throw new Error(message); } return payload.data || {}; }); } function setButtonBusy(button, busy) { button.disabled = !!busy; button.classList.toggle("is-loading", !!busy); } function findInScope(scope, id) { scope = scope || document; if (!scope) return null; if (scope.nodeType === 9) { return scope.getElementById(id); } if (scope.nodeType === 1) { if (scope.id === id) return scope; return typeof scope.querySelector === "function" ? scope.querySelector("#" + id) : null; } return null; } function collectActionButtons(scope, selector) { scope = scope || document; var list = []; if (!scope) return list; if (scope.nodeType === 1 && scope.matches && scope.matches(selector)) { list.push(scope); } if (typeof scope.querySelectorAll === "function") { var found = scope.querySelectorAll(selector); for (var i = 0; i < found.length; i++) list.push(found[i]); } else if (scope.nodeType === 9) { var all = document.querySelectorAll(selector); for (var j = 0; j < all.length; j++) list.push(all[j]); } // also keep legacy id hooks var legacyId = selector.indexOf("action-like") >= 0 ? "action-like" : (selector.indexOf("action-favorite") >= 0 ? "action-favorite" : ""); if (legacyId) { var legacy = findInScope(scope, legacyId); if (legacy && list.indexOf(legacy) === -1) list.push(legacy); } return list; } function initInteractionBar(root) { var scope = root || document; // Bind all like/favorite controls (in-content + footer tabbar extend). var likeBtns = collectActionButtons(scope, ".action-like[data-post-id]"); var favBtns = collectActionButtons(scope, ".action-favorite[data-post-id]"); function paintLike(btn, isActive, animate) { btn.dataset.initialActive = isActive ? "1" : "0"; // actived 为参考主题的选中态类名;action-animation 只在点亮瞬间附加, // 触发 main.css 中的 lovestar 关键帧。 btn.classList.toggle("actived", isActive); btn.classList.toggle("action-animation", !!(isActive && animate)); } function paintFavorite(btn, isActive, animate) { btn.dataset.initialActive = isActive ? "1" : "0"; btn.classList.toggle("actived", isActive); btn.classList.toggle("action-animation", !!(isActive && animate)); } function applyLikeState(isActive, count, animate) { for (var i = 0; i < likeBtns.length; i++) paintLike(likeBtns[i], isActive, animate); if ("number" === typeof count && !isNaN(count)) { setBaseStatCount("likes", count); } else { updateStatCounts("likes", getBaseStatCount("likes")); } } function applyFavoriteState(isActive, count, animate) { for (var i = 0; i < favBtns.length; i++) paintFavorite(favBtns[i], isActive, animate); if ("number" === typeof count && !isNaN(count)) { setBaseStatCount("favorites", count); } else { updateStatCounts("favorites", getBaseStatCount("favorites")); } } if (likeBtns.length) { var seedLike = likeBtns[0]; if (!seedLike.dataset.aksrtInteractionBound) { applyLikeState(seedLike.dataset.initialActive === "1", getBaseStatCount("likes")); likeBtns.forEach(function (likeBtn) { if (likeBtn.dataset.aksrtInteractionBound) return; likeBtn.dataset.aksrtInteractionBound = "1"; likeBtn.addEventListener("click", function () { var postId = likeBtn.dataset.postId ? String(likeBtn.dataset.postId) : ""; if (!postId || likeBtn.disabled) return; var nextState = likeBtn.dataset.initialActive !== "1"; setButtonBusy(likeBtn, true); requestInteraction("aksrt_toggle_like", postId, nextState).then(function (data) { applyLikeState(!!data.active, parseInt(data.count, 10) || 0, true); notyf(data.active ? "感谢你的点赞" : "已取消点赞", "success"); }).catch(function (error) { notyf(error && error.message ? error.message : "点赞失败", "danger"); }).finally(function () { setButtonBusy(likeBtn, false); }); }); }); } } if (favBtns.length) { var seedFav = favBtns[0]; if (!seedFav.dataset.aksrtInteractionBound) { applyFavoriteState(seedFav.dataset.initialActive === "1", getBaseStatCount("favorites")); favBtns.forEach(function (favBtn) { if (favBtn.dataset.aksrtInteractionBound) return; favBtn.dataset.aksrtInteractionBound = "1"; favBtn.addEventListener("click", function () { var postId = favBtn.dataset.postId ? String(favBtn.dataset.postId) : ""; if (!postId || favBtn.disabled) return; var nextState = favBtn.dataset.initialActive !== "1"; setButtonBusy(favBtn, true); requestInteraction("aksrt_toggle_favorite", postId, nextState).then(function (data) { applyFavoriteState(!!data.active, parseInt(data.count, 10) || 0, true); notyf(data.active ? "收藏成功" : "已取消收藏", "success"); }).catch(function (error) { notyf(error && error.message ? error.message : "收藏失败", "danger"); }).finally(function () { setButtonBusy(favBtn, false); }); }); }); } } } function init(root) { initSearchClear(); initArticleTools(); initPostMetaMenus(root); initPasswordToggles(root); initInteractionBar(root); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", function () { init(); }); } else { init(); } // AJAX 注入新内容后由 auto_fun 重扫(各 init 均带 data 标记,重复调用幂等)。 if (window.jQuery) { window.jQuery(document).off("auto_fun.aksrt-article").on("auto_fun.aksrt-article", function () { init(); }); } })();