mml2jax.js (10376B)
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/mml2jax.js 7 * 8 * Implements the MathML to Jax preprocessor that locates <math> nodes 9 * within the text of a document and replaces them with SCRIPT tags 10 * for processing by MathJax. 11 * 12 * --------------------------------------------------------------------- 13 * 14 * Copyright (c) 2010-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.mml2jax = { 30 version: "2.6.0", 31 config: { 32 preview: "mathml" // Use the <math> element as the 33 // preview. Set to "none" for no preview, 34 // set to "alttext" to use the alttext attribute 35 // of the <math> element, set to "altimg" to use 36 // an image described by the altimg* attributes 37 // or set to an array specifying an HTML snippet 38 // to use a fixed preview for all math 39 40 }, 41 MMLnamespace: "http://www.w3.org/1998/Math/MathML", 42 43 PreProcess: function (element) { 44 if (!this.configured) { 45 this.config = MathJax.Hub.CombineConfig("mml2jax",this.config); 46 if (this.config.Augment) {MathJax.Hub.Insert(this,this.config.Augment)} 47 this.InitBrowser(); 48 this.configured = true; 49 } 50 if (typeof(element) === "string") {element = document.getElementById(element)} 51 if (!element) {element = document.body} 52 var mathArray = []; 53 // 54 // Handle all math tags with no namespaces 55 // 56 this.PushMathElements(mathArray,element,"math"); 57 // 58 // Handle math with namespaces in XHTML 59 // 60 this.PushMathElements(mathArray,element,"math",this.MMLnamespace); 61 // 62 // Handle math with namespaces in HTML 63 // 64 var i, m; 65 if (typeof(document.namespaces) !== "undefined") { 66 // 67 // IE namespaces are listed in document.namespaces 68 // 69 try { 70 for (i = 0, m = document.namespaces.length; i < m; i++) { 71 var ns = document.namespaces[i]; 72 if (ns.urn === this.MMLnamespace) 73 {this.PushMathElements(mathArray,element,ns.name+":math")} 74 } 75 } catch (err) {} 76 } else { 77 // 78 // Everybody else 79 // 80 var html = document.getElementsByTagName("html")[0]; 81 if (html) { 82 for (i = 0, m = html.attributes.length; i < m; i++) { 83 var attr = html.attributes[i]; 84 if (attr.nodeName.substr(0,6) === "xmlns:" && attr.nodeValue === this.MMLnamespace) 85 {this.PushMathElements(mathArray,element,attr.nodeName.substr(6)+":math")} 86 } 87 } 88 } 89 this.ProcessMathArray(mathArray); 90 }, 91 92 PushMathElements: function (array,element,name,namespace) { 93 var math, preview = MathJax.Hub.config.preRemoveClass; 94 if (namespace) { 95 if (!element.getElementsByTagNameNS) return; 96 math = element.getElementsByTagNameNS(namespace,name); 97 } else { 98 math = element.getElementsByTagName(name); 99 } 100 for (var i = 0, m = math.length; i < m; i++) { 101 var parent = math[i].parentNode; 102 if (parent && parent.className !== preview && 103 !parent.isMathJax && !math[i].prefix === !namespace) array.push(math[i]); 104 } 105 }, 106 107 ProcessMathArray: function (math) { 108 var i, m = math.length; 109 if (m) { 110 if (this.MathTagBug) { 111 for (i = 0; i < m; i++) { 112 if (math[i].nodeName === "MATH") {this.ProcessMathFlattened(math[i])} 113 else {this.ProcessMath(math[i])} 114 } 115 } else { 116 for (i = 0; i < m; i++) {this.ProcessMath(math[i])} 117 } 118 } 119 }, 120 121 ProcessMath: function (math) { 122 var parent = math.parentNode; 123 if (!parent || parent.className === MathJax.Hub.config.preRemoveClass) return; 124 var script = document.createElement("script"); 125 script.type = "math/mml"; 126 parent.insertBefore(script,math); 127 if (this.AttributeBug) { 128 var html = this.OuterHTML(math); 129 if (this.CleanupHTML) { 130 html = html.replace(/<\?import .*?>/i,"").replace(/<\?xml:namespace .*?\/>/i,""); 131 html = html.replace(/ /g," "); 132 } 133 MathJax.HTML.setScript(script,html); parent.removeChild(math); 134 } else { 135 var span = MathJax.HTML.Element("span"); span.appendChild(math); 136 MathJax.HTML.setScript(script,span.innerHTML); 137 } 138 if (this.config.preview !== "none") {this.createPreview(math,script)} 139 }, 140 141 ProcessMathFlattened: function (math) { 142 var parent = math.parentNode; 143 if (!parent || parent.className === MathJax.Hub.config.preRemoveClass) return; 144 var script = document.createElement("script"); 145 script.type = "math/mml"; 146 parent.insertBefore(script,math); 147 var mml = "", node, MATH = math; 148 while (math && math.nodeName !== "/MATH") { 149 node = math; math = math.nextSibling; 150 mml += this.NodeHTML(node); 151 node.parentNode.removeChild(node); 152 } 153 if (math && math.nodeName === "/MATH") {math.parentNode.removeChild(math)} 154 script.text = mml + "</math>"; 155 if (this.config.preview !== "none") {this.createPreview(MATH,script)} 156 }, 157 158 NodeHTML: function (node) { 159 var html, i, m; 160 if (node.nodeName === "#text") { 161 html = this.quoteHTML(node.nodeValue); 162 } else if (node.nodeName === "#comment") { 163 html = "<!--" + node.nodeValue + "-->" 164 } else { 165 // In IE, outerHTML doesn't properly quote attributes, so quote them by hand 166 // In Opera, HTML special characters aren't quoted in attributes, so quote them 167 html = "<"+node.nodeName.toLowerCase(); 168 for (i = 0, m = node.attributes.length; i < m; i++) { 169 var attribute = node.attributes[i]; 170 if (attribute.specified && attribute.nodeName.substr(0,10) !== "_moz-math-") { 171 // Opera 11.5 beta turns xmlns into xmlns:xmlns, so put it back (*** check after 11.5 is out ***) 172 html += " "+attribute.nodeName.toLowerCase().replace(/xmlns:xmlns/,"xmlns")+"="; 173 var value = attribute.nodeValue; // IE < 8 doesn't properly set style by setAttributes 174 if (value == null && attribute.nodeName === "style" && node.style) {value = node.style.cssText} 175 html += '"'+this.quoteHTML(value)+'"'; 176 } 177 } 178 html += ">"; 179 // Handle internal HTML (possibly due to <semantics> annotation or missing </math>) 180 if (node.outerHTML != null && node.outerHTML.match(/(.<\/[A-Z]+>|\/>)$/)) { 181 for (i = 0, m = node.childNodes.length; i < m; i++) 182 {html += this.OuterHTML(node.childNodes[i])} 183 html += "</"+node.nodeName.toLowerCase()+">"; 184 } 185 } 186 return html; 187 }, 188 OuterHTML: function (node) { 189 if (node.nodeName.charAt(0) === "#") {return this.NodeHTML(node)} 190 if (!this.AttributeBug) {return node.outerHTML} 191 var html = this.NodeHTML(node); 192 for (var i = 0, m = node.childNodes.length; i < m; i++) 193 {html += this.OuterHTML(node.childNodes[i]);} 194 html += "</"+node.nodeName.toLowerCase()+">"; 195 return html; 196 }, 197 quoteHTML: function (string) { 198 if (string == null) {string = ""} 199 return string.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/\"/g,"""); 200 }, 201 202 createPreview: function (math,script) { 203 var preview = this.config.preview; 204 if (preview === "none") return; 205 var isNodePreview = false; 206 if (preview === "mathml") { 207 isNodePreview = true; 208 // mathml preview does not work with IE < 9, so fallback to alttext. 209 if (this.MathTagBug) {preview = "alttext"} else {preview = math.cloneNode(true)} 210 } 211 if (preview === "alttext" || preview === "altimg") { 212 isNodePreview = true; 213 var alttext = this.filterPreview(math.getAttribute("alttext")); 214 if (preview === "alttext") { 215 if (alttext != null) {preview = MathJax.HTML.TextNode(alttext)} else {preview = null} 216 } else { 217 var src = math.getAttribute("altimg"); 218 if (src != null) { 219 // FIXME: use altimg-valign when display="inline"? 220 var style = {width: math.getAttribute("altimg-width"), height: math.getAttribute("altimg-height")}; 221 preview = MathJax.HTML.Element("img",{src:src,alt:alttext,style:style}); 222 } else {preview = null} 223 } 224 } 225 if (preview) { 226 var span; 227 if (isNodePreview) { 228 span = MathJax.HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass}); 229 span.appendChild(preview); 230 } else { 231 span = MathJax.HTML.Element("span",{className:MathJax.Hub.config.preRemoveClass},preview); 232 } 233 script.parentNode.insertBefore(span,script); 234 } 235 }, 236 237 filterPreview: function (text) {return text}, 238 239 InitBrowser: function () { 240 var test = MathJax.HTML.Element("span",{id:"<", className: "mathjax", innerHTML: "<math><mi>x</mi><mspace /></math>"}); 241 var html = test.outerHTML || ""; 242 this.AttributeBug = html !== "" && !( 243 html.match(/id="<"/) && // "<" should convert to "<" 244 html.match(/class="mathjax"/) && // IE leaves out quotes 245 html.match(/<\/math>/) // Opera 9 drops tags after self-closing tags 246 ); 247 this.MathTagBug = test.childNodes.length > 1; // IE < 9 flattens unknown tags 248 this.CleanupHTML = MathJax.Hub.Browser.isMSIE; // remove namespace and other added tags 249 } 250 251 }; 252 253 // 254 // We register the preprocessors with the following priorities: 255 // - mml2jax.js: 5 256 // - jsMath2jax.js: 8 257 // - asciimath2jax.js, tex2jax.js: 10 (default) 258 // See issues 18 and 484 and the other *2jax.js files. 259 // 260 MathJax.Hub.Register.PreProcessor(["PreProcess",MathJax.Extension.mml2jax],5); 261 MathJax.Ajax.loadComplete("[MathJax]/extensions/mml2jax.js");