/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /************************************************************* * * MathJax/jax/output/NativeMML/jax.js * * Implements the NativeMML OutputJax that displays mathematics * using a browser's native MathML capabilities (if any). * * --------------------------------------------------------------------- * * Copyright (c) 2010-2015 The MathJax Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ (function (nMML,HUB,AJAX,HTML) { var MML, isMSIE = HUB.Browser.isMSIE; var EVENT, TOUCH, HOVER, ZOOM; // filled in later HUB.Register.StartupHook("MathZoom Ready",function () {ZOOM = MathJax.Extension.MathZoom}); var NOPADDING = function (side,obj) { var span = HTML.Element("span"); side = "padding"+side; if (obj) { span.style.cssText = (obj.getAttribute("style")||""); if (span.style.padding === "" && (span.style[side]||"") === "") { span.style[side] = "0px"; obj.setAttribute("style",span.style.cssText) } } }; var CELLSPACING = function (obj,rowSpacing,columnSpacing) { // // Webkit default padding on mtd cells is simply // // mtd {padding: 0.5ex;} // // Gecko default padding on mtd cells is // // mtd {padding-right: 0.4em; // padding-left: 0.4em; // padding-bottom: 0.5ex; // padding-top: 0.5ex;} // mtr:first-child > mtd {padding-top: 0ex;} // mtr:last-child > mtd {padding-bottom: 0ex;} // mtd:first-child {padding-left: 0em;} // mtd:last-child {padding-right: 0em;} // // that is the columnspacing/rowspacing is split into two adjacent cells, // and the periphery of boundary cells is set to zero. // // Here, we will set the left/top padding of each cell to // rowSpacing/columnSpacing (or 0px for the leftmost/topmost cells) and // reset the right/bottom padding to zero. // if (obj) { var span = HTML.Element("span"); span.style.cssText = (obj.getAttribute("style")||""); if (span.style.padding === "") { var padding = { paddingLeft: columnSpacing, paddingTop: rowSpacing, paddingRight: "0px", paddingBottom: "0px" }; for (var side in padding) {if (padding.hasOwnProperty(side)) { if ((span.style[side]||"") === "") {span.style[side] = padding[side];} }} } obj.setAttribute("style",span.style.cssText); } }; nMML.Augment({ // // User can configure styles // config: { styles: { ".MathJax_MathML": { "font-style": "normal", "font-weight": "normal", "line-height": "normal", "font-size": "100%", "font-size-adjust":"none", "text-indent": 0, "text-align": "left", "text-transform": "none", "letter-spacing": "normal", "word-spacing": "normal", "word-wrap": "normal", "white-space": "nowrap", "float": "none", "direction": "ltr", "max-width": "none", "max-height": "none", "min-width": 0, "min-height": 0, border: 0, padding: 0, margin: 0 }, "span.MathJax_MathML": { display: "inline!important" }, "div.MathJax_MathML": { display: "block!important" }, ".MathJax_mmlExBox": { display:"block!important", overflow:"hidden", height:"1px", width:"60ex", "min-height": 0, "max-height":"none", padding:0, border: 0, margin: 0 } } }, handlesVariants: false, // true if native support for mathvariants settings: HUB.config.menuSettings, ex: 1, scale: 1, // filled in later adjustWidths: [], // array of elements to have their widths adjusted Config: function () { this.SUPER(arguments).Config.call(this); if (this.settings.scale) {this.config.scale = this.settings.scale} // // Insert styling to take account of displayAlign and displayIndent // if (HUB.config.displayAlign !== "center") { var align = HUB.config.displayAlign, indent = HUB.config.displayIndent; var def = {"text-align": align+"!important"}; def["margin-"+align] = indent+"!important"; HUB.Insert(this.config.styles,{ "div.MathJax_MathML": def, "div.MathJax_MathML math": {"text-align": align}, "div.MathJax_MathContainer > span": {"text-align": align+"!important"} }); } if (!this.require) {this.require = []} this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js"); }, Startup: function () { // Set up event handling EVENT = MathJax.Extension.MathEvents.Event; TOUCH = MathJax.Extension.MathEvents.Touch; HOVER = MathJax.Extension.MathEvents.Hover; this.ContextMenu = EVENT.ContextMenu; this.Mousedown = EVENT.AltContextMenu; this.Mouseover = HOVER.Mouseover; this.Mouseout = HOVER.Mouseout; this.Mousemove = HOVER.Mousemove; if (!HUB.Browser.hasMathPlayer) { // Used in preTranslate to get scaling factors this.EmExSpan = HTML.Element("span", {style:{position:"absolute","font-size-adjust":"none"}}, [ ["div",{className:"MathJax_mmlExBox"}], ["span",{className:"MathJax_MathML"}] ] ); MML.math(MML.mspace().With({width:"60ex"})).toNativeMML(this.EmExSpan.lastChild); } // Set up styles return AJAX.Styles(this.config.styles); }, // // Set up MathPlayer for IE on the first time through. // Get the ex and em sizes. // InitializeMML: function () { this.initialized = true; if (HUB.Browser.hasMathPlayer) { try { // // Insert data needed to use MathPlayer for MathML output // if (!HUB.Browser.mpNamespace) { var mathplayer = document.createElement("object"); mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987"; document.getElementsByTagName("head")[0].appendChild(mathplayer); document.namespaces.add("m","http://www.w3.org/1998/Math/MathML"); HUB.Browser.mpNamespace = true; } if (!HUB.Browser.mpImported) { document.namespaces.m.doImport("#mathplayer"); HUB.Browser.mpImported = true; } } catch (err) { // // If that fails, give an alert about security settings // if (!this.config.noMathPlayerWarning) { alert(MathJax.Localization._(["MathML", "MathPlayer"], "MathJax was not able to set up MathPlayer.\n\n"+ "If MathPlayer is not installed, you need to install it first.\n"+ "Otherwise, your security settings may be preventing ActiveX \n"+ "controls from running. Use the Internet Options item under\n"+ "the Tools menu and select the Security tab, then press the\n"+ "Custom Level button. Check that the settings for\n"+ "'Run ActiveX Controls', and 'Binary and script behaviors'\n"+ "are enabled.\n\n"+ "Currently you will see error messages rather than\n"+ "typeset mathematics.")); } } } else { // // Get the default sizes (need styles in place to do this) // document.body.appendChild(this.EmExSpan); this.defaultEx = this.EmExSpan.firstChild.offsetWidth/60; this.defaultMEx = this.EmExSpan.lastChild.offsetWidth/60; document.body.removeChild(this.EmExSpan); } }, preTranslate: function (state) { var scripts = state.jax[this.id], i, m = scripts.length, script, prev, span, test, math, jax, ex, mex, scale; for (i = 0; i < m; i++) { script = scripts[i]; if (!script.parentNode) continue; if (!this.initialized) {this.InitializeMML()} // // Remove any existing output // prev = script.previousSibling; if (prev && prev.className === "MathJax_MathML") {prev.parentNode.removeChild(prev)} // // Add the MathJax span // jax = script.MathJax.elementJax; if (!jax) continue; math = jax.root; jax.NativeMML = {}; var type = (math.Get("display") === "block" ? "div" : "span"); span = HTML.Element(type,{ className: "MathJax_MathML", id:jax.inputID+"-Frame" },[["span",{ className:"MathJax_MathContainer", isMathJax: true, jaxID:this.id, style:{position:"relative", display:"inline-block", "white-space":"nowrap"} }, [["span",{isMathJax:true, style:{display:"inline-block"}}]] // for Firefox hover and zoom ]]); script.parentNode.insertBefore(span,script); // // Add the test span for determining scales // if (!isMSIE) {script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script)} } // // Determine the scaling factors for each script // (this only requires one reflow rather than a reflow for each equation) // for (i = 0; i < m; i++) { script = scripts[i]; if (!script.parentNode) continue; jax = script.MathJax.elementJax; if (!jax) continue; if (!isMSIE) { test = script.previousSibling; ex = test.firstChild.offsetWidth/60; mex = test.lastChild.offsetWidth/60; if (ex === 0 || ex === "NaN") {ex = this.defaultEx; mex = this.defaultMEx} scale = (this.config.matchFontHeight && mex > 1 ? ex/mex : 1); scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale) * this.config.scale); jax.NativeMML.ex = ex; jax.NativeMML.mex = mex; } else {scale = 100} jax.NativeMML.fontSize = scale+"%"; jax.NativeMML.scale = scale/100; } // // Remove the test spans used for determining scales // if (!isMSIE) { for (i = 0; i < m; i++) { script = scripts[i]; if (script.parentNode && script.MathJax.elementJax) { script.parentNode.removeChild(script.previousSibling); } } } }, // // Add a SPAN to use as a container, and render the math into it // Translate: function (script) { if (!script.parentNode) return; // // Get the jax and the container and set the size // var jax = script.MathJax.elementJax, math = jax.root; var span = document.getElementById(jax.inputID+"-Frame"); if (!span) return; var container = span.firstChild, mspan = container.firstChild; this.ex = jax.NativeMML.ex || this.defaultEx; this.scale = jax.NativeMML.scale || 1; if (this.scale !== 1) {span.style.fontSize = jax.NativeMML.fontSize} // // Convert to MathML (if restarted, remove any partial math) // try {math.toNativeMML(mspan,jax)} catch (err) { if (err.restart) {while (mspan.firstChild) {mspan.removeChild(mspan.firstChild)}} throw err; } // // Add event handlers // if (isMSIE) { if (container.addEventListener) { for (var id in this.MSIE9events) {if (this.MSIE9events.hasOwnProperty(id)) { container.addEventListener(id,this.MSIE9event,true); }} } else { var config = (this.config.showMathMenuMSIE != null ? this : HUB).config; if (config.showMathMenuMSIE && !this.settings.mpContext && !this.settings.mpMouse) {this.MSIEoverlay(container)} else {container.style.position = ""; mspan.firstChild.onmousedown = this.MSIEaltMenu} } } else { container.oncontextmenu = EVENT.Menu; container.onmouseover = EVENT.Mouseover; container.onmouseout = EVENT.Mouseout; container.onmousedown = EVENT.Mousedown; container.onclick = EVENT.Click; container.ondblclick = EVENT.DblClick; if (HUB.Browser.noContextMenu) { container.ontouchstart = TOUCH.start; container.ontouchend = TOUCH.end; } } }, postTranslate: function (state) { if (this.forceReflow) { // // Firefox messes up some mtable's when they are dynamically created // but gets them right on a reflow, so force reflow by toggling a stylesheet // var sheet = (document.styleSheets||[])[0]||{}; sheet.disabled = true; sheet.disabled = false; } }, // // Remove MathML preceeding the script // Remove: function (jax) { var span = jax.SourceElement(); if (!span) return; span = span.previousSibling; if (!span) return; if (span.className.match(/MathJax_MathML/)) {span.parentNode.removeChild(span)} }, // // The namespace to use for MML // MMLnamespace: "http://www.w3.org/1998/Math/MathML", isFullWidth: function (node) { if (!node) return; var width = node.getAttribute("width") || (String(node.getAttribute("style")).match(/(?:^| )width: *([^; ]*)/)||[])[1]; if (width) return !!width.match(/%/); if (node.nodeName.match(/^(semantics|math|mstyle)$/)) { width = this.isFullWidth(node.firstChild); } else if (node.nodeName.toLowerCase() === "mrow") { for (var i = 0, m = node.childNodes.length; i < m && !width; i++) width = this.isFullWidth(node.childNodes[i]); } if (width) { var style = "width:100%; "+(node.getAttribute("style")||""); node.setAttribute("style",style.replace(/ +$/,"")); } return width; }, // // For MSIE, we must overlay the MathPlayer object to trap the events // (since they can't be cancelled when the events are on the tag // itself). The events we DON'T want are transferred to the math element, // and the others are handled directly. // MSIEoverlay: function (span) { var math = span.firstChild; if (math.nodeName.toLowerCase() === "span") {math = math.firstChild} var bbox = this.getHoverBBox(null,math,{}); HTML.addElement(span,"span",{ style:{display:"inline-block", width:0, height:0, position:"relative"} },[["span",{isMathJax: true, className: "MathJax_MathPlayer_Overlay", style:{ display:"inline-block", position:"absolute", left:HOVER.Px(-bbox.w), top:HOVER.Px(-bbox.h-(bbox.y||0)-1), width:HOVER.Px(bbox.w), height:HOVER.Px(bbox.h+bbox.d), cursor:"pointer", "background-color":"white", filter:"alpha(opacity=0)" } }]]); HUB.Insert(span,{ msieMath: math, onmousedown: this.MSIEevent, oncontextmenu: this.MSIEevent, onclick: this.MSIEevent, onmouseup: this.MSIEevent, onmousemove: this.MSIEevent, ondblclick: this.MSIEevent, onmouseover: this.MSIEevent, onmouseout: this.MSIEevent }); }, MSIEevents: { mousedown:"Mousedown", contextmenu:"ContextMenu", click:"Click", mouseup:"Mouseup", mousemove:"Mousemove", dblclick: "DblClick", mouseover:"Mouseover", mouseout:"Mouseout" }, MSIEevent: function () { var event = window.event; var type = nMML.MSIEevents[event.type]; if (nMML[type] && nMML[type](event,this) === false) {return false} if (ZOOM && ZOOM.HandleEvent(event,type,this) === false) {return false} if (event.srcElement.className === "MathJax_MathPlayer_Overlay" && this.msieMath.fireEvent) { // // For now, ignore all other events. This will disable MathPlayer's zoom // feature, but also its support. // if (type === "ContextMenu" || type === "Mouseover" || type === "Mouseout") {this.msieMath.fireEvent("on"+event.type,event)} } return EVENT.False(event); }, MSIEaltMenu: function () { var container = this.parentNode.parentNode; while (!container.jaxID) {container = container.parentNode} EVENT.AltContextMenu(window.event,container); }, MSIE9events: { contextmenu:"Menu", click:"Click", dblclick: "DblClick", mouseup:"False", mouseover:"Mouseover", mouseout:"Mouseout" }, MSIE9event: function (event) { if (event.type === "contextmenu" && nMML.settings.mpContext) {return true} if (event.type === "mouseup" && nMML.settings.mpMouse) {return true} if (event.type === "click" && nMML.settings.mpContext) {return EVENT.AltContextMenu(event,this)} var type = nMML.MSIE9events[event.type]; return EVENT[type].call(this,event); }, getJaxFromMath: function (math) { math = math.parentNode; do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script"); return HUB.getJaxFor(math); }, getHoverSpan: function (jax,math) {return math.firstChild}, getHoverBBox: function (jax,span,math) {return EVENT.getBBox(span.parentNode)}, Zoom: function (jax,span,math,Mw,Mh) { jax.root.toNativeMML(span); if (this.msieIE8HeightBug) {span.style.position = "absolute"} if (nMML.widthBug) {span.style.width = span.parentNode.style.width = ""} if (span.parentNode.style.width.match(/%$/)) {span.parentNode.style.minWidth = Math.ceil(3*Mh/4)+"px"} // for full-width tables var mW = math.offsetWidth || math.scrollWidth, mH = math.offsetHeight || math.scrollHeight; var zW = span.offsetWidth, zH = span.offsetHeight; if (nMML.widthBug || span.style.width.match(/%/)) { // // FF doesn't get width of right, so get it from // var W = span.firstChild.firstChild.scrollWidth; if (W > zW) {zW = W; span.parentNode.style.width = span.style.minWidth = zW + "px";} } if (this.msieIE8HeightBug) {span.style.position = ""} return {Y:-EVENT.getBBox(span.parentNode).h, mW:mW, mH:mH, zW:zW, zH:zH} }, NAMEDSPACE: { negativeveryverythinmathspace: "-.0556em", negativeverythinmathspace: "-.1111em", negativethinmathspace: "-.1667em", negativemediummathspace: "-.2222em", negativethickmathspace: "-.2778em", negativeverythickmathspace: "-.3333em", negativeveryverythickmathspace: "-.3889em", veryverythinmathspace: ".0556em", verythinmathspace: ".1111em", thinmathspace: ".1667em", mediummathspace: ".2222em", thickmathspace: ".2778em", verythickmathspace: ".3333em", veryverythickmathspace: ".3889em" } }); HUB.Register.StartupHook("mml Jax Ready",function () { MML = MathJax.ElementJax.mml; MML.mbase.Augment({ // // Add a MathML tag of the correct type, and set its attributes // then populate it with its children and append it to the parent // toNativeMML: function (parent) { var tag = this.NativeMMLelement(this.type); this.NativeMMLattributes(tag); for (var i = 0, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} } parent.appendChild(tag); }, // // Look for attributes that are different from the defaults // and set those in the tag's attribute list // NativeMMLattributes: function (tag) { var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults); var names = (this.attrNames||MML.copyAttributeNames), skip = MML.skipAttributes, copy = MML.copyAttributes; if (!this.attrNames) { for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) { if (this[id] != null && this[id] !== defaults[id]) tag.setAttribute(id,this.NativeMMLattribute(this[id])); }} } for (var i = 0, m = names.length; i < m; i++) { if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue; var value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]} if (value != null) {tag.setAttribute(names[i],this.NativeMMLattribute(value))} } this.NativeMMLclass(tag); }, NativeMMLclass: function (tag) { var CLASS = []; if (this["class"]) {CLASS.push(this["class"])} if (this.isa(MML.TeXAtom)) { var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass]; if (TEXCLASS) {CLASS.push("MJX-TeXAtom-"+TEXCLASS)} } if (this.mathvariant && this.NativeMMLvariants[this.mathvariant]) {CLASS.push("MJX"+this.mathvariant)} if (this.variantForm) {CLASS.push("MJX-variant")} if (CLASS.length) {tag.setAttribute("class",CLASS.join(" "))} }, NativeMMLattribute: function (value) { value = String(value); if (nMML.NAMEDSPACE[value]) {value = nMML.NAMEDSPACE[value]} // MP doesn't do negative spaces else if (value.match(/^\s*(([-+])?(\d+(\.\d*)?|\.\d+))\s*mu\s*$/)) {value = (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em"} // FIXME: should take scriptlevel into account else if (this.NativeMMLvariants[value]) {value = this.NativeMMLvariants[value]} return value; }, NativeMMLvariants: { "-tex-caligraphic": MML.VARIANT.SCRIPT, "-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT, "-tex-oldstyle": MML.VARIANT.NORMAL, "-tex-oldstyle-bold": MML.VARIANT.BOLD, "-tex-mathit": MML.VARIANT.ITALIC }, // // Create a MathML element // NativeMMLelement: function (type) { var math = (document.createElementNS ? document.createElementNS(nMML.MMLnamespace,type) : (HUB.Browser.mpNamespace ? document.createElement("m:"+type) : document.createElement(type))); math.isMathJax = true; return math; } }); MML.mrow.Augment({ // // Make inferred rows not include an mrow tag // toNativeMML: function (parent) { var i, m; if (this.inferred && this.parent.inferRow) { for (i = 0, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(parent)} else {parent.appendChild(this.NativeMMLelement("mrow"))} } } else if (nMML.stretchyMoBug && (this.open || this.close)) { // // This element contains opening and/or closing fences. Opera is not // able to stretch operators, so let's use an element // instead. // var mfenced = this.NativeMMLelement("mfenced"); this.NativeMMLattributes(mfenced); i = 0, m = this.data.length; if (this.open) { mfenced.setAttribute("open", this.open); i++; } if (this.close) { mfenced.setAttribute("close", this.close); m--; } var tag = mfenced; if (m - i + 1 > 1) { // // If there are several children, put them in an // tag = this.NativeMMLelement("mrow"); parent.appendChild(mfenced); parent = mfenced; } for (; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} } parent.appendChild(tag); } else { this.SUPER(arguments).toNativeMML.call(this,parent); } } }); MML.msubsup.Augment({ // // Use proper version of msub, msup, or msubsup, depending on // which items are present // toNativeMML: function (parent) { var type = this.type; if (this.data[this.sup] == null) {type = "msub"} if (this.data[this.sub] == null) {type = "msup"} var tag = this.NativeMMLelement(type); this.NativeMMLattributes(tag); if (this.data[0]) {delete this.data[0].inferred} for (var i = 0, m = this.data.length; i < m; i++) {if (this.data[i]) {this.data[i].toNativeMML(tag)}} parent.appendChild(tag); } }); MML.munderover.Augment({ // // Use proper version of munder, mover, or munderover, depending on // which items are present // toNativeMML: function (parent) { var type = this.type; if (this.data[this.under] == null) {type = "mover"} if (this.data[this.over] == null) {type = "munder"} var tag = this.NativeMMLelement(type); this.NativeMMLattributes(tag); if (this.data[0]) {delete this.data[0].inferred} for (var i = 0, m = this.data.length; i < m; i++) {if (this.data[i]) {this.data[i].toNativeMML(tag)}} parent.appendChild(tag); } }); if (!isMSIE) { var SPLIT = HUB.SplitList; MML.mtable.Augment({ toNativeMML: function (parent) { var i, m; if (nMML.tableSpacingBug) { // // Parse the rowspacing/columnspacing. For convenience, we convert // them to a left/top padding value that will be applied to each // cell. The leftmost/topmost cells will use "0px". // var values = this.getValues("rowspacing", "columnspacing"); this.nMMLtopPadding = SPLIT("0px "+values.rowspacing); this.nMMLleftPadding = SPLIT("0px "+values.columnspacing); // // Transmit the top padding to each row. // If this.parent.nMML.topPadding does not contain enough value, // repeat the last one. // var tp = this.nMMLtopPadding, M = tp.length; for (i = 0, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].nMMLtopPadding = tp[i < M ? i : M-1]} } } if (nMML.tableLabelBug) { // // Look for labeled rows so we know how to handle them // for (i = 0, m = this.data.length; i < m; i++) { if (this.data[i] && this.data[i].isa(MML.mlabeledtr)) { var align = HUB.config.displayAlign.charAt(0), side = this.Get("side").charAt(0); this.nMMLhasLabels = true; this.nMMLlaMatch = (align === side); this.nMMLforceWidth = (align === "c" || !!((this.width||"").match("%"))); break; } } } // // Firefox < 13 doesn't handle width, so put it in styles instead // if (this.width && this.ffTableWidthBug) { var styles = (this.style||"").replace(/;\s*$/,"").split(";"); if (styles[0] === "") {styles.shift()} styles.push("width:"+this.width); this.style = styles.join(";"); } this.SUPER(arguments).toNativeMML.call(this,parent); // if (this.nMMLhasLabels) { var mtable = parent.firstChild; // // Add column attributes on the left when extra columns where inserted // if (this.nMMLforceWidth || side !== "r") { var n = (align !== "l" ? 1 : 0) + (side === "l" ? 1 : 0); if (n) { var attr = {columnalign:"left", columnwidth:"auto", columnspacing:"0px", columnlines:"none"}; for (var id in attr) {if (attr.hasOwnProperty(id) && this[id]) { var cols = [attr[id],attr[id]].slice(2-n).join(" ")+" "; mtable.setAttribute(id,cols+mtable.getAttribute(id)); }} } } // // Force the table width to 100% when needed // if (this.nMMLforceWidth || !this.nMMLlaMatch) {mtable.setAttribute("width","100%")} } } }); MML.mtr.Augment({ toNativeMML: function (parent) { this.SUPER(arguments).toNativeMML.call(this,parent); var mtr = parent.lastChild; if (nMML.tableSpacingBug) { // // Set the row/column spacing. If this.parent.nMMLleftPadding does // not contain enough value, repeat the last one. // var lp = this.parent.nMMLleftPadding, M = lp.length; for (var mtd = mtr.firstChild, i = 0; mtd; mtd = mtd.nextSibling, i++) { CELLSPACING(mtd,this.nMMLtopPadding,lp[i < M ? i : M-1]); } } if (nMML.tableLabelBug) { var forceWidth = this.parent.nMMLforceWidth, side = this.parent.Get("side").charAt(0), align = HUB.config.displayAlign.charAt(0); if (this.parent.nMMLhasLabels && mtr.firstChild) { // // If we add a label or padding column on the left of mlabeledtr, // mirror that here and remove padding from first table mtd // so the spacing is consistent with unlabeled equations // if (forceWidth || side !== "r") { NOPADDING("Left",mtr.firstChild); if (align !== "l") { mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild) .setAttribute("style","padding:0"); } if (side === "l") { mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild) .setAttribute("style","padding:0"); } } // // If columns were added on the right, remove mtd padding // so that spacing is consistent with unlabled equations // if (forceWidth || side !== "l") {NOPADDING("Right",mtr.lastChild)} } } } }); MML.mlabeledtr.Augment({ toNativeMML: function (parent) { var mtr = this.NativeMMLelement("mtr"); this.NativeMMLattributes(mtr); // // Add row data // for (var i = 1, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(mtr)} else {mtr.appendChild(this.NativeMMLelement("mtd"))} } if (nMML.tableSpacingBug) { // // Set the row/column spacing. If this.parent.nMMLleftPadding does // not contain enough value, repeat the last one. // var lp = this.parent.nMMLleftPadding, M = lp.length; i = 0; for (var mtd = mtr.firstChild; mtd; mtd = mtd.nextSibling, i++) { CELLSPACING(mtd,this.nMMLtopPadding,lp[i < M ? i : M-1]); } } if (nMML.tableLabelBug && this.data[0]) { var side = this.parent.Get("side").charAt(0), align = HUB.config.displayAlign.charAt(0), indent = HUB.config.displayIndent; // // Create label and either set the column width (if label is on the // same side as the alignment), or use mpadded to hide the label // width // this.data[0].toNativeMML(mtr); var label = mtr.lastChild, pad = label; if (side === align) { label.setAttribute("style","width:"+indent); label.setAttribute("columnalign",HUB.config.displayAlign); } else { pad = this.NativeMMLelement("mpadded"); pad.setAttribute("style","width:0"); pad.setAttribute("width","0px"); pad.appendChild(label.firstChild); label.appendChild(pad); } NOPADDING("",label); mtr.removeChild(label); // // Get spacing to use for separation of label from main table // var width = 100, forceWidth = this.parent.nMMLforceWidth; if ((this.parent.width||"").match(/%/)) { width -= parseFloat(this.parent.width) }; var w = width; // // Add spacing (and possibly label) at the left if needed // if (forceWidth || side !== "r") { NOPADDING("Left",mtr.firstChild); if (align !== "l") { if (align === "c") {w /= 2}; width -= w; mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild) .setAttribute("style","padding:0;width:"+w+"%"); } if (side === "l") {mtr.insertBefore(label,mtr.firstChild)} } // // Add spacing (and possibly label) at the right if needed // if (forceWidth || side !== "l") { NOPADDING("Right",mtr.lastChild); if (align !== "r") { mtr.appendChild(this.NativeMMLelement("mtd")) .setAttribute("style","padding:0;width:"+width+"%"); } if (side === "r") { if (side !== align) {pad.setAttribute("lspace","-1width")} mtr.appendChild(label); } } } // // Add row to table // parent.appendChild(mtr); } }); MML.mtd.Augment({ toNativeMML: function (parent) { var tag = parent.appendChild(this.NativeMMLelement(this.type)); this.NativeMMLattributes(tag); if (nMML.mtdWidthBug) { nMML.adjustWidths.push(tag); tag = tag.appendChild(this.NativeMMLelement("mrow")); } for (var i = 0, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} } } }); MML.mspace.Augment({ toNativeMML: function (parent) { this.SUPER(arguments).toNativeMML.call(this,parent); if (nMML.spaceWidthBug && this.width) { var mspace = parent.lastChild; var width = mspace.getAttribute("width"); var style = (mspace.getAttribute("style") || "").replace(/;?\s*/,"; "); mspace.setAttribute("style",style+"width:"+width); } } }); var fontDir = AJAX.fileURL(MathJax.OutputJax.fontDir+"/HTML-CSS/TeX/otf"); /* * Add fix for mathvariant issues */ nMML.Augment({ config: { styles: { '[class="MJX-tex-oldstyle"]': {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB"}, '[class="MJX-tex-oldstyle-bold"]': {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB", "font-weight":"bold"}, '[class="MJX-tex-caligraphic"]': {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB"}, '[class="MJX-tex-caligraphic-bold"]': {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB", "font-weight":"bold"}, '@font-face /*1*/': { "font-family": "MathJax_Caligraphic-WEB", "src": "url('"+fontDir+"/MathJax_Caligraphic-Regular.otf')" }, '@font-face /*2*/': { "font-family": "MathJax_Caligraphic-WEB", "font-weight":"bold", "src": "url('"+fontDir+"/MathJax_Caligraphic-Bold.otf')" } } } }); if (!this.handlesVariants) { nMML.Augment({ config: { styles: { '[mathvariant="double-struck"]': {"font-family":"MathJax_AMS, MathJax_AMS-WEB"}, '[mathvariant="script"]': {"font-family":"MathJax_Script, MathJax_Script-WEB"}, '[mathvariant="fraktur"]': {"font-family":"MathJax_Fraktur, MathJax_Fraktur-WEB"}, '[mathvariant="bold-script"]': {"font-family":"MathJax_Script, MathJax_Caligraphic-WEB", "font-weight":"bold"}, '[mathvariant="bold-fraktur"]': {"font-family":"MathJax_Fraktur, MathJax_Fraktur-WEB", "font-weight":"bold"}, '[mathvariant="monospace"]': {"font-family":"monospace"}, '[mathvariant="sans-serif"]': {"font-family":"sans-serif"}, '[mathvariant="bold-sans-serif"]': {"font-family":"sans-serif", "font-weight":"bold"}, '[mathvariant="sans-serif-italic"]': {"font-family":"sans-serif", "font-style":"italic"}, '[mathvariant="sans-serif-bold-italic"]': {"font-family":"sans-serif", "font-style":"italic", "font-weight":"bold"}, '@font-face /*3*/': { "font-family": "MathJax_AMS-WEB", "src": "url('"+fontDir+"/MathJax_AMS-Regular.otf')" }, '@font-face /*4*/': { "font-family": "MathJax_Script-WEB", "src": "url('"+fontDir+"/MathJax_Script-Regular.otf')" }, '@font-face /*5*/': { "font-family": "MathJax_Fraktur-WEB", "src": "url('"+fontDir+"/MathJax_Fraktur-Regular.otf')" }, '@font-face /*6*/': { "font-family": "MathJax_Fraktur-WEB", "font-weight":"bold", "src": "url('"+fontDir+"/MathJax_Fraktur-Bold.otf')" } } } }); } } MML.math.Augment({ toNativeMML: function (parent,jax) { var tag = this.NativeMMLelement(this.type), math = tag; var annotate = (jax ? MathJax.InputJax[jax.inputJax].annotationEncoding : null); var i, m; nMML.adjustWidths = []; // // Some browsers don't seem to add the xmlns attribute, so do it by hand. // tag.setAttribute("xmlns",nMML.MMLnamespace); this.NativeMMLattributes(tag); // // Use an extra in FF so that we can get the correct width // (the math element doesn't always have an accurate one, see below) // if (nMML.widthBug) {tag = tag.appendChild(this.NativeMMLelement("mrow"))} // // Addannotation if the input jax provides an annotation encoding // if (annotate) { tag = tag.appendChild(this.NativeMMLelement("semantics")) tag.appendChild(this.NativeMMLelement("mrow")); var annotation = tag.appendChild(this.NativeMMLelement("annotation")); annotation.appendChild(document.createTextNode(jax.originalText)); annotation.setAttribute("encoding",annotate); tag = tag.firstChild; // mrow } // // Add the children // for (i = 0, m = this.data.length; i < m; i++) { if (this.data[i]) {this.data[i].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} } // // Look for a top-level mtable and if it has labels // Make sure the containers have 100% width, when needed. // If the label is on the same side as alignment, // override the margin set by the stylesheet. // var mtable = ((this.data[0]||{data:[]}).data[0]||{}); if (mtable.nMMLhasLabels) { if (mtable.nMMLforceWidth || !mtable.nMMLlaMatch) { tag.setAttribute("style","width:100%") // mrow node if (annotate) tag.parentNode.setAttribute("style","width:100%"); // semantics node }; if (mtable.nMMLlaMatch) { if (parent.parentNode.parentNode.nodeName.toLowerCase() === "div") { parent.parentNode.parentNode.style .setProperty("margin-"+HUB.config.displayAlign,"0px","important"); } } } // // Check if container must have width set to 100% // var fullWidth = nMML.isFullWidth(math); if (fullWidth) {parent.style.width = parent.parentNode.style.width = "100%"} // // Add the math to the page // parent.appendChild(math); // // Firefox can't seem to get the width of elements right, so // use an to get the actual width and set the style on the // parent element to match. Even if we set the width properly, // it doesn't seem to propagate up to the correctly. // if (nMML.widthBug &&!fullWidth) { // // Convert size to ex's so that it scales properly if the print media // has a different font size. // parent.style.width = (math.firstChild.scrollWidth/nMML.ex/nMML.scale).toFixed(3) + "ex"; // // Save size for later when we check if Web fonts have arrived // if (jax) {jax.NativeMML.scrollWidth = math.firstChild.scrollWidth} } if (nMML.adjustWidths.length) { // // Firefox gets the widths of elements wrong, so run // through them (now that the math is part of the page) and // fix them up. Use ex's so that they print properly (see above). // var mtd = []; for (i = 0, m = nMML.adjustWidths.length; i < m; i++) { tag = nMML.adjustWidths[i]; var style = tag.getAttribute("style") || ""; if (!style.match(/(^|;)\s*min-width:/)) { var width = tag.firstChild.scrollWidth; mtd.push(width); width = (width/nMML.ex).toFixed(3)+"ex"; style = style.replace(/;?\s*$/,"; "); tag.setAttribute("style",style+"min-width:"+width); } } // // Save the lists so that we can check them later for web font downloads // if (!jax) {jax = HUB.getJaxFor(parent)} if (jax) {jax.NativeMML.mtds = mtd} math.MathJaxMtds = nMML.adjustWidths; nMML.adjustWidths = []; // clear it so we don't hold onto the DOM elements } } }); MML.mfenced.Augment({ toNativeMML: function (parent) { if (!nMML.mfencedBug) { this.SUPER(arguments).toNativeMML.call(this,parent); return; } // // Some browsers do not handle very well. The MathML spec // suggests this equivalent construction instead, so let's use it: // open, child1, sep1, child2, ... sep(N-1), childN, close // Opera is a bit special: it does not support stretchy , does not // parse mfenced@open/mfenced@close very well, does not support // mfenced@separators and only displays the first child of the // element... For this browser, we will use this construction: // // child1, sep1, child2, sep2, ..., sep(N-1), childN // // var isOpera = HUB.Browser.isOpera; var i, m, operator; // // parse the open, close and separators attributes. // var values = this.getValues("open","close","separators"); values.open = values.open.replace(/^\s+/,"").replace(/\s+$/,""); values.close = values.close.replace(/^\s+/,"").replace(/\s+$/,""); values.separators = values.separators.replace(/\s+/g,"").split(""); if (values.separators.length == 0) { // // No separators specified, do not use separators at all. // values.separators = null; } else if (values.separators.length < this.data.length-1) { // // There are not enough separators, repeat the last one. // var s = values.separators[values.separators.length-1]; for (i = this.data.length-1-values.separators.length; i > 0; i--) {values.separators.push(s)} } // // Create an container and attach the attributes of the // element to it. Note: removeAttribute does not raise any // exception when the attributes is absent. // var tag = this.NativeMMLelement(isOpera ? this.type : "mrow"); this.NativeMMLattributes(tag); tag.removeAttribute("separators"); if (isOpera) { tag.setAttribute("open", values.open); tag.setAttribute("close", values.close); if (this.data.length > 1) { parent.appendChild(tag); parent = tag; tag = this.NativeMMLelement("mrow"); } } else { tag.removeAttribute("open"); tag.removeAttribute("close"); } if (!isOpera) { // // Append the opening fence // operator = this.NativeMMLelement("mo"); operator.setAttribute("fence", "true"); operator.textContent = values.open; tag.appendChild(operator); } // // Append the content of the // for (i = 0, m = this.data.length; i < m; i++) { if (values.separators && i > 0) { operator = this.NativeMMLelement("mo"); operator.setAttribute("separator", "true"); operator.textContent = values.separators[i-1]; tag.appendChild(operator); } if (this.data[i]) {this.data[i].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} } if (!isOpera) { // // Append the closing fence // operator = this.NativeMMLelement("mo"); operator.setAttribute("fence", "true"); operator.textContent = values.close; tag.appendChild(operator); } // finally, append the new element to the parent. parent.appendChild(tag); } }); MML.TeXAtom.Augment({ // // Convert TeXatom to an mrow // toNativeMML: function (parent) { // FIXME: Handle spacing using mpadded? var tag = this.NativeMMLelement("mrow"); this.NativeMMLattributes(tag); this.data[0].toNativeMML(tag); parent.appendChild(tag); } }); MML.chars.Augment({ // // Add a text node // toNativeMML: function (parent) { parent.appendChild(document.createTextNode(this.toString())); } }); MML.entity.Augment({ // // Add a text node // toNativeMML: function (parent) { parent.appendChild(document.createTextNode(this.toString())); } }); MML.xml.Augment({ // // Insert the XML verbatim // toNativeMML: function (parent) { for (var i = 0, m = this.data.length; i < m; i++) {parent.appendChild(this.data[i].cloneNode(true))} } }); MML.mi.Augment({ toNativeMML: function (parent) { this.SUPER(arguments).toNativeMML.call(this,parent); if (nMML.miItalicBug) { if (this.Get("mathvariant") === MML.VARIANT.NORMAL) { // // When not explicitly specified, mathvariant is set to "italic" // with single char mi and to "normal" with multiple char mi. // Some browsers always set the default to "italic", so let's // attach an explicit mathvariant="normal" attribute. // var mi = parent.lastChild; mi.setAttribute("mathvariant",MML.VARIANT.NORMAL); } } } }); MML.mo.Augment({ toNativeMML: function (parent) { this.SUPER(arguments).toNativeMML.call(this,parent); if (nMML.webkitMoSpacingBug) { // // WebKit does not support lspace/rspace values around operators // (neither explicit nor given by the operator dictionary) and uses // constant values instead. So let's modify the CSS properties here. // var lspace = 0, rspace = 0, p = this.parent; if (p && p.type === "mrow" && (p.inferred || !p.isEmbellished())) { // // Retrieve the values of lspace/rspace and convert named spaces. // Other values (except unitless) will be parsed by the CSS engine. // var values = this.getValues("lspace", "rspace"); lspace = values.lspace, rspace = values.rspace; if (nMML.NAMEDSPACE[lspace]) {lspace = nMML.NAMEDSPACE[lspace]} if (nMML.NAMEDSPACE[rspace]) {rspace = nMML.NAMEDSPACE[rspace]} } // // Now update -webkit-margin-start and -webkit-margin-end. // var mo = parent.lastChild; var span = HTML.Element("span"); span.style.cssText = (mo.getAttribute("style")||""); span.style.setProperty("-webkit-margin-start", lspace); span.style.setProperty("-webkit-margin-end", rspace); mo.setAttribute("style",span.style.cssText); } } }); MML.mmultiscripts.Augment({ toNativeMML: function (parent) { // // Some browsers do not implement the mmultiscripts element. // Try to emulate the support using basic script elements. // if (!nMML.mmultiscriptsBug || this.data.length === 0) { this.SUPER(arguments).toNativeMML.call(this,parent); return; } // // The children of the mmultiscripts will be wrapped in an mrow so that // attributes and properties set on the original mmultiscripts will // be reflected on this mrow element. // var tag = this.NativeMMLelement("mrow"); this.NativeMMLattributes(tag); // // Create the base // if (this.data[0]) {this.data[0].toNativeMML(tag)} else {tag.appendChild(this.NativeMMLelement("mrow"))} var base = tag.removeChild(tag.lastChild); // // Process the postscript pairs // var m = this.data.length, i, msubsup; for (i = 1; i < m; i+=2) { if (this.data[i].type === "mprescripts") break; msubsup = this.NativeMMLelement("msubsup"); msubsup.appendChild(base); // // append the subscript // if (this.data[i]) {this.data[i].toNativeMML(msubsup)} else {msubsup.appendChild(this.NativeMMLelement("mrow"))} // // append the supscript // if (i+1 < m && this.data[i+1]) {this.data[i+1].toNativeMML(msubsup)} else {msubsup.appendChild(this.NativeMMLelement("mrow"))} base = msubsup; } tag.appendChild(base); // // Process the prescript pairs // for (i++; i < m; i+=2) { msubsup = this.NativeMMLelement("msubsup"); msubsup.appendChild(this.NativeMMLelement("mrow")); // // append the presubscript // if (this.data[i]) {this.data[i].toNativeMML(msubsup)} else {msubsup.appendChild(this.NativeMMLelement("mrow"))} // // append the presupscript // if (i+1 < m && this.data[i+1]) {this.data[i+1].toNativeMML(msubsup)} else {msubsup.appendChild(this.NativeMMLelement("mrow"))} tag.insertBefore(msubsup, base); } parent.appendChild(tag); } }); HUB.Register.StartupHook("TeX mathchoice Ready",function () { MML.TeXmathchoice.Augment({ // // Get the MathML for the selected choice // toNativeMML: function (parent) {this.Core().toNativeMML(parent)} }); }); // // Loading isn't complete until the element jax is modified, // but can't call loadComplete within the callback for "mml Jax Ready" // (it would call NativeMML's Require routine, asking for the mml jax again) // so wait until after the mml jax has finished processing. // setTimeout(MathJax.Callback(["loadComplete",nMML,"jax.js"]),0); }); // // Determine browser characteristics // HUB.Browser.Select({ MSIE: function (browser) { var mode = (document.documentMode || 0); nMML.msieIE8HeightBug = (mode === 8); }, Opera: function (browser) { nMML.operaPositionBug = true; nMML.stretchyMoBug = true; nMML.tableLabelBug = true; nMML.mfencedBug = true; nMML.miBug = true; nMML.mmultiscriptsBug = true; }, Firefox: function (browser) { var is29 = browser.versionAtLeast("29.0"); nMML.ffTableWidthBug = !browser.versionAtLeast("13.0"); // not implemented nMML.forceReflow = !is29; // with alignments set don't display properly without a reflow nMML.widthBug = !is29; // elements don't always get the correct width nMML.mtdWidthBug = true; // widths not properly determined nMML.handlesVariants = is29; // FF >=29 handles all math variants // In Firefox < 20, the intrinsic width of is not computed // correctly and thus the element is displayed incorrectly in . nMML.spaceWidthBug = !browser.versionAtLeast("20.0"); // mtable@rowspacing/mtable@columnspacing not supported. nMML.tableSpacingBug = !browser.versionAtLeast("33.0"); nMML.tableLabelBug = true; // mlabeledtr is not implemented. nMML.mfencedBug = true; // mfenced not displayed correctly }, Chrome: function (browser) { nMML.tableSpacingBug = true; nMML.tableLabelBug = true; nMML.mfencedBug = true; }, Safari: function (browser) { nMML.tableSpacingBug = true; nMML.tableLabelBug = true; nMML.mfencedBug = true; nMML.miItalicBug = true; nMML.webkitMoSpacingBug = true; nMML.spaceWidthBug = true; nMML.mmultiscriptsBug = true; } }); HUB.Register.StartupHook("End Cookie",function () { if (HUB.config.menuSettings.zoom !== "None") {AJAX.Require("[MathJax]/extensions/MathZoom.js")} }); })(MathJax.OutputJax.NativeMML, MathJax.Hub, MathJax.Ajax, MathJax.HTML);