JAVASCRIPT   64

tex2jax js

Guest on 2nd August 2022 03:56:27 PM

  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/tex2jax.js
  7.  *  
  8.  *  Implements the TeX to Jax preprocessor that locates TeX code
  9.  *  within the text of a document and replaces it with SCRIPT tags
  10.  *  for processing by MathJax.
  11.  *
  12.  *  ---------------------------------------------------------------------
  13.  *  
  14.  *  Copyright (c) 2009-2015 The MathJax Consortium
  15.  *
  16.  *  Licensed under the Apache License, Version 2.0 (the "License");
  17.  *  you may not use this file except in compliance with the License.
  18.  *  You may obtain a copy of the License at
  19.  *
  20.  *      http://www.apache.org/licenses/LICENSE-2.0
  21.  *
  22.  *  Unless required by applicable law or agreed to in writing, software
  23.  *  distributed under the License is distributed on an "AS IS" BASIS,
  24.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  25.  *  See the License for the specific language governing permissions and
  26.  *  limitations under the License.
  27.  */
  28.  
  29. MathJax.Extension.tex2jax = {
  30.   version: "2.6.0",
  31.   config: {
  32.     inlineMath: [              // The start/stop pairs for in-line math
  33. //    ['$','$'],               //  (comment out any you don't want, or add your own, but
  34.       ['\\(','\\)']            //  be sure that you don't have an extra comma at the end)
  35.     ],
  36.  
  37.     displayMath: [             // The start/stop pairs for display math
  38.       ['$$','$$'],             //  (comment out any you don't want, or add your own, but
  39.       ['\\[','\\]']            //  be sure that you don't have an extra comma at the end)
  40.     ],
  41.  
  42.     balanceBraces: true,       // determines whether tex2jax requires braces to be
  43.                                // balanced within math delimiters (allows for nested
  44.                                // dollar signs).  Set to false to get pre-v2.0 compatibility.
  45.  
  46.     skipTags: ["script","noscript","style","textarea","pre","code","annotation","annotation-xml"],
  47.                                // The names of the tags whose contents will not be
  48.                                // scanned for math delimiters
  49.  
  50.     ignoreClass: "tex2jax_ignore",    // the class name of elements whose contents should
  51.                                       // NOT be processed by tex2jax.  Note that this
  52.                                       // is a regular expression, so be sure to quote any
  53.                                       // regexp special characters
  54.  
  55.     processClass: "tex2jax_process",  // the class name of elements whose contents SHOULD
  56.                                       // be processed when they appear inside ones that
  57.                                       // are ignored.  Note that this is a regular expression,
  58.                                       // so be sure to quote any regexp special characters
  59.  
  60.     processEscapes: false,     // set to true to allow \$ to produce a dollar without
  61.                                //   starting in-line math mode
  62.  
  63.     processEnvironments: true, // set to true to process \begin{xxx}...\end{xxx} outside
  64.                                //   of math mode, false to prevent that
  65.  
  66.     processRefs: true,         // set to true to process \ref{...} outside of math mode
  67.  
  68.  
  69.     preview: "TeX"             // set to "none" to not insert MathJax_Preview spans
  70.                                //   or set to an array specifying an HTML snippet
  71.                                //   to use the same preview for every equation.
  72.  
  73.   },
  74.  
  75.   PreProcess: function (element) {
  76.     if (!this.configured) {
  77.       this.config = MathJax.Hub.CombineConfig("tex2jax",this.config);
  78.       if (this.config.Augment) {MathJax.Hub.Insert(this,this.config.Augment)}
  79.       if (typeof(this.config.previewTeX) !== "undefined" && !this.config.previewTeX)
  80.         {this.config.preview = "none"} // backward compatibility for previewTeX parameter
  81.       this.configured = true;
  82.     }
  83.     if (typeof(element) === "string") {element = document.getElementById(element)}
  84.     if (!element) {element = document.body}
  85.     if (this.createPatterns()) {this.scanElement(element,element.nextSibling)}
  86.   },
  87.  
  88.   createPatterns: function () {
  89.     var starts = [], parts = [], i, m, config = this.config;
  90.     this.match = {};
  91.     for (i = 0, m = config.inlineMath.length; i < m; i++) {
  92.       starts.push(this.patternQuote(config.inlineMath[i][0]));
  93.       this.match[config.inlineMath[i][0]] = {
  94.         mode: "",
  95.         end: config.inlineMath[i][1],
  96.         pattern: this.endPattern(config.inlineMath[i][1])
  97.       };
  98.     }
  99.     for (i = 0, m = config.displayMath.length; i < m; i++) {
  100.       starts.push(this.patternQuote(config.displayMath[i][0]));
  101.       this.match[config.displayMath[i][0]] = {
  102.         mode: "; mode=display",
  103.         end: config.displayMath[i][1],
  104.         pattern: this.endPattern(config.displayMath[i][1])
  105.       };
  106.     }
  107.     if (starts.length) {parts.push(starts.sort(this.sortLength).join("|"))}
  108.     if (config.processEnvironments) {parts.push("\\\\begin\\{([^}]*)\\}")}
  109.     if (config.processEscapes)      {parts.push("\\\\*\\\\\\\$")}
  110.     if (config.processRefs)         {parts.push("\\\\(eq)?ref\\{[^}]*\\}")}
  111.     this.start = new RegExp(parts.join("|"),"g");
  112.     this.skipTags = new RegExp("^("+config.skipTags.join("|")+")$","i");
  113.     var ignore = [];
  114.     if (MathJax.Hub.config.preRemoveClass) {ignore.push(MathJax.Hub.config.preRemoveClass)};
  115.     if (config.ignoreClass) {ignore.push(config.ignoreClass)}
  116.     this.ignoreClass = (ignore.length ? new RegExp("(^| )("+ignore.join("|")+")( |$)") : /^$/);
  117.     this.processClass = new RegExp("(^| )("+config.processClass+")( |$)");
  118.     return (parts.length > 0);
  119.   },
  120.  
  121.   patternQuote: function (s) {return s.replace(/([\^$(){}+*?\-|\[\]\:\\])/g,'\\$1')},
  122.  
  123.   endPattern: function (end) {
  124.     return new RegExp(this.patternQuote(end)+"|\\\\.|[{}]","g");
  125.   },
  126.  
  127.   sortLength: function (a,b) {
  128.     if (a.length !== b.length) {return b.length - a.length}
  129.     return (a == b ? 0 : (a < b ? -1 : 1));
  130.   },
  131.  
  132.   scanElement: function (element,stop,ignore) {
  133.     var cname, tname, ignoreChild, process;
  134.     while (element && element != stop) {
  135.       if (element.nodeName.toLowerCase() === '#text') {
  136.         if (!ignore) {element = this.scanText(element)}
  137.       } else {
  138.         cname = (typeof(element.className) === "undefined" ? "" : element.className);
  139.         tname = (typeof(element.tagName)   === "undefined" ? "" : element.tagName);
  140.         if (typeof(cname) !== "string") {cname = String(cname)} // jsxgraph uses non-string class names!
  141.         process = this.processClass.exec(cname);
  142.         if (element.firstChild && !cname.match(/(^| )MathJax/) &&
  143.              (process || !this.skipTags.exec(tname))) {
  144.           ignoreChild = (ignore || this.ignoreClass.exec(cname)) && !process;
  145.           this.scanElement(element.firstChild,stop,ignoreChild);
  146.         }
  147.       }
  148.       if (element) {element = element.nextSibling}
  149.     }
  150.   },
  151.  
  152.   scanText: function (element) {
  153.     if (element.nodeValue.replace(/\s+/,'') == '') {return element}
  154.     var match, prev;
  155.     this.search = {start: true};
  156.     this.pattern = this.start;
  157.     while (element) {
  158.       this.pattern.lastIndex = 0;
  159.       while (element && element.nodeName.toLowerCase() === '#text' &&
  160.             (match = this.pattern.exec(element.nodeValue))) {
  161.         if (this.search.start) {element = this.startMatch(match,element)}
  162.                           else {element = this.endMatch(match,element)}
  163.       }
  164.       if (this.search.matched) {element = this.encloseMath(element)}
  165.       if (element) {
  166.         do {prev = element; element = element.nextSibling}
  167.           while (element && (element.nodeName.toLowerCase() === 'br' ||
  168.                              element.nodeName.toLowerCase() === '#comment'));
  169.         if (!element || element.nodeName !== '#text')
  170.           {return (this.search.close ? this.prevEndMatch() : prev)}
  171.       }
  172.     }
  173.     return element;
  174.   },
  175.  
  176.   startMatch: function (match,element) {
  177.     var delim = this.match[match[0]];
  178.     if (delim != null) {                              // a start delimiter
  179.       this.search = {
  180.         end: delim.end, mode: delim.mode, pcount: 0,
  181.         open: element, olen: match[0].length, opos: this.pattern.lastIndex - match[0].length
  182.       };
  183.       this.switchPattern(delim.pattern);
  184.     } else if (match[0].substr(0,6) === "\\begin") {  // \begin{...}
  185.       this.search = {
  186.         end: "\\end{"+match[1]+"}", mode: "; mode=display", pcount: 0,
  187.         open: element, olen: 0, opos: this.pattern.lastIndex - match[0].length,
  188.         isBeginEnd: true
  189.       };
  190.       this.switchPattern(this.endPattern(this.search.end));
  191.     } else if (match[0].substr(0,4) === "\\ref" || match[0].substr(0,6) === "\\eqref") {
  192.       this.search = {
  193.         mode: "", end: "", open: element, pcount: 0,
  194.         olen: 0, opos: this.pattern.lastIndex - match[0].length
  195.       }
  196.       return this.endMatch([""],element);
  197.     } else {                                         // escaped dollar signs
  198.       // put $ in a span so it doesn't get processed again
  199.       // split off backslashes so they don't get removed later
  200.       var slashes = match[0].substr(0,match[0].length-1), n, span;
  201.       if (slashes.length % 2 === 0) {span = [slashes.replace(/\\\\/g,"\\")]; n = 1}
  202.         else {span = [slashes.substr(1).replace(/\\\\/g,"\\"),"$"]; n = 0}
  203.       span = MathJax.HTML.Element("span",null,span);
  204.       var text = MathJax.HTML.TextNode(element.nodeValue.substr(0,match.index));
  205.       element.nodeValue = element.nodeValue.substr(match.index + match[0].length - n);
  206.       element.parentNode.insertBefore(span,element);
  207.       element.parentNode.insertBefore(text,span);
  208.       this.pattern.lastIndex = n;
  209.     }
  210.     return element;
  211.   },
  212.  
  213.   endMatch: function (match,element) {
  214.     var search = this.search;
  215.     if (match[0] == search.end) {
  216.       if (!search.close || search.pcount === 0) {
  217.         search.close = element;
  218.         search.cpos = this.pattern.lastIndex;
  219.         search.clen = (search.isBeginEnd ? 0 : match[0].length);
  220.       }
  221.       if (search.pcount === 0) {
  222.         search.matched = true;
  223.         element = this.encloseMath(element);
  224.         this.switchPattern(this.start);
  225.       }
  226.     }
  227.     else if (match[0] === "{") {search.pcount++}
  228.     else if (match[0] === "}" && search.pcount) {search.pcount--}
  229.     return element;
  230.   },
  231.   prevEndMatch: function () {
  232.     this.search.matched = true;
  233.     var element = this.encloseMath(this.search.close);
  234.     this.switchPattern(this.start);
  235.     return element;
  236.   },
  237.  
  238.   switchPattern: function (pattern) {
  239.     pattern.lastIndex = this.pattern.lastIndex;
  240.     this.pattern = pattern;
  241.     this.search.start = (pattern === this.start);
  242.   },
  243.  
  244.   encloseMath: function (element) {
  245.     var search = this.search, close = search.close, CLOSE, math;
  246.     if (search.cpos === close.length) {close = close.nextSibling}
  247.        else {close = close.splitText(search.cpos)}
  248.     if (!close) {CLOSE = close = MathJax.HTML.addText(search.close.parentNode,"")}
  249.     search.close = close;
  250.     math = (search.opos ? search.open.splitText(search.opos) : search.open);
  251.     while (math.nextSibling && math.nextSibling !== close) {
  252.       if (math.nextSibling.nodeValue !== null) {
  253.         if (math.nextSibling.nodeName === "#comment") {
  254.           math.nodeValue += math.nextSibling.nodeValue.replace(/^\[CDATA\[((.|\n|\r)*)\]\]$/,"$1");
  255.         } else {
  256.           math.nodeValue += math.nextSibling.nodeValue;
  257.         }
  258.       } else if (this.msieNewlineBug) {
  259.         math.nodeValue += (math.nextSibling.nodeName.toLowerCase() === "br" ? "\n" : " ");
  260.       } else {
  261.         math.nodeValue += " ";
  262.       }
  263.       math.parentNode.removeChild(math.nextSibling);
  264.     }
  265.     var TeX = math.nodeValue.substr(search.olen,math.nodeValue.length-search.olen-search.clen);
  266.     math.parentNode.removeChild(math);
  267.     if (this.config.preview !== "none") {this.createPreview(search.mode,TeX)}
  268.     math = this.createMathTag(search.mode,TeX);
  269.     this.search = {}; this.pattern.lastIndex = 0;
  270.     if (CLOSE) {CLOSE.parentNode.removeChild(CLOSE)}
  271.     return math;
  272.   },
  273.  
  274.   insertNode: function (node) {
  275.     var search = this.search;
  276.     search.close.parentNode.insertBefore(node,search.close);
  277.   },
  278.  
  279.   createPreview: function (mode,tex) {
  280.     var preview = this.config.preview;
  281.     if (preview === "none") return;
  282.     if (preview === "TeX") {preview = [this.filterPreview(tex)]}
  283.     if (preview) {
  284.       preview = MathJax.HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass},preview);
  285.       this.insertNode(preview);
  286.     }
  287.   },
  288.  
  289.   createMathTag: function (mode,tex) {
  290.     var script = document.createElement("script");
  291.     script.type = "math/tex" + mode;
  292.     MathJax.HTML.setScript(script,tex);
  293.     this.insertNode(script);
  294.     return script;
  295.   },
  296.  
  297.   filterPreview: function (tex) {return tex},
  298.  
  299.   msieNewlineBug: (MathJax.Hub.Browser.isMSIE && document.documentMode < 9)
  300.  
  301. };
  302.  
  303. // We register the preprocessors with the following priorities:
  304. // - mml2jax.js: 5
  305. // - jsMath2jax.js: 8
  306. // - asciimath2jax.js, tex2jax.js: 10 (default)
  307. // See issues 18 and 484 and the other *2jax.js files.
  308. MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.tex2jax]);
  309. MathJax.Ajax.loadComplete("[MathJax]/extensions/tex2jax.js");

Raw Paste


Login or Register to edit or fork this paste. It's free.