The comment three-dot menu relied on the legacy data-toggle=dropdown delegation living in comment-move-form.js; when that bundle chain does not evaluate (stale cache or an earlier top-level error), the menu appears dead. Move the comment action dropdown to its own top-level document delegation in comments.js (data-aksrt-comment-drop), keep the .open-driven CSS contract including the mobile drop-fixed-sm sheet, sync aria-expanded, close on outside click / Escape / menu item click, and stay idempotent for AJAX-injected comments.
691 lines
25 KiB
JavaScript
691 lines
25 KiB
JavaScript
/**
|
||
* AKSRT Comment System — AJAX submit, captcha, ordering, author filter
|
||
*/
|
||
(function () {
|
||
"use strict";
|
||
|
||
function initCommentSystem() {
|
||
var form = document.querySelector("#commentform");
|
||
if (!form || form.dataset.aksrtCommentInit === "1") return;
|
||
form.dataset.aksrtCommentInit = "1";
|
||
|
||
// 修复:同时匹配 button 和 input 元素
|
||
var submitBtn = form.querySelector('[type="submit"]');
|
||
if (!submitBtn) return;
|
||
var isInputSubmit = submitBtn.tagName === 'INPUT';
|
||
|
||
// 保存原始按钮内容(用于恢复)
|
||
var originalBtnHTML = isInputSubmit ? submitBtn.value : submitBtn.innerHTML;
|
||
|
||
function setSubmitBtnContent(html, textFallback) {
|
||
if (isInputSubmit) {
|
||
submitBtn.value = textFallback;
|
||
} else {
|
||
submitBtn.innerHTML = html;
|
||
}
|
||
}
|
||
|
||
// 状态管理
|
||
var isSubmitting = false;
|
||
var countdownTimer = null;
|
||
|
||
// 显示/隐藏加载状态
|
||
function setLoading(loading) {
|
||
if (loading) {
|
||
isSubmitting = true;
|
||
submitBtn.classList.add("is-loading");
|
||
submitBtn.disabled = true;
|
||
var loadingText = (window.aksrtData && aksrtData.commentSubmittingText) || '发送中...';
|
||
setSubmitBtnContent('<i class="fa fas fa-spinner fa-spin"></i> ' + loadingText, loadingText);
|
||
} else {
|
||
isSubmitting = false;
|
||
submitBtn.classList.remove("is-loading");
|
||
submitBtn.classList.remove('countdown-active');
|
||
submitBtn.disabled = false;
|
||
setSubmitBtnContent(originalBtnHTML, originalBtnHTML);
|
||
}
|
||
}
|
||
|
||
// 收集表单数据(包含验证码字段)
|
||
function collectFormData() {
|
||
var formData = new FormData(form);
|
||
|
||
// 确保包含必要的隐藏字段
|
||
var postId = document.querySelector('input[name="comment_post_ID"]');
|
||
if (postId && !formData.get('comment_post_ID')) {
|
||
formData.append('comment_post_ID', postId.value);
|
||
}
|
||
|
||
return formData;
|
||
}
|
||
|
||
// 启动倒计时(用于频率限制)
|
||
function startCountdown(seconds) {
|
||
if (countdownTimer) clearInterval(countdownTimer);
|
||
|
||
submitBtn.classList.add('countdown-active');
|
||
submitBtn.disabled = true;
|
||
var remaining = seconds;
|
||
|
||
countdownTimer = setInterval(function () {
|
||
setSubmitBtnContent(
|
||
'<i class="fa fas fa-clock"></i> 请等待 ' + remaining + 's',
|
||
'请等待 ' + remaining + 's'
|
||
);
|
||
|
||
if (--remaining <= 0) {
|
||
clearInterval(countdownTimer);
|
||
countdownTimer = null;
|
||
submitBtn.classList.remove('countdown-active', 'is-loading');
|
||
submitBtn.disabled = false;
|
||
setSubmitBtnContent(originalBtnHTML, originalBtnHTML);
|
||
isSubmitting = false;
|
||
}
|
||
}, 1000);
|
||
}
|
||
|
||
function updateCommentCount(delta) {
|
||
var countEl = document.querySelector('#comments .title-theme small');
|
||
if (!countEl) return;
|
||
var text = countEl.textContent || '';
|
||
var match = text.match(/(\d+)/);
|
||
if (!match) return;
|
||
var nextCount = Math.max(0, parseInt(match[1], 10) + delta);
|
||
countEl.textContent = text.replace(match[1], String(nextCount));
|
||
}
|
||
// 执行实际的 REST 请求 — local insert instead of full-page re-fetch
|
||
function performSubmit(captchaPayload) {
|
||
setLoading(true);
|
||
|
||
var formData = collectFormData();
|
||
var restRoot = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, '') : '';
|
||
var editId = form.querySelector('[name="edit_comment_ID"]');
|
||
var isEdit = editId && parseInt(editId.value, 10) > 0;
|
||
var restBase = (restRoot || '') + '/wp-json/aksrt/v1/comment/' + (isEdit ? 'edit' : 'submit');
|
||
|
||
// Try REST endpoint for local-insert HTML response
|
||
var restBody = {};
|
||
formData.forEach(function (v, k) { restBody[k] = v; });
|
||
Object.assign(restBody, captchaPayload || {});
|
||
if (isEdit) {
|
||
restBody.comment_id = parseInt(editId.value, 10);
|
||
restBody.content = restBody.comment || restBody.content || '';
|
||
}
|
||
|
||
fetch(restBase, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-WP-Nonce': (window.aksrtData && aksrtData.restNonce) || ''
|
||
},
|
||
credentials: 'same-origin',
|
||
body: JSON.stringify(restBody)
|
||
})
|
||
.then(function (r) { return r.json(); })
|
||
.then(function (json) {
|
||
if (isEdit && json && json.success) {
|
||
var cid = restBody.comment_id;
|
||
var contentEl = document.querySelector('#comment-content-' + cid + ', [data-comment-id="' + cid + '"] .comment-content');
|
||
var rendered = (json.data && json.data.content) || json.content || '';
|
||
if (contentEl && rendered) contentEl.innerHTML = rendered;
|
||
var textarea = form.querySelector('#comment');
|
||
if (textarea) textarea.value = '';
|
||
if (editId) editId.remove();
|
||
restoreRespondPosition();
|
||
setLoading(false);
|
||
notyf(json.message || '评论已更新', 'success');
|
||
var respondEl = document.getElementById('respond');
|
||
if (respondEl && window.AksrtFixedInput) window.AksrtFixedInput(respondEl, 'hide');
|
||
return;
|
||
}
|
||
if (json && json.success && json.html) {
|
||
// Local insert: prepend new comment HTML
|
||
localInsertComment(json.html, json.is_reply, json.parent_id, json.needs_moderation);
|
||
|
||
var textarea = form.querySelector('#comment');
|
||
if (textarea) textarea.value = '';
|
||
|
||
var replyInfo = document.getElementById('comment-reply-info');
|
||
if (replyInfo) replyInfo.style.display = 'none';
|
||
|
||
// Wait time countdown
|
||
if (json.wait_time && json.wait_time > 0) {
|
||
startCountdown(json.wait_time);
|
||
} else {
|
||
setLoading(false);
|
||
}
|
||
|
||
// Move respond back to original position
|
||
restoreRespondPosition();
|
||
|
||
// Hide mobile FixedInput after successful submit.
|
||
var respondEl = document.getElementById('respond');
|
||
if (respondEl && window.AksrtFixedInput) {
|
||
window.AksrtFixedInput(respondEl, 'hide');
|
||
} else if (respondEl) {
|
||
respondEl.classList.remove('show');
|
||
}
|
||
|
||
// If editing via moveForm, clear edit field.
|
||
var editField = form.querySelector('[name="edit_comment_ID"]');
|
||
if (editField) editField.remove();
|
||
|
||
return;
|
||
}
|
||
|
||
if (json && json.code) {
|
||
notyf(json.message || '评论提交失败', 'danger');
|
||
if (json.wait_time) startCountdown(json.wait_time);
|
||
else setLoading(false);
|
||
return;
|
||
}
|
||
|
||
notyf('评论已提交', 'success');
|
||
setLoading(false);
|
||
setTimeout(function () { reloadCommentList(); }, 500);
|
||
})
|
||
.catch(function (error) {
|
||
console.error('[AKSRT Comment] 提交失败:', error);
|
||
notyf((error && error.message) || '网络连接失败,请检查网络后重试', 'danger');
|
||
setLoading(false);
|
||
})
|
||
.finally(function () {
|
||
if (window.AKSRTCaptcha) window.AKSRTCaptcha.reset('comment', form);
|
||
if (!countdownTimer && isSubmitting) setLoading(false);
|
||
});
|
||
}
|
||
|
||
function localInsertComment(html, isReply, parentId, needsModeration) {
|
||
var temp = document.createElement('div');
|
||
temp.innerHTML = html;
|
||
var newComment = temp.firstElementChild;
|
||
if (!newComment) { reloadCommentList(); return; }
|
||
|
||
newComment.style.overflow = 'hidden';
|
||
newComment.style.maxHeight = '0';
|
||
newComment.style.opacity = '0';
|
||
newComment.style.transition = 'max-height 0.4s ease, opacity 0.3s ease';
|
||
if (isReply && parentId) {
|
||
var parentComment = document.querySelector('#comment-' + parentId);
|
||
if (parentComment) {
|
||
var children = parentComment.querySelector(':scope > .children');
|
||
if (!children) {
|
||
children = document.createElement('ol');
|
||
children.className = 'children';
|
||
parentComment.appendChild(children);
|
||
}
|
||
children.insertBefore(newComment, children.firstChild);
|
||
} else {
|
||
prependToCommentList(newComment);
|
||
}
|
||
} else {
|
||
prependToCommentList(newComment);
|
||
}
|
||
|
||
// slideDown animation
|
||
requestAnimationFrame(function () {
|
||
newComment.style.maxHeight = newComment.scrollHeight + 100 + 'px';
|
||
newComment.style.opacity = '1';
|
||
setTimeout(function () {
|
||
newComment.style.maxHeight = '';
|
||
newComment.style.overflow = '';
|
||
newComment.style.transition = '';
|
||
}, 500);
|
||
});
|
||
|
||
if (needsModeration) {
|
||
notyf('评论已提交,等待管理员审核', 'info');
|
||
} else {
|
||
notyf('评论发布成功', 'success');
|
||
}
|
||
|
||
// legacy 事件顺序:先 post_ajax.ed,再全局 auto_fun 重扫
|
||
newComment.dispatchEvent(new CustomEvent('post_ajax.ed', { bubbles: true }));
|
||
if (typeof window.auto_fun === 'function') window.auto_fun();
|
||
updateCommentCount(1);
|
||
}
|
||
|
||
function prependToCommentList(el) {
|
||
var list = document.querySelector('#comments .commentlist');
|
||
if (!list) return;
|
||
// 插到筛选栏之后、最新评论之前
|
||
var filter = list.querySelector('.comment-filter');
|
||
if (filter && filter.nextSibling) {
|
||
list.insertBefore(el, filter.nextSibling);
|
||
} else {
|
||
list.insertBefore(el, list.firstChild);
|
||
}
|
||
}
|
||
|
||
function restoreRespondPosition() {
|
||
var respond = document.getElementById('respond');
|
||
var placeholder = document.getElementById('wp-temp-form-div');
|
||
if (respond && placeholder && placeholder.parentNode) {
|
||
placeholder.parentNode.insertBefore(respond, placeholder);
|
||
placeholder.remove();
|
||
}
|
||
}
|
||
// 核心表单提交事件处理器
|
||
function handleSubmit(e) {
|
||
// 如果已经在提交中或倒计时中,防止重复提交
|
||
if (isSubmitting || countdownTimer) {
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
return false;
|
||
}
|
||
|
||
// 阻止默认表单提交
|
||
e.preventDefault();
|
||
e.stopImmediatePropagation();
|
||
var captcha = window.AKSRTCaptcha;
|
||
setLoading(true);
|
||
(captcha ? captcha.run('comment', form) : Promise.resolve({})).then(function (captchaPayload) {
|
||
if (captchaPayload === null) {
|
||
notyf('人机验证未通过,请重试', 'warning');
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
performSubmit(captchaPayload);
|
||
}).catch(function () {
|
||
notyf('人机验证未通过,请重试', 'warning');
|
||
setLoading(false);
|
||
if (captcha) captcha.reset('comment', form);
|
||
});
|
||
|
||
return false;
|
||
}
|
||
|
||
// 绑定表单提交事件(使用捕获阶段,高优先级)
|
||
form.addEventListener('submit', handleSubmit, true);
|
||
}
|
||
|
||
|
||
function getCommentsRestBase() {
|
||
var root = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, "") : "";
|
||
return root + "/wp-json/aksrt/v1";
|
||
}
|
||
|
||
function getCurrentCommentPostId() {
|
||
var input = document.querySelector('input[name="comment_post_ID"]');
|
||
if (input && input.value) return parseInt(input.value, 10) || 0;
|
||
var comments = document.getElementById("comments");
|
||
if (comments && comments.dataset.postId) return parseInt(comments.dataset.postId, 10) || 0;
|
||
return 0;
|
||
}
|
||
|
||
function getCurrentCommentOrder() {
|
||
var active = document.querySelector("#comments .comment-order-box li.active .comment-orderby");
|
||
if (active && active.dataset && active.dataset.order) return active.dataset.order;
|
||
var url = new URL(window.location.href);
|
||
return url.searchParams.get("aksrt_comment_order") || "newest";
|
||
}
|
||
|
||
function getCurrentAuthorOnly() {
|
||
var btn = document.querySelector("#comments .btn-only-author");
|
||
return !!(btn && btn.classList.contains("active") && btn.dataset.authorOnly === "0");
|
||
}
|
||
|
||
/* ====== 全局无参数加载:评论状态仅保存在客户端,地址栏不携带 ? 参数 ====== */
|
||
var COMMENT_URL_PARAMS = ["cpage", "aksrt_comment_order", "aksrt_author_only"];
|
||
var commentUrlStateCleaned = false;
|
||
|
||
function stripCommentUrlParams() {
|
||
if (commentUrlStateCleaned || !window.history || typeof window.history.replaceState !== "function") return;
|
||
commentUrlStateCleaned = true;
|
||
var url = new URL(window.location.href);
|
||
var changed = false;
|
||
COMMENT_URL_PARAMS.forEach(function (key) {
|
||
if (url.searchParams.has(key)) {
|
||
url.searchParams.delete(key);
|
||
changed = true;
|
||
}
|
||
});
|
||
if (changed) {
|
||
// 服务端首屏已按参数渲染完毕,落地后地址栏回到无参干净链接。
|
||
window.history.replaceState(null, "", url.toString());
|
||
}
|
||
}
|
||
|
||
function getMaxCommentPages() {
|
||
var pagenav = document.querySelector("#comments .pagenav[data-max-pages]");
|
||
if (pagenav) {
|
||
var max = parseInt(pagenav.dataset.maxPages, 10);
|
||
if (max > 0) return max;
|
||
}
|
||
var jumpInput = document.querySelector("#comments .jump-input[max]");
|
||
if (jumpInput) {
|
||
var maxAttr = parseInt(jumpInput.getAttribute("max"), 10);
|
||
if (maxAttr > 0) return maxAttr;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
function parseCommentPageFromHref(href) {
|
||
if (!href || href === "#" || href.indexOf("javascript:") === 0) return 0;
|
||
try {
|
||
var url = new URL(href, window.location.href);
|
||
var cpage = parseInt(url.searchParams.get("cpage"), 10);
|
||
if (cpage > 0) return cpage;
|
||
var match = url.pathname.match(/\/comment-page-([0-9]{1,})\/?$/);
|
||
return match ? parseInt(match[1], 10) : 0;
|
||
} catch (e) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
function scrollCommentsIntoView() {
|
||
var commentsArea = document.getElementById("comments");
|
||
if (!commentsArea || typeof commentsArea.scrollIntoView !== "function") return;
|
||
var rect = commentsArea.getBoundingClientRect();
|
||
if (rect.top >= 0 && rect.top < window.innerHeight * 0.6) return;
|
||
commentsArea.scrollIntoView({ behavior: "smooth", block: "start" });
|
||
}
|
||
|
||
function loadCommentPage(page, opts) {
|
||
opts = opts || {};
|
||
var commentsArea = document.getElementById("comments");
|
||
if (!commentsArea) {
|
||
return Promise.reject(new Error("comments area missing"));
|
||
}
|
||
var maxPages = getMaxCommentPages();
|
||
page = parseInt(page, 10) || 1;
|
||
page = Math.max(1, page);
|
||
if (maxPages > 1) page = Math.min(page, maxPages);
|
||
|
||
showListLoading(commentsArea);
|
||
return fetchCommentFragment({
|
||
order: getCurrentCommentOrder(),
|
||
authorOnly: getCurrentAuthorOnly(),
|
||
page: page
|
||
}).then(function (json) {
|
||
if (json && json.success && json.data) {
|
||
applyFragmentPayload(commentsArea, json.data);
|
||
if (!opts.noScroll) scrollCommentsIntoView();
|
||
return true;
|
||
}
|
||
throw new Error("comment fragment failed");
|
||
});
|
||
}
|
||
|
||
function applyFragmentPayload(commentsArea, data) {
|
||
if (!commentsArea || !data || !data.html) return;
|
||
|
||
// data.html 是完整的 <div id="postcomments">(含筛选、列表、分页)
|
||
var tempList = document.createElement("div");
|
||
tempList.innerHTML = data.html;
|
||
var newListWrap = tempList.querySelector("#postcomments");
|
||
if (!newListWrap) return;
|
||
|
||
var currentListWrap = commentsArea.querySelector("#postcomments");
|
||
if (currentListWrap) {
|
||
currentListWrap.replaceWith(newListWrap);
|
||
} else {
|
||
commentsArea.appendChild(newListWrap);
|
||
}
|
||
|
||
var currentCount = commentsArea.querySelector(".title-theme small");
|
||
if (currentCount && data.count_text) currentCount.textContent = data.count_text;
|
||
|
||
// legacy 事件顺序:先 post_ajax.ed,再全局 auto_fun 重扫
|
||
newListWrap.dispatchEvent(new CustomEvent("post_ajax.ed", { bubbles: true }));
|
||
if (typeof window.auto_fun === "function") window.auto_fun();
|
||
}
|
||
|
||
function fetchCommentFragment(opts) {
|
||
opts = opts || {};
|
||
var postId = opts.postId || getCurrentCommentPostId();
|
||
if (!postId) return Promise.reject(new Error("missing post id"));
|
||
var order = opts.order || getCurrentCommentOrder() || "newest";
|
||
var authorOnly = !!opts.authorOnly;
|
||
var page = opts.page || 1;
|
||
var qs = new URLSearchParams();
|
||
qs.set("post_id", String(postId));
|
||
qs.set("order", order);
|
||
qs.set("page", String(page));
|
||
if (authorOnly) qs.set("author_only", "1");
|
||
var url = getCommentsRestBase() + "/comment/list?" + qs.toString();
|
||
return fetch(url, {
|
||
method: "GET",
|
||
credentials: "same-origin",
|
||
headers: {
|
||
"X-WP-Nonce": (window.aksrtData && aksrtData.restNonce) || ""
|
||
}
|
||
}).then(function (r) { return r.json(); });
|
||
}
|
||
|
||
/* ====== 评论列表动态刷新 ====== */
|
||
function showListLoading(commentsArea) {
|
||
var list = commentsArea.querySelector(".commentlist");
|
||
if (!list) return;
|
||
// 保留筛选栏,清空条目并显示 legacy 骨架
|
||
var filter = list.querySelector(".comment-filter");
|
||
var loader = list.querySelector(".post_ajax_loader");
|
||
var skeleton = loader ? loader.cloneNode(true) : null;
|
||
Array.prototype.slice.call(list.children).forEach(function (child) {
|
||
if (child !== filter && child !== loader) child.remove();
|
||
});
|
||
if (skeleton) {
|
||
skeleton.style.display = "block";
|
||
skeleton.removeAttribute("hidden");
|
||
list.appendChild(skeleton);
|
||
}
|
||
}
|
||
|
||
function reloadCommentList() {
|
||
loadCommentPage(1, { noScroll: true }).catch(function (error) {
|
||
console.error('[AKSRT Comment] 刷新评论列表失败:', error);
|
||
window.location.reload();
|
||
});
|
||
}
|
||
|
||
/* ====== 评论排序(最新 / 最早) ====== */
|
||
/**
|
||
* Locate #id within the given scope.
|
||
* Document has getElementById; Element roots (AJAX panels) only have querySelector.
|
||
*/
|
||
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 initCommentOrder(root) {
|
||
var scope = root || document;
|
||
var commentsArea = findInScope(scope, "comments");
|
||
if (!commentsArea || commentsArea.dataset.orderInit === "1") return;
|
||
// Event delegation survives filter HTML replacement after AJAX refresh.
|
||
commentsArea.dataset.orderInit = "1";
|
||
|
||
function renderLoadingState() {
|
||
showListLoading(commentsArea);
|
||
}
|
||
function applyCommentOrder(order) {
|
||
if (order !== "newest" && order !== "oldest" && order !== "hottest") {
|
||
order = "newest";
|
||
}
|
||
renderLoadingState();
|
||
var authorOnly = getCurrentAuthorOnly();
|
||
|
||
fetchCommentFragment({ order: order, authorOnly: authorOnly, page: 1 })
|
||
.then(function (json) {
|
||
if (json && json.success && json.data) {
|
||
applyFragmentPayload(commentsArea, json.data);
|
||
return;
|
||
}
|
||
throw new Error("order fragment failed");
|
||
})
|
||
.catch(function (error) {
|
||
console.error("[AKSRT Comment] 排序刷新失败:", error);
|
||
// 兜底整页刷新,地址栏保持无参。
|
||
window.location.reload();
|
||
});
|
||
}
|
||
|
||
function applyAuthorFilter(authorOnlyFlag) {
|
||
renderLoadingState();
|
||
var authorOnly = authorOnlyFlag === "1" || authorOnlyFlag === 1 || authorOnlyFlag === true;
|
||
var order = getCurrentCommentOrder();
|
||
|
||
fetchCommentFragment({ order: order, authorOnly: authorOnly, page: 1 })
|
||
.then(function (json) {
|
||
if (json && json.success && json.data) {
|
||
applyFragmentPayload(commentsArea, json.data);
|
||
return;
|
||
}
|
||
throw new Error("author fragment failed");
|
||
})
|
||
.catch(function (error) {
|
||
console.error("[AKSRT Comment] Author filter failed:", error);
|
||
// 兜底整页刷新,地址栏保持无参。
|
||
window.location.reload();
|
||
});
|
||
}
|
||
|
||
commentsArea.addEventListener("click", function (e) {
|
||
var target = e.target;
|
||
if (!target || typeof target.closest !== "function") return;
|
||
|
||
// 「只看作者」同时带 comment-orderby,需优先于排序按钮判断
|
||
var authorBtn = target.closest(".btn-only-author");
|
||
if (authorBtn && commentsArea.contains(authorBtn)) {
|
||
e.preventDefault();
|
||
applyAuthorFilter(authorBtn.getAttribute("data-author-only") || "0");
|
||
return;
|
||
}
|
||
|
||
var orderBtn = target.closest(".comment-orderby[data-order]");
|
||
if (orderBtn && commentsArea.contains(orderBtn)) {
|
||
e.preventDefault();
|
||
applyCommentOrder(orderBtn.getAttribute("data-order") || "newest");
|
||
return;
|
||
}
|
||
|
||
// 数字分页:本地 DOM 刷新,地址栏不携带参数(href 仅作无 JS 兜底)。
|
||
var pageLink = target.closest(".pagenav a.page-numbers");
|
||
if (pageLink && commentsArea.contains(pageLink) && !pageLink.classList.contains("pag-jump")) {
|
||
var page = parseCommentPageFromHref(pageLink.getAttribute("href"));
|
||
if (page > 0) {
|
||
e.preventDefault();
|
||
loadCommentPage(page).catch(function (error) {
|
||
console.error("[AKSRT Comment] 分页刷新失败:", error);
|
||
// AJAX 不可用时退回原始链接(pretty 链接本身无 ? 参数)。
|
||
window.location.href = pageLink.href;
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
|
||
/* ====== 数字分页跳页(legacy pag-jump) ====== */
|
||
var commentPageJumpBound = false;
|
||
|
||
function jumpToCommentPage(page) {
|
||
loadCommentPage(page).catch(function (error) {
|
||
console.error("[AKSRT Comment] 跳页失败:", error);
|
||
window.location.reload();
|
||
});
|
||
}
|
||
|
||
function initCommentPageJump() {
|
||
if (commentPageJumpBound) return;
|
||
commentPageJumpBound = true;
|
||
document.addEventListener("click", function (e) {
|
||
var jump = e.target.closest ? e.target.closest("#comments .pag-jump") : null;
|
||
if (!jump) return;
|
||
e.preventDefault();
|
||
var input = jump.querySelector(".jump-input");
|
||
if (input) jumpToCommentPage(input.value);
|
||
});
|
||
document.addEventListener("keydown", function (e) {
|
||
if (e.key !== "Enter") return;
|
||
var input = e.target;
|
||
if (!input || !input.classList || !input.classList.contains("jump-input")) return;
|
||
if (!input.closest("#comments .pagenav")) return;
|
||
e.preventDefault();
|
||
jumpToCommentPage(input.value);
|
||
});
|
||
}
|
||
|
||
/* ====== 评论操作三点下拉(主题自有交互,替代 vendor data-toggle 遗留委托) ======
|
||
* 一次性文档级委托:AJAX 注入的新评论无需重绑;
|
||
* open 类驱动的 .dropdown-menu 显隐 CSS 契约(含移动端 drop-fixed-sm 底部抽屉)保持不变。 */
|
||
function closeAllCommentDropdowns() {
|
||
document.querySelectorAll("#postcomments .dropdown.open").forEach(function (wrap) {
|
||
wrap.classList.remove("open");
|
||
var trigger = wrap.querySelector("[data-aksrt-comment-drop]");
|
||
if (trigger) trigger.setAttribute("aria-expanded", "false");
|
||
});
|
||
}
|
||
|
||
function initCommentActionDropdowns() {
|
||
if (document.documentElement.dataset.aksrtCommentDropBound === "1") return;
|
||
document.documentElement.dataset.aksrtCommentDropBound = "1";
|
||
|
||
document.addEventListener("click", function (event) {
|
||
var trigger = event.target.closest ? event.target.closest("[data-aksrt-comment-drop]") : null;
|
||
if (trigger) {
|
||
event.preventDefault();
|
||
var wrap = trigger.closest(".dropdown");
|
||
if (!wrap) return;
|
||
var wasOpen = wrap.classList.contains("open");
|
||
closeAllCommentDropdowns();
|
||
if (!wasOpen) {
|
||
wrap.classList.add("open");
|
||
trigger.setAttribute("aria-expanded", "true");
|
||
}
|
||
return;
|
||
}
|
||
// 点击菜单项(举报/置顶/编辑/批准/删除)后收起菜单。
|
||
var menuItem = event.target.closest ? event.target.closest("#postcomments .dropdown.open .dropdown-menu a") : null;
|
||
if (menuItem) {
|
||
closeAllCommentDropdowns();
|
||
return;
|
||
}
|
||
if (!event.target.closest || !event.target.closest("#postcomments .dropdown.open")) {
|
||
closeAllCommentDropdowns();
|
||
}
|
||
});
|
||
|
||
document.addEventListener("keydown", function (event) {
|
||
if (event.key === "Escape") closeAllCommentDropdowns();
|
||
});
|
||
}
|
||
|
||
function init() {
|
||
stripCommentUrlParams();
|
||
initCommentSystem();
|
||
initCommentPageJump();
|
||
initCommentOrder();
|
||
}
|
||
|
||
// 顶层立即绑定:纯文档级委托,不依赖 DOM 就绪与 init() 是否成功;
|
||
// 保证即使同文件后续初始化在异常环境中中断,三点菜单仍可开合。
|
||
initCommentActionDropdowns();
|
||
|
||
if (document.readyState === "loading") {
|
||
document.addEventListener("DOMContentLoaded", init);
|
||
} else {
|
||
init();
|
||
}
|
||
// AJAX 注入新内容后由 auto_fun 重扫(form/commentsArea 均带 data 标记,重复调用幂等)。
|
||
if (window.jQuery) {
|
||
window.jQuery(document).on("auto_fun", init);
|
||
}
|
||
|
||
// 供 pagination.js 等模块复用:评论页码加载 / 列表刷新(局部 DOM,无 ? 参数)。
|
||
window.AksrtComments = {
|
||
loadPage: function (page, opts) { return loadCommentPage(page, opts); },
|
||
reload: function () { reloadCommentList(); }
|
||
};
|
||
})();
|