Created
September 19, 2016 17:30
-
-
Save jdbrice/e9e1b0893748a836425883892116038e to your computer and use it in GitHub Desktop.
Mystical Magical way that Jupyter handles MathJax in markdown
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SEE : notebook/notebook/static/notebook/js/outputarea.js | |
// around line 632 | |
var append_markdown = function(markdown, md, element) { | |
var type = MIME_MARKDOWN; | |
var toinsert = this.create_output_subarea(md, "output_markdown rendered_html", type); | |
var text_and_math = mathjaxutils.remove_math(markdown); | |
var text = text_and_math[0]; | |
var math = text_and_math[1]; | |
marked(text, function (err, html) { | |
html = mathjaxutils.replace_math(html, math); | |
toinsert.append(html); | |
}); | |
dblclick_to_reset_size(toinsert.find('img')); | |
element.append(toinsert); | |
return toinsert; | |
}; | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Jupyter Development Team. | |
// Distributed under the terms of the Modified BSD License. | |
define([ | |
'base/js/utils', | |
'base/js/dialog', | |
], function(utils, dialog) { | |
"use strict"; | |
var init = function () { | |
if (window.MathJax) { | |
// MathJax loaded | |
MathJax.Hub.Config({ | |
tex2jax: { | |
inlineMath: [ ['$','$'], ["\\(","\\)"] ], | |
displayMath: [ ['$$','$$'], ["\\[","\\]"] ], | |
processEscapes: true, | |
processEnvironments: true | |
}, | |
MathML: { | |
extensions: ['content-mathml.js'] | |
}, | |
// Center justify equations in code and markdown cells. Elsewhere | |
// we use CSS to left justify single line equations in code cells. | |
displayAlign: 'center', | |
"HTML-CSS": { | |
availableFonts: [], | |
imageFont: null, | |
preferredFont: null, | |
webFont: "STIX-Web", | |
styles: {'.MathJax_Display': {"margin": 0}}, | |
linebreaks: { automatic: true } | |
}, | |
}); | |
MathJax.Hub.Configured(); | |
} else if (window.mathjax_url !== "") { | |
// Don't have MathJax, but should. Show dialog. | |
dialog.modal({ | |
title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'", | |
body : $("<p/>").addClass('dialog').text( | |
"Math/LaTeX rendering will be disabled." | |
), | |
buttons : { | |
OK : {class: "btn-danger"} | |
} | |
}); | |
} | |
}; | |
// Some magic for deferring mathematical expressions to MathJax | |
// by hiding them from the Markdown parser. | |
// Some of the code here is adapted with permission from Davide Cervone | |
// under the terms of the Apache2 license governing the MathJax project. | |
// Other minor modifications are also due to StackExchange and are used with | |
// permission. | |
var inline = "$"; // the inline math delimiter | |
// MATHSPLIT contains the pattern for math delimiters and special symbols | |
// needed for searching for math in the text input. | |
var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i; | |
// 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. | |
// The preProcess function is called on all blocks if it has been passed in | |
var process_math = function (i, j, pre_process, math, blocks) { | |
var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for & | |
.replace(/</g, "<") // use HTML entity for < | |
.replace(/>/g, ">") // use HTML entity for > | |
; | |
if (utils.browser === 'msie') { | |
block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n"); | |
} | |
while (j > i) { | |
blocks[j] = ""; | |
j--; | |
} | |
blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later | |
if (pre_process){ | |
block = pre_process(block); | |
} | |
math.push(block); | |
return blocks; | |
}; | |
// 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). | |
// | |
var remove_math = function (text) { | |
var math = []; // stores math strings for later | |
var start; | |
var end; | |
var last; | |
var braces; | |
// Except for extreme edge cases, this should catch precisely those pieces of the markdown | |
// source that will later be turned into code spans. While MathJax will not TeXify code spans, | |
// we still have to consider them at this point; the following issue has happened several times: | |
// | |
// `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables. | |
var hasCodeSpans = /`/.test(text), | |
de_tilde; | |
if (hasCodeSpans) { | |
text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) { | |
return wholematch.replace(/\$/g, "~D"); | |
}); | |
de_tilde = function (text) { | |
return text.replace(/~([TD])/g, function (wholematch, character) { | |
return { T: "~", D: "$" }[character]; | |
}); | |
}; | |
} else { | |
de_tilde = function (text) { return text; }; | |
} | |
var blocks = utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT); | |
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, look for the end delimiter, | |
// but don't go past double line breaks, and | |
// and balance braces within the math. | |
// | |
if (block === end) { | |
if (braces) { | |
last = i; | |
} | |
else { | |
blocks = process_math(start, i, de_tilde, math, blocks); | |
start = null; | |
end = null; | |
last = null; | |
} | |
} | |
else if (block.match(/\n.*\n/)) { | |
if (last) { | |
i = last; | |
blocks = process_math(start, i, de_tilde, math, blocks); | |
} | |
start = null; | |
end = null; | |
last = null; | |
braces = 0; | |
} | |
else if (block === "{") { | |
braces++; | |
} | |
else if (block === "}" && braces) { | |
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; | |
} | |
} | |
} | |
if (last) { | |
blocks = process_math(start, last, de_tilde, math, blocks); | |
start = null; | |
end = null; | |
last = null; | |
} | |
return [de_tilde(blocks.join("")), math]; | |
}; | |
// | |
// Put back the math strings that were saved, | |
// and clear the math array (no need to keep it around). | |
// | |
var replace_math = function (text, math) { | |
text = text.replace(/@@(\d+)@@/g, function (match, n) { | |
return math[n]; | |
}); | |
return text; | |
}; | |
var mathjaxutils = { | |
init : init, | |
remove_math : remove_math, | |
replace_math : replace_math | |
}; | |
return mathjaxutils; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment