www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

asciimath2jax.js (9768B)


      1 /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
      2 /* vim: set ts=2 et sw=2 tw=80: */
      3 
      4 /*************************************************************
      5  *
      6  *  MathJax/extensions/asciimath2jax.js
      7  *  
      8  *  Implements the AsciiMath to Jax preprocessor that locates AsciiMath
      9  *  code within the text of a document and replaces it with SCRIPT tags for
     10  *  processing by MathJax.
     11  *
     12  *  Modified by David Lippman, based on tex2jax.js.
     13  *  Additional work by Davide P. Cervone.
     14  *  
     15  *  ---------------------------------------------------------------------
     16  *  
     17  *  Copyright (c) 2012-2015 The MathJax Consortium
     18  * 
     19  *  Licensed under the Apache License, Version 2.0 (the "License");
     20  *  you may not use this file except in compliance with the License.
     21  *  You may obtain a copy of the License at
     22  * 
     23  *      http://www.apache.org/licenses/LICENSE-2.0
     24  * 
     25  *  Unless required by applicable law or agreed to in writing, software
     26  *  distributed under the License is distributed on an "AS IS" BASIS,
     27  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     28  *  See the License for the specific language governing permissions and
     29  *  limitations under the License.
     30  */
     31 
     32 MathJax.Extension.asciimath2jax = {
     33   version: "2.6.0",
     34   config: {
     35     delimiters: [['`','`']],   // The star/stop delimiter pairs for asciimath code
     36 
     37     skipTags: ["script","noscript","style","textarea","pre","code","annotation","annotation-xml"],
     38                                // The names of the tags whose contents will not be
     39                                // scanned for math delimiters
     40 
     41     ignoreClass: "asciimath2jax_ignore",   // the class name of elements whose contents should
     42                                            // NOT be processed by asciimath2jax.  Note that this
     43                                            // is a regular expression, so be sure to quote any
     44                                            // regexp special characters
     45 
     46     processClass: "asciimath2jax_process", // the class name of elements whose contents SHOULD
     47                                            // be processed when they appear inside ones that
     48                                            // are ignored.  Note that this is a regular expression,
     49                                            // so be sure to quote any regexp special characters
     50 
     51     preview: "AsciiMath"        // set to "none" to not insert MathJax_Preview spans
     52                                //   or set to an array specifying an HTML snippet
     53                                //   to use the same preview for every equation.
     54 
     55   },
     56   
     57   PreProcess: function (element) {
     58     if (!this.configured) {
     59       this.config = MathJax.Hub.CombineConfig("asciimath2jax",this.config);
     60       if (this.config.Augment) {MathJax.Hub.Insert(this,this.config.Augment)}
     61       this.configured = true;
     62     }
     63     if (typeof(element) === "string") {element = document.getElementById(element)}
     64     if (!element) {element = document.body}
     65     if (this.createPatterns()) {this.scanElement(element,element.nextSibling)}
     66   },
     67   
     68   createPatterns: function () {
     69     var starts = [], i, m, config = this.config; this.match = {};
     70     if (config.delimiters.length === 0) {return false}
     71     for (i = 0, m = config.delimiters.length; i < m; i++) {
     72       starts.push(this.patternQuote(config.delimiters[i][0]));
     73       this.match[config.delimiters[i][0]] = {
     74         mode: "",
     75         end: config.delimiters[i][1],
     76         pattern: this.endPattern(config.delimiters[i][1])
     77       };
     78     }
     79     this.start = new RegExp(starts.sort(this.sortLength).join("|"),"g");
     80     this.skipTags = new RegExp("^("+config.skipTags.join("|")+")$","i");
     81     var ignore = [];
     82     if (MathJax.Hub.config.preRemoveClass) {ignore.push(MathJax.Hub.config.preRemoveClass)}
     83     if (config.ignoreClass) {ignore.push(config.ignoreClass)}
     84     this.ignoreClass = (ignore.length ? new RegExp("(^| )("+ignore.join("|")+")( |$)") : /^$/);
     85     this.processClass = new RegExp("(^| )("+config.processClass+")( |$)");
     86     return true;
     87   },
     88   
     89   patternQuote: function (s) {return s.replace(/([\^$(){}+*?\-|\[\]\:\\])/g,'\\$1')},
     90   
     91   endPattern: function (end) {
     92     return new RegExp(this.patternQuote(end)+"|\\\\.","g");
     93   },
     94   
     95   sortLength: function (a,b) {
     96     if (a.length !== b.length) {return b.length - a.length}
     97     return (a == b ? 0 : (a < b ? -1 : 1));
     98   },
     99   
    100   scanElement: function (element,stop,ignore) {
    101     var cname, tname, ignoreChild, process;
    102     while (element && element != stop) {
    103       if (element.nodeName.toLowerCase() === '#text') {
    104         if (!ignore) {element = this.scanText(element)}
    105       } else {
    106         cname = (typeof(element.className) === "undefined" ? "" : element.className);
    107         tname = (typeof(element.tagName)   === "undefined" ? "" : element.tagName);
    108         if (typeof(cname) !== "string") {cname = String(cname)} // jsxgraph uses non-string class names!
    109         process = this.processClass.exec(cname);
    110         if (element.firstChild && !cname.match(/(^| )MathJax/) &&
    111             (process || !this.skipTags.exec(tname))) {
    112           ignoreChild = (ignore || this.ignoreClass.exec(cname)) && !process;
    113           this.scanElement(element.firstChild,stop,ignoreChild);
    114         }
    115       }
    116       if (element) {element = element.nextSibling}
    117     }
    118   },
    119   
    120   scanText: function (element) {
    121     if (element.nodeValue.replace(/\s+/,'') == '') {return element}
    122     var match, prev;
    123     this.search = {start: true};
    124     this.pattern = this.start;
    125     while (element) {
    126       this.pattern.lastIndex = 0;
    127       while (element && element.nodeName.toLowerCase() === '#text' &&
    128             (match = this.pattern.exec(element.nodeValue))) {
    129         if (this.search.start) {element = this.startMatch(match,element)}
    130                           else {element = this.endMatch(match,element)}
    131       }
    132       if (this.search.matched) {element = this.encloseMath(element)}
    133       if (element) {
    134         do {prev = element; element = element.nextSibling}
    135           while (element && (element.nodeName.toLowerCase() === 'br' ||
    136                              element.nodeName.toLowerCase() === '#comment'));
    137         if (!element || element.nodeName !== '#text') {return prev}
    138       }
    139     }
    140     return element;
    141   },
    142   
    143   startMatch: function (match,element) {
    144     var delim = this.match[match[0]];
    145     if (delim != null) {
    146       this.search = {
    147         end: delim.end, mode: delim.mode,
    148         open: element, olen: match[0].length,
    149         opos: this.pattern.lastIndex - match[0].length
    150       };
    151       this.switchPattern(delim.pattern);
    152     }
    153     return element;
    154   },
    155   
    156   endMatch: function (match,element) {
    157     if (match[0] == this.search.end) {
    158       this.search.close = element;
    159       this.search.cpos = this.pattern.lastIndex;
    160       this.search.clen = (this.search.isBeginEnd ? 0 : match[0].length);
    161       this.search.matched = true;
    162       element = this.encloseMath(element);
    163       this.switchPattern(this.start);
    164     }
    165     return element;
    166   },
    167   
    168   switchPattern: function (pattern) {
    169     pattern.lastIndex = this.pattern.lastIndex;
    170     this.pattern = pattern;
    171     this.search.start = (pattern === this.start);
    172   },
    173   
    174   encloseMath: function (element) {
    175     var search = this.search, close = search.close, CLOSE, math;
    176     if (search.cpos === close.length) {close = close.nextSibling}
    177        else {close = close.splitText(search.cpos)}
    178     if (!close) {CLOSE = close = MathJax.HTML.addText(search.close.parentNode,"")}
    179     search.close = close;
    180     math = (search.opos ? search.open.splitText(search.opos) : search.open);
    181     while (math.nextSibling && math.nextSibling !== close) {
    182       if (math.nextSibling.nodeValue !== null) {
    183         if (math.nextSibling.nodeName === "#comment") {
    184           math.nodeValue += math.nextSibling.nodeValue.replace(/^\[CDATA\[((.|\n|\r)*)\]\]$/,"$1");
    185         } else {
    186           math.nodeValue += math.nextSibling.nodeValue;
    187         }
    188       } else if (this.msieNewlineBug) {
    189         math.nodeValue += (math.nextSibling.nodeName.toLowerCase() === "br" ? "\n" : " ");
    190       } else {
    191         math.nodeValue += " ";
    192       }
    193       math.parentNode.removeChild(math.nextSibling);
    194     }
    195     var AM = math.nodeValue.substr(search.olen,math.nodeValue.length-search.olen-search.clen);
    196     math.parentNode.removeChild(math);
    197     if (this.config.preview !== "none") {this.createPreview(search.mode,AM)}
    198     math = this.createMathTag(search.mode,AM);
    199     this.search = {}; this.pattern.lastIndex = 0;
    200     if (CLOSE) {CLOSE.parentNode.removeChild(CLOSE)}
    201     return math;
    202   },
    203   
    204   insertNode: function (node) {
    205     var search = this.search;
    206     search.close.parentNode.insertBefore(node,search.close);
    207   },
    208   
    209   createPreview: function (mode,asciimath) {
    210     var preview = this.config.preview;
    211     if (preview === "none") return;
    212     if (preview === "AsciiMath") {preview = [this.filterPreview(asciimath)]}
    213     if (preview) {
    214       preview = MathJax.HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass},preview);
    215       this.insertNode(preview);
    216     }
    217   },
    218   
    219   createMathTag: function (mode,asciimath) {
    220     var script = document.createElement("script");
    221     script.type = "math/asciimath" + mode;
    222     MathJax.HTML.setScript(script,asciimath);
    223     this.insertNode(script);
    224     return script;
    225   },
    226   
    227   filterPreview: function (asciimath) {return asciimath},
    228   
    229   msieNewlineBug: (MathJax.Hub.Browser.isMSIE && (document.documentMode||0) < 9)
    230   
    231 };
    232 
    233 // We register the preprocessors with the following priorities:
    234 // - mml2jax.js: 5
    235 // - jsMath2jax.js: 8
    236 // - asciimath2jax.js, tex2jax.js: 10 (default)
    237 // See issues 18 and 484 and the other *2jax.js files.
    238 MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.asciimath2jax]);
    239 MathJax.Ajax.loadComplete("[MathJax]/extensions/asciimath2jax.js");