mathjaxutils.js
251 lines
| 9.9 KiB
| application/javascript
|
JavascriptLexer
Brian Granger
|
r6193 | //---------------------------------------------------------------------------- | ||
Aron Ahmadia
|
r8565 | // Copyright (C) 2008-2012 The IPython Development Team | ||
Brian Granger
|
r6193 | // | ||
// Distributed under the terms of the BSD License. The full license is in | ||||
// the file COPYING, distributed as part of this software. | ||||
//---------------------------------------------------------------------------- | ||||
//============================================================================ | ||||
Aron Ahmadia
|
r8565 | // MathJax utility functions | ||
Brian Granger
|
r6193 | //============================================================================ | ||
Matthias BUSSONNIER
|
r11524 | |||
Aron Ahmadia
|
r8565 | IPython.namespace('IPython.mathjaxutils'); | ||
Brian Granger
|
r6193 | |||
Aron Ahmadia
|
r8565 | IPython.mathjaxutils = (function (IPython) { | ||
Matthias BUSSONNIER
|
r11525 | "use strict"; | ||
Aron Ahmadia
|
r8565 | |||
var init = function () { | ||||
Aron Ahmadia
|
r8660 | if (window.MathJax) { | ||
Brian Granger
|
r6193 | // MathJax loaded | ||
Aron Ahmadia
|
r8662 | MathJax.Hub.Config({ | ||
tex2jax: { | ||||
inlineMath: [ ['$','$'], ["\\(","\\)"] ], | ||||
displayMath: [ ['$$','$$'], ["\\[","\\]"] ], | ||||
MinRK
|
r10941 | processEscapes: true, | ||
Aron Ahmadia
|
r8662 | processEnvironments: true | ||
}, | ||||
Brian E. Granger
|
r12944 | // Center justify equations in code and markdown cells. Elsewhere | ||
// we use CSS to left justify single line equations in code cells. | ||||
displayAlign: 'center', | ||||
Aron Ahmadia
|
r8662 | "HTML-CSS": { | ||
Brian E. Granger
|
r12803 | styles: {'.MathJax_Display': {"margin": 0}}, | ||
linebreaks: { automatic: true } | ||||
Aron Ahmadia
|
r8662 | } | ||
}); | ||||
MathJax.Hub.Configured(); | ||||
Matthias BUSSONNIER
|
r11525 | } else if (window.mathjax_url !== "") { | ||
Brian Granger
|
r6193 | // Don't have MathJax, but should. Show dialog. | ||
MinRK
|
r10895 | var message = $('<div/>') | ||
Brian Granger
|
r6193 | .append( | ||
Matthias BUSSONNIER
|
r14634 | $("<p/></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "Math/LaTeX rendering will be disabled." | ||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<p></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "If you have administrative access to the notebook server and" + | ||
" a working internet connection, you can install a local copy" + | ||||
" of MathJax for offline use with the following command on the server" + | ||||
" at a Python or IPython prompt:" | ||||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<pre></pre>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | ">>> from IPython.external import mathjax; mathjax.install_mathjax()" | ||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<p></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "This will try to install MathJax into the IPython source directory." | ||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<p></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "If IPython is installed to a location that requires" + | ||
" administrative privileges to write, you will need to make this call as" + | ||||
" an administrator, via 'sudo'." | ||||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<p></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "When you start the notebook server, you can instruct it to disable MathJax support altogether:" | ||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<pre></pre>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "$ ipython notebook --no-mathjax" | ||
) | ||||
).append( | ||||
Matthias BUSSONNIER
|
r14634 | $("<p></p>").addClass('dialog').text( | ||
Brian Granger
|
r6193 | "which will prevent this dialog from appearing." | ||
) | ||||
Matthias BUSSONNIER
|
r11525 | ); | ||
MinRK
|
r10895 | IPython.dialog.modal({ | ||
title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'", | ||||
body : message, | ||||
buttons : { | ||||
OK : {class: "btn-danger"} | ||||
} | ||||
}); | ||||
Matthias BUSSONNIER
|
r11525 | } | ||
Brian Granger
|
r6193 | }; | ||
Aron Ahmadia
|
r8568 | // 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. | ||||
Aron Ahmadia
|
r8565 | |||
var inline = "$"; // the inline math delimiter | ||||
// MATHSPLIT contains the pattern for math delimiters and special symbols | ||||
Matthias BUSSONNIER
|
r11525 | // needed for searching for math in the text input. | ||
Aron Ahmadia
|
r8565 | var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i; | ||
Matthias BUSSONNIER
|
r11525 | // The math is in blocks i through j, so | ||
Aron Ahmadia
|
r8565 | // 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 | ||||
Matthias BUSSONNIER
|
r11525 | var process_math = function (i, j, pre_process, math, blocks) { | ||
Aron Ahmadia
|
r8565 | 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 > | ||||
; | ||||
MinRK
|
r14204 | if (IPython.utils.browser === 'msie') { | ||
Matthias BUSSONNIER
|
r11525 | block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n"); | ||
Aron Ahmadia
|
r8565 | } | ||
while (j > i) { | ||||
blocks[j] = ""; | ||||
j--; | ||||
} | ||||
blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later | ||||
Matthias BUSSONNIER
|
r11525 | if (pre_process){ | ||
Aron Ahmadia
|
r8568 | block = pre_process(block); | ||
Matthias BUSSONNIER
|
r11525 | } | ||
Aron Ahmadia
|
r8565 | math.push(block); | ||
Matthias BUSSONNIER
|
r11525 | return blocks; | ||
}; | ||||
Aron Ahmadia
|
r8565 | |||
// 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). | ||||
// | ||||
Aron Ahmadia
|
r8568 | var remove_math = function (text) { | ||
Matthias BUSSONNIER
|
r11524 | var math = []; // stores math strings for later | ||
var start; | ||||
var end; | ||||
var last; | ||||
Matthias BUSSONNIER
|
r11525 | var braces; | ||
Aron Ahmadia
|
r8565 | // 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), | ||||
Aron Ahmadia
|
r8568 | de_tilde; | ||
Aron Ahmadia
|
r8565 | if (hasCodeSpans) { | ||
text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) { | ||||
return wholematch.replace(/\$/g, "~D"); | ||||
}); | ||||
Matthias BUSSONNIER
|
r11525 | de_tilde = function (text) { | ||
return text.replace(/~([TD])/g, function (wholematch, character) { | ||||
return { T: "~", D: "$" }[character]; | ||||
}); | ||||
}; | ||||
Aron Ahmadia
|
r8565 | } else { | ||
Aron Ahmadia
|
r8568 | de_tilde = function (text) { return text; }; | ||
Aron Ahmadia
|
r8565 | } | ||
Matthias BUSSONNIER
|
r11525 | |||
Matthias BUSSONNIER
|
r11524 | var blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT); | ||
Aron Ahmadia
|
r8565 | |||
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) { | ||||
Matthias BUSSONNIER
|
r11525 | last = i; | ||
Aron Ahmadia
|
r8565 | } | ||
else { | ||||
Matthias BUSSONNIER
|
r11525 | blocks = process_math(start, i, de_tilde, math, blocks); | ||
start = null; | ||||
end = null; | ||||
last = null; | ||||
Aron Ahmadia
|
r8565 | } | ||
} | ||||
else if (block.match(/\n.*\n/)) { | ||||
if (last) { | ||||
i = last; | ||||
Matthias BUSSONNIER
|
r11525 | blocks = process_math(start, i, de_tilde, math, blocks); | ||
Aron Ahmadia
|
r8565 | } | ||
Matthias BUSSONNIER
|
r11524 | start = null; | ||
end = null; | ||||
last = null; | ||||
Aron Ahmadia
|
r8565 | braces = 0; | ||
} | ||||
else if (block === "{") { | ||||
Matthias BUSSONNIER
|
r11525 | braces++; | ||
Aron Ahmadia
|
r8565 | } | ||
else if (block === "}" && braces) { | ||||
Matthias BUSSONNIER
|
r11525 | braces--; | ||
Aron Ahmadia
|
r8565 | } | ||
} | ||||
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) { | ||||
Matthias BUSSONNIER
|
r11525 | blocks = process_math(start, last, de_tilde, math, blocks); | ||
start = null; | ||||
end = null; | ||||
last = null; | ||||
Aron Ahmadia
|
r8565 | } | ||
Matthias BUSSONNIER
|
r11524 | return [de_tilde(blocks.join("")), math]; | ||
Matthias BUSSONNIER
|
r11525 | }; | ||
Brian Granger
|
r6193 | |||
Aron Ahmadia
|
r8565 | // | ||
// Put back the math strings that were saved, | ||||
// and clear the math array (no need to keep it around). | ||||
Matthias BUSSONNIER
|
r11525 | // | ||
Matthias BUSSONNIER
|
r11524 | var replace_math = function (text, math) { | ||
Aron Ahmadia
|
r8565 | text = text.replace(/@@(\d+)@@/g, function (match, n) { | ||
Matthias BUSSONNIER
|
r11525 | return math[n]; | ||
Aron Ahmadia
|
r8565 | }); | ||
return text; | ||||
Matthias BUSSONNIER
|
r11525 | }; | ||
Brian Granger
|
r6193 | |||
Aron Ahmadia
|
r8565 | return { | ||
init : init, | ||||
Aron Ahmadia
|
r8568 | remove_math : remove_math, | ||
Aron Ahmadia
|
r8660 | replace_math : replace_math | ||
Aron Ahmadia
|
r8565 | }; | ||
Brian Granger
|
r6193 | |||
Matthias BUSSONNIER
|
r11524 | }(IPython)); | ||