/*
* tex2math.js
*
* Part of the jsMath package for mathematics on the web.
*
* This file is a plugin that searches text wthin a web page
* for \(...\), \[...\], $...$ and $$...$$ and converts them to
* the appropriate <SPAN CLASS="math">...</SPAN> or
* <DIV CLASS="math">...</DIV> tags.
*
* ---------------------------------------------------------------------
*
* 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
*/
jsMath.Insert(jsMath,{
/*
* Call the main conversion routine with
* appropriate flags
*/
ConvertTeX: function (element) {
jsMath.tex2math.Convert(element,{
processSingleDollars: 1, processDoubleDollars: 1,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 1
});
},
ConvertTeX2: function (element) {
jsMath.tex2math.Convert(element,{
processSingleDollars: 0, processDoubleDollars: 1,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 0
});
},
ConvertLaTeX: function (element) {
jsMath.tex2math.Convert(element,{
processSingleDollars: 0, processDoubleDollars: 0,
processSlashParens: 1, processSlashBrackets: 1,
custom: 0, fixEscapedDollars: 0
});
},
ConvertCustom: function (element) {
jsMath.tex2math.Convert(element,{custom: 1, fixEscapedDollars: 0});
},
/*
* The main tex2math code
*/
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;
},
/*
* 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);
}
},
/*
* 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}
}
},
/*
* Looks through a text element for math delimiters and
* process them. If <BR> 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}
}
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;
},
/*
* 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);
if (close && close.parentNode) {
close.parentNode.insertBefore(math,close);
} else if (search.open.nextSibling) {
search.open.parentNode.insertBefore(math,search.open.nextSibling);
} else {
search.open.parentNode.appendChild(math);
}
this.search = {}; this.pattern.lastIndex = 0;
return element;
},
/*
* Create an element for the mathematics
*/
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 <P> or <SPAN>), 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,'<').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 = '<span class="math">\\displaystyle{'+text+'}</span>';
}
tag.innerHTML = text;
return tag;
}
}
});
/*
* Set the defaults
*/
if (jsMath.Controls.cookie.tex2math == null) {jsMath.Controls.cookie.tex2math = 1}
if (jsMath.tex2math.allowDisableTag == null) {jsMath.tex2math.allowDisableTag = 1}
/*
* 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);
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>