/** * AKSRT Comment System — AJAX submit, captcha, ordering, author filter */ (function () { "use strict"; 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 initCommentSystem() { var form = document.querySelector("#commentform"); if (!form) return; // 修复:同时匹配 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; } // 从 wp_die() HTML 中提取错误信息 function extractErrorMessage(html) { // WordPress wp_die() 输出格式:
错误信息
var match = html.match(/]*>([^<]+)<\/p>/i); return match ? match[1].trim() : '未知错误'; } // 从错误消息中提取等待时间(秒) function extractWaitTime(msg) { var match = msg.match(/(\d+)\s*秒/); return match ? parseInt(match[1]) : 0; } // 启动倒计时(用于频率限制) 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-count'); 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 = '共 ' + nextCount + ' 条'; } function handleResponse(responseText, statusCode) { try { // 情况1: 成功提交 - 包含评论ID或成功标记 if (statusCode === 200 || statusCode === 201) { if (responseText.includes('comment-submitted') || responseText.indexOf('id="comment-') !== -1 || responseText.indexOf('awaiting-moderation') !== -1) { // 区分是否需要审核 if (responseText.indexOf('审核') !== -1 || responseText.indexOf('moderation') !== -1 || responseText.includes('awaiting-moderation')) { showToast('评论已提交,等待管理员审核', 'info'); } else { showToast('评论发布成功', 'success'); } // 清空文本框 var textarea = form.querySelector('#comment'); if (textarea) textarea.value = ''; // 重置回复状态 var replyInfo = document.getElementById('comment-reply-info'); if (replyInfo) replyInfo.style.display = 'none'; // 刷新评论列表(延迟执行,确保后端处理完成) setTimeout(function () { reloadCommentList(); }, 500); setLoading(false); return true; } } // 情况2: HTTP 429 频率限制 if (statusCode === 429) { var errorMsg = extractErrorMessage(responseText); var waitSeconds = extractWaitTime(errorMsg); showToast(errorMsg, 'warning'); if (waitSeconds > 0) { startCountdown(waitSeconds); } else { setLoading(false); // 无具体等待时间时恢复按钮 } return false; } // 情况3: HTTP 403 权限不足(地区/IP/关键词等) if (statusCode === 403) { var forbiddenMsg = extractErrorMessage(responseText); showToast(forbiddenMsg, 'error'); setLoading(false); return false; } // 情况4: 包含WordPress错误信息 var errorMatch = responseText.match(/
]*>([^<]*(?:错误|失败|error|Error)[^<]*)<\/p>/i); if (errorMatch) { showToast(errorMatch[1].trim(), 'error'); setLoading(false); return false; } // 默认情况:可能是成功或其他 showToast('评论已提交', 'success'); var textarea2 = form.querySelector('#comment'); if (textarea2) textarea2.value = ''; // 局部刷新评论列表(不再整页 reload) setTimeout(function () { reloadCommentList(); }, 500); setLoading(false); return true; } catch (e) { console.error('[AKSRT Comment] 解析响应失败:', e); showToast('响应解析异常,请重试', 'error'); setLoading(false); return false; } } // 执行实际的 AJAX 请求 — local insert instead of full-page re-fetch function performSubmit() { setLoading(true); var formData = collectFormData(); var restBase = '/wp-json/aksrt/v1/comment/submit'; // Try REST endpoint for local-insert HTML response var restBody = {}; formData.forEach(function (v, k) { restBody[k] = v; }); 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 (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'; // Reset captcha var captchaImg = form.querySelector('.imagecaptcha img'); if (captchaImg) captchaImg.click(); // 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(); return; } if (json && json.code) { showToast(json.message || '评论提交失败', 'error'); if (json.wait_time) startCountdown(json.wait_time); else setLoading(false); return; } showToast('评论已提交', 'success'); setLoading(false); setTimeout(function () { reloadCommentList(); }, 500); }) .catch(function () { // Fallback: submit to wp-comments-post.php directly performLegacySubmit(); }); } 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 + ', [data-comment-id="' + parentId + '"]'); if (parentComment) { var children = parentComment.querySelector('.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) { showToast('评论已提交,等待管理员审核', 'info'); } else { showToast('评论发布成功', 'success'); } if (window.AksrtFrontend) AksrtFrontend.rehydrate(newComment, 'ajax'); updateCommentCount(1); } function prependToCommentList(el) { var list = document.querySelector('.commentlist, #comment-list > ol, #comment-list'); if (list) 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(); } } // Legacy fallback: POST to wp-comments-post.php function performLegacySubmit() { var formData = collectFormData(); var actionUrl = form.getAttribute('action') || '/wp-comments-post.php'; fetch(actionUrl, { method: 'POST', body: formData, credentials: 'same-origin', headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(function (response) { return response.text().then(function (text) { return { status: response.status, text: text, ok: response.ok }; }); }) .then(function (result) { handleResponse(result.text, result.status); }) .catch(function (error) { console.error('[AKSRT Comment] 提交失败:', error); if (error.message === 'Failed to fetch' || error.name === 'TypeError') { showToast('网络连接失败,请检查网络后重试', 'error'); } else { showToast(error.message || '评论提交失败,请重试', 'error'); } setLoading(false); }) .finally(function () { // 兜底:避免异常分支漏掉状态恢复导致按钮卡在"发送中" if (!countdownTimer && isSubmitting) { setLoading(false); } }); } // 核心表单提交事件处理器 function handleSubmit(e) { // 如果已经在提交中或倒计时中,防止重复提交 if (isSubmitting || countdownTimer) { e.preventDefault(); e.stopImmediatePropagation(); return false; } // 阻止默认表单提交 e.preventDefault(); e.stopImmediatePropagation(); // 检查验证码状态(如果有验证码组件) var captchaScene = window.AKSRTCaptchaState && window.AKSRTCaptchaState.scenes && window.AKSRTCaptchaState.scenes.comment; if (captchaScene && typeof captchaScene.verify === 'function') { // 有验证码:先触发验证流程 setLoading(true); captchaScene.verify(function (verified) { if (verified !== false) { // 验证通过,执行 AJAX 提交 performSubmit(); } else { // 验证失败 showToast('人机验证未通过,请重试', 'warning'); setLoading(false); } }); } else { // 无验证码或已经验证过:直接提交 performSubmit(); } return false; } // 绑定表单提交事件(使用捕获阶段,高优先级) form.addEventListener('submit', handleSubmit, true); } /* ====== 评论列表动态刷新 ====== */ function reloadCommentList() { var listContainer = document.getElementById('comment-list'); if (!listContainer) { // 降级:整页刷新 window.location.reload(); return; } var postIdInput = document.querySelector('input[name="comment_post_ID"]'); if (!postIdInput) return; // 显示骨架屏加载效果 listContainer.innerHTML = '