toMathML.js (9950B)
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/toMathML.js 7 * 8 * Implements a toMathML() method for the mml Element Jax that returns 9 * a MathML string from a given math expression. 10 * 11 * --------------------------------------------------------------------- 12 * 13 * Copyright (c) 2010-2015 The MathJax Consortium 14 * 15 * Licensed under the Apache License, Version 2.0 (the "License"); 16 * you may not use this file except in compliance with the License. 17 * You may obtain a copy of the License at 18 * 19 * http://www.apache.org/licenses/LICENSE-2.0 20 * 21 * Unless required by applicable law or agreed to in writing, software 22 * distributed under the License is distributed on an "AS IS" BASIS, 23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 * See the License for the specific language governing permissions and 25 * limitations under the License. 26 */ 27 28 MathJax.Hub.Register.LoadHook("[MathJax]/jax/element/mml/jax.js",function () { 29 var VERSION = "2.6.1"; 30 31 var MML = MathJax.ElementJax.mml, 32 SETTINGS = MathJax.Hub.config.menuSettings; 33 34 MML.mbase.Augment({ 35 36 toMathML: function (space) { 37 var inferred = (this.inferred && this.parent.inferRow); 38 if (space == null) {space = ""} 39 var tag = this.type, attr = this.toMathMLattributes(); 40 if (tag === "mspace") {return space + "<"+tag+attr+" />"} 41 var data = [], SPACE = (this.isToken ? "" : space+(inferred ? "" : " ")); 42 for (var i = 0, m = this.data.length; i < m; i++) { 43 if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))} 44 else if (!this.isToken && !this.isChars) {data.push(SPACE+"<mrow />")} 45 } 46 if (this.isToken || this.isChars) {return space + "<"+tag+attr+">"+data.join("")+"</"+tag+">"} 47 if (inferred) {return data.join("\n")} 48 if (data.length === 0 || (data.length === 1 && data[0] === "")) 49 {return space + "<"+tag+attr+" />"} 50 return space + "<"+tag+attr+">\n"+data.join("\n")+"\n"+ space +"</"+tag+">"; 51 }, 52 53 toMathMLattributes: function () { 54 var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults); 55 var names = (this.attrNames||MML.copyAttributeNames), 56 skip = MML.skipAttributes, copy = MML.copyAttributes; 57 var attr = []; 58 59 if (this.type === "math" && (!this.attr || !this.attr.xmlns)) 60 {attr.push('xmlns="http://www.w3.org/1998/Math/MathML"')} 61 if (!this.attrNames) { 62 for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) { 63 if (this[id] != null && this[id] !== defaults[id]) { 64 if (this.Get(id,null,1) !== this[id]) 65 attr.push(id+'="'+this.toMathMLattribute(this[id])+'"'); 66 } 67 }} 68 } 69 for (var i = 0, m = names.length; i < m; i++) { 70 if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue; 71 value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]} 72 if (value != null) {attr.push(names[i]+'="'+this.toMathMLquote(value)+'"')} 73 } 74 this.toMathMLclass(attr); 75 if (attr.length) {return " "+attr.join(" ")} else {return ""} 76 }, 77 toMathMLclass: function (attr) { 78 var CLASS = []; if (this["class"]) {CLASS.push(this["class"])} 79 if (this.isa(MML.TeXAtom) && SETTINGS.texHints) { 80 var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass]; 81 if (TEXCLASS) { 82 CLASS.push("MJX-TeXAtom-"+TEXCLASS) 83 if (TEXCLASS === "OP" && !this.movablelimits) CLASS.push("MJX-fixedlimits"); 84 } 85 } 86 if (this.mathvariant && this.toMathMLvariants[this.mathvariant]) 87 {CLASS.push("MJX"+this.mathvariant)} 88 if (this.variantForm) {CLASS.push("MJX-variant")} 89 if (CLASS.length) {attr.unshift('class="'+CLASS.join(" ")+'"')} 90 }, 91 toMathMLattribute: function (value) { 92 if (typeof(value) === "string" && 93 value.replace(/ /g,"").match(/^(([-+])?(\d+(\.\d*)?|\.\d+))mu$/)) { 94 // FIXME: should take scriptlevel into account 95 return (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em"; 96 } 97 else if (this.toMathMLvariants[value]) {return this.toMathMLvariants[value]} 98 return this.toMathMLquote(value); 99 }, 100 toMathMLvariants: { 101 "-tex-caligraphic": MML.VARIANT.SCRIPT, 102 "-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT, 103 "-tex-oldstyle": MML.VARIANT.NORMAL, 104 "-tex-oldstyle-bold": MML.VARIANT.BOLD, 105 "-tex-mathit": MML.VARIANT.ITALIC 106 }, 107 108 toMathMLquote: function (string) { 109 string = String(string).split(""); 110 for (var i = 0, m = string.length; i < m; i++) { 111 var n = string[i].charCodeAt(0); 112 if (n <= 0xD7FF || 0xE000 <= n) { 113 // Code points U+0000 to U+D7FF and U+E000 to U+FFFF. 114 // They are directly represented by n. 115 if (n > 0x7E || (n < 0x20 && n !== 0x0A && n !== 0x0D && n !== 0x09)) { 116 string[i] = "&#x"+n.toString(16).toUpperCase()+";"; 117 } else { 118 var c = 119 {'&':'&', '<':'<', '>':'>', '"':'"'}[string[i]]; 120 if (c) {string[i] = c} 121 } 122 } else if (i+1 < m) { 123 // Code points U+10000 to U+10FFFF. 124 // n is the lead surrogate, let's read the trail surrogate. 125 var trailSurrogate = string[i+1].charCodeAt(0); 126 var codePoint = (((n-0xD800)<<10)+(trailSurrogate-0xDC00)+0x10000); 127 string[i] = "&#x"+codePoint.toString(16).toUpperCase()+";"; 128 string[i+1] = ""; 129 i++; 130 } else { 131 // n is a lead surrogate without corresponding trail surrogate: 132 // remove that character. 133 string[i] = ""; 134 } 135 } 136 return string.join(""); 137 } 138 }); 139 140 // 141 // Override math.toMathML in order to add semantics tag 142 // for the input format, if the user requests that in the 143 // Show As menu. 144 // 145 MML.math.Augment({ 146 toMathML: function (space,jax) { 147 var annotation; 148 if (space == null) {space = ""} 149 if (jax && jax.originalText && SETTINGS.semantics) 150 {annotation = MathJax.InputJax[jax.inputJax].annotationEncoding} 151 var nested = (this.data[0] && this.data[0].data.length > 1); 152 var tag = this.type, attr = this.toMathMLattributes(); 153 var data = [], SPACE = space + (annotation ? " " + (nested ? " " : "") : "") + " "; 154 for (var i = 0, m = this.data.length; i < m; i++) { 155 if (this.data[i]) {data.push(this.data[i].toMathML(SPACE))} 156 else {data.push(SPACE+"<mrow />")} 157 } 158 if (data.length === 0 || (data.length === 1 && data[0] === "")) { 159 if (!annotation) {return "<"+tag+attr+" />"} 160 data.push(SPACE+"<mrow />"); 161 } 162 if (annotation) { 163 if (nested) {data.unshift(space+" <mrow>"); data.push(space+" </mrow>")} 164 data.unshift(space+" <semantics>"); 165 var xmlEscapedTex = jax.originalText.replace(/[&<>]/g, function(item) { 166 return { '>': '>', '<': '<','&': '&' }[item] 167 }); 168 data.push(space+' <annotation encoding="'+annotation+'">'+xmlEscapedTex+"</annotation>"); 169 data.push(space+" </semantics>"); 170 } 171 return space+"<"+tag+attr+">\n"+data.join("\n")+"\n"+space+"</"+tag+">"; 172 } 173 }); 174 175 MML.msubsup.Augment({ 176 toMathML: function (space) { 177 var tag = this.type; 178 if (this.data[this.sup] == null) {tag = "msub"} 179 if (this.data[this.sub] == null) {tag = "msup"} 180 var attr = this.toMathMLattributes(); 181 delete this.data[0].inferred; 182 var data = []; 183 for (var i = 0, m = this.data.length; i < m; i++) 184 {if (this.data[i]) {data.push(this.data[i].toMathML(space+" "))}} 185 return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">"; 186 } 187 }); 188 189 MML.munderover.Augment({ 190 toMathML: function (space) { 191 var tag = this.type; 192 var base = this.data[this.base]; 193 if (base && base.isa(MML.TeXAtom) && base.movablelimits && !base.Get("displaystyle")) { 194 type = "msubsup"; 195 if (this.data[this.under] == null) {tag = "msup"} 196 if (this.data[this.over] == null) {tag = "msub"} 197 } else { 198 if (this.data[this.under] == null) {tag = "mover"} 199 if (this.data[this.over] == null) {tag = "munder"} 200 } 201 var attr = this.toMathMLattributes(); 202 delete this.data[0].inferred; 203 var data = []; 204 for (var i = 0, m = this.data.length; i < m; i++) 205 {if (this.data[i]) {data.push(this.data[i].toMathML(space+" "))}} 206 return space + "<"+tag+attr+">\n" + data.join("\n") + "\n" + space + "</"+tag+">"; 207 } 208 }); 209 210 MML.TeXAtom.Augment({ 211 toMathML: function (space) { 212 // FIXME: Handle spacing using mpadded? 213 var attr = this.toMathMLattributes(); 214 if (!attr && this.data[0].data.length === 1) {return space.substr(2) + this.data[0].toMathML(space)} 215 return space+"<mrow"+attr+">\n" + this.data[0].toMathML(space+" ")+"\n"+space+"</mrow>"; 216 } 217 }); 218 219 MML.chars.Augment({ 220 toMathML: function (space) {return (space||"") + this.toMathMLquote(this.toString())} 221 }); 222 223 MML.entity.Augment({ 224 toMathML: function (space) {return (space||"") + "&"+this.data[0]+";<!-- "+this.toString()+" -->"} 225 }); 226 227 MML.xml.Augment({ 228 toMathML: function (space) {return (space||"") + this.toString()} 229 }); 230 231 MathJax.Hub.Register.StartupHook("TeX mathchoice Ready",function () { 232 MML.TeXmathchoice.Augment({ 233 toMathML: function (space) {return this.Core().toMathML(space)} 234 }); 235 }); 236 237 MathJax.Hub.Startup.signal.Post("toMathML Ready"); 238 239 }); 240 241 MathJax.Ajax.loadComplete("[MathJax]/extensions/toMathML.js");