362 lines
12 KiB
JavaScript
362 lines
12 KiB
JavaScript
/**
|
|
* 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 = '<i class="fas fa-spinner fa-spin"></i> ' + (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 = '<i class="fas fa-check"></i> ' + (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, '');
|
|
}
|
|
});
|
|
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();
|
|
}
|
|
})();
|