1242 lines
40 KiB
JavaScript
1242 lines
40 KiB
JavaScript
(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); |