diff --git a/public/js/extra.js b/public/js/extra.js index 99233c09..dfc206b0 100644 --- a/public/js/extra.js +++ b/public/js/extra.js @@ -15,6 +15,7 @@ import { stripTags } from '../../utils/string' import getUIElements from './lib/editor/ui-elements' import { emojifyImageDir } from './lib/editor/constants' +import { parseFenceCodeParams, serializeParamToAttribute } from './lib/markdown/utils' import markdownit from 'markdown-it' import markdownitContainer from 'markdown-it-container' @@ -1028,24 +1029,30 @@ export function scrollToHash () { location.hash = hash } +const fenceCodeAlias = { + sequence: 'sequence-diagram', + flow: 'flow-chart', + graphviz: 'graphviz', + mermaid: 'mermaid', + abc: 'abc', + vega: 'vega', + geo: 'geo' +} + function highlightRender (code, lang) { if (!lang || /no(-?)highlight|plain|text/.test(lang)) { return } + + const params = parseFenceCodeParams(lang) + const attr = serializeParamToAttribute(params) + lang = lang.split(/\s+/g)[0] + code = escapeHTML(code) - if (lang === 'sequence') { - return `
${code}
` - } else if (lang === 'flow') { - return `
${code}
` - } else if (lang === 'graphviz') { - return `
${code}
` - } else if (lang === 'mermaid') { - return `
${code}
` - } else if (lang === 'abc') { - return `
${code}
` - } else if (lang === 'vega') { - return `
${code}
` - } else if (lang === 'geo') { - return `
${code}
` + + const langAlias = fenceCodeAlias[lang] + if (langAlias) { + return `
${code}
` } + const result = { value: code } @@ -1167,7 +1174,7 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { } if (options.highlight) { - highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content) + highlighted = options.highlight(token.content, info) || md.utils.escapeHtml(token.content) } else { highlighted = md.utils.escapeHtml(token.content) } diff --git a/public/js/lib/markdown/utils.js b/public/js/lib/markdown/utils.js new file mode 100644 index 00000000..3aeb646c --- /dev/null +++ b/public/js/lib/markdown/utils.js @@ -0,0 +1,53 @@ +export function parseFenceCodeParams (lang) { + const attrMatch = lang.match(/{(.*)}/) + const params = {} + if (attrMatch && attrMatch.length >= 2) { + const attrs = attrMatch[1] + const paraMatch = attrs.match(/([#.](\S+?)\s)|((\S+?)\s*=\s*("(.+?)"|'(.+?)'|\[[^\]]*\]|\{[}]*\}|(\S+)))/g) + paraMatch && paraMatch.forEach(param => { + param = param.trim() + if (param[0] === '#') { + params['id'] = param.slice(1) + } else if (param[0] === '.') { + if (params['class']) params['class'] = [] + params['class'] = params['class'].concat(param.slice(1)) + } else { + const offset = param.indexOf('=') + const id = param.substring(0, offset).trim().toLowerCase() + let val = param.substring(offset + 1).trim() + const valStart = val[0] + const valEnd = val[val.length - 1] + if (['"', "'"].indexOf(valStart) !== -1 && ['"', "'"].indexOf(valEnd) !== -1 && valStart === valEnd) { + val = val.substring(1, val.length - 1) + } + if (id === 'class') { + if (params['class']) params['class'] = [] + params['class'] = params['class'].concat(val) + } else { + params[id] = val + } + } + }) + } + return params +} + +export function serializeParamToAttribute (params) { + if (Object.getOwnPropertyNames(params).length === 0) { + return '' + } else { + return ` data-params="${escape(JSON.stringify(params))}"` + } +} + +/** + * @param {HTMLElement} elem + */ +export function deserializeParamAttributeFromElement (elem) { + const params = elem.getAttribute('data-params') + if (params) { + return JSON.parse(unescape(params)) + } else { + return {} + } +} diff --git a/public/js/lib/syncscroll.js b/public/js/lib/syncscroll.js index 5bf209bc..9daf327c 100644 --- a/public/js/lib/syncscroll.js +++ b/public/js/lib/syncscroll.js @@ -73,7 +73,7 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => { } if (options.highlight) { - highlighted = options.highlight(token.content, langName) || md.utils.escapeHtml(token.content) + highlighted = options.highlight(token.content, info) || md.utils.escapeHtml(token.content) } else { highlighted = md.utils.escapeHtml(token.content) }