Published
Edited
Mar 2, 2021
Importers
1 star
Insert cell
Insert cell
Insert cell
example = (await fetch(
"https://gist.githubusercontent.com/bryangingechen/e340aa91244e730798a39156f371e89a/raw/46d1d5caf0cde719293414cc04ff56f892c37529/system_of_equations.md"
)).text()
Insert cell
example2 = removeMath(example, '$')
Insert cell
example2.text
Insert cell
example2.math
Insert cell
replaceMath(example2.text, example2.math)
Insert cell
SPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@|`+)/i
Insert cell
capturingStringSplit = function(str, regex) {
return str.split(regex);
}
Insert cell
//
// Break up the text into its component parts and search
// through them for math delimiters, braces, linebreaks, etc.
// Math delimiters must match and braces must balance.
// Don't allow math to pass through a double linebreak
// (which will be a paragraph).
// Handle backticks (don't do math inside them)
//
function removeMath(text, inline) {
//
// The math is in blocks i through j, so
// collect it into one block and clear the others.
// Replace &, <, and > by named entities.
// For IE, put <br> at the ends of comments since IE removes \n.
// Clear the current math positions and store the index of the
// math, then push the math string onto the storage array.
//
function processMath(i, j) {
var block = blocks
.slice(i, j + 1)
.join("")
.replace(/&/g, "&amp;") // use HTML entity for &
.replace(/</g, "&lt;") // use HTML entity for <
.replace(/>/g, "&gt;"); // use HTML entity for >
if (indent) block = block.replace(/\n /g, "\n");
// if (HUB.Browser.isMSIE) {
// block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
// }
while (j > i) blocks[j--] = "";
blocks[i] = "@@" + math.length + "@@";
math.push(block);
start = end = last = null;
}
let start = null;
let end = null;
let last = null;
let indent = null; // for tracking math delimiters
let braces = null;
const math = []; // stores math strings for latter

let blocks = capturingStringSplit(text.replace(/\r\n?/g, "\n"), SPLIT);

for (var i = 1, m = blocks.length; i < m; i += 2) {
var block = blocks[i];
if (block.charAt(0) === "@") {
//
// Things that look like our math markers will get
// stored and then retrieved along with the math.
//
blocks[i] = "@@" + math.length + "@@";
math.push(block);
} else if (start) {
//
// If we are in math or backticks,
// look for the end delimiter,
// but don't go past double line breaks,
// and balance braces within the math,
// but don't process math inside backticks.
//
if (block === end) {
if (braces > 0) {
last = i;
} else if (braces === 0) {
processMath(start, i);
} else {
start = end = last = null;
}
} else if (block.match(/\n.*\n/) || i + 2 >= m) {
if (last) {
i = last;
if (braces >= 0) processMath(start, i);
}
start = end = last = null;
braces = 0;
} else if (block === "{" && braces >= 0) {
braces++;
} else if (block === "}" && braces > 0) {
braces--;
}
} else {
//
// Look for math start delimiters and when
// found, set up the end delimiter.
//
if (block === inline || block === "$$") {
start = i;
end = block;
braces = 0;
} else if (block.substr(1, 5) === "begin") {
start = i;
end = "\\end" + block.substr(6);
braces = 0;
} else if (block.charAt(0) === "`") {
start = last = i;
end = block;
braces = -1; // no brace balancing
} else if (block.charAt(0) === "\n") {
if (block.match(/ $/)) indent = true;
}
}
}
if (last) processMath(start, last);

// the commonmark renderer will render any `\$` as a simple `$`
// which could become a problem if `$` is used as a mathjax inline delimiter
// because then even escaped equations (starting with `\$`) would be detected
// as mathjax equations. Let's double-escape to make sure we still have a `\$`
// after commonmark did its conversion.
function doubleEscapeDelimiters(text, inline) {
if (!inline.startsWith("\\")) {
return text.replace(/\\\$/g, '\\\\$');
}
return text;
}

return { text: doubleEscapeDelimiters(blocks.join(""), inline), math };
}
Insert cell
//
// Put back the math strings that were saved,
// and clear the math array (no need to keep it around).
//
function replaceMath(input, math) {
const text = input.replace(/@@(\d+)@@/g, function(match, n) {
return math[n];
});
// math = null;
return text;
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more