/** * AKSRT Front Submit * * Handles front-end post submission, draft save/load, * editor toolbar, cover preview, and form protection. * * @package AKSRT */ (function () { 'use strict'; var i18n = (window.aksrtSubmitData && window.aksrtSubmitData.i18n) || {}; var ajaxUrl = (window.aksrtSubmitData && window.aksrtSubmitData.ajaxUrl) || '/wp-admin/admin-ajax.php'; var nonce = (window.aksrtSubmitData && window.aksrtSubmitData.nonce) || ''; var FRONT_SUBMIT = { form: null, isDirty: false, isSubmitting: false, init: function () { this.form = document.getElementById('aksrt-submit-form'); if (!this.form) return; this.bindDirtyTracking(); this.bindSubmit(); this.bindDraftSave(); this.bindDraftDelete(); this.bindEditorToolbar(); this.bindCoverPreview(); this.bindWordCount(); this.bindLeaveWarning(); }, /** * Track form changes */ bindDirtyTracking: function () { var self = this; this.form.addEventListener('input', function () { self.isDirty = true; self.form.setAttribute('data-dirty', 'true'); }); }, /** * Leave page warning */ bindLeaveWarning: function () { var self = this; window.addEventListener('beforeunload', function (e) { if (self.isDirty && !self.isSubmitting) { e.preventDefault(); e.returnValue = i18n.confirmLeave || ''; } }); }, /** * Form submission */ bindSubmit: function () { var self = this; this.form.addEventListener('submit', function (e) { e.preventDefault(); if (self.isSubmitting) return; var submitBtn = document.getElementById('aksrt-submit-btn'); var statusInput = document.getElementById('submit-status'); statusInput.value = 'pending'; self.submitForm(submitBtn, 'pending'); }); }, /** * Draft save */ bindDraftSave: function () { var self = this; var draftBtn = document.getElementById('aksrt-save-draft'); if (!draftBtn) return; draftBtn.addEventListener('click', function () { if (self.isSubmitting) return; var statusInput = document.getElementById('submit-status'); statusInput.value = 'draft'; self.submitForm(draftBtn, 'draft'); }); }, /** * Submit form via AJAX */ submitForm: function (btn, status) { var self = this; var formData = new FormData(this.form); formData.append('action', 'aksrt_submit_post'); formData.append('_nonce', nonce); var originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = ' ' + (status === 'draft' ? (i18n.saving || '保存中...') : (i18n.submitting || '提交中...')); self.isSubmitting = true; fetch(ajaxUrl, { method: 'POST', body: formData, credentials: 'same-origin', }) .then(function (res) { return res.json(); }) .then(function (data) { if (data.success) { self.isDirty = false; self.form.setAttribute('data-dirty', 'false'); btn.innerHTML = ' ' + (data.data && data.data.message ? data.data.message : (status === 'draft' ? (i18n.saved || '草稿已保存') : (i18n.submitted || '投稿已提交'))); btn.classList.add('is-success'); if (status === 'pending' && data.data && data.data.redirect) { setTimeout(function () { window.location.href = data.data.redirect; }, 1500); } else if (status === 'draft') { // Reload to update draft list setTimeout(function () { window.location.reload(); }, 1000); } } else { alert(data.data && data.data.message ? data.data.message : (i18n.error || '操作失败')); btn.disabled = false; btn.innerHTML = originalText; } }) .catch(function () { alert(i18n.error || '操作失败,请重试'); btn.disabled = false; btn.innerHTML = originalText; }) .finally(function () { self.isSubmitting = false; }); }, /** * Draft delete */ bindDraftDelete: function () { document.addEventListener('click', function (e) { var btn = e.target.closest('.aksrt-submit-drafts__delete'); if (!btn) return; var draftId = btn.getAttribute('data-draft-id'); if (!draftId) return; if (!confirm(i18n.confirmDelete || '确定要删除这个草稿吗?')) return; var formData = new FormData(); formData.append('action', 'aksrt_delete_draft'); formData.append('_nonce', nonce); formData.append('draft_id', draftId); fetch(ajaxUrl, { method: 'POST', body: formData, credentials: 'same-origin', }) .then(function (res) { return res.json(); }) .then(function (data) { if (data.success) { var item = btn.closest('.aksrt-submit-drafts__item'); if (item) item.remove(); } else { alert(data.data && data.data.message ? data.data.message : '删除失败'); } }) .catch(function () { alert('删除失败'); }); }); }, /** * Editor toolbar actions */ bindEditorToolbar: function () { var textarea = document.getElementById('submit-content'); if (!textarea) return; document.addEventListener('click', function (e) { var btn = e.target.closest('[data-action]'); if (!btn) return; var action = btn.getAttribute('data-action'); var form = btn.closest('.aksrt-submit-form'); if (!form) return; var ta = form.querySelector('textarea'); if (!ta) return; switch (action) { case 'bold': FRONT_SUBMIT.wrapSelection(ta, '**'); break; case 'italic': FRONT_SUBMIT.wrapSelection(ta, '*'); break; case 'code': var sel = ta.value.substring(ta.selectionStart, ta.selectionEnd); if (sel.indexOf('\n') >= 0) { FRONT_SUBMIT.insertAtCursor(ta, '```\n', '\n```'); } else { FRONT_SUBMIT.wrapSelection(ta, '`'); } break; case 'link': FRONT_SUBMIT.askUrl(btn, '链接地址', function (url) { if (url) { var linkText = ta.value.substring(ta.selectionStart, ta.selectionEnd) || '链接文字'; FRONT_SUBMIT.insertAtCursor(ta, '[' + linkText + '](' + url + ')'); } }); break; case 'image': FRONT_SUBMIT.askUrl(btn, '图片地址', function (url) { if (url) { FRONT_SUBMIT.insertAtCursor(ta, '![图片](' + url + ')'); } }); break; case 'quote': FRONT_SUBMIT.insertAtCursor(ta, '> ', '\n'); break; case 'heading': FRONT_SUBMIT.insertAtCursor(ta, '## ', '\n'); break; case 'list': FRONT_SUBMIT.insertAtCursor(ta, '- ', '\n'); break; } }); }, askUrl: function (anchor, placeholder, callback) { 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(); }); }, insertAtCursor: function (textarea, before, after) { var start = textarea.selectionStart; var end = textarea.selectionEnd; var text = textarea.value; var selected = text.substring(start, end); var replacement = before + (selected || '') + (after || ''); textarea.value = text.substring(0, start) + replacement + text.substring(end); textarea.focus(); var newPos = start + before.length + selected.length; textarea.setSelectionRange(newPos, newPos); textarea.dispatchEvent(new Event('input', { bubbles: true })); }, wrapSelection: function (textarea, marker) { this.insertAtCursor(textarea, marker, marker); }, /** * Cover image preview */ bindCoverPreview: function () { var thumbInput = document.getElementById('submit-thumbnail'); var previewWrap = document.getElementById('cover-preview'); var previewImg = document.getElementById('cover-preview-img'); var removeBtn = document.getElementById('cover-remove'); if (!thumbInput || !previewWrap || !previewImg) return; thumbInput.addEventListener('change', function () { var url = thumbInput.value.trim(); if (url && /^https?:\/\//.test(url)) { previewImg.src = url; previewWrap.style.display = 'flex'; previewImg.onerror = function () { previewWrap.style.display = 'none'; }; } else { previewWrap.style.display = 'none'; } }); if (removeBtn) { removeBtn.addEventListener('click', function () { thumbInput.value = ''; previewWrap.style.display = 'none'; thumbInput.dispatchEvent(new Event('input', { bubbles: true })); }); } // Media library picker var pickBtn = document.getElementById('aksrt-pick-cover'); if (pickBtn && window.wp && window.wp.media) { pickBtn.addEventListener('click', function (e) { e.preventDefault(); var mediaFrame = wp.media({ title: '选择封面图', button: { text: '使用此图片' }, multiple: false, library: { type: 'image' }, }); mediaFrame.on('select', function () { var attachment = mediaFrame.state().get('selection').first().toJSON(); thumbInput.value = attachment.url; previewImg.src = attachment.url; previewWrap.style.display = 'flex'; thumbInput.dispatchEvent(new Event('input', { bubbles: true })); }); mediaFrame.open(); }); } }, /** * Word count */ bindWordCount: function () { var contentEl = document.getElementById('submit-content'); var countEl = document.getElementById('submit-word-count'); if (!contentEl || !countEl) return; var updateCount = function () { var text = contentEl.value.replace(/<[^>]*>/g, '').trim(); var chars = text.length; var words = text ? text.split(/\s+/).length : 0; countEl.textContent = chars + ' 字 · ~' + words + ' 词'; }; contentEl.addEventListener('input', updateCount); updateCount(); }, }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function () { FRONT_SUBMIT.init(); }); } else { FRONT_SUBMIT.init(); } })();