(function () {
'use strict';
var rootData = window.aksrtData || {};
var restRoot = (function () {
var siteUrl = String(rootData.siteUrl || '/').replace(/\/+$/, '');
return siteUrl + '/wp-json/aksrt/v1';
})();
function request(path, options) {
var config = options || {};
var headers = config.headers || {};
return fetch(restRoot + path, {
method: config.method || 'GET',
headers: Object.assign({
'Content-Type': 'application/json',
'X-WP-Nonce': rootData.restNonce || '',
}, headers),
body: config.body || null,
credentials: 'same-origin',
}).then(function (response) {
return response.json().catch(function () {
return {};
}).then(function (payload) {
if (!response.ok || payload.success === false) {
throw new Error(payload.message || ('HTTP ' + response.status));
}
return payload;
});
});
}
function setFeedback(node, text, state) {
if (!node) {
return;
}
node.textContent = text || '';
node.classList.remove('is-success', 'is-error', 'is-loading');
if (state) {
node.classList.add(state);
}
}
function bindComposeToolbar(form) {
// §7.4: toolbar replaced by wp_editor TinyMCE — no-op stub kept for boot() compat
}
function insertTextAtCursor(textarea, text, after, start, end) {
var value = textarea.value;
textarea.value = value.substring(0, start) + text + (after || '') + value.substring(end);
textarea.focus();
}
function askUrlInline(anchor, placeholder, callback) {
// kept for potential quicktags fallback
var existing = document.querySelector('.aksrt-inline-url-input');
if (existing) existing.remove();
var input = document.createElement('input');
input.type = 'url';
input.className = 'aksrt-inline-url-input';
input.placeholder = placeholder || 'https://';
input.value = 'https://';
input.style.cssText = 'display:block;width:100%;max-width:320px;margin:4px 0;padding:6px 10px;border:1px solid #dce3ec;border-radius:8px;font-size:13px;';
var parent = anchor.parentElement;
parent.insertBefore(input, anchor.nextSibling);
input.focus();
input.select();
function done() {
var val = input.value.trim();
input.remove();
callback(val);
}
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') { e.preventDefault(); done(); }
if (e.key === 'Escape') { input.remove(); callback(''); }
});
input.addEventListener('blur', function () { done(); });
}
function bindComposeVoteToggle(form) {
var toggle = form.querySelector('#compose-vote-enabled');
var fields = form.querySelector('.aksrt-forum-compose__vote-fields');
if (!toggle || !fields) {
return;
}
function sync() {
fields.hidden = !toggle.checked;
}
toggle.addEventListener('change', sync);
sync();
}
function getEditorContent() {
if (typeof tinymce !== 'undefined') {
var editor = tinymce.get('compose-content');
if (editor && !editor.isHidden()) {
return editor.getContent();
}
}
var textarea = document.querySelector('#compose-content');
return textarea ? textarea.value : '';
}
function bindComposeDraft(form) {
var draftButton = form.querySelector('#compose-draft');
var feedback = form.querySelector('.js-aksrt-action-feedback');
var ajaxUrl = form.getAttribute('data-ajax');
var nonce = form.getAttribute('data-nonce');
if (!draftButton || !ajaxUrl || !nonce) {
return;
}
draftButton.addEventListener('click', function () {
draftButton.disabled = true;
setFeedback(feedback, '正在保存草稿…', 'is-loading');
var data = new FormData(form);
data.set('content', getEditorContent());
data.append('action', 'aksrt_forum_save_draft');
data.append('_nonce', nonce);
var draftId = form.getAttribute('data-draft-id') || '0';
if (draftId && draftId !== '0') {
data.append('draft_id', draftId);
}
fetch(ajaxUrl, {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success) {
throw new Error((payload.data && payload.data.message) || '保存失败');
}
if (payload.data && payload.data.post_id) {
form.setAttribute('data-draft-id', String(payload.data.post_id));
}
setFeedback(feedback, payload.data.message || '草稿已保存', 'is-success');
draftButton.disabled = false;
}).catch(function (error) {
setFeedback(feedback, error.message || '保存失败', 'is-error');
draftButton.disabled = false;
});
});
}
function bindComposeSubmit(form) {
var submitButton = form.querySelector('#compose-submit');
var feedback = form.querySelector('.js-aksrt-action-feedback');
var ajaxUrl = form.getAttribute('data-ajax');
var nonce = form.getAttribute('data-nonce');
if (!submitButton || !ajaxUrl || !nonce) {
return;
}
form.addEventListener('submit', function (event) {
event.preventDefault();
var board = form.querySelector('#compose-board') || form.querySelector('[name="board_id"]');
var title = form.querySelector('#compose-title');
var content = getEditorContent();
if (!board || !board.value || !title || !title.value.trim() || !content.trim()) {
setFeedback(feedback, '请填写版块、标题和主题内容。', 'is-error');
return;
}
submitButton.disabled = true;
setFeedback(feedback, '正在提交主题…', 'is-loading');
var data = new FormData(form);
data.set('content', content);
data.append('action', 'aksrt_forum_create_topic');
data.append('_nonce', nonce);
var draftId = form.getAttribute('data-draft-id') || '0';
if (draftId && draftId !== '0') {
data.append('draft_id', draftId);
}
fetch(ajaxUrl, {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (response) {
return response.json();
}).then(function (payload) {
if (!payload.success) {
throw new Error((payload.data && payload.data.message) || '提交失败');
}
if (payload.data && payload.data.redirect) {
window.location.href = payload.data.redirect;
return;
}
setFeedback(feedback, (payload.data && payload.data.message) || '主题已提交。', 'is-success');
}).catch(function (error) {
setFeedback(feedback, error.message || '提交失败,请稍后重试。', 'is-error');
submitButton.disabled = false;
});
});
}
function bindModeratorApplyForm(form) {
var feedback = form.querySelector('.js-aksrt-action-feedback');
var submitButton = form.querySelector('button[type="submit"]');
form.addEventListener('submit', function (event) {
event.preventDefault();
var boardId = parseInt(form.getAttribute('data-board-id') || '0', 10);
var reasonField = form.querySelector('[name="reason"]');
var reason = reasonField ? reasonField.value.trim() : '';
if (!boardId || !reason) {
setFeedback(feedback, '请填写完整的申请理由。', 'is-error');
return;
}
if (submitButton) {
submitButton.disabled = true;
}
setFeedback(feedback, '正在提交申请…', 'is-loading');
request('/forum/moderator/apply', {
method: 'POST',
body: JSON.stringify({
board_id: boardId,
reason: reason,
}),
}).then(function (payload) {
setFeedback(feedback, payload.message || '申请已提交', 'is-success');
if (submitButton) submitButton.disabled = true;
// Disable the form to prevent re-submission
var reasonField = form.querySelector('[name="reason"]');
if (reasonField) reasonField.disabled = true;
}).catch(function (error) {
setFeedback(feedback, error.message || '申请提交失败。', 'is-error');
if (submitButton) {
submitButton.disabled = false;
}
});
});
}
function bindVoteForm(form) {
var feedback = form.querySelector('.js-aksrt-action-feedback');
var submitButton = form.querySelector('button[type="submit"]');
form.addEventListener('submit', function (event) {
event.preventDefault();
var topicId = parseInt(form.getAttribute('data-topic-id') || '0', 10);
var checked = Array.prototype.slice.call(form.querySelectorAll('input[name="option_ids[]"]:checked')).map(function (input) {
return input.value;
});
if (!topicId || checked.length === 0) {
setFeedback(feedback, '请选择至少一个投票选项。', 'is-error');
return;
}
if (submitButton) {
submitButton.disabled = true;
}
setFeedback(feedback, '正在提交投票…', 'is-loading');
request('/forum/topics/' + topicId + '/vote', {
method: 'POST',
body: JSON.stringify({ option_ids: checked }),
}).then(function (payload) {
setFeedback(feedback, payload.message || '投票成功', 'is-success');
// Disable all checkboxes and submit after vote
var checkboxes = form.querySelectorAll('input[type="checkbox"]');
for (var i = 0; i < checkboxes.length; i++) checkboxes[i].disabled = true;
if (submitButton) submitButton.disabled = true;
}).catch(function (error) {
setFeedback(feedback, error.message || '投票失败。', 'is-error');
if (submitButton) {
submitButton.disabled = false;
}
});
});
}
function buildModerationPayload(form, action) {
var noteField = form.querySelector('[name="note"]');
var payload = {
action: action,
note: noteField ? noteField.value.trim() : '',
};
if (action === 'review') {
payload.review_status = (form.querySelector('[name="review_status"]') || {}).value || '';
} else if (action === 'move_board') {
payload.target_board_id = parseInt((form.querySelector('[name="target_board_id"]') || {}).value || '0', 10);
} else if (action === 'visibility') {
payload.visibility = (form.querySelector('[name="visibility"]') || {}).value || '';
if (payload.visibility === 'credits') {
payload.credits_cost = parseInt((form.querySelector('[name="credits_cost"]') || {}).value || '0', 10);
} else if (payload.visibility === 'pay') {
payload.pay_price = (form.querySelector('[name="pay_price"]') || {}).value || '';
} else if (payload.visibility === 'role') {
payload.visibility_roles = Array.prototype.slice.call(form.querySelectorAll('[name="visibility_roles[]"] option:checked')).map(function (option) {
return option.value;
});
}
} else if (action === 'sticky') {
// 置顶分级(§7.3):发送 0~3 整数,而非布尔值。
payload.value = parseInt((form.querySelector('[name="sticky_value"]') || {}).value || '0', 10);
} else if (action === 'question_status') {
payload.value = parseInt((form.querySelector('[name="question_status_value"]') || {}).value || '0', 10);
} else if (action === 'delete' || action === 'undelete') {
payload.value = true;
} else {
var valueFieldName = action + '_value';
payload.value = ((form.querySelector('[name="' + valueFieldName + '"]') || {}).value || '0') === '1';
}
return payload;
}
function bindModerationFieldToggle(form) {
var actionField = form.querySelector('.js-aksrt-moderation-action');
var groups = form.querySelectorAll('.js-aksrt-moderation-field');
var visibilitySelect = form.querySelector('[name="visibility"]');
var visibilityExtras = form.querySelectorAll('.js-aksrt-visibility-extra');
if (!actionField || !groups.length) {
return;
}
function syncVisibilityExtras() {
if (!visibilitySelect || !visibilityExtras.length) {
return;
}
var visibility = visibilitySelect.value;
visibilityExtras.forEach(function (group) {
group.hidden = actionField.value !== 'visibility' || group.getAttribute('data-visibility-field') !== visibility;
});
}
function sync() {
var action = actionField.value;
groups.forEach(function (group) {
var fieldName = group.getAttribute('data-field');
if (group.classList.contains('js-aksrt-visibility-extra')) {
return;
}
group.hidden = fieldName !== action;
});
syncVisibilityExtras();
}
actionField.addEventListener('change', sync);
if (visibilitySelect) {
visibilitySelect.addEventListener('change', syncVisibilityExtras);
}
sync();
}
function bindModerationForm(form) {
var feedback = form.querySelector('.js-aksrt-action-feedback');
var submitButton = form.querySelector('button[type="submit"]');
bindModerationFieldToggle(form);
form.addEventListener('submit', function (event) {
event.preventDefault();
var topicId = parseInt(form.getAttribute('data-topic-id') || '0', 10);
var actionField = form.querySelector('.js-aksrt-moderation-action');
var action = actionField ? actionField.value : '';
if (!topicId || !action) {
setFeedback(feedback, '请选择治理动作。', 'is-error');
return;
}
if (submitButton) {
submitButton.disabled = true;
}
if (action === 'delete' && !window.confirm('确认将该主题移入回收站吗?')) {
if (submitButton) submitButton.disabled = false;
return;
}
if (action === 'undelete' && !window.confirm('确认恢复该主题吗?')) {
if (submitButton) submitButton.disabled = false;
return;
}
setFeedback(feedback, '正在执行治理动作…', 'is-loading');
request('/forum/topics/' + topicId + '/moderate', {
method: 'POST',
body: JSON.stringify(buildModerationPayload(form, action)),
}).then(function (payload) {
setFeedback(feedback, payload.message || '治理动作已执行', 'is-success');
if (submitButton) submitButton.disabled = true;
}).catch(function (error) {
setFeedback(feedback, error.message || '治理动作执行失败。', 'is-error');
if (submitButton) {
submitButton.disabled = false;
}
});
});
}
function bindQuickTopicAction(button) {
button.addEventListener('click', function () {
var topicId = parseInt(button.getAttribute('data-topic-id') || '0', 10);
var action = button.getAttribute('data-action') || '';
var rawValue = button.getAttribute('data-value') || '0';
var valueType = button.getAttribute('data-value-type') || 'bool';
// 置顶分级(§7.3):data-value-type="int" 时发送整数级别,否则发送布尔值。
var value = valueType === 'int' ? parseInt(rawValue, 10) : rawValue === '1';
if (!topicId || !action) {
return;
}
button.disabled = true;
request('/forum/topics/' + topicId + '/moderate', {
method: 'POST',
body: JSON.stringify({
action: action,
value: value,
}),
}).then(function () {
// Toggle button visual state instead of reloading
var icon = button.querySelector('i');
if (valueType === 'int' ? value > 0 : value) {
button.classList.add('is-active');
if (icon) icon.className = icon.className.replace('far', 'fas');
} else {
button.classList.remove('is-active');
if (icon) icon.className = icon.className.replace('fas', 'far');
}
button.disabled = false;
}).catch(function (error) {
var msg = error.message || '治理动作执行失败。';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); }
else { window.alert(msg); }
button.disabled = false;
});
});
}
function bindFavoriteButton(button) {
button.addEventListener('click', function () {
var topicId = button.getAttribute('data-topic-id');
if (!topicId || button.disabled) {
return;
}
button.disabled = true;
request('/forum/topics/' + topicId + '/favorite', {
method: 'POST',
body: JSON.stringify({}),
}).then(function (payload) {
var isFav = !!(payload.favorited);
var icon = button.querySelector('i');
var label = button.querySelector('.js-aksrt-favorite-label');
button.setAttribute('data-favorited', isFav ? '1' : '0');
if (icon) {
icon.className = isFav ? 'fas fa-bookmark' : 'far fa-bookmark';
}
if (label) {
label.textContent = isFav ? '已收藏' : '收藏';
}
button.disabled = false;
}).catch(function (error) {
var msg = error.message || '操作失败';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); }
else { window.alert(msg); }
button.disabled = false;
});
});
}
function bindScoreButton(button) {
button.addEventListener('click', function () {
var container = button.closest('.aksrt-forum-topic__score-actions');
if (!container || button.disabled) {
return;
}
var topicId = container.getAttribute('data-topic-id');
var amount = parseInt(button.getAttribute('data-amount') || '0', 10);
var feedback = container.querySelector('.js-aksrt-action-feedback');
if (!topicId || amount === 0) {
return;
}
button.disabled = true;
setFeedback(feedback, '提交中…', 'is-loading');
request('/forum/topics/' + topicId + '/score', {
method: 'POST',
body: JSON.stringify({ amount: amount }),
}).then(function (payload) {
var scoreEl = container.querySelector('.js-aksrt-score-value');
if (scoreEl && payload.score !== undefined) {
scoreEl.textContent = String(payload.score);
}
setFeedback(feedback, payload.message || '评分成功', 'is-success');
// Disable both score buttons after success (one-time per user)
container.querySelectorAll('.js-aksrt-topic-score').forEach(function (btn) {
btn.disabled = true;
});
}).catch(function (error) {
setFeedback(feedback, error.message || '评分失败', 'is-error');
button.disabled = false;
});
});
}
function bindAdoptButton(button) {
button.addEventListener('click', function () {
var topicId = button.getAttribute('data-topic-id');
var commentId = button.getAttribute('data-comment-id');
if (!topicId || !commentId || button.disabled) {
return;
}
if (!window.confirm('确认采纳该回复为最佳答案?')) {
return;
}
button.disabled = true;
request('/forum/topics/' + topicId + '/adopt', {
method: 'POST',
body: JSON.stringify({ comment_id: parseInt(commentId, 10) }),
}).then(function (payload) {
button.textContent = '✓ 已采纳';
button.classList.add('is-adopted');
// Disable all other adopt buttons
document.querySelectorAll('.js-aksrt-adopt-answer').forEach(function (btn) {
btn.disabled = true;
});
}).catch(function (error) {
var msg = error.message || '采纳失败';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); }
else { window.alert(msg); }
button.disabled = false;
});
});
}
function bindReportModal(modal) {
var form = modal.querySelector('.js-aksrt-report-form');
var feedback = modal.querySelector('.js-aksrt-action-feedback');
var targetLabel = modal.querySelector('.js-aksrt-report-target');
var targetTypeField = form ? form.querySelector('[name="target_type"]') : null;
var targetIdField = form ? form.querySelector('[name="target_id"]') : null;
var closeButtons = modal.querySelectorAll('.js-aksrt-report-close');
function openModal(trigger) {
if (!form) return;
var targetType = trigger.getAttribute('data-target-type') || 'post';
var targetId = trigger.getAttribute('data-target-id') || '';
var targetTitle = trigger.getAttribute('data-target-title') || '';
if (targetTypeField) targetTypeField.value = targetType;
if (targetIdField) targetIdField.value = targetId;
if (targetLabel) {
targetLabel.textContent = '举报对象:' + (targetTitle || ('#' + targetId));
}
modal.hidden = false;
document.body.classList.add('is-report-modal-open');
}
function closeModal() {
modal.hidden = true;
document.body.classList.remove('is-report-modal-open');
if (form) form.reset();
setFeedback(feedback, '', '');
}
document.querySelectorAll('.js-aksrt-report-trigger').forEach(function (trigger) {
trigger.addEventListener('click', function () {
openModal(trigger);
});
});
closeButtons.forEach(function (btn) {
btn.addEventListener('click', closeModal);
});
if (!form) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
var reason = (form.querySelector('[name="reason"]') || {}).value || '';
if (!reason.trim()) {
setFeedback(feedback, '请填写举报说明。', 'is-error');
return;
}
setFeedback(feedback, '正在提交举报…', 'is-loading');
request('/report/submit', {
method: 'POST',
body: JSON.stringify({
target_type: targetTypeField ? targetTypeField.value : 'post',
target_id: parseInt(targetIdField ? targetIdField.value : '0', 10),
category: (form.querySelector('[name="category"]') || {}).value || 'other',
reason: reason,
}),
}).then(function (payload) {
setFeedback(feedback, payload.message || '举报已提交', 'is-success');
setTimeout(closeModal, 900);
}).catch(function (error) {
setFeedback(feedback, error.message || '举报提交失败。', 'is-error');
});
});
}
function insertForumEditorContent(html) {
if (window.tinymce) {
var ed = window.tinymce.get('compose-content') || window.tinymce.get('post_content');
if (ed && !ed.isHidden()) {
ed.focus();
ed.execCommand('mceInsertContent', false, html);
return;
}
}
var ta = document.getElementById('compose-content') || document.getElementById('post_content');
if (!ta) return;
var start = ta.selectionStart || 0;
var end = ta.selectionEnd || 0;
var val = ta.value || '';
ta.value = val.slice(0, start) + html + val.slice(end);
ta.focus();
}
function bindComposeShortcodeTools(root) {
root = root || document;
root.addEventListener('click', function (e) {
var btn = e.target.closest('[data-insert-shortcode]');
if (!btn) return;
e.preventDefault();
var type = btn.getAttribute('data-insert-shortcode');
if (type === 'hide') {
insertForumEditorContent('[hidecontent type="login"]隐藏内容[/hidecontent]');
} else if (type === 'reply') {
insertForumEditorContent('[hidecontent type="reply"]回复后可见[/hidecontent]');
} else if (type === 'payshow') {
insertForumEditorContent('[hidecontent type="payshow"]付费可见内容[/hidecontent]');
} else if (type === 'vip') {
insertForumEditorContent('[vip]VIP 可见内容[/vip]');
}
});
}
function bindComposeVisibilityToggle(form) {
var visSelect = form.querySelector('#compose-visibility');
var creditsField = form.querySelector('#compose-credits-cost-field');
var payField = form.querySelector('#compose-pay-price-field');
var roleField = form.querySelector('#compose-role-field');
if (!visSelect) return;
function syncVisFields() {
var val = visSelect.value;
if (creditsField) creditsField.style.display = val === 'credits' ? '' : 'none';
if (payField) payField.style.display = val === 'pay' ? '' : 'none';
if (roleField) roleField.style.display = val === 'role' ? '' : 'none';
}
visSelect.addEventListener('change', syncVisFields);
syncVisFields();
}
function bindUnlockButton(button) {
button.addEventListener('click', function () {
var topicId = parseInt(button.getAttribute('data-topic-id') || '0', 10);
var cost = button.getAttribute('data-cost') || '0';
var feedback = button.parentElement ? button.parentElement.querySelector('.js-aksrt-action-feedback') : null;
if (!topicId || !window.confirm('确认使用 ' + cost + ' 积分解锁此主题?')) {
return;
}
button.disabled = true;
setFeedback(feedback, '正在解锁…', 'is-loading');
var data = new FormData();
data.append('action', 'aksrt_forum_unlock_topic');
data.append('topic_id', topicId);
data.append('_nonce', rootData.forumNonce || '');
fetch((rootData.ajaxUrl || '/wp-admin/admin-ajax.php'), {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success) {
throw new Error((payload.data && payload.data.message) || '解锁失败');
}
setFeedback(feedback, '解锁成功,正在刷新…', 'is-success');
setTimeout(function () { window.location.reload(); }, 800);
}).catch(function (error) {
setFeedback(feedback, error.message || '解锁失败', 'is-error');
button.disabled = false;
});
});
}
function formatFollowPayLabel(pay) {
pay = pay || {};
if (pay.is_free) {
return 'VIP 免费关注';
}
if (pay.label) {
return String(pay.label);
}
if (pay.modo === 'points') {
return String(pay.amount || 0) + ' 积分';
}
return '¥' + String(pay.amount || '0');
}
function purchasePlateFollow(plateId, button, payHint) {
payHint = payHint || {};
var modo = String(payHint.modo || 'money');
// Cash (paid or VIP free): multi-gateway cashier — free VIP still creates 0-order for ledger.
// No Credits×100 bypass.
if (modo === 'money') {
button.disabled = false;
if (typeof window.aksrtOpenCashier === 'function') {
window.aksrtOpenCashier(plateId, button, '', '', 'plate_follow');
return;
}
var msgCashier = '收银台未加载,请刷新页面后重试';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msgCashier, 'error'); }
else { window.alert(msgCashier); }
return;
}
// Points / points VIP free: keep AJAX purchase path.
var confirmMsg = '确认付费关注该版块?';
if (payHint.is_free) {
confirmMsg = '确认使用 VIP 免费关注该版块?';
} else {
confirmMsg = '确认支付 ' + formatFollowPayLabel(payHint) + ' 关注该版块?';
}
if (!window.confirm(confirmMsg)) {
button.disabled = false;
return;
}
var data = new FormData();
data.append('action', 'aksrt_forum_purchase_plate_follow');
data.append('plate_id', plateId);
data.append('_nonce', rootData.forumNonce || '');
fetch((rootData.ajaxUrl || '/wp-admin/admin-ajax.php'), {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success) {
var errData = payload.data || {};
// Server may redirect money path to cashier.
if (errData.require_cashier && typeof window.aksrtOpenCashier === 'function') {
button.disabled = false;
window.aksrtOpenCashier(plateId, button, '', '', 'plate_follow');
return;
}
throw new Error(errData.message || '支付失败');
}
var res = payload.data || {};
button.setAttribute('data-followed', '1');
button.classList.add('is-followed');
if (button.querySelector('i') && button.childNodes.length) {
button.innerHTML = ' 已关注';
} else {
button.textContent = '已关注';
}
if (typeof window.aksrtToast === 'function') {
window.aksrtToast(res.message || '关注成功', 'success');
}
setTimeout(function () { window.location.reload(); }, 600);
}).catch(function (error) {
var msg = error.message || '支付失败';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); }
else { window.alert(msg); }
button.disabled = false;
});
}
function bindFollowPlateButton(button) {
button.addEventListener('click', function () {
var plateId = parseInt(button.getAttribute('data-plate-id') || '0', 10);
if (!plateId || button.disabled) {
return;
}
button.disabled = true;
var data = new FormData();
data.append('action', 'aksrt_forum_follow_plate');
data.append('plate_id', plateId);
data.append('_nonce', rootData.forumNonce || '');
fetch((rootData.ajaxUrl || '/wp-admin/admin-ajax.php'), {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
// Paid follow required
if (!payload.success && payload.data && payload.data.require_pay) {
purchasePlateFollow(plateId, button, payload.data.pay || {});
return;
}
if (!payload.success) {
throw new Error((payload.data && payload.data.message) || '操作失败');
}
var res = payload.data || {};
var followed = !!res.followed;
button.setAttribute('data-followed', followed ? '1' : '0');
button.classList.toggle('is-followed', followed);
button.innerHTML = followed
? ' 已关注'
: ' 关注';
button.disabled = false;
}).catch(function (error) {
var msg = error.message || '操作失败';
if (typeof window.aksrtToast === 'function') { window.aksrtToast(msg, 'error'); }
else { window.alert(msg); }
button.disabled = false;
});
});
}
function bindComposeCoverToggle(form) {
var coverTypeSelect = form.querySelector('#compose-cover-type');
var imageField = form.querySelector('#compose-cover-image-field');
var slideField = form.querySelector('#compose-cover-slide-field');
var videoField = form.querySelector('#compose-cover-video-field');
if (!coverTypeSelect) return;
function syncCoverFields() {
var val = coverTypeSelect.value;
if (imageField) imageField.style.display = val === 'image' ? '' : 'none';
if (slideField) slideField.style.display = val === 'slide' ? '' : 'none';
if (videoField) videoField.style.display = val === 'video' ? '' : 'none';
}
coverTypeSelect.addEventListener('change', syncCoverFields);
syncCoverFields();
var imageInput = form.querySelector('#compose-cover-image');
var imagePreview = form.querySelector('#compose-cover-preview');
var imageBtn = form.querySelector('#compose-cover-upload-btn');
if (imageBtn && typeof wp !== 'undefined' && wp.media) {
imageBtn.addEventListener('click', function () {
var frame = wp.media({ title: '选择封面图片', multiple: false, library: { type: 'image' } });
frame.on('select', function () {
var attachment = frame.state().get('selection').first().toJSON();
if (imageInput) imageInput.value = attachment.url;
if (imagePreview) imagePreview.innerHTML = '';
});
frame.open();
});
}
var slideInput = form.querySelector('#compose-featured-slide');
var slidePreview = form.querySelector('#compose-slide-preview');
var slideBtn = form.querySelector('#compose-slide-upload-btn');
if (slideBtn && typeof wp !== 'undefined' && wp.media) {
slideBtn.addEventListener('click', function () {
var frame = wp.media({ title: '选择幻灯片图片', multiple: true, library: { type: 'image' } });
frame.on('select', function () {
var urls = frame.state().get('selection').map(function (a) { return a.toJSON().url; });
urls = urls.slice(0, 9);
if (slideInput) slideInput.value = JSON.stringify(urls);
if (slidePreview) {
slidePreview.innerHTML = urls.map(function (u) { return '
'; }).join('');
}
});
frame.open();
});
}
}
function bindEditPlateButton(button) {
button.addEventListener('click', function () {
var plateId = button.getAttribute('data-plate-id');
var name = window.prompt('版块名称:', button.getAttribute('data-name') || '');
if (name === null) return;
var description = window.prompt('版块描述:', button.getAttribute('data-description') || '');
if (description === null) description = '';
request('/forum/front/plates/' + plateId, {
method: 'POST',
body: JSON.stringify({ name: name, description: description }),
}).then(function () {
window.location.reload();
}).catch(function (error) {
window.alert(error.message || '编辑失败');
});
});
}
function bindDeletePlateButton(button) {
button.addEventListener('click', function () {
var plateId = button.getAttribute('data-plate-id');
var topicCount = parseInt(button.getAttribute('data-topic-count') || '0', 10);
var msg = '确认删除此版块?';
if (topicCount > 0) {
msg += '\n该版块下有 ' + topicCount + ' 个主题,删除后将移入回收站。';
}
if (!window.confirm(msg)) return;
request('/forum/front/plates/' + plateId, {
method: 'DELETE',
body: JSON.stringify({ cascade: 'trash' }),
}).then(function () {
window.location.href = (rootData.siteUrl || '') + '/forums/';
}).catch(function (error) {
window.alert(error.message || '删除失败');
});
});
}
// ──────────────────────────────────────────────────────────
// §7.4 — Remote search selector
// ──────────────────────────────────────────────────────────
function bindRemoteSelects(container) {
var selects = container.querySelectorAll('.aksrt-remote-select');
selects.forEach(function (wrapper) {
var searchAction = wrapper.getAttribute('data-search-action');
var hiddenInput = wrapper.querySelector('input[type="hidden"]');
var textInput = wrapper.querySelector('.js-aksrt-remote-input');
var dropdown = wrapper.querySelector('.aksrt-remote-select__dropdown');
if (!searchAction || !hiddenInput || !textInput || !dropdown) return;
var debounceTimer = null;
var ajaxUrl = (rootData.ajaxUrl || '/wp-admin/admin-ajax.php');
textInput.addEventListener('input', function () {
clearTimeout(debounceTimer);
var q = textInput.value.trim();
debounceTimer = setTimeout(function () {
fetch(ajaxUrl + '?action=' + encodeURIComponent(searchAction) + '&q=' + encodeURIComponent(q), {
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success || !payload.data) return;
dropdown.innerHTML = '';
payload.data.forEach(function (item) {
var div = document.createElement('div');
div.className = 'aksrt-remote-select__option';
div.textContent = item.name;
div.setAttribute('data-id', String(item.id));
div.addEventListener('mousedown', function (e) {
e.preventDefault();
hiddenInput.value = item.id;
textInput.value = item.name;
dropdown.hidden = true;
});
dropdown.appendChild(div);
});
dropdown.hidden = payload.data.length === 0;
});
}, 250);
});
textInput.addEventListener('focus', function () {
if (dropdown.children.length > 0) dropdown.hidden = false;
textInput.dispatchEvent(new Event('input'));
});
textInput.addEventListener('blur', function () {
setTimeout(function () { dropdown.hidden = true; }, 200);
});
});
}
// ──────────────────────────────────────────────────────────
// §7.4 — Tag tokenizer
// ──────────────────────────────────────────────────────────
function bindTagInputs(container) {
var wrappers = container.querySelectorAll('.aksrt-tag-input');
wrappers.forEach(function (wrapper) {
var searchAction = wrapper.getAttribute('data-search-action');
var maxTags = parseInt(wrapper.getAttribute('data-max') || '5', 10);
var hiddenInput = wrapper.querySelector('input[type="hidden"]');
var tokenContainer = wrapper.querySelector('.aksrt-tag-input__tokens');
var textInput = wrapper.querySelector('.js-aksrt-tag-search');
var dropdown = wrapper.querySelector('.aksrt-tag-input__dropdown');
if (!hiddenInput || !tokenContainer || !textInput) return;
var tags = [];
var initialVal = (hiddenInput.value || '').trim();
if (initialVal) {
initialVal.split(/[,,]/).forEach(function (t) {
t = t.trim();
if (t && tags.indexOf(t) === -1) tags.push(t);
});
}
function syncHidden() {
hiddenInput.value = tags.join(', ');
}
function renderTokens() {
tokenContainer.innerHTML = '';
tags.forEach(function (tag, idx) {
var span = document.createElement('span');
span.className = 'aksrt-tag-input__token';
span.textContent = tag;
var close = document.createElement('button');
close.type = 'button';
close.className = 'aksrt-tag-input__token-remove';
close.innerHTML = '×';
close.addEventListener('click', function () {
tags.splice(idx, 1);
syncHidden();
renderTokens();
});
span.appendChild(close);
tokenContainer.appendChild(span);
});
}
function addTag(name) {
name = name.trim();
if (!name || tags.indexOf(name) !== -1 || tags.length >= maxTags) return;
tags.push(name);
syncHidden();
renderTokens();
textInput.value = '';
}
renderTokens();
textInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter' || e.key === ',') {
e.preventDefault();
addTag(textInput.value);
if (dropdown) dropdown.hidden = true;
}
if (e.key === 'Backspace' && textInput.value === '' && tags.length > 0) {
tags.pop();
syncHidden();
renderTokens();
}
});
if (searchAction && dropdown) {
var ajaxUrl = (rootData.ajaxUrl || '/wp-admin/admin-ajax.php');
var debounce = null;
textInput.addEventListener('input', function () {
clearTimeout(debounce);
var q = textInput.value.trim();
if (!q) { dropdown.hidden = true; return; }
debounce = setTimeout(function () {
fetch(ajaxUrl + '?action=' + encodeURIComponent(searchAction) + '&q=' + encodeURIComponent(q), {
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success || !payload.data) return;
dropdown.innerHTML = '';
payload.data.forEach(function (item) {
var div = document.createElement('div');
div.className = 'aksrt-tag-input__suggestion';
div.textContent = item.name + (item.count !== undefined ? ' (' + item.count + ')' : '');
div.addEventListener('mousedown', function (e) {
e.preventDefault();
addTag(item.name);
dropdown.hidden = true;
});
dropdown.appendChild(div);
});
dropdown.hidden = payload.data.length === 0;
});
}, 250);
});
textInput.addEventListener('blur', function () {
setTimeout(function () { dropdown.hidden = true; }, 200);
});
}
});
}
// ──────────────────────────────────────────────────────────
// §7.4 — Title character counter
// ──────────────────────────────────────────────────────────
function bindTitleCounter(form) {
var titleInput = form.querySelector('#compose-title');
var counterCurrent = form.querySelector('.js-aksrt-counter-current');
if (!titleInput || !counterCurrent) return;
function update() {
counterCurrent.textContent = String(titleInput.value.length);
var max = parseInt(titleInput.getAttribute('data-aksrt-maxlength') || '30', 10);
var parent = counterCurrent.closest('.aksrt-forum-compose__char-counter');
if (parent) {
parent.classList.toggle('is-over', titleInput.value.length > max);
parent.classList.toggle('is-near', titleInput.value.length > max * 0.8 && titleInput.value.length <= max);
}
}
titleInput.addEventListener('input', update);
update();
}
// ──────────────────────────────────────────────────────────
// §7.4 — Draft list toggle & delete
// ──────────────────────────────────────────────────────────
function bindDraftUI() {
var toggle = document.querySelector('.js-aksrt-draft-toggle');
var list = document.querySelector('#compose-draft-list');
if (toggle && list) {
toggle.addEventListener('click', function () {
list.hidden = !list.hidden;
var icon = toggle.querySelector('i.fa-chevron-down, i.fa-chevron-up');
if (icon) {
icon.classList.toggle('fa-chevron-down');
icon.classList.toggle('fa-chevron-up');
}
});
}
document.querySelectorAll('.js-aksrt-delete-draft').forEach(function (btn) {
btn.addEventListener('click', function () {
var draftId = btn.getAttribute('data-draft-id');
if (!draftId || !window.confirm('确认删除此草稿?')) return;
var ajaxUrl = (rootData.ajaxUrl || '/wp-admin/admin-ajax.php');
var nonce = '';
var form = document.querySelector('.js-aksrt-forum-compose-form');
if (form) nonce = form.getAttribute('data-nonce') || '';
var data = new FormData();
data.append('action', 'aksrt_forum_delete_draft');
data.append('_nonce', nonce);
data.append('draft_id', draftId);
fetch(ajaxUrl, { method: 'POST', body: data, credentials: 'same-origin' })
.then(function (r) {
return r.text().then(function (text) {
var payload = null;
try {
payload = text ? JSON.parse(text) : null;
} catch (e) {
payload = null;
}
return { ok: r.ok, status: r.status, payload: payload, raw: text };
});
})
.then(function (res) {
if (res.payload && res.payload.success) {
var item = btn.closest('.aksrt-forum-compose__draft-item');
if (item) item.remove();
return;
}
var msg = (res.payload && res.payload.data && res.payload.data.message)
|| (res.payload && res.payload.message)
|| (res.status === 500
? '服务器错误:请确认主题已更新到最新 main(Forum.php 草稿 autoload 修复)'
: '删除失败');
if (window.console && console.warn) {
console.warn('[aksrt] delete draft failed', res.status, msg);
}
window.alert(msg);
})
.catch(function (err) {
if (window.console && console.warn) {
console.warn('[aksrt] delete draft network error', err);
}
window.alert('删除失败,请稍后重试');
});
});
});
}
// ──────────────────────────────────────────────────────────
// §7.4 — Edit form AJAX submit
// ──────────────────────────────────────────────────────────
function bindEditForm(form) {
var submitButton = form.querySelector('#compose-submit');
var feedback = form.querySelector('.js-aksrt-action-feedback');
var ajaxUrl = form.getAttribute('data-ajax');
var nonce = form.getAttribute('data-nonce');
var topicId = form.getAttribute('data-topic-id');
if (!submitButton || !ajaxUrl || !nonce || !topicId) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
var title = form.querySelector('#compose-title');
var content = getEditorContent();
if (!title || !title.value.trim() || !content.trim()) {
setFeedback(feedback, '标题和内容不能为空。', 'is-error');
return;
}
submitButton.disabled = true;
setFeedback(feedback, '正在保存…', 'is-loading');
var data = new FormData(form);
data.set('content', content);
data.append('action', 'aksrt_forum_edit_topic');
data.append('_nonce', nonce);
data.append('topic_id', topicId);
fetch(ajaxUrl, {
method: 'POST',
body: data,
credentials: 'same-origin',
}).then(function (r) { return r.json(); }).then(function (payload) {
if (!payload.success) {
throw new Error((payload.data && payload.data.message) || '保存失败');
}
if (payload.data && payload.data.redirect) {
window.location.href = payload.data.redirect;
return;
}
setFeedback(feedback, payload.data.message || '已保存', 'is-success');
submitButton.disabled = false;
}).catch(function (error) {
setFeedback(feedback, error.message || '保存失败', 'is-error');
submitButton.disabled = false;
});
});
}
// ── §7.6 Forum comment engine ─────────────────────────────────────────
function bindForumCommentEngine() {
var section = document.getElementById('aksrt-forum-replies');
if (!section) return;
var topicId = section.dataset.topicId;
var nonce = section.dataset.nonce;
var list = section.querySelector('.js-aksrt-forum-replies-list');
var loadMore = section.querySelector('.js-aksrt-forum-load-more');
var currentSort = 'newest';
var currentAuthorOnly = false;
// Sort tabs
section.querySelectorAll('.aksrt-forum-replies__tab').forEach(function (tab) {
tab.addEventListener('click', function () {
section.querySelectorAll('.aksrt-forum-replies__tab').forEach(function (t) { t.classList.remove('is-active'); });
tab.classList.add('is-active');
currentSort = tab.dataset.sort || 'newest';
loadComments(1);
});
});
// Author-only toggle
var authorToggle = section.querySelector('.aksrt-forum-replies__author-toggle');
if (authorToggle) {
authorToggle.addEventListener('change', function () {
currentAuthorOnly = authorToggle.checked;
loadComments(1);
});
}
// Load more button
if (loadMore) {
loadMore.addEventListener('click', function () {
var page = parseInt(loadMore.dataset.page, 10) || 1;
loadComments(page + 1, true);
});
}
function loadComments(page, append) {
var fd = new FormData();
fd.append('action', 'aksrt_forum_comments');
fd.append('nonce', nonce);
fd.append('topic_id', topicId);
fd.append('sort', currentSort);
fd.append('author_only', currentAuthorOnly ? '1' : '');
fd.append('page', page);
list.style.opacity = append ? '1' : '0.5';
fetch(window.aksrtForumGovernance ? window.aksrtForumGovernance.ajaxUrl : (window.ajaxurl || '/wp-admin/admin-ajax.php'), {
method: 'POST',
body: fd,
credentials: 'same-origin'
}).then(function (r) { return r.json(); })
.then(function (res) {
list.style.opacity = '1';
if (!res.success) return;
if (append) {
list.insertAdjacentHTML('beforeend', res.data.html);
} else {
list.innerHTML = res.data.html;
}
if (loadMore) {
loadMore.dataset.page = String(page);
loadMore.dataset.max = String(res.data.max_pages);
loadMore.closest('.aksrt-forum-replies__load-more').style.display =
res.data.has_more ? '' : 'none';
}
bindDynamicCommentActions(list);
}).catch(function () {
list.style.opacity = '1';
});
}
bindDynamicCommentActions(list);
}
function bindDynamicCommentActions(container) {
if (!container) return;
container.querySelectorAll('.js-aksrt-comment-like').forEach(bindCommentLike);
container.querySelectorAll('.js-aksrt-adopt-answer').forEach(bindAdoptButton);
container.querySelectorAll('.js-aksrt-comment-reply').forEach(bindCommentReply);
container.querySelectorAll('.js-aksrt-comment-top').forEach(bindCommentTop);
}
function bindCommentLike(btn) {
if (btn._likebound) return;
btn._likebound = true;
btn.addEventListener('click', function () {
var commentId = btn.dataset.commentId;
var active = btn.dataset.active === '1';
var newActive = !active;
btn.disabled = true;
fetch(window.aksrtForumGovernance ? window.aksrtForumGovernance.restUrl + 'aksrt/v1/comment/like' : '/wp-json/aksrt/v1/comment/like', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': window.aksrtForumGovernance ? window.aksrtForumGovernance.restNonce : ''
},
body: JSON.stringify({ comment_id: parseInt(commentId, 10), active: newActive }),
credentials: 'same-origin'
}).then(function (r) { return r.json(); })
.then(function (res) {
btn.disabled = false;
if (!res.success && !res.data) return;
var data = res.data || res;
btn.dataset.active = data.active ? '1' : '0';
btn.classList.toggle('is-active', !!data.active);
var icon = btn.querySelector('i');
if (icon) {
icon.className = data.active ? 'fas fa-thumbs-up' : 'far fa-thumbs-up';
}
var count = btn.querySelector('.aksrt-forum-reply__like-count');
if (count) {
count.textContent = data.count > 0 ? String(data.count) : '';
}
}).catch(function () { btn.disabled = false; });
});
}
function bindCommentReply(btn) {
if (btn._replybound) return;
btn._replybound = true;
btn.addEventListener('click', function () {
var commentId = btn.dataset.commentId;
var author = btn.dataset.author || '';
var form = document.querySelector('.aksrt-forum-topic__reply-form');
if (!form) return;
var textarea = form.querySelector('textarea[name="comment"]');
if (!textarea) return;
// Set reply parent
var parentInput = form.querySelector('input[name="comment_parent"]');
if (!parentInput) {
parentInput = document.createElement('input');
parentInput.type = 'hidden';
parentInput.name = 'comment_parent';
form.querySelector('form').appendChild(parentInput);
}
parentInput.value = commentId;
textarea.placeholder = '@' + author + ' ';
textarea.focus();
form.scrollIntoView({ behavior: 'smooth', block: 'center' });
});
}
function bindCommentTop(btn) {
if (btn._topbound) return;
btn._topbound = true;
btn.addEventListener('click', function () {
var commentId = btn.dataset.commentId;
var currentLevel = parseInt(btn.dataset.level, 10) || 0;
var newLevel = currentLevel > 0 ? 0 : 1;
btn.disabled = true;
var section = document.getElementById('aksrt-forum-replies');
var nonce = section ? section.dataset.nonce : '';
var fd = new FormData();
fd.append('action', 'aksrt_forum_comment_top');
fd.append('nonce', nonce);
fd.append('comment_id', commentId);
fd.append('level', newLevel);
fetch(window.aksrtForumGovernance ? window.aksrtForumGovernance.ajaxUrl : (window.ajaxurl || '/wp-admin/admin-ajax.php'), {
method: 'POST',
body: fd,
credentials: 'same-origin'
}).then(function (r) { return r.json(); })
.then(function (res) {
btn.disabled = false;
if (res.success) {
btn.dataset.level = String(newLevel);
var icon = btn.querySelector('i');
btn.childNodes.forEach(function (n) {
if (n.nodeType === 3) n.textContent = newLevel > 0 ? ' 取消置顶' : ' 置顶';
});
// Reload comments to reflect pin ordering
var activeTab = section && section.querySelector('.aksrt-forum-replies__tab.is-active');
if (activeTab) activeTab.click();
}
}).catch(function () { btn.disabled = false; });
});
}
// ── §7.10 Forum home tab engine ──────────────────────────────────────
function bindForumHomeTabs() {
var nav = document.getElementById('aksrt-forum-home-tabs');
if (!nav) return;
var content = document.getElementById('aksrt-forum-home-content');
if (!content) return;
var nonce = nav.dataset.nonce || '';
var ajaxUrl = nav.dataset.ajaxUrl || (window.aksrtForumGovernance ? window.aksrtForumGovernance.ajaxUrl : '/wp-admin/admin-ajax.php');
nav.querySelectorAll('.aksrt-forum-home-tabs__btn').forEach(function (btn) {
btn.addEventListener('click', function () {
var tabType = btn.dataset.tabType;
nav.querySelectorAll('.aksrt-forum-home-tabs__btn').forEach(function (b) { b.classList.remove('is-active'); });
btn.classList.add('is-active');
loadHomeTab(tabType, 1);
});
});
// Load-more on home content
content.addEventListener('click', function (e) {
var moreBtn = e.target.closest('.js-aksrt-home-load-more');
if (!moreBtn) return;
var page = parseInt(moreBtn.dataset.page, 10) || 1;
var tabType = moreBtn.dataset.tab || 'synthesis';
loadHomeTab(tabType, page + 1, true, moreBtn);
});
function loadHomeTab(tabType, page, append, moreBtn) {
var fd = new FormData();
fd.append('action', 'aksrt_forum_home_tab');
fd.append('nonce', nonce);
fd.append('tab_type', tabType);
fd.append('page', page);
if (!append) {
content.style.opacity = '0.5';
}
if (moreBtn) moreBtn.disabled = true;
fetch(ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(function (res) {
content.style.opacity = '1';
if (!res.success) return;
if (res.data.type === 'plate') {
// Plate tab — reload the whole page to get server-rendered board grid
window.location.href = window.location.pathname + '?forum_tab=plate';
return;
}
if (append) {
var list = content.querySelector('.js-aksrt-home-topic-list');
if (list) {
list.insertAdjacentHTML('beforeend', res.data.html);
}
if (moreBtn) {
moreBtn.disabled = false;
moreBtn.dataset.page = String(page);
if (!res.data.has_more) {
moreBtn.closest('.aksrt-forum-home__load-more').style.display = 'none';
}
}
} else {
var html = '