Files
hustoj/web/include/vditor-adapter.js

1242 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function (global) {
'use strict';
var documentRef = global.document;
if (!documentRef) {
return;
}
var currentScriptRef = documentRef.currentScript || (function () {
var scripts = documentRef.getElementsByTagName('script');
return scripts.length > 0 ? scripts[scripts.length - 1] : null;
})();
var DEFAULT_CDN = global.HUSTOJ_VDITOR_CDN || getLocalCdnBase();
global.HUSTOJ_VDITOR_CDN = DEFAULT_CDN;
var assetsPromise = null;
var aceAssetsPromise = null;
var styleInjected = false;
function getLocalCdnBase() {
var scriptSrc = currentScriptRef && currentScriptRef.src;
if (!scriptSrc) {
return 'include/vditor';
}
return scriptSrc.replace(/\/vditor-adapter\.js(?:[?#].*)?$/, '/vditor');
}
function getLocalAceBase() {
var scriptSrc = currentScriptRef && currentScriptRef.src;
if (!scriptSrc) {
return 'ace';
}
return scriptSrc.replace(/\/include\/vditor-adapter\.js(?:[?#].*)?$/, '/ace');
}
function getLocalSarasaFontUrl() {
var scriptSrc = currentScriptRef && currentScriptRef.src;
if (!scriptSrc) {
return 'template/syzoj/fonts/SarasaMonoSC-Regular.ttf';
}
return scriptSrc.replace(/\/include\/vditor-adapter\.js(?:[?#].*)?$/, '/template/syzoj/fonts/SarasaMonoSC-Regular.ttf');
}
function injectAdapterStyle() {
var sarasaFontUrl = getLocalSarasaFontUrl();
var style;
if (styleInjected) {
return;
}
style = documentRef.createElement('style');
style.type = 'text/css';
style.setAttribute('data-hustoj-vditor', 'adapter-style');
style.textContent = [
'@font-face{font-family:"Sarasa Mono SC Local";src:local("等距更纱黑体 SC"),local("Sarasa Mono SC"),url("' + sarasaFontUrl + '") format("truetype");font-weight:400;font-style:normal;font-display:swap;}',
'.hustoj-vditor-hidden{display:none !important;}',
'.hustoj-vditor-wrapper{width:100%;margin:0 0 12px;}',
'.hustoj-vditor-wrapper,.hustoj-vditor-preview,.hustoj-vditor-markdown,.hustoj-vditor-code{--hustoj-vditor-font-family:"Sarasa Mono SC Local","等距更纱黑体 SC","Sarasa Mono SC","Sarasa Fixed SC","Sarasa Gothic SC",Consolas,"Cascadia Mono","JetBrains Mono","Courier New",monospace;--hustoj-vditor-font-size:24px;--hustoj-vditor-preview-code-font-size:24px;}',
'.hustoj-vditor-markdown,.hustoj-vditor-markdown .vditor,.hustoj-vditor-markdown .vditor-content,.hustoj-vditor-markdown .vditor-ir,.hustoj-vditor-markdown .vditor-sv,.hustoj-vditor-markdown .vditor-reset,.hustoj-vditor-markdown .vditor-wysiwyg,.hustoj-vditor-markdown .vditor-sv__preview{text-align:left !important;}',
'.hustoj-vditor-markdown .vditor{margin-left:0 !important;margin-right:auto !important;}',
'.hustoj-vditor-markdown .vditor-toolbar{justify-content:flex-start;}',
'.hustoj-vditor-markdown .vditor-content,.hustoj-vditor-markdown .vditor-reset,.hustoj-vditor-markdown .vditor-reset *,.hustoj-vditor-markdown textarea{font-family:var(--hustoj-vditor-font-family) !important;}',
'.hustoj-vditor-code{text-align:left;}',
'.hustoj-vditor-code.ace_editor{position:relative;border:1px solid #d4d4d5;border-radius:6px;line-height:1.5;}',
'.hustoj-vditor-code .ace_scroller,',
'.hustoj-vditor-code .ace_gutter,',
'.hustoj-vditor-code .ace_content{font-family:var(--hustoj-vditor-font-family) !important;font-size:var(--hustoj-vditor-font-size) !important;line-height:1.5 !important;}',
'.hustoj-vditor-code .ace_gutter{background:#f8f8f8;color:#8a8a8a;}',
'.hustoj-vditor-dark.ace_editor{background:#272822;border-color:#4a4a4a;}',
'.hustoj-vditor-dark .ace_gutter{background:#2f3129;color:#bcbcbc;}',
'.hustoj-vditor-dark .ace_print-margin{background:#3a3d34;}',
'.hustoj-vditor-preview{--hustoj-diagram-font-family:"Microsoft YaHei","PingFang SC","Hiragino Sans GB","Noto Sans CJK SC","Source Han Sans SC","WenQuanYi Micro Hei",Arial,sans-serif;--hustoj-diagram-font-size:14px;}',
'.hustoj-vditor-code .vditor-toolbar{display:none !important;}',
'.hustoj-vditor-code .vditor-reset,',
'.hustoj-vditor-code .vditor-content,',
'.hustoj-vditor-code .vditor,',
'.hustoj-vditor-code textarea{text-align:left !important;font-family:var(--hustoj-vditor-font-family) !important;font-size:var(--hustoj-vditor-font-size) !important;line-height:1.5 !important;}',
'.hustoj-vditor-code .vditor-sv,',
'.hustoj-vditor-code .vditor-ir{min-height:100%;}',
'.hustoj-vditor-dark .vditor{background:#272822;border-color:#4a4a4a;}',
'.hustoj-vditor-dark .vditor-reset,',
'.hustoj-vditor-dark .vditor-content,',
'.hustoj-vditor-dark textarea{color:#f8f8f2 !important;background:#272822 !important;}',
'.hustoj-vditor-dark .vditor-toolbar svg{fill:#f8f8f2;}',
'.hustoj-vditor-mail .vditor-toolbar{border-bottom:1px solid #ddd;}',
'.hustoj-vditor-mail .vditor{height:100%;min-height:180px;}',
'.hustoj-vditor-preview .vditor-reset pre{position:relative;margin:16px 0;padding:16px 18px;border:1px solid #cfd7e6;border-radius:12px;background:linear-gradient(180deg,#f9fbff 0%,#f3f7ff 100%);box-shadow:0 8px 20px rgba(15,23,42,0.06);}',
'.hustoj-vditor-preview .vditor-reset pre code{display:block;font-family:var(--hustoj-vditor-font-family) !important;font-size:var(--hustoj-vditor-preview-code-font-size) !important;line-height:1.7;}',
'.hustoj-vditor-preview .vditor-reset pre div.vditor-copy{display:block !important;opacity:1 !important;visibility:visible !important;position:absolute;top:12px;right:12px;z-index:2;}',
'.hustoj-vditor-preview .vditor-copy span{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid #c8d3e6;border-radius:999px;background:rgba(255,255,255,0.96);box-shadow:0 4px 12px rgba(15,23,42,0.08);font-size:12px;font-weight:600;color:#2f4f7f;position:static !important;}',
'.hustoj-vditor-preview .vditor-copy span::after{content:attr(aria-label);}',
'.hustoj-vditor-preview .vditor-copy svg{width:14px !important;height:14px !important;color:currentColor;}',
'.hustoj-vditor-preview .hustoj-vditor-sample-heading{display:flex;align-items:center;margin:24px 0 0;padding:12px 16px;border:1px solid #d6dde8;border-bottom:0;border-radius:14px 14px 0 0;background:#eef4ff;color:#274061;font-size:1.05em;font-weight:700;line-height:1.4;}',
'.hustoj-vditor-preview .vditor-reset pre.hustoj-vditor-sample-box{margin-top:0;border-top-left-radius:0;border-top-right-radius:0;border-top-color:#d6dde8;padding-top:52px;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart{margin:18px 0;padding:14px;border:1px solid #d7dce5;border-radius:12px;background:#fff;overflow:auto;text-align:center;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid svg,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart svg{display:block;max-width:100%;height:auto !important;overflow:visible;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid foreignObject,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart foreignObject,',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .label,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .label{overflow:visible;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .label div,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .label div,',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .edgeLabel,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .edgeLabel{font-family:var(--hustoj-diagram-font-family) !important;font-size:var(--hustoj-diagram-font-size) !important;white-space:normal !important;line-height:1.4 !important;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .nodeLabel,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .nodeLabel,',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .nodeLabel *,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .nodeLabel *,',
'.hustoj-vditor-preview .vditor-reset .language-mermaid svg text,',
'.hustoj-vditor-preview .vditor-reset .language-mermaid svg tspan,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart svg text,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart svg tspan{font-family:var(--hustoj-diagram-font-family) !important;font-size:var(--hustoj-diagram-font-size) !important;font-variant-ligatures:none;text-rendering:geometricPrecision;}',
'.hustoj-vditor-preview .vditor-reset .language-mermaid .nodeLabel p,',
'.hustoj-vditor-preview .vditor-reset .language-flowchart .nodeLabel p{margin:0;line-height:1.4 !important;}'
].join('');
documentRef.head.appendChild(style);
styleInjected = true;
}
function ensureStyleTag(href) {
var existing = documentRef.querySelector('link[data-hustoj-vditor-css="' + href + '"]');
if (existing) {
return;
}
var link = documentRef.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.setAttribute('data-hustoj-vditor-css', href);
documentRef.head.appendChild(link);
}
function ensureScript(src) {
return new Promise(function (resolve, reject) {
var existing = documentRef.querySelector('script[data-hustoj-vditor-js="' + src + '"]');
if (existing) {
if (global.Vditor) {
resolve(global.Vditor);
} else {
existing.addEventListener('load', function () {
resolve(global.Vditor);
}, { once: true });
existing.addEventListener('error', reject, { once: true });
}
return;
}
var script = documentRef.createElement('script');
script.src = src;
script.async = true;
script.setAttribute('data-hustoj-vditor-js', src);
script.onload = function () {
resolve(global.Vditor);
};
script.onerror = function () {
reject(new Error('Failed to load ' + src));
};
documentRef.head.appendChild(script);
});
}
function ensureAssets(options) {
var cdn = (options && options.cdn) || DEFAULT_CDN;
global.HUSTOJ_VDITOR_CDN = cdn;
if (global.Vditor) {
injectAdapterStyle();
return Promise.resolve(global.Vditor);
}
if (assetsPromise) {
return assetsPromise;
}
ensureStyleTag(cdn + '/dist/index.css');
injectAdapterStyle();
assetsPromise = ensureScript(cdn + '/dist/index.js').then(function () {
return global.Vditor;
});
return assetsPromise;
}
function ensureAceAssets(options) {
var aceBase = (options && options.aceBase) || getLocalAceBase();
injectAdapterStyle();
if (global.ace) {
if (global.ace.config && typeof global.ace.config.set === 'function') {
global.ace.config.set('basePath', aceBase);
}
return Promise.resolve(global.ace);
}
if (aceAssetsPromise) {
return aceAssetsPromise;
}
aceAssetsPromise = new Promise(function (resolve, reject) {
var aceScriptSrc = aceBase + '/ace.js';
var languageToolsSrc = aceBase + '/ext-language_tools.js';
var existing = documentRef.querySelector('script[data-hustoj-ace-js="' + aceScriptSrc + '"]');
var loadLanguageTools = function () {
var languageTools = documentRef.querySelector('script[data-hustoj-ace-js="' + languageToolsSrc + '"]');
var done = function () {
finish();
};
if (languageTools) {
languageTools.addEventListener('load', done, { once: true });
languageTools.addEventListener('error', done, { once: true });
return;
}
languageTools = documentRef.createElement('script');
languageTools.src = languageToolsSrc;
languageTools.async = true;
languageTools.setAttribute('data-hustoj-ace-js', languageToolsSrc);
languageTools.onload = done;
languageTools.onerror = done;
documentRef.head.appendChild(languageTools);
};
var finish = function () {
if (global.ace && global.ace.config && typeof global.ace.config.set === 'function') {
global.ace.config.set('basePath', aceBase);
}
resolve(global.ace);
};
if (existing) {
if (global.ace) {
loadLanguageTools();
} else {
existing.addEventListener('load', loadLanguageTools, { once: true });
existing.addEventListener('error', reject, { once: true });
}
return;
}
var script = documentRef.createElement('script');
script.src = aceScriptSrc;
script.async = true;
script.setAttribute('data-hustoj-ace-js', aceScriptSrc);
script.onload = loadLanguageTools;
script.onerror = function () {
reject(new Error('Failed to load ' + aceScriptSrc));
};
documentRef.head.appendChild(script);
});
return aceAssetsPromise;
}
function resolveElement(target) {
if (!target) {
return null;
}
if (typeof target === 'string') {
return documentRef.getElementById(target) || documentRef.querySelector(target);
}
return target;
}
function getElementValue(element) {
if (!element) {
return '';
}
if (typeof element.value === 'string') {
return element.value;
}
return element.textContent || '';
}
function computeHeight(element, fallbackHeight) {
var computed;
var rows;
var height;
if (element && global.getComputedStyle) {
computed = global.getComputedStyle(element);
height = parseInt(computed.height, 10);
if (!height) {
height = parseInt(computed.minHeight, 10);
}
}
if (!height && element) {
rows = parseInt(element.getAttribute('rows'), 10);
if (rows) {
height = Math.max(rows * 24, fallbackHeight || 320);
}
}
return height || fallbackHeight || 360;
}
function aceThemeToVditorTheme(themeName) {
if (!themeName) {
return 'classic';
}
if (themeName.indexOf('monokai') !== -1 || themeName.indexOf('dark') !== -1) {
return 'dark';
}
return 'classic';
}
function aceThemeToContentTheme(themeName) {
return aceThemeToVditorTheme(themeName) === 'dark' ? 'dark' : 'light';
}
function normalizeMode(modeName) {
if (!modeName) {
return '';
}
return String(modeName).replace(/^ace\/mode\//, '');
}
function dispatchNativeEvent(element, eventName) {
var eventObject;
if (!element || typeof Event === 'undefined') {
return;
}
eventObject = new Event(eventName, { bubbles: true });
element.dispatchEvent(eventObject);
}
function syncFieldValue(field, value, onChange) {
if (!field) {
if (typeof onChange === 'function') {
onChange(value, null);
}
if (typeof global.sync === 'function') {
global.sync();
}
return;
}
field.value = value;
dispatchNativeEvent(field, 'input');
dispatchNativeEvent(field, 'change');
if (typeof onChange === 'function') {
onChange(value, field);
}
if (typeof global.sync === 'function') {
global.sync();
}
}
function decodeHtmlEntities(text) {
var textarea = documentRef.createElement('textarea');
textarea.innerHTML = typeof text === 'string' ? text : '';
return textarea.value;
}
function toElementArray(target) {
var resolved;
if (!target) {
return [];
}
if (typeof target === 'string') {
return Array.prototype.slice.call(documentRef.querySelectorAll(target));
}
if (target.nodeType === 1) {
return [target];
}
if (typeof target.length === 'number') {
return Array.prototype.slice.call(target).map(function (item) {
return resolveElement(item);
}).filter(function (item) {
return !!item;
});
}
resolved = resolveElement(target);
return resolved ? [resolved] : [];
}
function mergeObjects(target, source) {
var key;
if (!source) {
return target;
}
for (key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
return target;
}
function getMarkdownSource(element, options) {
var rawSource;
var source;
if (!element) {
return '';
}
if (!options.refreshSource && typeof element._hustojVditorSource === 'string') {
return element._hustojVditorSource;
}
if (typeof options.getSource === 'function') {
rawSource = options.getSource(element);
} else if (options.useTextContent) {
rawSource = element.textContent || '';
} else {
rawSource = element.innerHTML || '';
}
source = String(rawSource || '');
if (options.decodeEntities !== false) {
source = decodeHtmlEntities(source);
}
element._hustojVditorSource = source;
return source;
}
function buildPreviewOptions(options, afterRender) {
var previewOptions = {
cdn: options.cdn || DEFAULT_CDN,
mode: options.mode || 'light',
anchor: typeof options.anchor === 'undefined' ? 0 : options.anchor,
speech: {
enable: false
},
hljs: {
style: options.codeTheme || 'github'
},
markdown: {
autoSpace: false,
fixTermTypo: false,
toc: false,
codeBlockPreview: true,
mathBlockPreview: true,
sanitize: false
},
math: {
inlineDigit: true
},
after: afterRender
};
if (options.previewOptions) {
mergeObjects(previewOptions, options.previewOptions);
if (options.previewOptions.speech) {
previewOptions.speech = mergeObjects(previewOptions.speech, options.previewOptions.speech);
}
if (options.previewOptions.hljs) {
previewOptions.hljs = mergeObjects(previewOptions.hljs, options.previewOptions.hljs);
}
if (options.previewOptions.markdown) {
previewOptions.markdown = mergeObjects(previewOptions.markdown, options.previewOptions.markdown);
}
if (options.previewOptions.math) {
previewOptions.math = mergeObjects(previewOptions.math, options.previewOptions.math);
}
}
previewOptions.after = afterRender;
return previewOptions;
}
function decorateRenderedPreview(element) {
if (!element || !element.classList) {
return;
}
element.classList.add('hustoj-vditor-preview');
decorateSampleBlocks(element);
Array.prototype.forEach.call(
element.querySelectorAll('.language-mermaid, .language-flowchart'),
function (diagramContainer) {
Array.prototype.forEach.call(diagramContainer.querySelectorAll('svg'), function (svgElement) {
svgElement.style.maxWidth = '100%';
svgElement.style.height = 'auto';
svgElement.style.overflow = 'visible';
});
Array.prototype.forEach.call(
diagramContainer.querySelectorAll('foreignObject, .label, .label div, .edgeLabel, .nodeLabel, .nodeLabel p'),
function (node) {
if (node.tagName === 'FOREIGNOBJECT') {
node.style.overflow = 'visible';
}
if (node.tagName === 'DIV' || node.tagName === 'SPAN') {
node.style.whiteSpace = 'normal';
}
if (node.tagName === 'P') {
node.style.margin = '0';
node.style.lineHeight = '1.4';
}
if (node.classList && node.classList.contains('nodeLabel')) {
node.style.lineHeight = '1.4';
}
}
);
}
);
}
function normalizeHeadingText(text) {
return String(text || '').replace(/\s+/g, ' ').trim();
}
function parseSampleHeading(text) {
var normalized = normalizeHeadingText(text).replace(/[:]+$/, '');
var match = normalized.match(/^(?:(?:样例|示例|sample|example)\s*)?(输入|输出|input|output)\s*([0-9]+)?$/i);
var kind;
if (!match) {
return null;
}
kind = match[1].toLowerCase();
if (kind === '输入' || kind === 'input') {
kind = 'input';
} else {
kind = 'output';
}
return {
kind: kind,
index: match[2] || ''
};
}
function getSiblingCodeBlock(heading) {
var sibling = heading ? heading.nextElementSibling : null;
while (sibling && sibling.tagName === 'P' && !normalizeHeadingText(sibling.textContent)) {
sibling = sibling.nextElementSibling;
}
if (!sibling || sibling.tagName !== 'PRE') {
return null;
}
return sibling;
}
function decorateSampleBlocks(element) {
Array.prototype.forEach.call(element.querySelectorAll('h1, h2, h3, h4, h5, h6'), function (heading) {
var sampleMeta = parseSampleHeading(heading.textContent);
var codeBlock;
if (!sampleMeta) {
return;
}
codeBlock = getSiblingCodeBlock(heading);
if (!codeBlock) {
return;
}
heading.classList.add('hustoj-vditor-sample-heading');
heading.setAttribute('data-hustoj-sample-kind', sampleMeta.kind);
if (sampleMeta.index) {
heading.setAttribute('data-hustoj-sample-index', sampleMeta.index);
}
codeBlock.classList.add('hustoj-vditor-sample-box');
codeBlock.setAttribute('data-hustoj-sample-kind', sampleMeta.kind);
if (sampleMeta.index) {
codeBlock.setAttribute('data-hustoj-sample-index', sampleMeta.index);
}
});
}
function renderMarkdownBlocks(target, options) {
var renderOptions = options || {};
return ensureAssets(renderOptions).then(function () {
var elements = toElementArray(target);
var tasks = [];
elements.forEach(function (element) {
var source = getMarkdownSource(element, renderOptions);
if (!source && !renderOptions.renderEmpty) {
return;
}
tasks.push(new Promise(function (resolve, reject) {
var previewOptions = buildPreviewOptions(renderOptions, function () {
decorateRenderedPreview(element);
if (typeof renderOptions.afterEach === 'function') {
renderOptions.afterEach(element, source);
}
resolve(element);
});
try {
global.Vditor.preview(element, source, previewOptions);
} catch (error) {
reject(error);
}
}));
});
return Promise.all(tasks).then(function (result) {
if (typeof renderOptions.afterAll === 'function') {
renderOptions.afterAll(result);
}
return result;
});
});
}
function editorUploadFormatter(files, responseText) {
var payload;
var fileName = 'upload';
var result;
try {
payload = JSON.parse(responseText);
} catch (error) {
return JSON.stringify({
msg: '上传返回解析失败',
code: 1,
data: { errFiles: [fileName], succMap: {} }
});
}
if (files && files.length > 0 && files[0] && files[0].name) {
fileName = files[0].name;
}
if (typeof payload.code !== 'undefined' && payload.data) {
return responseText;
}
if (payload.error === 0 && payload.url) {
result = {
msg: '',
code: 0,
data: {
errFiles: [],
succMap: {}
}
};
result.data.succMap[fileName] = payload.url;
return JSON.stringify(result);
}
return JSON.stringify({
msg: payload.message || payload.msg || '上传失败',
code: 1,
data: {
errFiles: [fileName],
succMap: {}
}
});
}
function buildUploadOptions(options) {
if (!options || !options.uploadUrl) {
return undefined;
}
return {
url: options.uploadUrl,
max: options.uploadMax || 400 * 1024 * 1024,
multiple: false,
fieldName: options.fieldName || 'imgFile',
accept: options.accept || '*/*',
format: function (files, responseText) {
return editorUploadFormatter(files, responseText);
}
};
}
function getDefaultMarkdownToolbar() {
return [
'emoji',
'headings',
'bold',
'italic',
'strike',
'|',
'line',
'quote',
'list',
'ordered-list',
'check',
'|',
'link',
'upload',
'table',
'code',
'inline-code',
'|',
'undo',
'redo',
'fullscreen',
'outline'
];
}
function getDefaultMailToolbar() {
return [
'emoji',
'headings',
'bold',
'italic',
'strike',
'|',
'link',
'upload',
'code',
'inline-code',
'|',
'undo',
'redo',
'|',
'fullscreen'
];
}
function createTextareaEditor(textarea, options) {
var wrapper = documentRef.createElement('div');
var height = computeHeight(textarea, options.height || 360);
var vditor;
wrapper.className = 'hustoj-vditor-wrapper hustoj-vditor-markdown';
textarea.classList.add('hustoj-vditor-hidden');
textarea.parentNode.insertBefore(wrapper, textarea.nextSibling);
return new Promise(function (resolve) {
vditor = new global.Vditor(wrapper, {
cdn: options.cdn || DEFAULT_CDN,
cache: { enable: false },
mode: options.mode || 'sv',
value: textarea.value || '',
lang: options.lang || 'zh_CN',
theme: options.theme || 'classic',
icon: options.icon || 'ant',
width: options.width || '100%',
minHeight: options.minHeight || Math.min(height, 320),
height: options.height || height,
toolbar: options.toolbar || getDefaultMarkdownToolbar(),
toolbarConfig: options.toolbarConfig || { pin: true },
resize: options.resize || { enable: true, position: 'bottom' },
outline: options.outline || { enable: false },
preview: options.preview || {
mode: 'both',
delay: 0,
actions: [],
markdown: {
sanitize: false,
autoSpace: false,
codeBlockPreview: true,
mathBlockPreview: true,
fixTermTypo: false
}
},
upload: buildUploadOptions(options),
ctrlEnter: function (value) {
var form;
syncFieldValue(textarea, value, options.onChange);
if (!options.ctrlEnterFormName) {
return;
}
form = documentRef.querySelector('form[name="' + options.ctrlEnterFormName + '"]');
if (form) {
form.submit();
}
},
input: function (value) {
syncFieldValue(textarea, value, options.onChange);
},
blur: function (value) {
syncFieldValue(textarea, value, options.onChange);
},
after: function () {
syncFieldValue(textarea, vditor.getValue(), options.onChange);
resolve(vditor);
}
});
});
}
function createCodeEditor(options) {
return ensureAceAssets(options).then(function () {
var container = resolveElement(options.container);
var hiddenField = resolveElement(options.hiddenField);
var sourceField = resolveElement(options.sourceField);
var initialValue = options.initialValue;
var adapter = null;
var aceEditor = null;
var session = null;
var state;
var aceBase = (options && options.aceBase) || getLocalAceBase();
if (!container) {
throw new Error('Vditor code editor container not found');
}
if (typeof initialValue === 'undefined') {
initialValue = getElementValue(sourceField || container);
}
state = {
theme: options.theme || 'ace/theme/chrome',
fontSize: parseInt(options.fontSize, 10) || 24,
mode: normalizeMode(options.mode || ''),
wrap: true
};
container.classList.add('hustoj-vditor-code');
container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px');
container.classList.toggle('hustoj-vditor-dark', aceThemeToVditorTheme(state.theme) === 'dark');
container.style.width = options.width || '100%';
if (typeof options.height === 'string') {
container.style.height = options.height;
} else {
container.style.height = (options.height || computeHeight(container, 420)) + 'px';
}
if (sourceField && sourceField !== container) {
sourceField.classList.add('hustoj-vditor-hidden');
}
container.textContent = initialValue || '';
function applyTheme() {
var editorTheme = aceThemeToVditorTheme(state.theme);
container.classList.toggle('hustoj-vditor-dark', editorTheme === 'dark');
if (aceEditor) {
aceEditor.setTheme(state.theme);
}
}
function applyFontSize() {
container.style.setProperty('--hustoj-vditor-font-size', state.fontSize + 'px');
if (aceEditor) {
aceEditor.setFontSize(state.fontSize);
}
}
function syncSourceValue(value) {
if (sourceField && sourceField !== container && typeof sourceField.value === 'string') {
sourceField.value = value;
}
if (hiddenField && typeof hiddenField.value === 'string') {
hiddenField.value = value;
}
if (typeof options.onChange === 'function') {
options.onChange(value);
}
}
session = {
setMode: function (modeName) {
state.mode = normalizeMode(modeName);
if (aceEditor) {
aceEditor.session.setMode(modeName || 'text');
}
},
getValue: function () {
return adapter.getValue();
},
setValue: function (value) {
adapter.setValue(value);
}
};
return new Promise(function (resolve) {
if (global.ace && global.ace.config && typeof global.ace.config.set === 'function') {
global.ace.config.set('basePath', aceBase);
}
aceEditor = global.ace.edit(container);
aceEditor.$blockScrolling = Infinity;
aceEditor.setValue(initialValue || '', -1);
aceEditor.session.setUseWorker(false);
aceEditor.session.setUseWrapMode(true);
aceEditor.renderer.setShowPrintMargin(false);
aceEditor.setHighlightActiveLine(true);
aceEditor.setOptions({
showFoldWidgets: false,
tabSize: 4,
useSoftTabs: true,
behavioursEnabled: false,
wrapBehavioursEnabled: false,
fontSize: state.fontSize
});
if (state.mode) {
aceEditor.session.setMode('ace/mode/' + state.mode);
}
aceEditor.session.on('change', function () {
syncSourceValue(aceEditor.getValue());
});
aceEditor.on('blur', function () {
syncSourceValue(aceEditor.getValue());
});
adapter = {
getValue: function () {
return aceEditor.getValue();
},
setValue: function (value) {
var nextValue = typeof value === 'undefined' ? '' : String(value);
aceEditor.setValue(nextValue, -1);
syncSourceValue(nextValue);
return nextValue;
},
getSession: function () {
return aceEditor.getSession();
},
session: aceEditor.getSession(),
setTheme: function (themeName) {
state.theme = themeName || state.theme;
applyTheme();
},
getTheme: function () {
return state.theme;
},
setOptions: function (editorOptions) {
if (!editorOptions) {
return;
}
if (editorOptions.fontSize) {
this.setFontSize(editorOptions.fontSize);
}
if (typeof editorOptions.readOnly !== 'undefined') {
this.setReadOnly(editorOptions.readOnly);
}
aceEditor.setOptions(editorOptions);
},
setOption: function (name, value) {
if (name === 'wrap') {
state.wrap = value !== 'off';
aceEditor.session.setUseWrapMode(state.wrap);
return;
}
aceEditor.setOption(name, value);
},
resize: function () {
aceEditor.resize();
applyFontSize();
},
getFontSize: function () {
return String(state.fontSize);
},
setFontSize: function (fontSize) {
state.fontSize = parseInt(fontSize, 10) || state.fontSize;
applyFontSize();
},
setReadOnly: function (readOnly) {
aceEditor.setReadOnly(!!readOnly);
},
focus: function () {
aceEditor.focus();
},
_ace: aceEditor
};
applyTheme();
applyFontSize();
if (options.readOnly) {
adapter.setReadOnly(true);
}
syncSourceValue(aceEditor.getValue());
resolve(adapter);
});
});
}
function createDeferredCodeEditor(options) {
var initialElement = resolveElement(options && (options.sourceField || options.container));
var proxy = {
_impl: null,
_theme: (options && options.theme) || 'ace/theme/chrome',
_fontSize: parseInt(options && options.fontSize, 10) || 18,
_value: typeof options !== 'undefined' && typeof options.initialValue !== 'undefined' ? options.initialValue : getElementValue(initialElement),
_mode: normalizeMode(options && options.mode),
_options: null,
_readOnly: typeof options !== 'undefined' ? options.readOnly : undefined,
_wrap: 'free'
};
function getProxySession() {
return {
setMode: function (modeName) {
proxy._mode = normalizeMode(modeName);
if (proxy._impl) {
proxy._impl.getSession().setMode(modeName);
}
},
getValue: function () {
return proxy.getValue();
},
setValue: function (value) {
proxy.setValue(value);
}
};
}
proxy.getValue = function () {
if (proxy._impl) {
return proxy._impl.getValue();
}
if (typeof proxy._value !== 'undefined' && proxy._value !== null && proxy._value !== '') {
return proxy._value;
}
return getElementValue(resolveElement(options && (options.sourceField || options.container)));
};
proxy.setValue = function (value) {
proxy._value = value;
if (proxy._impl) {
return proxy._impl.setValue(value);
}
return value;
};
proxy.getSession = function () {
return getProxySession();
};
proxy.session = getProxySession();
proxy.setTheme = function (themeName) {
proxy._theme = themeName || proxy._theme;
if (proxy._impl) {
proxy._impl.setTheme(proxy._theme);
}
};
proxy.getTheme = function () {
if (proxy._impl) {
return proxy._impl.getTheme();
}
return proxy._theme;
};
proxy.setOptions = function (editorOptions) {
proxy._options = editorOptions;
if (editorOptions && editorOptions.fontSize) {
proxy._fontSize = parseInt(editorOptions.fontSize, 10) || proxy._fontSize;
}
if (proxy._impl) {
proxy._impl.setOptions(editorOptions);
}
};
proxy.setOption = function (name, value) {
if (name === 'wrap') {
proxy._wrap = value;
}
if (proxy._impl) {
proxy._impl.setOption(name, value);
}
};
proxy.resize = function () {
if (proxy._impl) {
proxy._impl.resize();
}
};
proxy.getFontSize = function () {
if (proxy._impl) {
return proxy._impl.getFontSize();
}
return String(proxy._fontSize);
};
proxy.setFontSize = function (fontSize) {
proxy._fontSize = parseInt(fontSize, 10) || proxy._fontSize;
if (proxy._impl) {
proxy._impl.setFontSize(proxy._fontSize);
}
};
proxy.setReadOnly = function (readOnly) {
proxy._readOnly = readOnly;
if (proxy._impl) {
proxy._impl.setReadOnly(readOnly);
}
};
proxy.focus = function () {
if (proxy._impl) {
proxy._impl.focus();
}
};
createCodeEditor(options || {}).then(function (instance) {
proxy._impl = instance;
instance.setTheme(proxy._theme);
instance.setFontSize(proxy._fontSize);
if (proxy._options) {
instance.setOptions(proxy._options);
}
if (proxy._mode) {
instance.getSession().setMode('ace/mode/' + proxy._mode);
}
if (typeof proxy._readOnly !== 'undefined') {
instance.setReadOnly(proxy._readOnly);
}
instance.setOption('wrap', proxy._wrap);
if (typeof proxy._value !== 'undefined' && proxy._value !== null) {
instance.setValue(proxy._value);
}
}).catch(function (error) {
console.error('Failed to initialize deferred Vditor code editor.', error);
});
return proxy;
}
function createRichTextEditor(options) {
return ensureAssets(options).then(function () {
var container = resolveElement(options.container);
var hiddenField = resolveElement(options.hiddenField);
var initialValue = options.initialValue || '';
var vditor = null;
var adapter = null;
if (!container) {
throw new Error('Vditor rich editor container not found');
}
container.classList.add('hustoj-vditor-mail');
container.textContent = '';
function syncHtml() {
var html = vditor.getHTML();
if (hiddenField && typeof hiddenField.value === 'string') {
hiddenField.value = html;
}
if (typeof options.onChange === 'function') {
options.onChange(html);
}
}
return new Promise(function (resolve) {
vditor = new global.Vditor(container, {
cdn: options.cdn || DEFAULT_CDN,
cache: { enable: false },
mode: options.mode || 'wysiwyg',
lang: options.lang || 'zh_CN',
value: initialValue,
theme: options.theme || 'classic',
icon: options.icon || 'ant',
width: options.width || '100%',
minHeight: options.minHeight || computeHeight(container, 200),
height: options.height || computeHeight(container, 200),
toolbar: options.toolbar || getDefaultMailToolbar(),
toolbarConfig: options.toolbarConfig || { pin: true },
preview: options.preview || {
mode: 'editor',
delay: 0,
actions: [],
markdown: {
sanitize: false,
autoSpace: false,
fixTermTypo: false
}
},
upload: buildUploadOptions(options),
input: syncHtml,
blur: syncHtml,
after: function () {
adapter = {
getHTML: function () {
return vditor.getHTML();
},
getValue: function () {
return vditor.getValue();
},
setValue: function (value) {
vditor.setValue(value || '', true);
syncHtml();
},
focus: function () {
vditor.focus();
},
txt: {
html: function () {
return vditor.getHTML();
}
},
_vditor: vditor
};
syncHtml();
resolve(adapter);
}
});
});
});
}
function initTextareaVditors(options) {
return ensureAssets(options).then(function () {
var selector = (options && options.selector) || 'textarea.vditor-editor';
var textareas = documentRef.querySelectorAll(selector);
var tasks = [];
Array.prototype.forEach.call(textareas, function (textarea) {
tasks.push(createTextareaEditor(textarea, options || {}));
});
return Promise.all(tasks);
});
}
global.HustOJVditor = {
ensureAssets: ensureAssets,
initTextareaVditors: initTextareaVditors,
createCodeEditor: createCodeEditor,
createDeferredCodeEditor: createDeferredCodeEditor,
createRichTextEditor: createRichTextEditor,
renderMarkdownBlocks: renderMarkdownBlocks,
aceThemeToVditorTheme: aceThemeToVditorTheme,
decodeHtmlEntities: decodeHtmlEntities,
editorUploadFormatter: editorUploadFormatter
};
})(window);