diff --git a/IPython/frontend/html/notebook/static/js/initmathjax.js b/IPython/frontend/html/notebook/static/js/mathjaxutils.js
similarity index 54%
rename from IPython/frontend/html/notebook/static/js/initmathjax.js
rename to IPython/frontend/html/notebook/static/js/mathjaxutils.js
index 5987eaa..8eeeb7f 100644
--- a/IPython/frontend/html/notebook/static/js/initmathjax.js
+++ b/IPython/frontend/html/notebook/static/js/mathjaxutils.js
@@ -1,23 +1,28 @@
//----------------------------------------------------------------------------
-// Copyright (C) 2008-2011 The IPython Development Team
+// Copyright (C) 2008-2012 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------
//============================================================================
-// MathJax initialization
+// MathJax utility functions
//============================================================================
-var IPython = (function (IPython) {
+IPython.namespace('IPython.mathjaxutils');
- var init_mathjax = function () {
+IPython.mathjaxutils = (function (IPython) {
+
+ var init = function () {
if (window.MathJax) {
// MathJax loaded
MathJax.Hub.Config({
+ TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true },
+ extensions: ["autoload-all.js"] },
tex2jax: {
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
- displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
+ displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
+ processEnvironments: true
},
displayAlign: 'left', // Change this to 'center' to center equations.
"HTML-CSS": {
@@ -74,10 +79,152 @@ var IPython = (function (IPython) {
};
};
+ // Some magic for deferring mathematical expressions to MathJaX
+ // Some of the code here is adapted with permission from Stack Exchange Inc.
+
+ var inline = "$"; // the inline math delimiter
+ var blocks, start, end, last, braces; // used in searching for math
+ var math; // stores math until pagedown (Markdown parser) is done
+ var HUB = MathJax.Hub;
+
+ // 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
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
+ function processMath(i, j, preProcess) {
+ var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for &
+ .replace(//g, ">") // use HTML entity for >
+ ;
+ if (HUB.Browser.isMSIE) {
+ block = block.replace(/(%[^\n]*)\n/g, "$1
\n")
+ }
+ while (j > i) {
+ blocks[j] = "";
+ j--;
+ }
+ blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
+ if (preProcess)
+ block = preProcess(block);
+ math.push(block);
+ start = end = last = null;
+ }
+
+ // 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).
+ //
+ function removeMath(text) {
+ start = end = last = null; // for tracking math delimiters
+ math = []; // stores math strings for later
+
+ // 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. --> $foo ` and `$bar
are variables.
+
+ var hasCodeSpans = /`/.test(text),
+ deTilde;
+ if (hasCodeSpans) {
+ text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
+ return wholematch.replace(/\$/g, "~D");
+ });
+ deTilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
+ } else {
+ deTilde = function (text) { return text; };
+ }
+
+ blocks = IPython.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 {
+ processMath(start, i, deTilde)
+ }
+ }
+ else if (block.match(/\n.*\n/)) {
+ if (last) {
+ i = last;
+ processMath(start, i, deTilde)
+ }
+ start = end = 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) {
+ processMath(start, last, deTilde)
+ }
+ return deTilde(blocks.join(""));
+ }
- // Set module variables
- IPython.init_mathjax = init_mathjax;
+ //
+ // Put back the math strings that were saved,
+ // and clear the math array (no need to keep it around).
+ //
+ function replaceMath(text) {
+ text = text.replace(/@@(\d+)@@/g, function (match, n) {
+ return math[n]
+ });
+ math = null;
+ return text;
+ }
- return IPython;
+ return {
+ init : init,
+ processMath : processMath,
+ removeMath : removeMath,
+ replaceMath : replaceMath,
+ };
}(IPython));
\ No newline at end of file
diff --git a/IPython/frontend/html/notebook/static/js/notebookmain.js b/IPython/frontend/html/notebook/static/js/notebookmain.js
index a7d7d43..13059fc 100644
--- a/IPython/frontend/html/notebook/static/js/notebookmain.js
+++ b/IPython/frontend/html/notebook/static/js/notebookmain.js
@@ -31,7 +31,7 @@ $(document).ready(function () {
}
// end monkey patching CodeMirror
- IPython.init_mathjax();
+ IPython.mathjaxutils.init();
IPython.read_only = $('body').data('readOnly') === 'True';
$('div#main_app').addClass('border-box-sizing ui-widget');
diff --git a/IPython/frontend/html/notebook/static/js/printnotebookmain.js b/IPython/frontend/html/notebook/static/js/printnotebookmain.js
index ba773cc..59a62b0 100644
--- a/IPython/frontend/html/notebook/static/js/printnotebookmain.js
+++ b/IPython/frontend/html/notebook/static/js/printnotebookmain.js
@@ -12,7 +12,7 @@
$(document).ready(function () {
- IPython.init_mathjax();
+ IPython.mathjaxutils.init();
IPython.read_only = $('body').data('readOnly') === 'True';
$('div#main_app').addClass('border-box-sizing ui-widget');
diff --git a/IPython/frontend/html/notebook/static/js/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js
index 14704d1..54bc5a3 100644
--- a/IPython/frontend/html/notebook/static/js/textcell.js
+++ b/IPython/frontend/html/notebook/static/js/textcell.js
@@ -1,5 +1,5 @@
//----------------------------------------------------------------------------
-// Copyright (C) 2008-2011 The IPython Development Team
+// Copyright (C) 2008-2012 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
@@ -221,7 +221,11 @@ var IPython = (function (IPython) {
if (this.rendered === false) {
var text = this.get_text();
if (text === "") { text = this.placeholder; }
+
+ text = IPython.mathjaxutils.removeMath(text)
var html = IPython.markdown_converter.makeHtml(text);
+ html = IPython.mathjaxutils.replaceMath(html)
+
try {
this.set_rendered(html);
} catch (e) {
diff --git a/IPython/frontend/html/notebook/static/js/utils.js b/IPython/frontend/html/notebook/static/js/utils.js
index 9dfa478..d38d6da 100644
--- a/IPython/frontend/html/notebook/static/js/utils.js
+++ b/IPython/frontend/html/notebook/static/js/utils.js
@@ -1,5 +1,5 @@
//----------------------------------------------------------------------------
-// Copyright (C) 2008-2011 The IPython Development Team
+// Copyright (C) 2008-2012 The IPython Development Team
//
// Distributed under the terms of the BSD License. The full license is in
// the file COPYING, distributed as part of this software.
@@ -13,6 +13,123 @@ IPython.namespace('IPython.utils');
IPython.utils = (function (IPython) {
+ //============================================================================
+ // Cross-browser RegEx Split
+ //============================================================================
+
+ // This code has been MODIFIED from the code licensed below to not replace the
+ // default browser split. The license is reproduced here.
+
+ // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
+ /*!
+ * Cross-Browser Split 1.1.1
+ * Copyright 2007-2012 Steven Levithan
+ * Available under the MIT License
+ * ECMAScript compliant, uniform cross-browser split method
+ */
+
+ /**
+ * Splits a string into an array of strings using a regex or string
+ * separator. Matches of the separator are not included in the result array.
+ * However, if `separator` is a regex that contains capturing groups,
+ * backreferences are spliced into the result each time `separator` is
+ * matched. Fixes browser bugs compared to the native
+ * `String.prototype.split` and can be used reliably cross-browser.
+ * @param {String} str String to split.
+ * @param {RegExp|String} separator Regex or string to use for separating
+ * the string.
+ * @param {Number} [limit] Maximum number of items to include in the result
+ * array.
+ * @returns {Array} Array of substrings.
+ * @example
+ *
+ * // Basic use
+ * regex_split('a b c d', ' ');
+ * // -> ['a', 'b', 'c', 'd']
+ *
+ * // With limit
+ * regex_split('a b c d', ' ', 2);
+ * // -> ['a', 'b']
+ *
+ * // Backreferences in result array
+ * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
+ * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
+ */
+ var regex_split = function (str, separator, limit) {
+ // If `separator` is not a regex, use `split`
+ if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
+ return split.call(str, separator, limit);
+ }
+ var output = [],
+ flags = (separator.ignoreCase ? "i" : "") +
+ (separator.multiline ? "m" : "") +
+ (separator.extended ? "x" : "") + // Proposed for ES6
+ (separator.sticky ? "y" : ""), // Firefox 3+
+ lastLastIndex = 0,
+ // Make `global` and avoid `lastIndex` issues by working with a copy
+ separator = new RegExp(separator.source, flags + "g"),
+ separator2, match, lastIndex, lastLength;
+ str += ""; // Type-convert
+
+ compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined"
+ if (!compliantExecNpcg) {
+ // Doesn't need flags gy, but they don't hurt
+ separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
+ }
+ /* Values for `limit`, per the spec:
+ * If undefined: 4294967295 // Math.pow(2, 32) - 1
+ * If 0, Infinity, or NaN: 0
+ * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
+ * If negative number: 4294967296 - Math.floor(Math.abs(limit))
+ * If other: Type-convert, then use the above rules
+ */
+ limit = typeof(limit) === "undefined" ?
+ -1 >>> 0 : // Math.pow(2, 32) - 1
+ limit >>> 0; // ToUint32(limit)
+ while (match = separator.exec(str)) {
+ // `separator.lastIndex` is not reliable cross-browser
+ lastIndex = match.index + match[0].length;
+ if (lastIndex > lastLastIndex) {
+ output.push(str.slice(lastLastIndex, match.index));
+ // Fix browsers whose `exec` methods don't consistently return `undefined` for
+ // nonparticipating capturing groups
+ if (!compliantExecNpcg && match.length > 1) {
+ match[0].replace(separator2, function () {
+ for (var i = 1; i < arguments.length - 2; i++) {
+ if (typeof(arguments[i]) === "undefined") {
+ match[i] = undefined;
+ }
+ }
+ });
+ }
+ if (match.length > 1 && match.index < str.length) {
+ Array.prototype.push.apply(output, match.slice(1));
+ }
+ lastLength = match[0].length;
+ lastLastIndex = lastIndex;
+ if (output.length >= limit) {
+ break;
+ }
+ }
+ if (separator.lastIndex === match.index) {
+ separator.lastIndex++; // Avoid an infinite loop
+ }
+ }
+ if (lastLastIndex === str.length) {
+ if (lastLength || !separator.test("")) {
+ output.push("");
+ }
+ } else {
+ output.push(str.slice(lastLastIndex));
+ }
+ return output.length > limit ? output.slice(0, limit) : output;
+ };
+
+ //============================================================================
+ // End contributed Cross-browser RegEx Split
+ //============================================================================
+
+
var uuid = function () {
// http://www.ietf.org/rfc/rfc4122.txt
var s = [];
@@ -138,6 +255,7 @@ IPython.utils = (function (IPython) {
return {
+ regex_split : regex_split,
uuid : uuid,
fixConsole : fixConsole,
keycodes : keycodes,
diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html
index 08579a6..ef991d4 100644
--- a/IPython/frontend/html/notebook/templates/notebook.html
+++ b/IPython/frontend/html/notebook/templates/notebook.html
@@ -199,7 +199,7 @@ data-notebook-id={{notebook_id}}
-
+
diff --git a/IPython/frontend/html/notebook/templates/printnotebook.html b/IPython/frontend/html/notebook/templates/printnotebook.html
index ca0a34a..93aeae4 100644
--- a/IPython/frontend/html/notebook/templates/printnotebook.html
+++ b/IPython/frontend/html/notebook/templates/printnotebook.html
@@ -69,7 +69,7 @@ data-notebook-id={{notebook_id}}
-
+