267 lines
9.9 KiB
JavaScript
267 lines
9.9 KiB
JavaScript
/**
|
|
* 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 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();
|
|
notyf("请在打印对话框中选择“另存为 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 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) {
|
|
btn.dataset.initialActive = isActive ? "1" : "0";
|
|
btn.classList.toggle("is-active", isActive);
|
|
var icon = btn.querySelector("i");
|
|
if (icon) icon.className = isActive ? "fa fas fa-heart" : "fa fas fa-heart";
|
|
}
|
|
|
|
function paintFavorite(btn, isActive) {
|
|
btn.dataset.initialActive = isActive ? "1" : "0";
|
|
btn.classList.toggle("is-active", isActive);
|
|
var icon = btn.querySelector("i");
|
|
if (icon) icon.className = isActive ? "fa fas fa-star" : "fa fas fa-star";
|
|
}
|
|
|
|
function applyLikeState(isActive, count) {
|
|
for (var i = 0; i < likeBtns.length; i++) paintLike(likeBtns[i], isActive);
|
|
if ("number" === typeof count && !isNaN(count)) {
|
|
setBaseStatCount("likes", count);
|
|
} else {
|
|
updateStatCounts("likes", getBaseStatCount("likes"));
|
|
}
|
|
}
|
|
|
|
function applyFavoriteState(isActive, count) {
|
|
for (var i = 0; i < favBtns.length; i++) paintFavorite(favBtns[i], isActive);
|
|
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);
|
|
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);
|
|
notyf(data.active ? "收藏成功" : "已取消收藏", "success");
|
|
}).catch(function (error) {
|
|
notyf(error && error.message ? error.message : "收藏失败", "danger");
|
|
}).finally(function () {
|
|
setButtonBusy(favBtn, false);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
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); });
|
|
})();
|