discourse-math/assets/javascripts/lib/discourse-markdown/discourse-math.js.es6

183 lines
3.9 KiB
JavaScript

// inspired by https://github.com/classeur/markdown-it-mathjax/blob/master/markdown-it-mathjax.js
//
//
//
function isSafeBoundary(character_code, delimiter_code, md) {
if (character_code === delimiter_code) {
return false;
}
if (md.utils.isWhiteSpace(character_code)) {
return true;
}
if (md.utils.isMdAsciiPunct(character_code)) {
return true;
}
if (md.utils.isPunctChar(character_code)) {
return true;
}
return false;
}
function math_input(state, silent, delimiter_code) {
let pos = state.pos,
posMax = state.posMax;
if (
silent ||
state.src.charCodeAt(pos) !== delimiter_code ||
posMax < pos + 2
) {
return false;
}
// too short
if (state.src.charCodeAt(pos + 1) === delimiter_code) {
return false;
}
if (pos > 0) {
let prev = state.src.charCodeAt(pos - 1);
if (!isSafeBoundary(prev, delimiter_code, state.md)) {
return false;
}
}
let found;
for (let i = pos + 1; i < posMax; i++) {
let code = state.src.charCodeAt(i);
if (code === delimiter_code && state.src.charCodeAt(i - 1) !== 92 /* \ */) {
found = i;
break;
}
}
if (!found) {
return false;
}
if (found + 1 <= posMax) {
let next = state.src.charCodeAt(found + 1);
if (next && !isSafeBoundary(next, delimiter_code, state.md)) {
return false;
}
}
let data = state.src.slice(pos + 1, found);
let token = state.push("html_raw", "", 0);
const escaped = state.md.utils.escapeHtml(data);
let math_class = delimiter_code === 36 ? "'math'" : "'asciimath'";
token.content = `<span class=${math_class}>${escaped}</span>`;
state.pos = found + 1;
return true;
}
function inlineMath(state, silent) {
return math_input(state, silent, 36 /* $ */);
}
function asciiMath(state, silent) {
return math_input(state, silent, 37 /* % */);
}
function isBlockMarker(state, start, max, md) {
if (state.src.charCodeAt(start) !== 36 /* $ */) {
return false;
}
start++;
if (state.src.charCodeAt(start) !== 36 /* $ */) {
return false;
}
start++;
// ensure we only have newlines after our $$
for (let i = start; i < max; i++) {
if (!md.utils.isSpace(state.src.charCodeAt(i))) {
return false;
}
}
return true;
}
function blockMath(state, startLine, endLine, silent) {
let start = state.bMarks[startLine] + state.tShift[startLine],
max = state.eMarks[startLine];
if (!isBlockMarker(state, start, max, state.md)) {
return false;
}
if (silent) {
return true;
}
let nextLine = startLine;
let closed = false;
for (;;) {
nextLine++;
// unclosed $$ is considered math
if (nextLine >= endLine) {
break;
}
if (
isBlockMarker(
state,
state.bMarks[nextLine] + state.tShift[nextLine],
state.eMarks[nextLine],
state.md
)
) {
closed = true;
break;
}
}
let token = state.push("html_raw", "", 0);
let endContent = closed ? state.eMarks[nextLine - 1] : state.eMarks[nextLine];
let content = state.src.slice(
state.bMarks[startLine + 1] + state.tShift[startLine + 1],
endContent
);
const escaped = state.md.utils.escapeHtml(content);
token.content = `<div class='math'>\n${escaped}\n</div>\n`;
state.line = closed ? nextLine + 1 : nextLine;
return true;
}
export function setup(helper) {
if (!helper.markdownIt) {
return;
}
let enable_asciimath;
helper.registerOptions((opts, siteSettings) => {
opts.features.math = siteSettings.discourse_math_enabled;
enable_asciimath = siteSettings.discourse_math_enable_asciimath;
});
helper.registerPlugin(md => {
if (enable_asciimath) {
md.inline.ruler.after("escape", "asciimath", asciiMath);
}
md.inline.ruler.after("escape", "math", inlineMath);
md.block.ruler.after("code", "math", blockMath, {
alt: ["paragraph", "reference", "blockquote", "list"]
});
});
}