Annotation of loncom/html/adm/jsMath/plugins/tex2math.js, revision 1.1
1.1 ! albertel 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 wthin 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: * jsMath is free software; you can redistribute it and/or modify
! 14: * it under the terms of the GNU General Public License as published by
! 15: * the Free Software Foundation; either version 2 of the License, or
! 16: * (at your option) any later version.
! 17: *
! 18: * jsMath is distributed in the hope that it will be useful,
! 19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
! 20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 21: * GNU General Public License for more details.
! 22: *
! 23: * You should have received a copy of the GNU General Public License
! 24: * along with jsMath; if not, write to the Free Software
! 25: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
! 26: */
! 27:
! 28: jsMath.Insert(jsMath,{
! 29:
! 30: /*
! 31: * Call the main conversion routine with
! 32: * appropriate flags
! 33: */
! 34:
! 35: ConvertTeX: function (element) {
! 36: jsMath.tex2math.Convert(element,{
! 37: processSingleDollars: 1, processDoubleDollars: 1,
! 38: processSlashParens: 1, processSlashBrackets: 1,
! 39: custom: 0, fixEscapedDollars: 1
! 40: });
! 41: },
! 42:
! 43: ConvertTeX2: function (element) {
! 44: jsMath.tex2math.Convert(element,{
! 45: processSingleDollars: 0, processDoubleDollars: 1,
! 46: processSlashParens: 1, processSlashBrackets: 1,
! 47: custom: 0, fixEscapedDollars: 0
! 48: });
! 49: },
! 50:
! 51: ConvertLaTeX: function (element) {
! 52: jsMath.tex2math.Convert(element,{
! 53: processSingleDollars: 0, processDoubleDollars: 0,
! 54: processSlashParens: 1, processSlashBrackets: 1,
! 55: custom: 0, fixEscapedDollars: 0
! 56: });
! 57: },
! 58:
! 59: ConvertCustom: function (element) {
! 60: jsMath.tex2math.Convert(element,{custom: 1, fixEscapedDollars: 0});
! 61: },
! 62:
! 63: /*
! 64: * The main tex2math code
! 65: */
! 66: tex2math: {
! 67:
! 68: /*
! 69: * Define a custom search by indicating the
! 70: * strings to use for starting and ending
! 71: * in-line and display mathematics
! 72: */
! 73: CustomSearch: function (iOpen,iClose,dOpen,dClose) {
! 74: this.inLineOpen = iOpen; this.inLineClose = iClose;
! 75: this.displayOpen = dOpen; this.displayClose = dClose;
! 76: this.createPattern('customPattern',new RegExp(
! 77: '('+this.patternQuote(dOpen)+'|'
! 78: +this.patternQuote(iOpen)+'|'
! 79: +this.patternQuote(dClose)+'|'
! 80: +this.patternQuote(iClose)+'|\\\\.)','g'
! 81: ));
! 82: },
! 83:
! 84: patternQuote: function (s) {
! 85: s = s.replace(/([\^(){}+*?\-|\[\]\:\\])/g,'\\$1');
! 86: return s;
! 87: },
! 88:
! 89:
! 90: /*
! 91: * Set up for the correct type of search, and recursively
! 92: * convert the mathematics. Disable tex2math if the cookie
! 93: * isn't set, or of there is an element with ID of 'tex2math_off'.
! 94: */
! 95: Convert: function (element,flags) {
! 96: if (!element) {element = document.body}
! 97: if (typeof(element) == 'string') {element = document.getElementById(element)}
! 98: if (jsMath.Controls.cookie.tex2math &&
! 99: (!jsMath.tex2math.allowDisableTag || !document.getElementById('tex2math_off'))) {
! 100: this.custom = 0; for (var i in flags) {this[i] = flags[i]}
! 101: if (this.custom) {
! 102: this.pattern = this.customPattern;
! 103: this.ProcessMatch = this.customProcessMatch;
! 104: } else {
! 105: this.pattern = this.stdPattern;
! 106: this.ProcessMatch = this.stdProcessMatch;
! 107: }
! 108: if (this.processDoubleDollars || this.processSingleDollars ||
! 109: this.processSlashParens || this.processSlashBrackets ||
! 110: this.custom) jsMath.tex2math.ScanElement(element);
! 111: }
! 112: },
! 113:
! 114: /*
! 115: * Recursively look through a document for text nodes that could
! 116: * contain mathematics.
! 117: */
! 118: ScanElement: function (element,ignore) {
! 119: if (!element) {element = document.body}
! 120: if (typeof(element) == 'string') {element = document.getElementById(element)}
! 121: while (element) {
! 122: if (element.nodeName == '#text') {
! 123: if (!ignore) {element = this.ScanText(element)}
! 124: } else if (element.firstChild && element.className != 'math') {
! 125: var off = ignore || element.className == 'tex2math_ignore' ||
! 126: (element.tagName && element.tagName.match(/^(script|noscript|style|textarea|pre)$/i));
! 127: off = off && element.className != 'tex2math_process';
! 128: this.ScanElement(element.firstChild,off);
! 129: }
! 130: if (element) {element = element.nextSibling}
! 131: }
! 132: },
! 133:
! 134: /*
! 135: * Looks through a text element for math delimiters and
! 136: * process them. If <BR> tags are found in the middle, they
! 137: * are ignored (this is for BBS systems that have editors
! 138: * that insert these automatically).
! 139: */
! 140: ScanText: function (element) {
! 141: if (element.nodeValue.replace(/\s+/,'') == '') {return element}
! 142: var match; var prev; this.search = {};
! 143: while (element) {
! 144: this.pattern.lastIndex = 0;
! 145: while (element.nodeName == '#text' &&
! 146: (match = this.pattern.exec(element.nodeValue))) {
! 147: element = this.ProcessMatch(match[0],match.index,element);
! 148: }
! 149: if (this.search.matched) {element = this.EncloseMath(element)}
! 150: prev = element; element = element.nextSibling;
! 151: while (element && element.nodeName.toLowerCase() == 'br')
! 152: {prev = element; element = element.nextSibling}
! 153: if (!element || element.nodeName != '#text') {return prev}
! 154: }
! 155: return element;
! 156: },
! 157:
! 158: /*
! 159: * If a matching end tag has been found, process the mathematics.
! 160: * Otherwise, update the search data for the given delimiter,
! 161: * or ignore it, as the item dictates.
! 162: */
! 163: stdProcessMatch: function (match,index,element) {
! 164: if (match == this.search.end) {
! 165: this.search.close = element;
! 166: this.search.clength = match.length;
! 167: this.search.cpos = this.pattern.lastIndex;
! 168: element = this.EncloseMath(element);
! 169: } else {
! 170: switch (match) {
! 171: case '\\(':
! 172: if (this.search.end != '$' && this.search.end != '$$' &&
! 173: this.processSlashParens) {
! 174: this.ScanMark('span',element,'\\)');
! 175: }
! 176: break;
! 177:
! 178: case '\\[':
! 179: if (this.search.end != '$' && this.search.end != '$$' &&
! 180: this.processSlashBrackets) {
! 181: this.ScanMark('div',element,'\\]');
! 182: }
! 183: break;
! 184:
! 185: case '$$':
! 186: if (this.processDoubleDollars) {
! 187: var type = (this.doubleDollarsAreInLine? 'span': 'div');
! 188: this.ScanMark(type,element,'$$');
! 189: }
! 190: break;
! 191:
! 192: case '$':
! 193: if (this.search.end == null && this.processSingleDollars) {
! 194: this.ScanMark('span',element,'$');
! 195: }
! 196: break;
! 197:
! 198: case '\\$':
! 199: if (this.search.end == null && this.fixEscapedDollars) {
! 200: element.nodeValue = element.nodeValue.substr(0,index)
! 201: + element.nodeValue.substr(index+1);
! 202: }
! 203: break;
! 204: }
! 205: }
! 206: return element;
! 207: },
! 208:
! 209: /*
! 210: * If a matching end tag has been found, process the mathematics.
! 211: * Otherwise, update the search data for the given delimiter,
! 212: * or ignore it, as the item dictates.
! 213: */
! 214: customProcessMatch: function (match,index,element) {
! 215: if (match == this.search.end) {
! 216: this.search.close = element;
! 217: this.search.clength = match.length;
! 218: this.search.cpos = this.pattern.lastIndex;
! 219: this.search.matched = 1;
! 220: } else if (match == this.inLineOpen) {
! 221: if (this.search.matched) {element = this.EncloseMath(element)}
! 222: this.ScanMark('span',element,this.inLineClose);
! 223: } else if (match == this.displayOpen) {
! 224: if (this.search.matched) {element = this.EncloseMath(element)}
! 225: this.ScanMark('div',element,this.displayClose);
! 226: }
! 227: return element;
! 228: },
! 229:
! 230: /*
! 231: * Return a structure that records the starting location
! 232: * for the math element, and the end delimiter we want to find.
! 233: */
! 234: ScanMark: function (type,element,end) {
! 235: var len = RegExp.$1.length;
! 236: this.search = {
! 237: type: type, end: end, open: element, olength: len,
! 238: pos: this.pattern.lastIndex - len
! 239: };
! 240: },
! 241:
! 242: /*
! 243: * Surround the mathematics by an appropriate
! 244: * SPAN or DIV element marked as CLASS="math".
! 245: */
! 246: EncloseMath: function (element) {
! 247: var search = this.search;
! 248: var close = search.close;
! 249: if (search.cpos == close.length) {close = close.nextSibling}
! 250: else {close = close.splitText(search.cpos)}
! 251: if (!close) {close = document.createTextNode("")}
! 252: if (element == search.close) {element = close}
! 253: var math = search.open.splitText(search.pos);
! 254: while (math.nextSibling && math.nextSibling != close) {
! 255: if (math.nextSibling.nodeValue) {math.nodeValue += math.nextSibling.nodeValue}
! 256: math.parentNode.removeChild(math.nextSibling);
! 257: }
! 258: var TeX = math.nodeValue.substr(search.olength,
! 259: math.nodeValue.length-search.olength-search.clength);
! 260: math.parentNode.removeChild(math);
! 261: math = this.createMathTag(search.type,TeX);
! 262: if (close && close.parentNode) {
! 263: close.parentNode.insertBefore(math,close);
! 264: } else if (search.open.nextSibling) {
! 265: search.open.parentNode.insertBefore(math,search.open.nextSibling);
! 266: } else {
! 267: search.open.parentNode.appendChild(math);
! 268: }
! 269: this.search = {}; this.pattern.lastIndex = 0;
! 270: return element;
! 271: },
! 272:
! 273: /*
! 274: * Create an element for the mathematics
! 275: */
! 276: createMathTag: function (type,text) {
! 277: var tag = document.createElement(type); tag.className = "math";
! 278: var math = document.createTextNode(text);
! 279: tag.appendChild(math);
! 280: return tag;
! 281: },
! 282:
! 283: //
! 284: // MSIE won't let you insert a DIV within tags that are supposed to
! 285: // contain in-line data (like <P> or <SPAN>), so we have to fake it
! 286: // using SPAN tags that force the formatting to work like DIV. We
! 287: // use a separate SPAN that is the full width of the containing
! 288: // item, and that has the margins from the div.typeset style
! 289: // and we name is jsMath.recenter to get jsMath to recenter it when
! 290: // it is typeset (HACK!!!)
! 291: //
! 292: MSIEcreateMathTag: function (type,text) {
! 293: var tag = document.createElement("span");
! 294: tag.className = "math";
! 295: text = text.replace(/</g,'<').replace(/>/g,'>');
! 296: if (type == 'div') {
! 297: tag.className = (jsMath.tex2math.center)? "jsMath.recenter": "";
! 298: tag.style.width = "100%"; tag.style.margin = jsMath.tex2math.margin;
! 299: tag.style.display = "inline-block";
! 300: text = '<span class="math">\\displaystyle{'+text+'}</span>';
! 301: }
! 302: tag.innerHTML = text;
! 303: return tag;
! 304: }
! 305:
! 306: }
! 307: });
! 308:
! 309: /*
! 310: * Set the defaults
! 311: */
! 312: if (jsMath.Controls.cookie.tex2math == null) {jsMath.Controls.cookie.tex2math = 1}
! 313: if (jsMath.tex2math.allowDisableTag == null) {jsMath.tex2math.allowDisableTag = 1}
! 314:
! 315: /*
! 316: * MSIE can't handle the DIV's properly, so we need to do it by
! 317: * hand. Look up the style for typeset math to see if the user
! 318: * has changed it, and get whether it is centered or indented
! 319: * so we can mirror that using a SPAN
! 320: */
! 321: if (jsMath.browser == 'MSIE' && navigator.platform == 'Win32') {
! 322: jsMath.tex2math.createMathTag = jsMath.tex2math.MSIEcreateMathTag;
! 323: jsMath.Add(jsMath.tex2math,{margin: "", center: 0});
! 324: for (var i = 0; i < document.styleSheets.length; i++) {
! 325: var rules = document.styleSheets[i].cssRules;
! 326: if (!rules) {rules = document.styleSheets[i].rules}
! 327: for (var j = 0; j < rules.length; j++) {
! 328: if (rules[j].selectorText.toLowerCase() == 'div.typeset') {
! 329: if (rules[j].style.margin != "")
! 330: {jsMath.tex2math.margin = rules[j].style.margin}
! 331: jsMath.tex2math.center =
! 332: (rules[j].style.textAlign == 'center')? 1: 0;
! 333: }
! 334: }
! 335: }
! 336: }
! 337:
! 338: /*
! 339: * MSIE on the mac doesn't handle lastIndex correctly, so
! 340: * override it and implement it correctly.
! 341: */
! 342: if (jsMath.browser == 'MSIE' && navigator.platform == 'MacPPC') {
! 343: jsMath.tex2math.createPattern = function (name,pattern) {
! 344: jsMath.tex2math[name] = pattern;
! 345: pattern.oldExec = pattern.exec;
! 346: pattern.exec = function (string) {
! 347: var pattern = jsMath.tex2math[name];
! 348: if (pattern.lastIndex == null) (pattern.lastIndex = 0);
! 349: var match = pattern.oldExec(string.substr(pattern.lastIndex));
! 350: if (match) {pattern.lastIndex += match.lastIndex}
! 351: else {pattern.lastIndex = null}
! 352: return match;
! 353: }
! 354: }
! 355: } else {
! 356: jsMath.tex2math.createPattern =
! 357: function (name,pattern) {jsMath.tex2math[name] = pattern}
! 358: }
! 359:
! 360: /*
! 361: * The standard pattern for TeX and LaTeX strings
! 362: */
! 363: jsMath.tex2math.createPattern('stdPattern',/(\\[\(\)\[\]$]|\$\$|\$)/g);
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>