Files
2026-08-29 19:06:07 +08:00

163 lines
4.9 KiB
JavaScript

(function (root) {
'use strict';
var documentRef = root.document;
var editors = {};
var submitBound = false;
var toolbarKeys = [
'headerSelect', 'fontSize', 'color', 'bgColor', '|',
'bold', 'italic', 'underline', 'through', '|',
'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyJustify', '|',
'insertTable', 'clearStyle', 'insertLink', 'undo', 'redo'
];
function findTextarea(target) {
if (!target || !documentRef) return null;
if (typeof target === 'string') return documentRef.querySelector(target);
if (target.nodeType === 1) return target;
return target[0] || null;
}
function ensureId(textarea) {
if (!textarea.id) textarea.id = 'rich-editor-' + Math.floor(Math.random() * 1000000);
return textarea.id;
}
function fieldValue(editor) {
return String(editor.getText() || '').trim() ? editor.getHtml() : '';
}
function syncEditor(instance) {
instance.textarea.value = fieldValue(instance.editor);
}
function makeToolbarAccessible(toolbar) {
var labelButtons = function () {
Array.prototype.forEach.call(toolbar.querySelectorAll('button[data-tooltip]'), function (button) {
if (!button.getAttribute('aria-label')) button.setAttribute('aria-label', button.getAttribute('data-tooltip'));
});
};
labelButtons();
if (root.MutationObserver) {
new root.MutationObserver(labelButtons).observe(toolbar, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['data-tooltip']
});
}
}
function createRichEditor(target, options) {
var textarea = findTextarea(target);
var settings = options || {};
var id;
var host;
var toolbar;
var content;
var editor;
var wangEditor = root.wangEditor;
var instance;
if (!textarea || !wangEditor || !wangEditor.createEditor || !wangEditor.createToolbar) return null;
if (textarea.dataset.wangEditorInitialized === 'true') return editors[textarea.id] || null;
id = ensureId(textarea);
host = documentRef.createElement('div');
host.className = 'rich-editor-host';
toolbar = documentRef.createElement('div');
toolbar.className = 'rich-editor-toolbar';
toolbar.id = id + '-toolbar';
content = documentRef.createElement('div');
content.className = 'rich-editor-content';
content.id = id + '-content';
host.appendChild(toolbar);
host.appendChild(content);
textarea.insertAdjacentElement('afterend', host);
try {
editor = wangEditor.createEditor({
selector: '#' + content.id,
html: textarea.value || '',
config: Object.assign({
placeholder: textarea.getAttribute('placeholder') || '请输入内容',
onChange: function (currentEditor) {
textarea.value = fieldValue(currentEditor);
},
onBlur: function (currentEditor) {
textarea.value = fieldValue(currentEditor);
}
}, settings.editorConfig || {}),
mode: settings.mode || 'default'
});
wangEditor.createToolbar({
editor: editor,
selector: '#' + toolbar.id,
config: Object.assign({ toolbarKeys: toolbarKeys }, settings.toolbarConfig || {}),
mode: settings.mode || 'default'
});
makeToolbarAccessible(toolbar);
} catch (error) {
host.remove();
return null;
}
textarea.hidden = true;
textarea.setAttribute('aria-hidden', 'true');
textarea.dataset.wangEditorInitialized = 'true';
instance = {
textarea: textarea,
editor: editor,
sync: function () { syncEditor(instance); },
getHtml: function () { return fieldValue(editor); },
getText: function () { return editor.getText(); }
};
editors[id] = instance;
instance.sync();
return instance;
}
function initRichEditors(rootNode, options) {
var scope = rootNode || documentRef;
var instances = {};
if (!scope || !scope.querySelectorAll) return instances;
Array.prototype.forEach.call(scope.querySelectorAll('.js-rich-editor'), function (textarea) {
var instance = createRichEditor(textarea, options);
if (instance) instances[textarea.id] = instance;
});
return instances;
}
function bindFormSync() {
if (submitBound || !documentRef) return;
submitBound = true;
documentRef.addEventListener('submit', function (event) {
Object.keys(editors).forEach(function (id) {
var instance = editors[id];
if (instance.textarea.form === event.target) instance.sync();
});
});
}
function initAll() {
bindFormSync();
return initRichEditors(documentRef);
}
root.AppRichEditor = {
init: createRichEditor,
initAll: initRichEditors,
syncAll: function () {
Object.keys(editors).forEach(function (id) { editors[id].sync(); });
}
};
if (!documentRef) return;
if (documentRef.readyState === 'loading') documentRef.addEventListener('DOMContentLoaded', initAll);
else initAll();
})(window);