/** * 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) { 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 showToast(msg, type) { if (window.aksrt && aksrt.notyf) { aksrt.notyf(msg, { type: type || "info", duration: 2200 }); } else { console.log("[AKSRT Toast]", type, msg); } } function initArticleTools() { var buttons = document.querySelectorAll("[data-article-tool]"); if (!buttons.length) return; buttons.forEach(function (button) { button.addEventListener("click", function () { var tool = button.getAttribute("data-article-tool"); if (tool === "print") { window.print(); return; } if (tool === "pdf") { window.print(); if (typeof showToast === "function") { showToast("请在打印对话框中选择“另存为 PDF”", "info"); } } }); }); } 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 initInteractionBar(root) { var scope = root || document; // Element roots (AJAX rehydrate) do not implement getElementById var likeBtn = findInScope(scope, "action-like"); var favBtn = findInScope(scope, "action-favorite"); if (likeBtn && likeBtn.dataset.aksrtInteractionBound) return; if (favBtn && favBtn.dataset.aksrtInteractionBound) return; if (likeBtn) { var likePostId = likeBtn.dataset.postId ? String(likeBtn.dataset.postId) : ""; function applyLikeState(isActive, count) { likeBtn.dataset.initialActive = isActive ? "1" : "0"; likeBtn.classList.toggle("is-active", isActive); var likeIcon = likeBtn.querySelector("i"); if (likeIcon) { likeIcon.className = isActive ? "fas fa-heart" : "far fa-heart"; } if ("number" === typeof count && !isNaN(count)) { setBaseStatCount("likes", count); } else { updateStatCounts("likes", getBaseStatCount("likes")); } } applyLikeState(likeBtn.dataset.initialActive === "1", getBaseStatCount("likes")); likeBtn.addEventListener("click", function () { if (!likePostId || likeBtn.disabled) return; var nextState = likeBtn.dataset.initialActive !== "1"; setButtonBusy(likeBtn, true); requestInteraction("aksrt_toggle_like", likePostId, nextState).then(function (data) { applyLikeState(!!data.active, parseInt(data.count, 10) || 0); showToast(data.active ? "感谢你的点赞" : "已取消点赞", "success"); }).catch(function (error) { showToast(error && error.message ? error.message : "点赞失败", "error"); }).finally(function () { setButtonBusy(likeBtn, false); }); }); } if (favBtn) { var favoritePostId = favBtn.dataset.postId ? String(favBtn.dataset.postId) : ""; function applyFavoriteState(isActive, count) { favBtn.dataset.initialActive = isActive ? "1" : "0"; favBtn.classList.toggle("is-active", isActive); var favoriteIcon = favBtn.querySelector("i"); if (favoriteIcon) { favoriteIcon.className = isActive ? "fas fa-star" : "far fa-star"; } if ("number" === typeof count && !isNaN(count)) { setBaseStatCount("favorites", count); } else { updateStatCounts("favorites", getBaseStatCount("favorites")); } } applyFavoriteState(favBtn.dataset.initialActive === "1", getBaseStatCount("favorites")); favBtn.addEventListener("click", function () { if (!favoritePostId || favBtn.disabled) return; var nextState = favBtn.dataset.initialActive !== "1"; setButtonBusy(favBtn, true); requestInteraction("aksrt_toggle_favorite", favoritePostId, nextState).then(function (data) { applyFavoriteState(!!data.active, parseInt(data.count, 10) || 0); showToast(data.active ? "收藏成功" : "已取消收藏", "success"); }).catch(function (error) { showToast(error && error.message ? error.message : "收藏失败", "error"); }).finally(function () { setButtonBusy(favBtn, false); }); }); } if (likeBtn) likeBtn.dataset.aksrtInteractionBound = "1"; if (favBtn) favBtn.dataset.aksrtInteractionBound = "1"; } window.AksrtFrontend.register("aksrt-search-clear", function () { initSearchClear(); }); window.AksrtFrontend.register("aksrt-article-tools", function () { initArticleTools(); }); window.AksrtFrontend.register("aksrt-password-toggles", function (root) { initPasswordToggles(root); }); window.AksrtFrontend.register("aksrt-interaction-bar", function (root) { initInteractionBar(root); }); })();