/************************************************************* * * 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-2011 Design Science, Inc. * * 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,EVENT) { var MML, isMSIE = HUB.Browser.isMSIE; var EVENT, TOUCH, HOVER; // filled in later nMML.Augment({ LEFTBUTTON: (isMSIE ? 1 : 0), // the event.button value for left button MENUKEY: "altKey", // the event value for alternate context menu noContextMenuBug: HUB.Browser.isKonequeror, msieQuirks: (isMSIE && !(document.compatMode === "BackCompat")), msieEventBug: HUB.Browser.isIE9, // // User can configure styles // config: {styles: {}}, settings: HUB.config.menuSettings, Startup: function () { // Set up event handling EVENT = MathJax.Extension.UIevents.Event; TOUCH = MathJax.Extension.UIevents.Touch; HOVER = MathJax.Extension.UIevents.Hover; this.HandleEvent = EVENT.HandleEvent; this.Mouseover = HOVER.Mouseover; this.Mouseout = HOVER.Mouseout; this.Mousemove = HOVER.Mousemove; this.MSIEmouseover = EVENT.Mouseover; this.MSIEmouseout = EVENT.Mouseout; this.MSIEmousemove = EVENT.Mousemove; return AJAX.Styles(this.config.styles); }, 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"; MathJax.Hub.Insert(this.config.styles,{ "div.MathJax_MathML": def, "div.MathJax_MathML math": {"text-align": align}, "div.MathJax_MathContainer > span": {"text-align": align+"!important"} }); } this.require.push(MathJax.OutputJax.extensionDir+"/UIevents.js"); }, // // Set up MathPlayer for IE on the first time through. // InitializeMML: function () { this.initialized = true; if (HUB.Browser.isMSIE) { try { // // Insert data needed to use MathPlayer for MathML output // 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("mjx","http://www.w3.org/1998/Math/MathML"); document.namespaces.mjx.doImport("#mathplayer"); } catch (err) { // // If that fails, give an alert about security settings // alert("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."); } } }, // // Add a SPAN to use as a container, and render the math into it // Translate: function (script) { if (!script.parentNode) return; if (!this.initialized) {this.InitializeMML()} var prev = script.previousSibling; if (prev && String(prev.className).match(/^MathJax(_MathML|_Display)?$/)) {prev.parentNode.removeChild(prev)} var math = script.MathJax.elementJax.root; var type = (math.Get("display") === "block" ? "div" : "span"); var span = HTML.Element(type,{ className: "MathJax_MathML", style: {"font-size": this.config.scale+"%"} },[["span",{ className:"MathJax_MathContainer", isMathJax: true, jaxID: "NativeMML", style:{position:"relative", display:"inline-block", "white-space":"nowrap"} }, [["span",{isMathJax:true, style:{display:"inline-block"}}]] // for Firefox hover and zoom ]]), container = span.firstChild; if (isMSIE && this.config.showMathMenuMSIE) {container.style.display = "inline-block"} math.toNativeMML(container.firstChild); script.parentNode.insertBefore(span,script); if (isMSIE) { if (this.config.showMathMenuMSIE) {this.MSIEoverlay(span)} } 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; } }, // // 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", // // 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; span.style.position = "absolute"; // so we can measure height/depth var HD = span.scrollHeight, W = span.offsetWidth; var tmp = HTML.addElement(span,"img",{src:"about:blank",style:{width:0,height:HD+"px"}}); var D = span.scrollHeight - HD; span.removeChild(tmp); span.style.position = ""; // back to normal var top, left, isDisplay = (span.parentNode.nodeName.toLowerCase() === "div"); if (isDisplay && this.quirks) {top = -HD; left = Math.floor(-W/2)} else {top = D-HD, left = -W} HTML.addElement(span,"span",{ style:{display:"inline-block", width:0, height:0, position:"relative"} },[["span",{ style:{display:"inline-block", position:"absolute", left:left+"px", top:top+"px", width:math.offsetWidth+"px", height:HD+"px", cursor:"pointer", "background-color":"white", filter:"alpha(opacity=0)"}, onmousedown: this.MSIEevent, oncontextmenu: this.MSIEevent, onclick: this.MSIEevent, onmouseup: this.MSIEevent, onmousemove: this.MSIEevent, ondblclick: this.MSIEevent, onmouseover: this.MSIEevent, onmouseout: this.MSIEevent }]]); }, MSIEmath: function (span) { // display="block" seems to produce an extra span, so need extra firstChild var math = span.parentNode.previousSibling.firstChild; return (math.nodeName.toLowerCase() === "span" ? math.firstChild : math); }, MSIEevent: function () { var math = nMML.MSIEmath(this); var event = window.event; var action = nMML["MSIE"+event.type]; if (action && action.call(nMML,event,math,this)) {return false} if (math.fireEvent) {math.fireEvent("on"+event.type,event)} return false; }, MSIEmousedown: function (event,math,span) { if (event[this.MENUKEY] && event.button === this.LEFTBUTTON && this.settings.context !== "MathJax") { this.trapUp = this.trapClick = true; this.ContextMenu(event,span,true); return true; } if (this.MSIEzoomKeys && this.MSIEzoomKeys(event)) {this.trapUp = true; return true} return false; }, MSIEcontextmenu: function (event,math,span) { if (this.settings.context === "MathJax") { this.trapUp = this.trapClick = true; this.ContextMenu(event,span); return true; } return false; }, // Other IE event handlers are in MathZoom.js ContextMenu: function (event,math,force) { if (this.config.showMathMenu && (this.settings.context === "MathJax" || force)) { if (document.selection) {setTimeout("document.selection.empty()",0)} if (this.msieEventBug) {event = window.event} delete this.trapClick; delete this.trapUp; return EVENT.ContextMenu(event,this.getJaxFromMath(math)); } }, Mousedown: function (event,math) { if (this.config.showMathMenu) { if (this.settings.context === "MathJax") { if (!this.noContextMenuBug || event.button !== 2) return } else { if (!event[EVENT.MENUKEY] || event.button !== EVENT.LEFTBUTTON) return } return this.ContextMenu(event,math,true); } }, getJaxFromMath: function (math) { return HUB.getJaxFor(math.parentNode.nextSibling); }, getHoverSpan: function (jax,math) {return math.firstChild}, getHoverBBox: function (jax,span,math) { span = span.parentNode; span.appendChild(this.topImg); var h = this.topImg.offsetTop; span.removeChild(this.topImg); return {w:span.offsetWidth, h:h, d:span.offsetHeight-h, Units:this.PX, em:1} }, PX: function (n) {return n+"px"}, NAMEDSPACE: { negativeveryverythinmathspace: "-.0556em", negativeverythinmathspace: "-.1111em", negativethinmathspace: "-.1667em", negativemediummathspace: "-.2222em", negativethickmathspace: "-.2778em", negativeverythickmathspace: "-.3333em", negativeveryverythickmathspace: "-.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.defaults; var copy = this.NativeMMLcopyAttributes, skip = this.NativeMMLskipAttributes; if (this.type === "mstyle") {defaults = MML.math.prototype.defaults} for (var id in defaults) {if (!skip[id] && defaults.hasOwnProperty(id)) { if (this[id] != null) {tag.setAttribute(id,this.NativeMMLattribute(id,this[id]))} }} for (var i = 0, m = copy.length; i < m; i++) { if (this[copy[i]] != null) {tag.setAttribute(copy[i],this.NativeMMLattribute(copy[i],this[copy[i]]))} } }, NativeMMLcopyAttributes: [ "fontfamily","fontsize","fontweight","fontstyle", "color","background", "id","class","href","style" ], NativeMMLskipAttributes: {texClass: 1, useHeight: 1, texprimestyle: 1}, NativeMMLattribute: function (id,value) { value = String(value); if (nMML.NAMEDSPACE[value]) {value = nMML.NAMEDSPACE[value]} // MP doesn't do negative spaes else if (value.match(/^\s*([-+]?(\d+(\.\d*)?|\.\d+))\s*mu\s*$/)) {value = ((1/18)*RegExp.$1)+"em"} // FIXME: should take scriptlevel into account else if (value === "-tex-caligraphic") {value = "script"} else if (value === "-tex-oldstyle") {value = "normal"} return value; }, // // Create a MathML element // NativeMMLelement: function (type) { var math = (isMSIE ? document.createElement("mjx:"+type) : document.createElementNS(nMML.MMLnamespace,type)); math.isMathJax = true; return math; } }); MML.mrow.Augment({ // // Make inferred rows not include an mrow tag // toNativeMML: function (parent) { if (this.inferred && this.parent.inferRow) { for (var 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 { 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); 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); 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 (HUB.Browser.isFirefox) { // // Zoom and hover don't work well with display="block", // so fake it with inline and an mstyle with displaystyle="true" // MML.math.Augment({ toNativeMML: function (parent) { var tag = parent.appendChild(this.NativeMMLelement(this.type)); this.NativeMMLattributes(tag); if (this.Get("display") === "block") { tag.setAttribute("display","inline"); tag = tag.appendChild(this.NativeMMLelement("mstyle")); tag.setAttribute("displaystyle","true"); } 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.mtable.Augment({ toNativeMML: function (parent) { // // FF doesn't handle width, so put it in styles instead // if (this.width) { var styles = (this.style||"").replace(/;\s*$/,"").split(";"); styles.push("width:"+this.width); this.style = styles.join(";"); } this.SUPER(arguments).toNativeMML.call(this,parent); } }); MML.mlabeledtr.Augment({ toNativeMML: function (parent) { // // FF doesn't handle mlabeledtr, so remove the label // var tag = this.NativeMMLelement("mtr"); this.NativeMMLattributes(tag); for (var i = 1, 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); } }); var fontDir = MathJax.Hub.config.root + "/fonts/HTML-CSS/TeX/otf"; /* * Add fix for mathvariant issues in FF */ nMML.Augment({ config: { styles: { '[mathvariant="double-struck"]': {"font-family":"MathJax_AMS"}, '[mathvariant="script"]': {"font-family":"MathJax_Script"}, '[mathvariant="fraktur"]': {"font-family":"MathJax_Fraktur"}, '[mathvariant="-tex-oldstyle"]': {"font-family":"MathJax_Caligraphic"}, '[mathvariant="-tex-oldstyle-bold"]': {"font-family":"MathJax_Caligraphic", "font-weight":"bold"}, '[mathvariant="-tex-caligraphic"]': {"font-family":"MathJax_Caligraphic"}, '[mathvariant="-tex-caligraphic-bold"]': {"font-family":"MathJax_Caligraphic", "font-weight":"bold"}, '[mathvariant="bold-script"]': {"font-family":"MathJax_Script", "font-weight":"bold"}, '[mathvariant="bold-fraktur"]': {"font-family":"MathJax_Fraktur", "font-weight":"bold"}, '[mathvariant="monospace"]': {"font-family":"monospace"}, '[mathvariant="sans-serif"]': {"font-family":"sansserif"}, '[mathvariant="bold-sans-serif"]': {"font-family":"sansserif", "font-weight":"bold"}, '[mathvariant="sans-serif-italic"]': {"font-family":"sansserif", "font-style":"italic"}, '[mathvariant="sans-serif-bold-italic"]': {"font-family":"sansserif", "font-style":"italic", "font-weight":"bold"}, '@font-face /*1*/': { "font-family": "MathJax_AMS", "src": "local('MathJax_AMS'), url('"+fontDir+"/MathJax_AMS-Regular.otf')" }, '@font-face /*2*/': { "font-family": "MathJax_Script", "src": "local('MathJax_Script'), url('"+fontDir+"/MathJax_Script-Regular.otf')" }, '@font-face /*3*/': { "font-family": "MathJax_Fraktur", "src": "local('MathJax_Fraktur'), url('"+fontDir+"/MathJax_Fraktur-Regular.otf')" }, '@font-face /*4*/': { "font-family": "MathJax_Caligraphic", "src": "local('MathJax_Caligraphic'), url('"+fontDir+"/MathJax_Caligraphic-Regular.otf')" }, '@font-face /*5*/': { "font-family": "MathJax_Fraktur", "font-weight":"bold", "src": "local('MathJax_Fraktur-Bold'), url('"+fontDir+"/MathJax_Fraktur-Bold.otf')" }, '@font-face /*6*/': { "font-family": "MathJax_Caligraphic", "font-weight":"bold", "src": "local('MathJax_Caligraphic-Bold'), url('"+fontDir+"/MathJax_Caligraphic-Bold.otf')" } } } }); } MML.TeXAtom.Augment({ // // Convert TeXatom to an mrow // toNativeMML: function (parent) { // FIXME: Handle spacing using mpadded? var tag = this.NativeMMLelement("mrow"); 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))} } }); 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 quirks = (document.compatMode === "BackCompat"); var isIE8 = browser.versionAtLeast("8.0") && document.documentMode > 7; nMML.msieInlineBlockAlignBug = (!isIE8 || quirks); nMML.msieTopBug = (!browser.versionAtLeast("8.0") || document.documentMode === 7); }, Opera: function (browser) { nMML.operaPositionBug = true; } }); // // Used in measuring zoom and hover positions // nMML.topImg = (nMML.msieInlineBlockAlignBug ? HTML.Element("img",{style:{width:0,height:0,position:"relative"},src:"about:blank"}) : HTML.Element("span",{style:{width:0,height:0,display:"inline-block"}}) ); if (nMML.operaPositionBug || nMML.msieTopBug) {nMML.topImg.style.border="1px solid"} 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, MathJax.HTML.Event);