--- loncom/html/adm/jsMath/plugins/tex2math.js 2005/12/07 18:57:49 1.1
+++ loncom/html/adm/jsMath/plugins/tex2math.js 2006/03/27 19:32:29 1.2
@@ -10,30 +10,38 @@
*
* ---------------------------------------------------------------------
*
- * jsMath is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * jsMath is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with jsMath; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Copyright 2004-2006 by Davide P. Cervone
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
-jsMath.Insert(jsMath,{
+if (!jsMath.tex2math) {jsMath.tex2math = {}} // make sure jsMath.tex2math is defined
+if (!jsMath.tex2math.loaded) { // only load it once
+
+if (!jsMath.Controls) {jsMath.Controls = {}}
+if (!jsMath.Controls.cookie) {jsMath.Controls.cookie = {}}
+
+jsMath.Add(jsMath.tex2math,{
+ loaded: 1,
+ window: window,
+
/*
- * Call the main conversion routine with
- * appropriate flags
+ * Call the main conversion routine with appropriate flags
*/
-
+
ConvertTeX: function (element) {
- jsMath.tex2math.Convert(element,{
+ this.Convert(element,{
processSingleDollars: 1, processDoubleDollars: 1,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 1
@@ -41,7 +49,7 @@ jsMath.Insert(jsMath,{
},
ConvertTeX2: function (element) {
- jsMath.tex2math.Convert(element,{
+ this.Convert(element,{
processSingleDollars: 0, processDoubleDollars: 1,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 0
@@ -49,7 +57,7 @@ jsMath.Insert(jsMath,{
},
ConvertLaTeX: function (element) {
- jsMath.tex2math.Convert(element,{
+ this.Convert(element,{
processSingleDollars: 0, processDoubleDollars: 0,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 0
@@ -57,208 +65,237 @@ jsMath.Insert(jsMath,{
},
ConvertCustom: function (element) {
- jsMath.tex2math.Convert(element,{custom: 1, fixEscapedDollars: 0});
+ this.Convert(element,{custom: 1, fixEscapedDollars: 0});
},
+
+ /*******************************************************************/
/*
- * The main tex2math code
+ * Define a custom search by indicating the
+ * strings to use for starting and ending
+ * in-line and display mathematics
*/
- tex2math: {
-
- /*
- * Define a custom search by indicating the
- * strings to use for starting and ending
- * in-line and display mathematics
- */
- CustomSearch: function (iOpen,iClose,dOpen,dClose) {
- this.inLineOpen = iOpen; this.inLineClose = iClose;
- this.displayOpen = dOpen; this.displayClose = dClose;
- this.createPattern('customPattern',new RegExp(
- '('+this.patternQuote(dOpen)+'|'
- +this.patternQuote(iOpen)+'|'
- +this.patternQuote(dClose)+'|'
- +this.patternQuote(iClose)+'|\\\\.)','g'
- ));
- },
-
- patternQuote: function (s) {
- s = s.replace(/([\^(){}+*?\-|\[\]\:\\])/g,'\\$1');
- return s;
- },
+ CustomSearch: function (iOpen,iClose,dOpen,dClose) {
+ this.inLineOpen = iOpen; this.inLineClose = iClose;
+ this.displayOpen = dOpen; this.displayClose = dClose;
+ this.createPattern('customPattern',new RegExp(
+ '('+this.patternQuote(dOpen)+'|'
+ +this.patternQuote(iOpen)+'|'
+ +this.patternQuote(dClose)+'|'
+ +this.patternQuote(iClose)+'|\\\\.)','g'
+ ));
+ },
+
+ patternQuote: function (s) {
+ s = s.replace(/([\^(){}+*?\-|\[\]\:\\])/g,'\\$1');
+ return s;
+ },
+ /*
+ * MSIE on the Mac doesn't handle lastIndex correctly, so
+ * override it and implement it correctly.
+ */
+ createPattern: function (name,pattern) {
+ jsMath.tex2math[name] = pattern;
+ if (this.fixPatterns) {
+ pattern.oldExec = pattern.exec;
+ pattern.exec = this.msiePatternExec;
+ }
+ },
+ msiePatternExec: function (string) {
+ if (this.lastIndex == null) (this.lastIndex = 0);
+ var match = this.oldExec(string.substr(this.lastIndex));
+ if (match) {this.lastIndex += match.lastIndex}
+ else {this.lastIndex = null}
+ return match;
+ },
- /*
- * Set up for the correct type of search, and recursively
- * convert the mathematics. Disable tex2math if the cookie
- * isn't set, or of there is an element with ID of 'tex2math_off'.
- */
- Convert: function (element,flags) {
- if (!element) {element = document.body}
- if (typeof(element) == 'string') {element = document.getElementById(element)}
- if (jsMath.Controls.cookie.tex2math &&
- (!jsMath.tex2math.allowDisableTag || !document.getElementById('tex2math_off'))) {
- this.custom = 0; for (var i in flags) {this[i] = flags[i]}
- if (this.custom) {
- this.pattern = this.customPattern;
- this.ProcessMatch = this.customProcessMatch;
- } else {
- this.pattern = this.stdPattern;
- this.ProcessMatch = this.stdProcessMatch;
- }
- if (this.processDoubleDollars || this.processSingleDollars ||
- this.processSlashParens || this.processSlashBrackets ||
- this.custom) jsMath.tex2math.ScanElement(element);
+ /*******************************************************************/
+
+ /*
+ * Set up for the correct type of search, and recursively
+ * convert the mathematics. Disable tex2math if the cookie
+ * isn't set, or of there is an element with ID of 'tex2math_off'.
+ */
+ Convert: function (element,flags) {
+ this.Init();
+ if (!element) {element = jsMath.document.body}
+ if (typeof(element) == 'string') {element = jsMath.document.getElementById(element)}
+ if (jsMath.Controls.cookie.tex2math &&
+ (!jsMath.tex2math.allowDisableTag || !jsMath.document.getElementById('tex2math_off'))) {
+ this.custom = 0; for (var i in flags) {this[i] = flags[i]}
+ if (this.custom) {
+ this.pattern = this.customPattern;
+ this.ProcessMatch = this.customProcessMatch;
+ } else {
+ this.pattern = this.stdPattern;
+ this.ProcessMatch = this.stdProcessMatch;
}
- },
+ if (this.processDoubleDollars || this.processSingleDollars ||
+ this.processSlashParens || this.processSlashBrackets ||
+ this.custom) this.ScanElement(element);
+ }
+ },
- /*
- * Recursively look through a document for text nodes that could
- * contain mathematics.
- */
- ScanElement: function (element,ignore) {
- if (!element) {element = document.body}
- if (typeof(element) == 'string') {element = document.getElementById(element)}
- while (element) {
- if (element.nodeName == '#text') {
- if (!ignore) {element = this.ScanText(element)}
- } else if (element.firstChild && element.className != 'math') {
- var off = ignore || element.className == 'tex2math_ignore' ||
- (element.tagName && element.tagName.match(/^(script|noscript|style|textarea|pre)$/i));
- off = off && element.className != 'tex2math_process';
- this.ScanElement(element.firstChild,off);
- }
- if (element) {element = element.nextSibling}
+ /*
+ * Recursively look through a document for text nodes that could
+ * contain mathematics.
+ */
+ ScanElement: function (element,ignore) {
+ if (!element) {element = jsMath.document.body}
+ if (typeof(element) == 'string') {element = jsMath.document.getElementById(element)}
+ while (element) {
+ if (element.nodeName == '#text') {
+ if (!ignore) {element = this.ScanText(element)}
+ } else if (element.firstChild && element.className != 'math') {
+ var off = ignore || element.className == 'tex2math_ignore' ||
+ (element.tagName && element.tagName.match(/^(script|noscript|style|textarea|pre)$/i));
+ off = off && element.className != 'tex2math_process';
+ this.ScanElement(element.firstChild,off);
}
- },
-
- /*
- * Looks through a text element for math delimiters and
- * process them. If
tags are found in the middle, they
- * are ignored (this is for BBS systems that have editors
- * that insert these automatically).
- */
- ScanText: function (element) {
- if (element.nodeValue.replace(/\s+/,'') == '') {return element}
- var match; var prev; this.search = {};
- while (element) {
- this.pattern.lastIndex = 0;
- while (element.nodeName == '#text' &&
- (match = this.pattern.exec(element.nodeValue))) {
- element = this.ProcessMatch(match[0],match.index,element);
- }
- if (this.search.matched) {element = this.EncloseMath(element)}
- prev = element; element = element.nextSibling;
- while (element && element.nodeName.toLowerCase() == 'br')
- {prev = element; element = element.nextSibling}
- if (!element || element.nodeName != '#text') {return prev}
+ if (element) {element = element.nextSibling}
+ }
+ },
+
+ /*
+ * Looks through a text element for math delimiters and
+ * process them. If
tags are found in the middle, they
+ * are ignored (this is for BBS systems that have editors
+ * that insert these automatically).
+ */
+ ScanText: function (element) {
+ if (element.nodeValue.replace(/\s+/,'') == '') {return element}
+ var match; var prev; this.search = {};
+ while (element) {
+ this.pattern.lastIndex = 0;
+ while (element && element.nodeName == '#text' &&
+ (match = this.pattern.exec(element.nodeValue))) {
+ element = this.ProcessMatch(match[0],match.index,element);
}
- return element;
- },
-
- /*
- * If a matching end tag has been found, process the mathematics.
- * Otherwise, update the search data for the given delimiter,
- * or ignore it, as the item dictates.
- */
- stdProcessMatch: function (match,index,element) {
- if (match == this.search.end) {
- this.search.close = element;
- this.search.clength = match.length;
- this.search.cpos = this.pattern.lastIndex;
- element = this.EncloseMath(element);
- } else {
- switch (match) {
- case '\\(':
- if (this.search.end != '$' && this.search.end != '$$' &&
- this.processSlashParens) {
- this.ScanMark('span',element,'\\)');
- }
- break;
-
- case '\\[':
- if (this.search.end != '$' && this.search.end != '$$' &&
- this.processSlashBrackets) {
- this.ScanMark('div',element,'\\]');
- }
- break;
-
- case '$$':
- if (this.processDoubleDollars) {
- var type = (this.doubleDollarsAreInLine? 'span': 'div');
- this.ScanMark(type,element,'$$');
- }
- break;
-
- case '$':
- if (this.search.end == null && this.processSingleDollars) {
- this.ScanMark('span',element,'$');
- }
- break;
-
- case '\\$':
- if (this.search.end == null && this.fixEscapedDollars) {
- element.nodeValue = element.nodeValue.substr(0,index)
- + element.nodeValue.substr(index+1);
- }
- break;
- }
+ if (this.search.matched) {element = this.EncloseMath(element)}
+ if (!element) {return null}
+ prev = element; element = element.nextSibling;
+ while (element && element.nodeName.toLowerCase() == 'br')
+ {prev = element; element = element.nextSibling}
+ if (!element || element.nodeName != '#text') {return prev}
+ }
+ return element;
+ },
+
+ /*
+ * If a matching end tag has been found, process the mathematics.
+ * Otherwise, update the search data for the given delimiter,
+ * or ignore it, as the item dictates.
+ */
+ stdProcessMatch: function (match,index,element) {
+ if (match == this.search.end) {
+ this.search.close = element;
+ this.search.clength = match.length;
+ this.search.cpos = this.pattern.lastIndex;
+ element = this.EncloseMath(element);
+ } else {
+ switch (match) {
+ case '\\(':
+ if (this.search.end != '$' && this.search.end != '$$' &&
+ this.processSlashParens) {
+ this.ScanMark('span',element,'\\)');
+ }
+ break;
+
+ case '\\[':
+ if (this.search.end != '$' && this.search.end != '$$' &&
+ this.processSlashBrackets) {
+ this.ScanMark('div',element,'\\]');
+ }
+ break;
+
+ case '$$':
+ if (this.processDoubleDollars) {
+ var type = (this.doubleDollarsAreInLine? 'span': 'div');
+ this.ScanMark(type,element,'$$');
+ }
+ break;
+
+ case '$':
+ if (this.search.end == null && this.processSingleDollars) {
+ this.ScanMark('span',element,'$');
+ }
+ break;
+
+ case '\\$':
+ if (this.search.end == null && this.fixEscapedDollars) {
+ element.nodeValue = element.nodeValue.substr(0,index)
+ + element.nodeValue.substr(index+1);
+ }
+ break;
}
- return element;
- },
+ }
+ return element;
+ },
- /*
- * If a matching end tag has been found, process the mathematics.
- * Otherwise, update the search data for the given delimiter,
- * or ignore it, as the item dictates.
- */
- customProcessMatch: function (match,index,element) {
- if (match == this.search.end) {
- this.search.close = element;
- this.search.clength = match.length;
- this.search.cpos = this.pattern.lastIndex;
- this.search.matched = 1;
- } else if (match == this.inLineOpen) {
- if (this.search.matched) {element = this.EncloseMath(element)}
- this.ScanMark('span',element,this.inLineClose);
- } else if (match == this.displayOpen) {
- if (this.search.matched) {element = this.EncloseMath(element)}
- this.ScanMark('div',element,this.displayClose);
- }
- return element;
- },
+ /*
+ * If a matching end tag has been found, process the mathematics.
+ * Otherwise, update the search data for the given delimiter,
+ * or ignore it, as the item dictates.
+ */
+ customProcessMatch: function (match,index,element) {
+ if (match == this.search.end) {
+ this.search.close = element;
+ this.search.clength = match.length;
+ this.search.cpos = this.pattern.lastIndex;
+ this.search.matched = 1;
+ } else if (match == this.inLineOpen) {
+ if (this.search.matched) {element = this.EncloseMath(element)}
+ this.ScanMark('span',element,this.inLineClose);
+ } else if (match == this.displayOpen) {
+ if (this.search.matched) {element = this.EncloseMath(element)}
+ this.ScanMark('div',element,this.displayClose);
+ }
+ return element;
+ },
- /*
- * Return a structure that records the starting location
- * for the math element, and the end delimiter we want to find.
- */
- ScanMark: function (type,element,end) {
- var len = RegExp.$1.length;
- this.search = {
- type: type, end: end, open: element, olength: len,
- pos: this.pattern.lastIndex - len
- };
- },
-
- /*
- * Surround the mathematics by an appropriate
- * SPAN or DIV element marked as CLASS="math".
- */
- EncloseMath: function (element) {
- var search = this.search;
- var close = search.close;
- if (search.cpos == close.length) {close = close.nextSibling}
- else {close = close.splitText(search.cpos)}
- if (!close) {close = document.createTextNode("")}
- if (element == search.close) {element = close}
- var math = search.open.splitText(search.pos);
- while (math.nextSibling && math.nextSibling != close) {
- if (math.nextSibling.nodeValue) {math.nodeValue += math.nextSibling.nodeValue}
- math.parentNode.removeChild(math.nextSibling);
- }
- var TeX = math.nodeValue.substr(search.olength,
- math.nodeValue.length-search.olength-search.clength);
- math.parentNode.removeChild(math);
- math = this.createMathTag(search.type,TeX);
+ /*
+ * Return a structure that records the starting location
+ * for the math element, and the end delimiter we want to find.
+ */
+ ScanMark: function (type,element,end) {
+ var len = RegExp.$1.length;
+ this.search = {
+ type: type, end: end, open: element, olength: len,
+ pos: this.pattern.lastIndex - len
+ };
+ },
+
+ /*******************************************************************/
+
+ /*
+ * Surround the mathematics by an appropriate
+ * SPAN or DIV element marked as CLASS="math".
+ */
+ EncloseMath: function (element) {
+ if (this.callback) {if (!this.callback()) {return null}}
+ var search = this.search;
+ var close = search.close;
+ if (search.cpos == close.length) {close = close.nextSibling}
+ else {close = close.splitText(search.cpos)}
+ if (!close) {close = jsMath.document.createTextNode("")}
+ if (element == search.close) {element = close}
+ var math = search.open.splitText(search.pos);
+ while (math.nextSibling && math.nextSibling != close) {
+ if (math.nextSibling.nodeValue) {math.nodeValue += math.nextSibling.nodeValue}
+ else {math.nodeValue += ' '}
+ math.parentNode.removeChild(math.nextSibling);
+ }
+ var TeX = math.nodeValue.substr(search.olength,
+ math.nodeValue.length-search.olength-search.clength);
+ math.parentNode.removeChild(math);
+ math = this.createMathTag(search.type,TeX);
+ //
+ // This is where older, buggy browsers can fail under unpredicatble
+ // circumstances, so we trap errors and at least get to continue
+ // with the rest of the math. (## should add error message ##)
+ //
+ try {
if (close && close.parentNode) {
close.parentNode.insertBefore(math,close);
} else if (search.open.nextSibling) {
@@ -266,98 +303,93 @@ jsMath.Insert(jsMath,{
} else {
search.open.parentNode.appendChild(math);
}
- this.search = {}; this.pattern.lastIndex = 0;
- return element;
- },
+ } catch (err) {}
+ this.search = {}; this.pattern.lastIndex = 0;
+ return math;
+ },
+ /*
+ * Create an element for the mathematics
+ */
+ createMathTag: function (type,text) {
+ var tag = jsMath.document.createElement(type); tag.className = "math";
+ var math = jsMath.document.createTextNode(text);
+ tag.appendChild(math);
+ return tag;
+ },
+
+ //
+ // MSIE won't let you insert a DIV within tags that are supposed to
+ // contain in-line data (like
or ), so we have to fake it
+ // using SPAN tags that force the formatting to work like DIV. We
+ // use a separate SPAN that is the full width of the containing
+ // item, and that has the margins and centering from the div.typeset
+ // style.
+ //
+ MSIEcreateMathTag: function (type,text) {
+ var tag = jsMath.document.createElement("span");
+ tag.className = "math";
+ text = text.replace(//g,'>');
+ if (type == 'div') {
+ tag.className = "";
+ tag.style.width = "100%"; tag.style.margin = jsMath.tex2math.margin;
+ tag.style.display = "inline-block";
+ text = '\\displaystyle{'+text+'}';
+ if (jsMath.tex2math.center) {
+ tag.style.textAlign = "center";
+ text = ''+text+''
+ }
+ }
+ tag.innerHTML = text;
+ return tag;
+ },
+
+ /*******************************************************************/
+
+ Init: function () {
+
+ if (this.inited || !jsMath.browser) return
/*
- * Create an element for the mathematics
+ * MSIE can't handle the DIV's properly, so we need to do it by
+ * hand. Look up the style for typeset math to see if the user
+ * has changed it, and get whether it is centered or indented
+ * so we can mirror that using a SPAN
*/
- createMathTag: function (type,text) {
- var tag = document.createElement(type); tag.className = "math";
- var math = document.createTextNode(text);
- tag.appendChild(math);
- return tag;
- },
-
- //
- // MSIE won't let you insert a DIV within tags that are supposed to
- // contain in-line data (like or ), so we have to fake it
- // using SPAN tags that force the formatting to work like DIV. We
- // use a separate SPAN that is the full width of the containing
- // item, and that has the margins from the div.typeset style
- // and we name is jsMath.recenter to get jsMath to recenter it when
- // it is typeset (HACK!!!)
- //
- MSIEcreateMathTag: function (type,text) {
- var tag = document.createElement("span");
- tag.className = "math";
- text = text.replace(//g,'>');
- if (type == 'div') {
- tag.className = (jsMath.tex2math.center)? "jsMath.recenter": "";
- tag.style.width = "100%"; tag.style.margin = jsMath.tex2math.margin;
- tag.style.display = "inline-block";
- text = '\\displaystyle{'+text+'}';
+ if (jsMath.browser == 'MSIE' && navigator.platform == 'Win32') {
+ this.createMathTag = this.MSIEcreateMathTag;
+ this.margin = ""; this.center = 0;
+ for (var i = 0; i < jsMath.document.styleSheets.length; i++) {
+ var rules = jsMath.document.styleSheets[i].cssRules;
+ if (!rules) {rules = jsMath.document.styleSheets[i].rules}
+ for (var j = 0; j < rules.length; j++) {
+ if (rules[j].selectorText.toLowerCase() == 'div.typeset') {
+ if (rules[j].style.margin != "") {this.margin = rules[j].style.margin}
+ this.center = (rules[j].style.textAlign == 'center');
+ }
+ }
}
- tag.innerHTML = text;
- return tag;
}
-
+ this.inited = 1;
+ },
+
+ /*
+ * Test to see if we need to override the pattern exec() call
+ * (for MSIE on the Mac).
+ */
+ TestPatterns: function () {
+ var pattern = /a/g;
+ var match = pattern.exec("xax");
+ this.fixPatterns = (pattern.lastIndex != 2 && match.lastIndex == 2);
}
+
});
/*
- * Set the defaults
+ * Initialize
*/
if (jsMath.Controls.cookie.tex2math == null) {jsMath.Controls.cookie.tex2math = 1}
if (jsMath.tex2math.allowDisableTag == null) {jsMath.tex2math.allowDisableTag = 1}
+jsMath.tex2math.TestPatterns();
+jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$)/g);
-/*
- * MSIE can't handle the DIV's properly, so we need to do it by
- * hand. Look up the style for typeset math to see if the user
- * has changed it, and get whether it is centered or indented
- * so we can mirror that using a SPAN
- */
-if (jsMath.browser == 'MSIE' && navigator.platform == 'Win32') {
- jsMath.tex2math.createMathTag = jsMath.tex2math.MSIEcreateMathTag;
- jsMath.Add(jsMath.tex2math,{margin: "", center: 0});
- for (var i = 0; i < document.styleSheets.length; i++) {
- var rules = document.styleSheets[i].cssRules;
- if (!rules) {rules = document.styleSheets[i].rules}
- for (var j = 0; j < rules.length; j++) {
- if (rules[j].selectorText.toLowerCase() == 'div.typeset') {
- if (rules[j].style.margin != "")
- {jsMath.tex2math.margin = rules[j].style.margin}
- jsMath.tex2math.center =
- (rules[j].style.textAlign == 'center')? 1: 0;
- }
- }
- }
-}
-
-/*
- * MSIE on the mac doesn't handle lastIndex correctly, so
- * override it and implement it correctly.
- */
-if (jsMath.browser == 'MSIE' && navigator.platform == 'MacPPC') {
- jsMath.tex2math.createPattern = function (name,pattern) {
- jsMath.tex2math[name] = pattern;
- pattern.oldExec = pattern.exec;
- pattern.exec = function (string) {
- var pattern = jsMath.tex2math[name];
- if (pattern.lastIndex == null) (pattern.lastIndex = 0);
- var match = pattern.oldExec(string.substr(pattern.lastIndex));
- if (match) {pattern.lastIndex += match.lastIndex}
- else {pattern.lastIndex = null}
- return match;
- }
- }
-} else {
- jsMath.tex2math.createPattern =
- function (name,pattern) {jsMath.tex2math[name] = pattern}
}
-
-/*
- * The standard pattern for TeX and LaTeX strings
- */
-jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$)/g);