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,'&lt;').replace(/>/g,'&gt;');
        !           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>