File:
[LON-CAPA] /
loncom /
html /
adm /
jsMath /
plugins /
tex2math.js
Revision
1.4:
download - view:
text,
annotated -
select for diffs
Tue Oct 9 21:29:28 2007 UTC (17 years, 4 months ago) by
albertel
Branches:
MAIN
CVS tags:
version_2_9_X,
version_2_9_99_0,
version_2_9_1,
version_2_9_0,
version_2_8_X,
version_2_8_99_1,
version_2_8_99_0,
version_2_8_2,
version_2_8_1,
version_2_8_0,
version_2_7_X,
version_2_7_99_1,
version_2_7_99_0,
version_2_7_1,
version_2_7_0,
version_2_6_X,
version_2_6_99_1,
version_2_6_99_0,
version_2_6_3,
version_2_6_2,
version_2_6_1,
version_2_6_0,
version_2_5_99_1,
version_2_5_99_0,
version_2_12_X,
version_2_11_X,
version_2_11_6_msu,
version_2_11_6,
version_2_11_5_msu,
version_2_11_5,
version_2_11_4_uiuc,
version_2_11_4_msu,
version_2_11_4,
version_2_11_3_uiuc,
version_2_11_3_msu,
version_2_11_3,
version_2_11_2_uiuc,
version_2_11_2_msu,
version_2_11_2_educog,
version_2_11_2,
version_2_11_1,
version_2_11_0_RC3,
version_2_11_0_RC2,
version_2_11_0_RC1,
version_2_11_0,
version_2_10_X,
version_2_10_1,
version_2_10_0_RC2,
version_2_10_0_RC1,
version_2_10_0,
loncapaMITrelate_1,
language_hyphenation_merge,
language_hyphenation,
bz6209-base,
bz6209,
bz5969,
bz2851,
PRINT_INCOMPLETE_base,
PRINT_INCOMPLETE,
HEAD,
GCI_3,
GCI_2,
GCI_1,
BZ5971-printing-apage,
BZ5434-fox,
BZ4492-merge,
BZ4492-feature_horizontal_radioresponse
- jsMath 3.4e
1: /*
2: * tex2math.js
3: *
4: * Part of the jsMath package for mathematics on the web.
5: *
6: * This file is a plugin that searches text within a web page
7: * for \(...\), \[...\], $...$ and $$...$$ and converts them to
8: * the appropriate <SPAN CLASS="math">...</SPAN> or
9: * <DIV CLASS="math">...</DIV> tags.
10: *
11: * ---------------------------------------------------------------------
12: *
13: * Copyright 2004-2007 by Davide P. Cervone
14: *
15: * Licensed under the Apache License, Version 2.0 (the "License");
16: * you may not use this file except in compliance with the License.
17: * You may obtain a copy of the License at
18: *
19: * http://www.apache.org/licenses/LICENSE-2.0
20: *
21: * Unless required by applicable law or agreed to in writing, software
22: * distributed under the License is distributed on an "AS IS" BASIS,
23: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24: * See the License for the specific language governing permissions and
25: * limitations under the License.
26: */
27:
28: if (!jsMath.tex2math) {jsMath.tex2math = {}} // make sure jsMath.tex2math is defined
29: if (!jsMath.tex2math.loaded) { // only load it once
30:
31: if (!jsMath.Controls) {jsMath.Controls = {}}
32: if (!jsMath.Controls.cookie) {jsMath.Controls.cookie = {}}
33:
34: jsMath.Add(jsMath.tex2math,{
35:
36: loaded: 1,
37: window: window,
38:
39: /*
40: * Call the main conversion routine with appropriate flags
41: */
42:
43: ConvertTeX: function (element) {
44: this.Convert(element,{
45: processSingleDollars: 1, processDoubleDollars: 1,
46: processSlashParens: 1, processSlashBrackets: 1,
47: processLaTeXenvironments: 0,
48: custom: 0, fixEscapedDollars: 1
49: });
50: },
51:
52: ConvertTeX2: function (element) {
53: this.Convert(element,{
54: processSingleDollars: 0, processDoubleDollars: 1,
55: processSlashParens: 1, processSlashBrackets: 1,
56: processLaTeXenvironments: 0,
57: custom: 0, fixEscapedDollars: 0
58: });
59: },
60:
61: ConvertLaTeX: function (element) {
62: this.Convert(element,{
63: processSingleDollars: 0, processDoubleDollars: 0,
64: processSlashParens: 1, processSlashBrackets: 1,
65: processLaTeXenvironments: 1,
66: custom: 0, fixEscapedDollars: 0
67: });
68: },
69:
70: ConvertCustom: function (element) {
71: this.Convert(element,{custom: 1, fixEscapedDollars: 0});
72: },
73:
74: /*******************************************************************/
75:
76: /*
77: * Define a custom search by indicating the
78: * strings to use for starting and ending
79: * in-line and display mathematics
80: */
81: CustomSearch: function (iOpen,iClose,dOpen,dClose) {
82: this.inLineOpen = iOpen; this.inLineClose = iClose;
83: this.displayOpen = dOpen; this.displayClose = dClose;
84: this.createPattern('customPattern',new RegExp(
85: '('+this.patternQuote(dOpen)+'|'
86: +this.patternQuote(iOpen)+'|'
87: +this.patternQuote(dClose)+'|'
88: +this.patternQuote(iClose)+'|\\\\.)','g'
89: ));
90: },
91:
92: patternQuote: function (s) {
93: s = s.replace(/([\^$(){}+*?\-|\[\]\:\\])/g,'\\$1');
94: return s;
95: },
96:
97: /*
98: * MSIE on the Mac doesn't handle lastIndex correctly, so
99: * override it and implement it correctly.
100: */
101: createPattern: function (name,pattern) {
102: jsMath.tex2math[name] = pattern;
103: if (this.fixPatterns) {
104: pattern.oldExec = pattern.exec;
105: pattern.exec = this.msiePatternExec;
106: }
107: },
108: msiePatternExec: function (string) {
109: if (this.lastIndex == null) (this.lastIndex = 0);
110: var match = this.oldExec(string.substr(this.lastIndex));
111: if (match) {this.lastIndex += match.lastIndex}
112: else {this.lastIndex = null}
113: return match;
114: },
115:
116: /*******************************************************************/
117:
118: /*
119: * Set up for the correct type of search, and recursively
120: * convert the mathematics. Disable tex2math if the cookie
121: * isn't set, or of there is an element with ID of 'tex2math_off'.
122: */
123: Convert: function (element,flags) {
124: this.Init();
125: if (!element) {element = jsMath.document.body}
126: if (typeof(element) == 'string') {element = jsMath.document.getElementById(element)}
127: if (jsMath.Controls.cookie.tex2math &&
128: (!jsMath.tex2math.allowDisableTag || !jsMath.document.getElementById('tex2math_off'))) {
129: this.custom = 0; for (var i in flags) {this[i] = flags[i]}
130: if (this.custom) {
131: this.pattern = this.customPattern;
132: this.ProcessMatch = this.customProcessMatch;
133: } else {
134: this.pattern = this.stdPattern;
135: this.ProcessMatch = this.stdProcessMatch;
136: }
137: if (this.processDoubleDollars || this.processSingleDollars ||
138: this.processSlashParens || this.processSlashBrackets ||
139: this.processLaTeXenvironments || this.custom) this.ScanElement(element);
140: }
141: },
142:
143: /*
144: * Recursively look through a document for text nodes that could
145: * contain mathematics.
146: */
147: ScanElement: function (element,ignore) {
148: if (!element) {element = jsMath.document.body}
149: if (typeof(element) == 'string') {element = jsMath.document.getElementById(element)}
150: while (element) {
151: if (element.nodeName == '#text') {
152: if (!ignore) {element = this.ScanText(element)}
153: } else {
154: if (element.className == null) {element.className = ''}
155: if (element.firstChild && element.className != 'math') {
156: var off = ignore || element.className == 'tex2math_ignore' ||
157: (element.tagName && element.tagName.match(/^(script|noscript|style|textarea|pre)$/i));
158: off = off && element.className != 'tex2math_process';
159: this.ScanElement(element.firstChild,off);
160: }
161: }
162: if (element) {element = element.nextSibling}
163: }
164: },
165:
166: /*
167: * Looks through a text element for math delimiters and
168: * process them. If <BR> tags are found in the middle, they
169: * are ignored (this is for BBS systems that have editors
170: * that insert these automatically).
171: */
172: ScanText: function (element) {
173: if (element.nodeValue.replace(/\s+/,'') == '') {return element}
174: var match; var prev; this.search = {};
175: while (element) {
176: this.pattern.lastIndex = 0;
177: while (element && element.nodeName == '#text' &&
178: (match = this.pattern.exec(element.nodeValue))) {
179: this.pattern.match = match;
180: element = this.ProcessMatch(match[0],match.index,element);
181: }
182: if (this.search.matched) {element = this.EncloseMath(element)}
183: if (!element) {return null}
184: prev = element; element = element.nextSibling;
185: while (element && element.nodeName.toLowerCase() == 'br')
186: {prev = element; element = element.nextSibling}
187: if (!element || element.nodeName != '#text') {return prev}
188: }
189: return element;
190: },
191:
192: /*
193: * If a matching end tag has been found, process the mathematics.
194: * Otherwise, update the search data for the given delimiter,
195: * or ignore it, as the item dictates.
196: */
197: stdProcessMatch: function (match,index,element) {
198: if (match == this.search.end) {
199: this.search.close = element;
200: this.search.cpos = this.pattern.lastIndex;
201: this.search.clength = (match.substr(0,4) == '\\end' ? 0 : match.length);
202: element = this.EncloseMath(element);
203: } else {
204: switch (match) {
205: case '\\(':
206: if ((this.search.end == null ||
207: (this.search.end != '$' && this.search.end != '$$')) &&
208: this.processSlashParens) {
209: this.ScanMark('span',element,'\\)');
210: }
211: break;
212:
213: case '\\[':
214: if ((this.search.end == null ||
215: (this.search.end != '$' && this.search.end != '$$')) &&
216: this.processSlashBrackets) {
217: this.ScanMark('div',element,'\\]');
218: }
219: break;
220:
221: case '$$':
222: if (this.processDoubleDollars) {
223: var type = (this.doubleDollarsAreInLine? 'span': 'div');
224: this.ScanMark(type,element,'$$');
225: }
226: break;
227:
228: case '$':
229: if (this.search.end == null && this.processSingleDollars) {
230: this.ScanMark('span',element,'$');
231: }
232: break;
233:
234: case '\\$':
235: if (this.search.end == null && this.fixEscapedDollars) {
236: element.nodeValue = element.nodeValue.substr(0,index)
237: + element.nodeValue.substr(index+1);
238: this.pattern.lastIndex--;
239: }
240: break;
241:
242: default:
243: if (match.substr(0,6) == '\\begin' && this.search.end == null &&
244: this.processLaTeXenvironments) {
245: this.ScanMark('span',element,'\\end'+match.substr(6));
246: this.search.olength = 0;
247: }
248: break;
249: }
250: }
251: return element;
252: },
253:
254: /*
255: * If a matching end tag has been found, process the mathematics.
256: * Otherwise, update the search data for the given delimiter,
257: * or ignore it, as the item dictates.
258: */
259: customProcessMatch: function (match,index,element) {
260: if (match == this.search.end) {
261: this.search.close = element;
262: this.search.clength = match.length;
263: this.search.cpos = this.pattern.lastIndex;
264: if (match == this.inLineOpen || match == this.displayOpen) {
265: element = this.EncloseMath(element);
266: } else {this.search.matched = 1}
267: } else if (match == this.inLineOpen) {
268: if (this.search.matched) {element = this.EncloseMath(element)}
269: this.ScanMark('span',element,this.inLineClose);
270: } else if (match == this.displayOpen) {
271: if (this.search.matched) {element = this.EncloseMath(element)}
272: this.ScanMark('div',element,this.displayClose);
273: }
274: return element;
275: },
276:
277: /*
278: * Return a structure that records the starting location
279: * for the math element, and the end delimiter we want to find.
280: */
281: ScanMark: function (type,element,end) {
282: var len = this.pattern.match[1].length;
283: this.search = {
284: type: type, end: end, open: element, olength: len,
285: pos: this.pattern.lastIndex - len
286: };
287: },
288:
289: /*******************************************************************/
290:
291: /*
292: * Surround the mathematics by an appropriate
293: * SPAN or DIV element marked as CLASS="math".
294: */
295: EncloseMath: function (element) {
296: var search = this.search; search.end = null;
297: var close = search.close;
298: if (search.cpos == close.length) {close = close.nextSibling}
299: else {close = close.splitText(search.cpos)}
300: if (!close) {close = jsMath.document.createTextNode("")}
301: if (element == search.close) {element = close}
302: var math = search.open.splitText(search.pos);
303: while (math.nextSibling && math.nextSibling != close) {
304: if (math.nextSibling.nodeValue) {math.nodeValue += math.nextSibling.nodeValue}
305: else {math.nodeValue += ' '}
306: math.parentNode.removeChild(math.nextSibling);
307: }
308: var TeX = math.nodeValue.substr(search.olength,
309: math.nodeValue.length-search.olength-search.clength);
310: math.parentNode.removeChild(math);
311: math = this.createMathTag(search.type,TeX);
312: //
313: // This is where older, buggy browsers can fail under unpredicatble
314: // circumstances, so we trap errors and at least get to continue
315: // with the rest of the math. (## should add error message ##)
316: //
317: try {
318: if (close && close.parentNode) {
319: close.parentNode.insertBefore(math,close);
320: } else if (search.open.nextSibling) {
321: search.open.parentNode.insertBefore(math,search.open.nextSibling);
322: } else {
323: search.open.parentNode.appendChild(math);
324: }
325: } catch (err) {}
326: this.search = {}; this.pattern.lastIndex = 0;
327: return math;
328: },
329:
330: /*
331: * Create an element for the mathematics
332: */
333: createMathTag: function (type,text) {
334: var tag = jsMath.document.createElement(type); tag.className = "math";
335: var math = jsMath.document.createTextNode(text);
336: tag.appendChild(math);
337: return tag;
338: },
339:
340: //
341: // MSIE won't let you insert a DIV within tags that are supposed to
342: // contain in-line data (like <P> or <SPAN>), so we have to fake it
343: // using SPAN tags that force the formatting to work like DIV. We
344: // use a separate SPAN that is the full width of the containing
345: // item, and that has the margins and centering from the div.typeset
346: // style.
347: //
348: MSIEcreateMathTag: function (type,text) {
349: var tag = jsMath.document.createElement("span");
350: tag.className = "math";
351: text = text.replace(/</g,'<').replace(/>/g,'>');
352: if (type == 'div') {
353: tag.className = "tex2math_div";
354: text = '<span class="math">\\displaystyle{'+text+'}</span>';
355: }
356: tag.innerHTML = text;
357: return tag;
358: },
359:
360: /*******************************************************************/
361:
362: Init: function () {
363: if (!jsMath.browser && document.all && !window.opera) {
364: jsMath.browser = 'MSIE';
365: jsMath.platform = (navigator.platform.match(/Mac/) ? "mac" :
366: navigator.platform.match(/Win/) ? "pc" : "unix");
367: }
368: if (this.inited || !jsMath.browser) return;
369: /*
370: * MSIE can't handle the DIV's properly, so we need to do it by
371: * hand. Use an extra SPAN that uses CSS to act like a DIV.
372: */
373: if (jsMath.browser == 'MSIE' && jsMath.platform == 'pc')
374: {this.createMathTag = this.MSIEcreateMathTag}
375: this.inited = 1;
376: },
377:
378: /*
379: * Test to see if we need to override the pattern exec() call
380: * (for MSIE on the Mac).
381: */
382: TestPatterns: function () {
383: var pattern = /a/g;
384: var match = pattern.exec("xax");
385: this.fixPatterns = (pattern.lastIndex != 2 && match.lastIndex == 2);
386: }
387:
388: });
389:
390: /*
391: * Initialize
392: */
393: if (jsMath.Controls.cookie.tex2math == null) {jsMath.Controls.cookie.tex2math = 1}
394: if (jsMath.tex2math.allowDisableTag == null) {jsMath.tex2math.allowDisableTag = 1}
395: jsMath.tex2math.TestPatterns();
396: jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$|\\(begin|end)\{[^}]+\})/g);
397:
398: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>