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");