markdown-it-simplemath
Version:
markdown-it plugin that adds simple math support
143 lines (121 loc) • 4.04 kB
JavaScript
/*
* Copyright Adam Pritchard 2015
* MIT License : http://adampritchard.mit-license.org/
*/
/*
This is basically a stripped down, simplified version of:
https://github.com/runarberg/markdown-it-math
The extra math libraries included by that project were too onerous for use in
Markdown Here, so this has been altered specifically for the needs of MDH.
*/
;
/*jshint node:true*/
function scanDelims(state, start) {
var pos = state.pos, lastChar, nextChar, count,
isLastWhiteSpace, isLastPunctChar,
isNextWhiteSpace, isNextPunctChar,
can_open = true,
can_close = true,
max = state.posMax,
isWhiteSpace = state.md.utils.isWhiteSpace,
isPunctChar = state.md.utils.isPunctChar,
isMdAsciiPunct = state.md.utils.isMdAsciiPunct;
// treat beginning of the line as a whitespace
lastChar = start > 0 ? state.src.charCodeAt(start - 1) : 0x20;
if (pos >= max) {
can_open = false;
}
count = pos - start;
// treat end of the line as a whitespace
nextChar = pos < max ? state.src.charCodeAt(pos) : 0x20;
isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctChar(String.fromCharCode(lastChar));
isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctChar(String.fromCharCode(nextChar));
isLastWhiteSpace = isWhiteSpace(lastChar);
isNextWhiteSpace = isWhiteSpace(nextChar);
if (isNextWhiteSpace) {
can_open = false;
} else if (isNextPunctChar) {
if (!(isLastWhiteSpace || isLastPunctChar)) {
can_open = false;
}
}
if (isLastWhiteSpace) {
can_close = false;
} else if (isLastPunctChar) {
if (!(isNextWhiteSpace || isNextPunctChar)) {
can_close = false;
}
}
return {
can_open: can_open,
can_close: can_close,
delims: count
};
}
function makeMath_inline(open, close) {
return function math_inline(state, silent) {
var startCount,
found,
res,
token,
closeDelim,
max = state.posMax,
start = state.pos,
openDelim = state.src.slice(start, start + open.length);
if (openDelim !== open) { return false; }
if (silent) { return false; } // Don’t run any pairs in validation mode
res = scanDelims(state, start + open.length);
startCount = res.delims;
if (!res.can_open) {
state.pos += startCount;
// Earlier we checked !silent, but this implementation does not need it
state.pending += state.src.slice(start, state.pos);
return true;
}
state.pos = start + open.length;
while (state.pos < max) {
closeDelim = state.src.slice(state.pos, state.pos + close.length);
if (closeDelim === close) {
res = scanDelims(state, state.pos + close.length);
if (res.can_close) {
found = true;
break;
}
}
state.md.inline.skipToken(state);
}
if (!found) {
// Parser failed to find ending tag, so it is not a valid math
state.pos = start;
return false;
}
if (start + close.length === state.pos) {
// There is nothing between the delimiters -- don't match.
state.pos = start;
return false;
}
// Found!
state.posMax = state.pos;
state.pos = start + close.length;
// Earlier we checked !silent, but this implementation does not need it
token = state.push('math_inline', 'math', 0);
token.content = state.src.slice(state.pos, state.posMax);
token.markup = open;
state.pos = state.posMax + close.length;
state.posMax = max;
return true;
};
}
module.exports = function math_plugin(md, options) {
var inlineOpen = '$',
inlineClose = '$';
if (!options || !options.inlineRenderer) {
throw new Error('options.inlineRender is required');
}
var inlineRenderer = function(tokens, idx) {
return options.inlineRenderer(tokens[idx].content);
};
var math_inline = makeMath_inline(inlineOpen, inlineClose);
md.inline.ruler.before('escape', 'math_inline', math_inline);
md.renderer.rules.math_inline = inlineRenderer;
};