From 0caac8cd280e011a333da0a15d6eef81bc812167 Mon Sep 17 00:00:00 2001 From: Aron Ahmadia Date: Tue, 9 Oct 2012 17:51:21 +0300 Subject: [PATCH 1/4] partial refactor, can't quite get environments working --- .../html/notebook/static/js/mathjaxutils.js | 230 ++++++++++++++++++ .../html/notebook/static/js/notebookmain.js | 2 +- .../html/notebook/static/js/textcell.js | 6 +- .../frontend/html/notebook/static/js/utils.js | 120 ++++++++- .../html/notebook/templates/notebook.html | 2 +- 5 files changed, 356 insertions(+), 4 deletions(-) create mode 100644 IPython/frontend/html/notebook/static/js/mathjaxutils.js diff --git a/IPython/frontend/html/notebook/static/js/mathjaxutils.js b/IPython/frontend/html/notebook/static/js/mathjaxutils.js new file mode 100644 index 000000000..8eeeb7fb5 --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/mathjaxutils.js @@ -0,0 +1,230 @@ +//---------------------------------------------------------------------------- +// 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 utility functions +//============================================================================ + +IPython.namespace('IPython.mathjaxutils'); + +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: [ ['$$','$$'], ["\\[","\\]"] ], + processEnvironments: true + }, + displayAlign: 'left', // Change this to 'center' to center equations. + "HTML-CSS": { + styles: {'.MathJax_Display': {"margin": 0}} + } + }); + } else if (window.mathjax_url != "") { + // Don't have MathJax, but should. Show dialog. + var dialog = $('
') + .append( + $("

").addClass('dialog').html( + "Math/LaTeX rendering will be disabled." + ) + ).append( + $("

").addClass('dialog').html( + "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( + $("
").addClass('dialog').html(
+                        ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
+                    )
+                ).append(
+                    $("

").addClass('dialog').html( + "This will try to install MathJax into the IPython source directory." + ) + ).append( + $("

").addClass('dialog').html( + "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( + $("

").addClass('dialog').html( + "When you start the notebook server, you can instruct it to disable MathJax support altogether:" + ) + ).append( + $("
").addClass('dialog').html(
+                        "$ ipython notebook --no-mathjax"
+                    )
+                ).append(
+                    $("

").addClass('dialog').html( + "which will prevent this dialog from appearing." + ) + ).dialog({ + title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'", + width: "70%", + modal: true, + }) + } else { + // No MathJax, but none expected. No dialog. + }; + }; + + // 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("")); + } + + // + // 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 { + 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 a7d7d4362..13059fc0e 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/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js index 14704d10c..54bc5a3b9 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 9dfa47842..d38d6da06 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 08579a688..ef991d4d8 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}} - + From 346173bcad085192d9b5268fdf7c441d9fd38b49 Mon Sep 17 00:00:00 2001 From: Aron Ahmadia Date: Tue, 9 Oct 2012 19:31:08 +0300 Subject: [PATCH 2/4] correct environment rendering --- .../html/notebook/static/js/mathjaxutils.js | 13 +++++++++++-- .../frontend/html/notebook/static/js/textcell.js | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/IPython/frontend/html/notebook/static/js/mathjaxutils.js b/IPython/frontend/html/notebook/static/js/mathjaxutils.js index 8eeeb7fb5..484751e99 100644 --- a/IPython/frontend/html/notebook/static/js/mathjaxutils.js +++ b/IPython/frontend/html/notebook/static/js/mathjaxutils.js @@ -17,8 +17,7 @@ IPython.mathjaxutils = (function (IPython) { if (window.MathJax) { // MathJax loaded MathJax.Hub.Config({ - TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true }, - extensions: ["autoload-all.js"] }, + TeX: { equationNumbers: { autoNumber: "AMS", useLabelIds: true } }, tex2jax: { inlineMath: [ ['$','$'], ["\\(","\\)"] ], displayMath: [ ['$$','$$'], ["\\[","\\]"] ], @@ -220,11 +219,21 @@ IPython.mathjaxutils = (function (IPython) { return text; } + function queueRender() { + // see https://groups.google.com/forum/?fromgroups=#!topic/mathjax-users/cpwy5eCH1ZQ + MathJax.Hub.Queue( + ["resetEquationNumbers",MathJax.InputJax.TeX], + ["PreProcess",MathJax.Hub], + ["Reprocess",MathJax.Hub] + ); + } + return { init : init, processMath : processMath, removeMath : removeMath, replaceMath : replaceMath, + queueRender : queueRender }; }(IPython)); \ No newline at end of file diff --git a/IPython/frontend/html/notebook/static/js/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js index 54bc5a3b9..94c776c1f 100644 --- a/IPython/frontend/html/notebook/static/js/textcell.js +++ b/IPython/frontend/html/notebook/static/js/textcell.js @@ -250,6 +250,8 @@ var IPython = (function (IPython) { return '' + code + ''; }); + + IPython.mathjaxutils.queueRender() this.rendered = true; } }; From 2ec0115f7799805139868ab14a1782e9cc0bcd0e Mon Sep 17 00:00:00 2001 From: Aron Ahmadia Date: Wed, 10 Oct 2012 13:55:33 +0300 Subject: [PATCH 3/4] added math typesetting example notebook --- .../Typesetting Math Using MathJax.ipynb | 347 ++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 docs/examples/notebooks/Typesetting Math Using MathJax.ipynb diff --git a/docs/examples/notebooks/Typesetting Math Using MathJax.ipynb b/docs/examples/notebooks/Typesetting Math Using MathJax.ipynb new file mode 100644 index 000000000..3595add8e --- /dev/null +++ b/docs/examples/notebooks/Typesetting Math Using MathJax.ipynb @@ -0,0 +1,347 @@ +{ + "metadata": { + "name": "Typesetting Math Using MathJax" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Markdown parser included in IPython is MathJax-aware. This means that you can freely mix in mathematical expressions using the [MathJax subset of Tex and LaTeX](http://docs.mathjax.org/en/latest/tex.html#tex-support). [Some examples from the MathJax site](http://www.mathjax.org/demos/tex-samples/) are reproduced below, as well as the Markdown+TeX source." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Motivating Examples\n", + "\n", + "---\n", + "\n", + "## The Lorenz Equations\n", + "### Source\n", + "```\\begin{aligned}\n", + "\\dot{x} & = \\sigma(y-x) \\\\\n", + "\\dot{y} & = \\rho x - y - xz \\\\\n", + "\\dot{z} & = -\\beta z + xy\n", + "\\end{aligned}\n", + "```\n", + "### Display\n", + "\\begin{aligned}\n", + "\\dot{x} & = \\sigma(y-x) \\\\\n", + "\\dot{y} & = \\rho x - y - xz \\\\\n", + "\\dot{z} & = -\\beta z + xy\n", + "\\end{aligned}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Cauchy-Schwarz Inequality\n", + "### Source\n", + "```\\begin{equation*}\n", + "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n", + "\\end{equation*}\n", + "```\n", + "### Display\n", + "\\begin{equation*}\n", + "\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A Cross Product Formula\n", + "### Source\n", + "```\\begin{equation*}\n", + "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n", + "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n", + "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n", + "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n", + "\\end{vmatrix} \n", + "\\end{equation*}\n", + "```\n", + "### Display\n", + "\\begin{equation*}\n", + "\\mathbf{V}_1 \\times \\mathbf{V}_2 = \\begin{vmatrix}\n", + "\\mathbf{i} & \\mathbf{j} & \\mathbf{k} \\\\\n", + "\\frac{\\partial X}{\\partial u} & \\frac{\\partial Y}{\\partial u} & 0 \\\\\n", + "\\frac{\\partial X}{\\partial v} & \\frac{\\partial Y}{\\partial v} & 0\n", + "\\end{vmatrix} \n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The probability of getting \\(k\\) heads when flipping \\(n\\) coins is\n", + "### Source\n", + "```\\begin{equation*}\n", + "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n", + "\\end{equation*}\n", + "```\n", + "### Display\n", + "\\begin{equation*}\n", + "P(E) = {n \\choose k} p^k (1-p)^{ n-k} \n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## An Identity of Ramanujan\n", + "### Source\n", + "```\\begin{equation*}\n", + "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n", + "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n", + "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n", + "\\end{equation*}\n", + "```\n", + "### Display\n", + "\\begin{equation*}\n", + "\\frac{1}{\\Bigl(\\sqrt{\\phi \\sqrt{5}}-\\phi\\Bigr) e^{\\frac25 \\pi}} =\n", + "1+\\frac{e^{-2\\pi}} {1+\\frac{e^{-4\\pi}} {1+\\frac{e^{-6\\pi}}\n", + "{1+\\frac{e^{-8\\pi}} {1+\\ldots} } } } \n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A Rogers-Ramanujan Identity\n", + "### Source\n", + "```\\begin{equation*}\n", + "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n", + "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n", + "\\quad\\quad \\text{for $|q|<1$}. \n", + "\\end{equation*}\n", + "```\n", + "### Display\n", + "\\begin{equation*}\n", + "1 + \\frac{q^2}{(1-q)}+\\frac{q^6}{(1-q)(1-q^2)}+\\cdots =\n", + "\\prod_{j=0}^{\\infty}\\frac{1}{(1-q^{5j+2})(1-q^{5j+3})},\n", + "\\quad\\quad \\text{for $|q|<1$}. \n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Maxwell's Equations\n", + "### Source\n", + "```\\begin{aligned}\n", + "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n", + "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n", + "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n", + "\\end{aligned}\n", + "```\n", + "### Display\n", + "\\begin{aligned}\n", + "\\nabla \\times \\vec{\\mathbf{B}} -\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{E}}}{\\partial t} & = \\frac{4\\pi}{c}\\vec{\\mathbf{j}} \\\\ \\nabla \\cdot \\vec{\\mathbf{E}} & = 4 \\pi \\rho \\\\\n", + "\\nabla \\times \\vec{\\mathbf{E}}\\, +\\, \\frac1c\\, \\frac{\\partial\\vec{\\mathbf{B}}}{\\partial t} & = \\vec{\\mathbf{0}} \\\\\n", + "\\nabla \\cdot \\vec{\\mathbf{B}} & = 0 \n", + "\\end{aligned}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Equation Numbering and References\n", + "\n", + "---\n", + "\n", + "These equation reference examples are adapted from an [example page in the MathJax documentation](http://cdn.mathjax.org/mathjax/latest/test/sample-eqrefs.html). Note that it's okay to reference equations across cells. Click inside this cell to see the source.\n", + "\n", + "## Labeled equations and references\n", + "\n", + "Here is a labeled equation:\n", + "\\begin{equation}\n", + "x+1\\over\\sqrt{1-x^2}\\label{ref1}\n", + "\\end{equation}\n", + "\n", + "with a reference to ref1: \\ref{ref1},\n", + "and another numbered one with no label:\n", + "\\begin{equation}\n", + "x+1\\over\\sqrt{1-x^2}\n", + "\\end{equation}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \\nonumber and equation*\n", + "\n", + "This one uses \\nonumber:\n", + "\\begin{equation}\n", + "x+1\\over\\sqrt{1-x^2}\\nonumber\n", + "\\end{equation}\n", + "\n", + "Here's one with the equation* environment:\n", + "\\begin{equation*}\n", + "x+1\\over\\sqrt{1-x^2}\n", + "\\end{equation*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Forward references\n", + "\n", + "This is a forward reference [\\ref{ref2}] and another \\eqref{ref2} for the \n", + "following equation:\n", + "\n", + "\\begin{equation}\n", + "x+1\\over\\sqrt{1-x^2}\\label{ref2}\n", + "\\end{equation}\n", + "\n", + "More math:\n", + "\\begin{equation}\n", + "x+1\\over\\sqrt{1-x^2}\n", + "\\end{equation}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### References inline and in environments\n", + "\n", + "Here is a ref inside math: $\\ref{ref2}+1$ and text after it.\n", + "\n", + "\\begin{align} \n", + "x& = y_1-y_2+y_3-y_5+y_8-\\dots \n", + "&& \\text{by \\eqref{ref1}}\\\\ \n", + "& = y'\\circ y^* && \\text{(by \\eqref{ref3})}\\\\ \n", + "& = y(0) y' && \\text {by Axiom 1.} \n", + "\\end{align} \n", + "\n", + "### Missing references\n", + "Here's a bad ref [\\ref{ref4}] to a nonexistent label.\n", + "\n", + "### Numbering align environments\n", + "An alignment:\n", + "\\begin{align}\n", + "a&=b\\label{ref3}\\cr\n", + "&=c+d\n", + "\\end{align}\n", + "and a starred one:\n", + "\\begin{align*}\n", + "a&=b\\cr\n", + "&=c+d\n", + "\\end{align*}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Inline Typesetting (Mixing Markdown and TeX)\n", + "\n", + "---\n", + "\n", + "While display equations look good for a page of samples, the ability to mix math and *formatted* **text** in a paragraph is also important.\n", + "\n", + "## Source\n", + "``` This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. \n", + "```\n", + "## Display\n", + "This expression $\\sqrt{3x-1}+(1+x)^2$ is an example of a TeX inline equation in a **[Markdown-formatted](http://daringfireball.net/projects/markdown/)** sentence. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Other Syntax\n", + "\n", + "---\n", + "\n", + "You will notice in other places on the web that `$$` are needed explicitly to begin and end MathJax typesetting. This is **not** required if you will be using TeX environments, but the IPython notebook will accept this syntax on legacy notebooks. \n", + "\n", + "### Source\n", + "```$$\n", + "\\begin{array}{c}\n", + "y_1 \\\\\\\n", + "y_2 \\mathtt{t}_i \\\\\\\n", + "z_{3,4}\n", + "\\end{array}\n", + "$$\n", + "```\n", + "\n", + "```\n", + "$$\n", + "\\begin{array}{c}\n", + "y_1 \\cr\n", + "y_2 \\mathtt{t}_i \\cr\n", + "y_{3}\n", + "\\end{array}\n", + "$$\n", + "```\n", + "\n", + "```\n", + "$$\\begin{eqnarray} \n", + "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n", + "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n", + "\\end{eqnarray}$$\n", + "```\n", + "\n", + "```\n", + "$$\n", + "x=4\n", + "$$\n", + "```\n", + "\n", + "### Display\n", + "$$\n", + "\\begin{array}{c}\n", + "y_1 \\\\\\\n", + "y_2 \\mathtt{t}_i \\\\\\\n", + "z_{3,4}\n", + "\\end{array}\n", + "$$\n", + "\n", + "$$\n", + "\\begin{array}{c}\n", + "y_1 \\cr\n", + "y_2 \\mathtt{t}_i \\cr\n", + "y_{3}\n", + "\\end{array}\n", + "$$\n", + "\n", + "$$\\begin{eqnarray} \n", + "x' &=& &x \\sin\\phi &+& z \\cos\\phi \\\\\n", + "z' &=& - &x \\cos\\phi &+& z \\sin\\phi \\\\\n", + "\\end{eqnarray}$$\n", + "\n", + "$$\n", + "x=4\n", + "$$" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [] + } + ], + "metadata": {} + } + ] +} \ No newline at end of file From 8d3fbe5901f783cdebc9781c3530fa9a154d5093 Mon Sep 17 00:00:00 2001 From: Aron Ahmadia Date: Sat, 13 Oct 2012 14:29:17 +0300 Subject: [PATCH 4/4] Refactored to JS standards. Fixed Attribution. thisStyle --> this_style. function foo() --> var foo = function() StackExchange improperly attributed for Davide Cervone's Markdown+MathJax handling. This has been fixed. Ref: http://stackoverflow.com/a/11231030/122022 http://www.math.union.edu/~dpvc/transfer/mathjax/mathjax-editing.js --- .../html/notebook/static/js/mathjaxutils.js | 42 ++++++++++--------- .../html/notebook/static/js/textcell.js | 6 +-- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/IPython/frontend/html/notebook/static/js/mathjaxutils.js b/IPython/frontend/html/notebook/static/js/mathjaxutils.js index 484751e99..2dea85e65 100644 --- a/IPython/frontend/html/notebook/static/js/mathjaxutils.js +++ b/IPython/frontend/html/notebook/static/js/mathjaxutils.js @@ -78,8 +78,12 @@ IPython.mathjaxutils = (function (IPython) { }; }; - // Some magic for deferring mathematical expressions to MathJaX - // Some of the code here is adapted with permission from Stack Exchange Inc. + // 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 var blocks, start, end, last, braces; // used in searching for math @@ -97,7 +101,7 @@ IPython.mathjaxutils = (function (IPython) { // 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 process_math = function (i, j, pre_process) { var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for & .replace(//g, ">") // use HTML entity for > @@ -110,8 +114,8 @@ IPython.mathjaxutils = (function (IPython) { j--; } blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later - if (preProcess) - block = preProcess(block); + if (pre_process) + block = pre_process(block); math.push(block); start = end = last = null; } @@ -122,7 +126,7 @@ IPython.mathjaxutils = (function (IPython) { // Don't allow math to pass through a double linebreak // (which will be a paragraph). // - function removeMath(text) { + var remove_math = function (text) { start = end = last = null; // for tracking math delimiters math = []; // stores math strings for later @@ -133,14 +137,14 @@ IPython.mathjaxutils = (function (IPython) { // `$foo` and `$bar` are varibales. --> $foo ` and `$bar are variables. var hasCodeSpans = /`/.test(text), - deTilde; + de_tilde; 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]; }) }; + de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) }; } else { - deTilde = function (text) { return text; }; + de_tilde = function (text) { return text; }; } blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT); @@ -166,13 +170,13 @@ IPython.mathjaxutils = (function (IPython) { last = i } else { - processMath(start, i, deTilde) + process_math(start, i, de_tilde) } } else if (block.match(/\n.*\n/)) { if (last) { i = last; - processMath(start, i, deTilde) + process_math(start, i, de_tilde) } start = end = last = null; braces = 0; @@ -202,16 +206,16 @@ IPython.mathjaxutils = (function (IPython) { } } if (last) { - processMath(start, last, deTilde) + process_math(start, last, de_tilde) } - return deTilde(blocks.join("")); + return de_tilde(blocks.join("")); } // // Put back the math strings that were saved, // and clear the math array (no need to keep it around). // - function replaceMath(text) { + var replace_math = function (text) { text = text.replace(/@@(\d+)@@/g, function (match, n) { return math[n] }); @@ -219,7 +223,7 @@ IPython.mathjaxutils = (function (IPython) { return text; } - function queueRender() { + var queue_render = function () { // see https://groups.google.com/forum/?fromgroups=#!topic/mathjax-users/cpwy5eCH1ZQ MathJax.Hub.Queue( ["resetEquationNumbers",MathJax.InputJax.TeX], @@ -230,10 +234,10 @@ IPython.mathjaxutils = (function (IPython) { return { init : init, - processMath : processMath, - removeMath : removeMath, - replaceMath : replaceMath, - queueRender : queueRender + process_math : process_math, + remove_math : remove_math, + replace_math : replace_math, + queue_render : queue_render }; }(IPython)); \ No newline at end of file diff --git a/IPython/frontend/html/notebook/static/js/textcell.js b/IPython/frontend/html/notebook/static/js/textcell.js index 94c776c1f..8b46bbef0 100644 --- a/IPython/frontend/html/notebook/static/js/textcell.js +++ b/IPython/frontend/html/notebook/static/js/textcell.js @@ -222,9 +222,9 @@ var IPython = (function (IPython) { var text = this.get_text(); if (text === "") { text = this.placeholder; } - text = IPython.mathjaxutils.removeMath(text) + text = IPython.mathjaxutils.remove_math(text) var html = IPython.markdown_converter.makeHtml(text); - html = IPython.mathjaxutils.replaceMath(html) + html = IPython.mathjaxutils.replace_math(html) try { this.set_rendered(html); @@ -251,7 +251,7 @@ var IPython = (function (IPython) { return '' + code + ''; }); - IPython.mathjaxutils.queueRender() + IPython.mathjaxutils.queue_render() this.rendered = true; } };