/** * Front-submit TinyMCE tools matching the reference article editor. * * @package AKSRT */ (function (window, document) { 'use strict'; if (!window.tinymce || !window.tinymce.PluginManager) { return; } var cfg = window.aksrtSubmitData || {}; var editorCfg = cfg.editor || {}; var i18n = cfg.i18n || {}; var activeModal = null; function notify(message, type, duration, id) { if (typeof window.notyf === 'function') { window.notyf( message, type, duration === undefined ? 5 : duration, id || 'aksrt-front-submit-editor-image-toast' ); } } function escapeAttr(value) { return String(value) .replace(/&/g, '&') .replace(/"/g, '"') .replace(//g, '>'); } function validImageUrl(value) { var raw = String(value).trim(); if (!/^https?:\/\//i.test(raw)) { return ''; } try { var url = new URL(raw); return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : ''; } catch (error) { return ''; } } function imageMarkup(url, attachmentId, alt) { var safeUrl = validImageUrl(url); if (!safeUrl) { return ''; } var idAttr = attachmentId ? ' data-edit-file-id="' + Number(attachmentId) + '"' : ''; var altAttr = alt ? ' alt="' + escapeAttr(alt) + '"' : ' alt=""'; return '

'; } function closeImageModal() { if (!activeModal) { return; } document.removeEventListener('keydown', activeModal.onKeydown); activeModal.root.remove(); activeModal = null; document.body.classList.remove('aksrt-editor-media-open'); } function uploadImage(file) { var upload = (cfg.uploads || {}).image || null; if (!upload || !upload.url) { return Promise.reject(new Error(i18n.uploadUnavailable || '当前账号不能上传图片')); } if (!file || !/^image\//.test(file.type || '')) { return Promise.reject(new Error(i18n.invalidImage || '请选择有效的图片文件')); } if (upload.maxSize && file.size > upload.maxSize) { return Promise.reject(new Error(i18n.fileTooLarge || '文件大小超过限制')); } var body = new FormData(); body.append('file', file); return fetch(upload.url, { method: 'POST', credentials: 'same-origin', headers: { 'X-WP-Nonce': cfg.restNonce || '' }, body: body, }).then(function (response) { return response.json().catch(function () { return {}; }).then(function (payload) { if (!response.ok || !payload || !payload.success || !payload.url) { throw new Error((payload && payload.message) || i18n.error || '图片上传失败'); } return payload; }); }); } function createImageModal(editor) { closeImageModal(); var canUpload = !!editorCfg.canUploadImage && !!((cfg.uploads || {}).image); var root = document.createElement('div'); root.className = 'aksrt-editor-media'; root.setAttribute('role', 'dialog'); root.setAttribute('aria-modal', 'true'); root.setAttribute('aria-labelledby', 'aksrt-editor-media-title'); root.innerHTML = '' + '
' + '' + '

' + escapeAttr(i18n.insertImage || '插入图片') + '

' + '
' + (canUpload ? '' : '') + '' + '
' + '
' + (canUpload ? '
' + '' + '
' + '' + '
' : '') + '
' + '

' + escapeAttr(i18n.externalImageHint || '每行填写一个图片地址:') + '

' + '' + '' + '
' + '
' + '
'; document.body.appendChild(root); document.body.classList.add('aksrt-editor-media-open'); var fileInput = root.querySelector('[data-editor-media-files]'); var uploadButton = root.querySelector('[data-editor-media-upload]'); var selection = root.querySelector('[data-editor-media-selection]'); var drop = root.querySelector('.aksrt-editor-media__drop'); var files = []; function showSelection() { if (!selection || !uploadButton) { return; } selection.textContent = files.length ? (i18n.selectedImages || '已选择 %d 张图片').replace('%d', String(files.length)) : ''; uploadButton.disabled = files.length === 0; } function setFiles(nextFiles) { files = Array.prototype.filter.call(nextFiles || [], function (file) { return /^image\//.test(file.type || ''); }); showSelection(); } root.addEventListener('click', function (event) { var close = event.target.closest('[data-editor-media-close]'); if (close) { event.preventDefault(); closeImageModal(); return; } var tab = event.target.closest('[data-editor-media-tab]'); if (tab) { var name = tab.getAttribute('data-editor-media-tab'); root.querySelectorAll('[data-editor-media-tab]').forEach(function (item) { var active = item === tab; item.classList.toggle('is-active', active); item.setAttribute('aria-selected', active ? 'true' : 'false'); }); root.querySelectorAll('[data-editor-media-panel]').forEach(function (panel) { panel.classList.toggle('is-active', panel.getAttribute('data-editor-media-panel') === name); }); return; } if (event.target.closest('[data-editor-media-external]')) { var textarea = root.querySelector('[data-editor-media-urls]'); var urls = String(textarea ? textarea.value : '').split(/\r?\n/); var html = urls.map(function (url) { return imageMarkup(url, 0, ''); }).join(''); if (!html) { notify(i18n.invalidImageUrl || '请填写有效的图片地址', 'warning'); return; } editor.insertContent(html + '

'); editor.nodeChanged(); closeImageModal(); return; } if (event.target.closest('[data-editor-media-upload]')) { if (!files.length || !uploadButton) { return; } uploadButton.disabled = true; uploadButton.textContent = i18n.uploading || '正在上传…'; notify(i18n.uploading || '正在上传正文图片…', 'load', ''); Promise.all(files.map(uploadImage)).then(function (items) { var html = items.map(function (item) { return imageMarkup(item.url, item.attachment_id, item.name || ''); }).join(''); if (!html) { throw new Error(i18n.error || '图片上传失败'); } editor.insertContent(html + '

'); editor.nodeChanged(); notify(i18n.uploaded || '正文图片上传成功', 'success'); closeImageModal(); }).catch(function (error) { notify(error.message || i18n.error || '正文图片上传失败', 'danger'); uploadButton.disabled = false; uploadButton.textContent = i18n.confirmUpload || '确认上传'; }); } }); if (fileInput) { fileInput.addEventListener('change', function () { setFiles(fileInput.files); }); } if (drop) { ['dragenter', 'dragover'].forEach(function (name) { drop.addEventListener(name, function (event) { event.preventDefault(); drop.classList.add('is-dragging'); }); }); ['dragleave', 'drop'].forEach(function (name) { drop.addEventListener(name, function (event) { event.preventDefault(); drop.classList.remove('is-dragging'); }); }); drop.addEventListener('drop', function (event) { setFiles(event.dataTransfer && event.dataTransfer.files); }); } var onKeydown = function (event) { if (event.key === 'Escape') { closeImageModal(); } }; activeModal = { root: root, onKeydown: onKeydown }; document.addEventListener('keydown', onKeydown); var focusTarget = root.querySelector('[data-editor-media-tab].is-active'); if (focusTarget) { focusTarget.focus(); } } function selectedContent(editor) { var content = String(editor.selection.getContent() || '').trim(); return content ? '

' + content + '

' : '

' + escapeAttr(i18n.hiddenPlaceholder || '隐藏内容') + '

'; } function insertHiddenContent(editor, item) { var node = editor.selection.getNode(); var content = selectedContent(editor); var suffix = '

'; if (node && editor.dom.hasClass(node, 'tinymce-hide')) { var editable = node.querySelector('[contenteditable="true"]'); content = editable ? editable.innerHTML : content; editor.dom.remove(node); suffix = ''; } editor.insertContent( '
' + '

' + escapeAttr(item.open) + '

' + '
' + content + '
' + '

' + escapeAttr(item.close) + '

' + '
' + suffix ); editor.nodeChanged(); } window.tinymce.PluginManager.add('aksrt_editor_tools', function (editor) { if (editorCfg.canUploadImage) { editor.addButton('aksrt_image', { text: '', icon: 'image', tooltip: i18n.insertImage || '插入图片', onclick: function () { createImageModal(editor); }, }); } if (editorCfg.canHide) { var menu = [ { text: i18n.replyVisible || '回复可见', type: 'reply', open: '[reply]', close: '[/reply]', }, { text: i18n.loginVisible || '登录可见', type: 'login', open: '[aksrt_hide mode="login"]', close: '[/aksrt_hide]', }, { text: i18n.vipVisible || 'VIP 可见', type: 'vip', open: '[vip]', close: '[/vip]', }, ]; if (editorCfg.canPay) { menu.push({ text: i18n.payVisible || '付费可见', type: 'payshow', open: '[payshow]', close: '[/payshow]', }); } menu = menu.map(function (item) { return { text: item.text, onclick: function () { insertHiddenContent(editor, item); }, }; }); editor.addButton('aksrt_hide', { text: '', icon: 'preview', tooltip: i18n.hiddenContent || '隐藏内容', type: 'menubutton', stateSelector: '.tinymce-hide', menu: menu, }); } }); })(window, document);