##// END OF EJS Templates
Merge pull request #4282 from ellisonbg/linebreaks...
Matthias Bussonnier -
r12816:ae736449 merge
parent child Browse files
Show More
@@ -1,256 +1,257 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MathJax utility functions
9 // MathJax utility functions
10 //============================================================================
10 //============================================================================
11
11
12
12
13 IPython.namespace('IPython.mathjaxutils');
13 IPython.namespace('IPython.mathjaxutils');
14
14
15 IPython.mathjaxutils = (function (IPython) {
15 IPython.mathjaxutils = (function (IPython) {
16 "use strict";
16 "use strict";
17
17
18 var init = function () {
18 var init = function () {
19 if (window.MathJax) {
19 if (window.MathJax) {
20 // MathJax loaded
20 // MathJax loaded
21 MathJax.Hub.Config({
21 MathJax.Hub.Config({
22 tex2jax: {
22 tex2jax: {
23 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
23 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
24 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
24 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
25 processEscapes: true,
25 processEscapes: true,
26 processEnvironments: true
26 processEnvironments: true
27 },
27 },
28 displayAlign: 'left', // Change this to 'center' to center equations.
28 displayAlign: 'left', // Change this to 'center' to center equations.
29 "HTML-CSS": {
29 "HTML-CSS": {
30 styles: {'.MathJax_Display': {"margin": 0}}
30 styles: {'.MathJax_Display': {"margin": 0}},
31 linebreaks: { automatic: true }
31 }
32 }
32 });
33 });
33 MathJax.Hub.Configured();
34 MathJax.Hub.Configured();
34 } else if (window.mathjax_url !== "") {
35 } else if (window.mathjax_url !== "") {
35 // Don't have MathJax, but should. Show dialog.
36 // Don't have MathJax, but should. Show dialog.
36 var message = $('<div/>')
37 var message = $('<div/>')
37 .append(
38 .append(
38 $("<p/></p>").addClass('dialog').html(
39 $("<p/></p>").addClass('dialog').html(
39 "Math/LaTeX rendering will be disabled."
40 "Math/LaTeX rendering will be disabled."
40 )
41 )
41 ).append(
42 ).append(
42 $("<p></p>").addClass('dialog').html(
43 $("<p></p>").addClass('dialog').html(
43 "If you have administrative access to the notebook server and" +
44 "If you have administrative access to the notebook server and" +
44 " a working internet connection, you can install a local copy" +
45 " a working internet connection, you can install a local copy" +
45 " of MathJax for offline use with the following command on the server" +
46 " of MathJax for offline use with the following command on the server" +
46 " at a Python or IPython prompt:"
47 " at a Python or IPython prompt:"
47 )
48 )
48 ).append(
49 ).append(
49 $("<pre></pre>").addClass('dialog').html(
50 $("<pre></pre>").addClass('dialog').html(
50 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
51 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
51 )
52 )
52 ).append(
53 ).append(
53 $("<p></p>").addClass('dialog').html(
54 $("<p></p>").addClass('dialog').html(
54 "This will try to install MathJax into the IPython source directory."
55 "This will try to install MathJax into the IPython source directory."
55 )
56 )
56 ).append(
57 ).append(
57 $("<p></p>").addClass('dialog').html(
58 $("<p></p>").addClass('dialog').html(
58 "If IPython is installed to a location that requires" +
59 "If IPython is installed to a location that requires" +
59 " administrative privileges to write, you will need to make this call as" +
60 " administrative privileges to write, you will need to make this call as" +
60 " an administrator, via 'sudo'."
61 " an administrator, via 'sudo'."
61 )
62 )
62 ).append(
63 ).append(
63 $("<p></p>").addClass('dialog').html(
64 $("<p></p>").addClass('dialog').html(
64 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
65 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
65 )
66 )
66 ).append(
67 ).append(
67 $("<pre></pre>").addClass('dialog').html(
68 $("<pre></pre>").addClass('dialog').html(
68 "$ ipython notebook --no-mathjax"
69 "$ ipython notebook --no-mathjax"
69 )
70 )
70 ).append(
71 ).append(
71 $("<p></p>").addClass('dialog').html(
72 $("<p></p>").addClass('dialog').html(
72 "which will prevent this dialog from appearing."
73 "which will prevent this dialog from appearing."
73 )
74 )
74 );
75 );
75 IPython.dialog.modal({
76 IPython.dialog.modal({
76 title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
77 title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
77 body : message,
78 body : message,
78 buttons : {
79 buttons : {
79 OK : {class: "btn-danger"}
80 OK : {class: "btn-danger"}
80 }
81 }
81 });
82 });
82 }
83 }
83 };
84 };
84
85
85 // Some magic for deferring mathematical expressions to MathJax
86 // Some magic for deferring mathematical expressions to MathJax
86 // by hiding them from the Markdown parser.
87 // by hiding them from the Markdown parser.
87 // Some of the code here is adapted with permission from Davide Cervone
88 // Some of the code here is adapted with permission from Davide Cervone
88 // under the terms of the Apache2 license governing the MathJax project.
89 // under the terms of the Apache2 license governing the MathJax project.
89 // Other minor modifications are also due to StackExchange and are used with
90 // Other minor modifications are also due to StackExchange and are used with
90 // permission.
91 // permission.
91
92
92 var inline = "$"; // the inline math delimiter
93 var inline = "$"; // the inline math delimiter
93
94
94 // MATHSPLIT contains the pattern for math delimiters and special symbols
95 // MATHSPLIT contains the pattern for math delimiters and special symbols
95 // needed for searching for math in the text input.
96 // needed for searching for math in the text input.
96 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
97 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
97
98
98 // The math is in blocks i through j, so
99 // The math is in blocks i through j, so
99 // collect it into one block and clear the others.
100 // collect it into one block and clear the others.
100 // Replace &, <, and > by named entities.
101 // Replace &, <, and > by named entities.
101 // For IE, put <br> at the ends of comments since IE removes \n.
102 // For IE, put <br> at the ends of comments since IE removes \n.
102 // Clear the current math positions and store the index of the
103 // Clear the current math positions and store the index of the
103 // math, then push the math string onto the storage array.
104 // math, then push the math string onto the storage array.
104 // The preProcess function is called on all blocks if it has been passed in
105 // The preProcess function is called on all blocks if it has been passed in
105 var process_math = function (i, j, pre_process, math, blocks) {
106 var process_math = function (i, j, pre_process, math, blocks) {
106 var hub = MathJax.Hub;
107 var hub = MathJax.Hub;
107 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
108 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
108 .replace(/</g, "&lt;") // use HTML entity for <
109 .replace(/</g, "&lt;") // use HTML entity for <
109 .replace(/>/g, "&gt;") // use HTML entity for >
110 .replace(/>/g, "&gt;") // use HTML entity for >
110 ;
111 ;
111 if (hub.Browser.isMSIE) {
112 if (hub.Browser.isMSIE) {
112 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
113 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n");
113 }
114 }
114 while (j > i) {
115 while (j > i) {
115 blocks[j] = "";
116 blocks[j] = "";
116 j--;
117 j--;
117 }
118 }
118 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
119 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
119 if (pre_process){
120 if (pre_process){
120 block = pre_process(block);
121 block = pre_process(block);
121 }
122 }
122 math.push(block);
123 math.push(block);
123 return blocks;
124 return blocks;
124 };
125 };
125
126
126 // Break up the text into its component parts and search
127 // Break up the text into its component parts and search
127 // through them for math delimiters, braces, linebreaks, etc.
128 // through them for math delimiters, braces, linebreaks, etc.
128 // Math delimiters must match and braces must balance.
129 // Math delimiters must match and braces must balance.
129 // Don't allow math to pass through a double linebreak
130 // Don't allow math to pass through a double linebreak
130 // (which will be a paragraph).
131 // (which will be a paragraph).
131 //
132 //
132 var remove_math = function (text) {
133 var remove_math = function (text) {
133 if (!window.MathJax) {
134 if (!window.MathJax) {
134 return [text, null];
135 return [text, null];
135 }
136 }
136
137
137 var math = []; // stores math strings for later
138 var math = []; // stores math strings for later
138 var start;
139 var start;
139 var end;
140 var end;
140 var last;
141 var last;
141 var braces;
142 var braces;
142
143
143 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
144 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
144 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
145 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
145 // we still have to consider them at this point; the following issue has happened several times:
146 // we still have to consider them at this point; the following issue has happened several times:
146 //
147 //
147 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
148 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
148
149
149 var hasCodeSpans = /`/.test(text),
150 var hasCodeSpans = /`/.test(text),
150 de_tilde;
151 de_tilde;
151 if (hasCodeSpans) {
152 if (hasCodeSpans) {
152 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
153 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
153 return wholematch.replace(/\$/g, "~D");
154 return wholematch.replace(/\$/g, "~D");
154 });
155 });
155 de_tilde = function (text) {
156 de_tilde = function (text) {
156 return text.replace(/~([TD])/g, function (wholematch, character) {
157 return text.replace(/~([TD])/g, function (wholematch, character) {
157 return { T: "~", D: "$" }[character];
158 return { T: "~", D: "$" }[character];
158 });
159 });
159 };
160 };
160 } else {
161 } else {
161 de_tilde = function (text) { return text; };
162 de_tilde = function (text) { return text; };
162 }
163 }
163
164
164 var blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
165 var blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
165
166
166 for (var i = 1, m = blocks.length; i < m; i += 2) {
167 for (var i = 1, m = blocks.length; i < m; i += 2) {
167 var block = blocks[i];
168 var block = blocks[i];
168 if (block.charAt(0) === "@") {
169 if (block.charAt(0) === "@") {
169 //
170 //
170 // Things that look like our math markers will get
171 // Things that look like our math markers will get
171 // stored and then retrieved along with the math.
172 // stored and then retrieved along with the math.
172 //
173 //
173 blocks[i] = "@@" + math.length + "@@";
174 blocks[i] = "@@" + math.length + "@@";
174 math.push(block);
175 math.push(block);
175 }
176 }
176 else if (start) {
177 else if (start) {
177 //
178 //
178 // If we are in math, look for the end delimiter,
179 // If we are in math, look for the end delimiter,
179 // but don't go past double line breaks, and
180 // but don't go past double line breaks, and
180 // and balance braces within the math.
181 // and balance braces within the math.
181 //
182 //
182 if (block === end) {
183 if (block === end) {
183 if (braces) {
184 if (braces) {
184 last = i;
185 last = i;
185 }
186 }
186 else {
187 else {
187 blocks = process_math(start, i, de_tilde, math, blocks);
188 blocks = process_math(start, i, de_tilde, math, blocks);
188 start = null;
189 start = null;
189 end = null;
190 end = null;
190 last = null;
191 last = null;
191 }
192 }
192 }
193 }
193 else if (block.match(/\n.*\n/)) {
194 else if (block.match(/\n.*\n/)) {
194 if (last) {
195 if (last) {
195 i = last;
196 i = last;
196 blocks = process_math(start, i, de_tilde, math, blocks);
197 blocks = process_math(start, i, de_tilde, math, blocks);
197 }
198 }
198 start = null;
199 start = null;
199 end = null;
200 end = null;
200 last = null;
201 last = null;
201 braces = 0;
202 braces = 0;
202 }
203 }
203 else if (block === "{") {
204 else if (block === "{") {
204 braces++;
205 braces++;
205 }
206 }
206 else if (block === "}" && braces) {
207 else if (block === "}" && braces) {
207 braces--;
208 braces--;
208 }
209 }
209 }
210 }
210 else {
211 else {
211 //
212 //
212 // Look for math start delimiters and when
213 // Look for math start delimiters and when
213 // found, set up the end delimiter.
214 // found, set up the end delimiter.
214 //
215 //
215 if (block === inline || block === "$$") {
216 if (block === inline || block === "$$") {
216 start = i;
217 start = i;
217 end = block;
218 end = block;
218 braces = 0;
219 braces = 0;
219 }
220 }
220 else if (block.substr(1, 5) === "begin") {
221 else if (block.substr(1, 5) === "begin") {
221 start = i;
222 start = i;
222 end = "\\end" + block.substr(6);
223 end = "\\end" + block.substr(6);
223 braces = 0;
224 braces = 0;
224 }
225 }
225 }
226 }
226 }
227 }
227 if (last) {
228 if (last) {
228 blocks = process_math(start, last, de_tilde, math, blocks);
229 blocks = process_math(start, last, de_tilde, math, blocks);
229 start = null;
230 start = null;
230 end = null;
231 end = null;
231 last = null;
232 last = null;
232 }
233 }
233 return [de_tilde(blocks.join("")), math];
234 return [de_tilde(blocks.join("")), math];
234 };
235 };
235
236
236 //
237 //
237 // Put back the math strings that were saved,
238 // Put back the math strings that were saved,
238 // and clear the math array (no need to keep it around).
239 // and clear the math array (no need to keep it around).
239 //
240 //
240 var replace_math = function (text, math) {
241 var replace_math = function (text, math) {
241 if (!window.MathJax) {
242 if (!window.MathJax) {
242 return text;
243 return text;
243 }
244 }
244 text = text.replace(/@@(\d+)@@/g, function (match, n) {
245 text = text.replace(/@@(\d+)@@/g, function (match, n) {
245 return math[n];
246 return math[n];
246 });
247 });
247 return text;
248 return text;
248 };
249 };
249
250
250 return {
251 return {
251 init : init,
252 init : init,
252 remove_math : remove_math,
253 remove_math : remove_math,
253 replace_math : replace_math
254 replace_math : replace_math
254 };
255 };
255
256
256 }(IPython));
257 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now