进行了一些小修复
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
/**
|
||||
* 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, '<')
|
||||
.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 '<p><img src="' + escapeAttr(safeUrl) + '"' + idAttr + altAttr + '></p>';
|
||||
}
|
||||
|
||||
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 =
|
||||
'<button type="button" class="aksrt-editor-media__backdrop" data-editor-media-close tabindex="-1" aria-label="' +
|
||||
escapeAttr(i18n.close || '关闭') + '"></button>' +
|
||||
'<div class="aksrt-editor-media__dialog">' +
|
||||
'<button type="button" class="aksrt-editor-media__close" data-editor-media-close aria-label="' +
|
||||
escapeAttr(i18n.close || '关闭') + '">×</button>' +
|
||||
'<h3 id="aksrt-editor-media-title" class="aksrt-editor-media__title">' +
|
||||
escapeAttr(i18n.insertImage || '插入图片') + '</h3>' +
|
||||
'<div class="aksrt-editor-media__tabs" role="tablist">' +
|
||||
(canUpload
|
||||
? '<button type="button" class="is-active" role="tab" aria-selected="true" data-editor-media-tab="upload">' +
|
||||
escapeAttr(i18n.uploadImage || '上传图片') + '</button>'
|
||||
: '') +
|
||||
'<button type="button" class="' + (canUpload ? '' : 'is-active') + '" role="tab" aria-selected="' +
|
||||
(canUpload ? 'false' : 'true') + '" data-editor-media-tab="external">' +
|
||||
escapeAttr(i18n.externalImage || '外链图片') + '</button>' +
|
||||
'</div>' +
|
||||
'<div class="aksrt-editor-media__panels">' +
|
||||
(canUpload
|
||||
? '<section class="aksrt-editor-media__panel is-active" data-editor-media-panel="upload">' +
|
||||
'<label class="aksrt-editor-media__drop">' +
|
||||
'<input type="file" accept="image/gif,image/jpeg,image/png,image/webp" multiple data-editor-media-files>' +
|
||||
'<i class="fa fas fa-cloud-upload-alt" aria-hidden="true"></i>' +
|
||||
'<strong>' + escapeAttr(i18n.selectImages || '选择或拖入图片') + '</strong>' +
|
||||
'<span>' + escapeAttr(i18n.imageUploadHint || '可批量选择,上传完成后自动插入正文') + '</span>' +
|
||||
'</label>' +
|
||||
'<div class="aksrt-editor-media__selection" data-editor-media-selection></div>' +
|
||||
'<button type="button" class="but c-blue aksrt-editor-media__submit" data-editor-media-upload disabled>' +
|
||||
escapeAttr(i18n.confirmUpload || '确认上传') + '</button>' +
|
||||
'</section>'
|
||||
: '') +
|
||||
'<section class="aksrt-editor-media__panel ' + (canUpload ? '' : 'is-active') + '" data-editor-media-panel="external">' +
|
||||
'<p>' + escapeAttr(i18n.externalImageHint || '每行填写一个图片地址:') + '</p>' +
|
||||
'<textarea class="form-control" rows="5" placeholder="https://..." data-editor-media-urls></textarea>' +
|
||||
'<button type="button" class="but c-blue aksrt-editor-media__submit" data-editor-media-external>' +
|
||||
escapeAttr(i18n.confirmInsert || '确认插入') + '</button>' +
|
||||
'</section>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
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 + '<p></p>');
|
||||
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 + '<p></p>');
|
||||
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 ? '<p>' + content + '</p>' : '<p>' + escapeAttr(i18n.hiddenPlaceholder || '隐藏内容') + '</p>';
|
||||
}
|
||||
|
||||
function insertHiddenContent(editor, item) {
|
||||
var node = editor.selection.getNode();
|
||||
var content = selectedContent(editor);
|
||||
var suffix = '<p></p>';
|
||||
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(
|
||||
'<div class="tinymce-hide" data-aksrt-hide-type="' + escapeAttr(item.type) + '" contenteditable="false">' +
|
||||
'<p class="hide-before">' + escapeAttr(item.open) + '</p>' +
|
||||
'<div contenteditable="true">' + content + '</div>' +
|
||||
'<p class="hide-after">' + escapeAttr(item.close) + '</p>' +
|
||||
'</div>' + 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);
|
||||
Reference in New Issue
Block a user