/** * 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(' ' + 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( ' 请等待 ' + 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 是完整的