www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

jax.js (55631B)


      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/jax/output/NativeMML/jax.js
      7  *
      8  *  Implements the NativeMML OutputJax that displays mathematics
      9  *  using a browser's native MathML capabilities (if any).
     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 (function (nMML,HUB,AJAX,HTML) {
     29   var MML, isMSIE = HUB.Browser.isMSIE;
     30   
     31   var EVENT, TOUCH, HOVER, ZOOM; // filled in later
     32 
     33   HUB.Register.StartupHook("MathZoom Ready",function () {ZOOM = MathJax.Extension.MathZoom});
     34   
     35   var NOPADDING = function (side,obj) {
     36     var span = HTML.Element("span"); side = "padding"+side;
     37     if (obj) {
     38       span.style.cssText = (obj.getAttribute("style")||"");
     39       if (span.style.padding === "" && (span.style[side]||"") === "") {
     40         span.style[side] = "0px"; obj.setAttribute("style",span.style.cssText)
     41       }
     42     }
     43   };
     44   
     45   var CELLSPACING = function (obj,rowSpacing,columnSpacing) {
     46     //
     47     // Webkit default padding on mtd cells is simply
     48     //
     49     // mtd {padding: 0.5ex;}
     50     //
     51     // Gecko default padding on mtd cells is
     52     //
     53     // mtd {padding-right: 0.4em;
     54     //      padding-left: 0.4em;
     55     //      padding-bottom: 0.5ex;
     56     //      padding-top: 0.5ex;}
     57     // mtr:first-child > mtd {padding-top: 0ex;}
     58     // mtr:last-child > mtd {padding-bottom: 0ex;}
     59     // mtd:first-child {padding-left: 0em;}
     60     // mtd:last-child {padding-right: 0em;}
     61     //
     62     // that is the columnspacing/rowspacing is split into two adjacent cells,
     63     // and the periphery of boundary cells is set to zero.
     64     //
     65     // Here, we will set the left/top padding of each cell to
     66     // rowSpacing/columnSpacing (or 0px for the leftmost/topmost cells) and
     67     // reset the right/bottom padding to zero.
     68     //
     69     if (obj) {
     70       var span = HTML.Element("span");
     71       span.style.cssText = (obj.getAttribute("style")||"");
     72       if (span.style.padding === "") {
     73         var padding = { paddingLeft: columnSpacing, paddingTop: rowSpacing,
     74                         paddingRight: "0px", paddingBottom: "0px" };
     75         for (var side in padding) {if (padding.hasOwnProperty(side)) {
     76           if ((span.style[side]||"") === "") {span.style[side] = padding[side];}
     77         }}
     78       }
     79       obj.setAttribute("style",span.style.cssText);
     80     }
     81   };
     82  
     83   nMML.Augment({
     84     //
     85     //  User can configure styles
     86     //
     87     config: {
     88       styles: {
     89         ".MathJax_MathML": {
     90           "font-style":      "normal",
     91           "font-weight":     "normal",
     92           "line-height":     "normal",
     93           "font-size":       "100%",
     94           "font-size-adjust":"none",
     95           "text-indent":     0,
     96           "text-align":      "left",
     97           "text-transform":  "none",
     98           "letter-spacing":  "normal",
     99           "word-spacing":    "normal",
    100           "word-wrap":       "normal",
    101           "white-space":     "nowrap",
    102           "float":           "none",
    103           "direction":       "ltr",
    104           "max-width": "none", "max-height": "none",
    105           "min-width": 0, "min-height": 0,
    106           border: 0, padding: 0, margin: 0
    107         },
    108         
    109         "span.MathJax_MathML": {
    110           display: "inline!important"
    111         },
    112         
    113         "div.MathJax_MathML": {
    114           display: "block!important"
    115         },
    116         
    117         ".MathJax_mmlExBox": {
    118           display:"block!important", overflow:"hidden",
    119           height:"1px", width:"60ex",
    120           "min-height": 0, "max-height":"none",
    121           padding:0, border: 0, margin: 0
    122         }
    123       }
    124     },
    125     handlesVariants: false, // true if native support for mathvariants
    126     settings: HUB.config.menuSettings,
    127     ex: 1, scale: 1,  // filled in later
    128     adjustWidths: [], // array of elements to have their widths adjusted
    129     
    130     Config: function () {
    131       this.SUPER(arguments).Config.call(this);
    132       if (this.settings.scale) {this.config.scale = this.settings.scale}
    133       //
    134       //  Insert styling to take account of displayAlign and displayIndent
    135       //
    136       if (HUB.config.displayAlign !== "center") {
    137         var align = HUB.config.displayAlign, indent = HUB.config.displayIndent;
    138         var def = {"text-align": align+"!important"}; def["margin-"+align] = indent+"!important";
    139         HUB.Insert(this.config.styles,{
    140           "div.MathJax_MathML": def,
    141           "div.MathJax_MathML math": {"text-align": align},
    142           "div.MathJax_MathContainer > span": {"text-align": align+"!important"}
    143         });
    144       }
    145       if (!this.require) {this.require = []}
    146       this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js");
    147     },
    148     Startup: function () {
    149       //  Set up event handling
    150       EVENT = MathJax.Extension.MathEvents.Event;
    151       TOUCH = MathJax.Extension.MathEvents.Touch;
    152       HOVER = MathJax.Extension.MathEvents.Hover;
    153       this.ContextMenu = EVENT.ContextMenu;
    154       this.Mousedown   = EVENT.AltContextMenu;
    155       this.Mouseover   = HOVER.Mouseover;
    156       this.Mouseout    = HOVER.Mouseout;
    157       this.Mousemove   = HOVER.Mousemove;
    158 
    159       if (!HUB.Browser.hasMathPlayer) {
    160         // Used in preTranslate to get scaling factors
    161         this.EmExSpan = HTML.Element("span",
    162           {style:{position:"absolute","font-size-adjust":"none"}},
    163           [
    164             ["div",{className:"MathJax_mmlExBox"}],
    165             ["span",{className:"MathJax_MathML"}]
    166           ]
    167         );
    168         MML.math(MML.mspace().With({width:"60ex"})).toNativeMML(this.EmExSpan.lastChild);
    169       }
    170 
    171       //  Set up styles
    172       return AJAX.Styles(this.config.styles);
    173     },
    174     //
    175     //  Set up MathPlayer for IE on the first time through.
    176     //  Get the ex and em sizes.
    177     //
    178     InitializeMML: function () {
    179       this.initialized = true;
    180       if (HUB.Browser.hasMathPlayer) {
    181         try {
    182           //
    183           //  Insert data needed to use MathPlayer for MathML output
    184           //
    185           if (!HUB.Browser.mpNamespace) {
    186             var mathplayer = document.createElement("object");
    187             mathplayer.id = "mathplayer"; mathplayer.classid = "clsid:32F66A20-7614-11D4-BD11-00104BD3F987";
    188             document.getElementsByTagName("head")[0].appendChild(mathplayer);
    189             document.namespaces.add("m","http://www.w3.org/1998/Math/MathML");
    190             HUB.Browser.mpNamespace = true;
    191           }
    192           if (!HUB.Browser.mpImported) {
    193             document.namespaces.m.doImport("#mathplayer");
    194             HUB.Browser.mpImported = true;
    195           }
    196         } catch (err) {
    197           //
    198           //  If that fails, give an alert about security settings
    199           //
    200           if (!this.config.noMathPlayerWarning) {
    201 	    alert(MathJax.Localization._(["MathML", "MathPlayer"],
    202 	          "MathJax was not able to set up MathPlayer.\n\n"+
    203 	          "If MathPlayer is not installed, you need to install it first.\n"+
    204 	          "Otherwise, your security settings may be preventing ActiveX     \n"+
    205 	          "controls from running.  Use the Internet Options item under\n"+
    206 	          "the Tools menu and select the Security tab, then press the\n"+
    207 	          "Custom Level button. Check that the settings for\n"+
    208 	          "'Run ActiveX Controls', and 'Binary and script behaviors'\n"+
    209 	          "are enabled.\n\n"+
    210 	          "Currently you will see error messages rather than\n"+
    211 	          "typeset mathematics."));
    212           }
    213         }
    214       } else {
    215         //
    216         //  Get the default sizes (need styles in place to do this)
    217         //
    218         document.body.appendChild(this.EmExSpan);
    219         this.defaultEx  = this.EmExSpan.firstChild.offsetWidth/60;
    220         this.defaultMEx = this.EmExSpan.lastChild.offsetWidth/60;
    221         document.body.removeChild(this.EmExSpan);
    222       }
    223     },
    224     
    225     preTranslate: function (state) {
    226       var scripts = state.jax[this.id], i, m = scripts.length,
    227           script, prev, span, test, math, jax, ex, mex, scale;
    228       for (i = 0; i < m; i++) {
    229         script = scripts[i]; if (!script.parentNode) continue;
    230 	if (!this.initialized) {this.InitializeMML()}
    231         //
    232         //  Remove any existing output
    233         //
    234         prev = script.previousSibling;
    235         if (prev && prev.className === "MathJax_MathML") {prev.parentNode.removeChild(prev)}
    236         //
    237         //  Add the MathJax span
    238         //
    239         jax = script.MathJax.elementJax; if (!jax) continue;
    240         math = jax.root; jax.NativeMML = {};
    241         var type = (math.Get("display") === "block" ? "div" : "span");
    242 	span = HTML.Element(type,{
    243 	  className: "MathJax_MathML", id:jax.inputID+"-Frame"
    244 	},[["span",{
    245             className:"MathJax_MathContainer", isMathJax: true, jaxID:this.id,
    246             style:{position:"relative", display:"inline-block", "white-space":"nowrap"}
    247           }, [["span",{isMathJax:true, style:{display:"inline-block"}}]] // for Firefox hover and zoom
    248 	]]);
    249         script.parentNode.insertBefore(span,script);
    250         //
    251         //  Add the test span for determining scales
    252         //
    253         if (!isMSIE) {script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script)}
    254       }
    255       //
    256       //  Determine the scaling factors for each script
    257       //  (this only requires one reflow rather than a reflow for each equation)
    258       //
    259       for (i = 0; i < m; i++) {
    260         script = scripts[i]; if (!script.parentNode) continue;
    261         jax = script.MathJax.elementJax; if (!jax) continue;
    262         if (!isMSIE) {
    263           test = script.previousSibling;
    264           ex = test.firstChild.offsetWidth/60;
    265           mex = test.lastChild.offsetWidth/60;
    266           if (ex === 0 || ex === "NaN") {ex = this.defaultEx; mex = this.defaultMEx}
    267           scale = (this.config.matchFontHeight && mex > 1 ? ex/mex : 1);
    268           scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale) * this.config.scale);
    269           jax.NativeMML.ex = ex; jax.NativeMML.mex = mex;
    270         } else {scale = 100}
    271         jax.NativeMML.fontSize = scale+"%";
    272         jax.NativeMML.scale = scale/100;
    273       }
    274       //
    275       //  Remove the test spans used for determining scales
    276       //
    277       if (!isMSIE) {
    278         for (i = 0; i < m; i++) {
    279           script = scripts[i];
    280           if (script.parentNode && script.MathJax.elementJax) {
    281             script.parentNode.removeChild(script.previousSibling);
    282           }
    283         }
    284       }
    285     },
    286 
    287     //
    288     //  Add a SPAN to use as a container, and render the math into it
    289     //  
    290     Translate: function (script) {
    291       if (!script.parentNode) return;
    292       //
    293       //  Get the jax and the container and set the size
    294       //
    295       var jax = script.MathJax.elementJax, math = jax.root;
    296       var span = document.getElementById(jax.inputID+"-Frame"); if (!span) return;
    297       var container = span.firstChild, mspan = container.firstChild;
    298       this.ex = jax.NativeMML.ex || this.defaultEx;
    299       this.scale = jax.NativeMML.scale || 1;
    300       if (this.scale !== 1) {span.style.fontSize = jax.NativeMML.fontSize}
    301       //
    302       //  Convert to MathML (if restarted, remove any partial math)
    303       //
    304       try {math.toNativeMML(mspan,jax)} catch (err) {
    305         if (err.restart) {while (mspan.firstChild) {mspan.removeChild(mspan.firstChild)}}
    306         throw err;
    307       }
    308       //
    309       //  Add event handlers
    310       //
    311       if (isMSIE) {
    312         if (container.addEventListener) {
    313           for (var id in this.MSIE9events) {if (this.MSIE9events.hasOwnProperty(id)) {
    314             container.addEventListener(id,this.MSIE9event,true);
    315           }}
    316         } else {
    317           var config = (this.config.showMathMenuMSIE != null ? this : HUB).config;
    318           if (config.showMathMenuMSIE && !this.settings.mpContext && !this.settings.mpMouse)
    319                 {this.MSIEoverlay(container)} else
    320                 {container.style.position = ""; mspan.firstChild.onmousedown = this.MSIEaltMenu}
    321         }
    322       } else {
    323         container.oncontextmenu = EVENT.Menu;
    324         container.onmouseover   = EVENT.Mouseover;
    325         container.onmouseout    = EVENT.Mouseout;
    326         container.onmousedown   = EVENT.Mousedown;
    327         container.onclick       = EVENT.Click;
    328         container.ondblclick    = EVENT.DblClick;
    329         // Added for keyboard accessible menu.
    330         container.onkeydown = EVENT.Keydown;
    331         container.tabIndex = HUB.getTabOrder(jax);
    332 	if (HUB.Browser.noContextMenu) {
    333 	  container.ontouchstart = TOUCH.start;
    334 	  container.ontouchend   = TOUCH.end;
    335 	}
    336       }
    337     },
    338 
    339     postTranslate: function (state) {
    340       if (this.forceReflow) {
    341         //
    342         //  Firefox messes up some mtable's when they are dynamically created
    343         //  but gets them right on a reflow, so force reflow by toggling a stylesheet
    344         //
    345         var sheet = (document.styleSheets||[])[0]||{};
    346         sheet.disabled = true; sheet.disabled = false;
    347       }
    348     },
    349     
    350     //
    351     //  Remove MathML preceeding the script
    352     //
    353     Remove: function (jax) {
    354       var span = jax.SourceElement(); if (!span) return;
    355       span = span.previousSibling; if (!span) return;
    356       if (span.className.match(/MathJax_MathML/)) {span.parentNode.removeChild(span)}
    357     },
    358     //
    359     //  The namespace to use for MML
    360     //
    361     MMLnamespace: "http://www.w3.org/1998/Math/MathML",
    362 
    363     isFullWidth: function (node) {
    364       if (!node) return;
    365       var width = node.getAttribute("width") ||
    366                   (String(node.getAttribute("style")).match(/(?:^| )width: *([^; ]*)/)||[])[1];
    367       if (width) return !!width.match(/%/);
    368       if (node.nodeName.match(/^(semantics|math|mstyle)$/)) {
    369         width = this.isFullWidth(node.firstChild);
    370       } else if (node.nodeName.toLowerCase() === "mrow") {
    371         for (var i = 0, m = node.childNodes.length; i < m && !width; i++)
    372           width = this.isFullWidth(node.childNodes[i]);
    373       }
    374       if (width) {
    375         var style = "width:100%; "+(node.getAttribute("style")||"");
    376         node.setAttribute("style",style.replace(/ +$/,""));
    377       }
    378       return width;
    379     },
    380     
    381     //
    382     //  For MSIE, we must overlay the MathPlayer object to trap the events
    383     //  (since they can't be cancelled when the events are on the <math> tag
    384     //  itself).  The events we DON'T want are transferred to the math element,
    385     //  and the others are handled directly.
    386     //
    387     MSIEoverlay: function (span) {
    388       var math = span.firstChild;
    389       if (math.nodeName.toLowerCase() === "span") {math = math.firstChild}
    390       var bbox = this.getHoverBBox(null,math,{});
    391       HTML.addElement(span,"span",{
    392         style:{display:"inline-block", width:0, height:0, position:"relative"}
    393       },[["span",{isMathJax: true, className: "MathJax_MathPlayer_Overlay",
    394         style:{
    395           display:"inline-block", position:"absolute",
    396           left:HOVER.Px(-bbox.w), top:HOVER.Px(-bbox.h-(bbox.y||0)-1),
    397           width:HOVER.Px(bbox.w), height:HOVER.Px(bbox.h+bbox.d), cursor:"pointer",
    398           "background-color":"white", filter:"alpha(opacity=0)"
    399         }
    400       }]]);
    401       HUB.Insert(span,{
    402         msieMath: math,
    403         onmousedown: this.MSIEevent, oncontextmenu: this.MSIEevent, onclick: this.MSIEevent,
    404         onmouseup: this.MSIEevent, onmousemove: this.MSIEevent, ondblclick: this.MSIEevent,
    405         onmouseover: this.MSIEevent, onmouseout: this.MSIEevent
    406       });
    407     },
    408     MSIEevents: {
    409       mousedown:"Mousedown", contextmenu:"ContextMenu", click:"Click",
    410       mouseup:"Mouseup", mousemove:"Mousemove", dblclick: "DblClick",
    411       mouseover:"Mouseover", mouseout:"Mouseout"
    412     },
    413     MSIEevent: function () {
    414       var event = window.event;
    415       var type = nMML.MSIEevents[event.type];
    416       if (nMML[type] && nMML[type](event,this) === false) {return false}
    417       if (ZOOM && ZOOM.HandleEvent(event,type,this) === false) {return false}
    418       if (event.srcElement.className === "MathJax_MathPlayer_Overlay" && this.msieMath.fireEvent) {
    419         //
    420         //  For now, ignore all other events.  This will disable MathPlayer's zoom
    421         //  feature, but also its <maction> support.
    422         //
    423         if (type === "ContextMenu" || type === "Mouseover" || type === "Mouseout")
    424           {this.msieMath.fireEvent("on"+event.type,event)}
    425       }
    426       return EVENT.False(event);
    427     },
    428     MSIEaltMenu: function () {
    429       var container = this.parentNode.parentNode;
    430       while (!container.jaxID) {container = container.parentNode}
    431       EVENT.AltContextMenu(window.event,container);
    432     },
    433 
    434     MSIE9events: {
    435       contextmenu:"Menu", click:"Click", dblclick: "DblClick",
    436       mouseup:"False", mouseover:"Mouseover", mouseout:"Mouseout"
    437     },
    438     MSIE9event: function (event) {
    439       if (event.type === "contextmenu" && nMML.settings.mpContext) {return true}
    440       if (event.type === "mouseup" && nMML.settings.mpMouse) {return true}
    441       if (event.type === "click" && nMML.settings.mpContext)
    442         {return EVENT.AltContextMenu(event,this)}
    443       var type = nMML.MSIE9events[event.type];
    444       return EVENT[type].call(this,event);
    445     },
    446 
    447     getJaxFromMath: function (math) {
    448       math = math.parentNode;
    449       do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script");
    450       return HUB.getJaxFor(math);
    451     },
    452     getHoverSpan: function (jax,math) {return math.firstChild},
    453     getHoverBBox: function (jax,span,math) {return EVENT.getBBox(span.parentNode)},
    454 
    455     Zoom: function (jax,span,math,Mw,Mh) {
    456       jax.root.toNativeMML(span);
    457       if (this.msieIE8HeightBug) {span.style.position = "absolute"}
    458       if (nMML.widthBug) {span.style.width = span.parentNode.style.width = ""}
    459       if (span.parentNode.style.width.match(/%$/)) 
    460         {span.parentNode.style.minWidth = Math.ceil(3*Mh/4)+"px"} // for full-width tables
    461       var mW = math.offsetWidth  || math.scrollWidth,
    462           mH = math.offsetHeight || math.scrollHeight;
    463       var zW = span.offsetWidth, zH = span.offsetHeight;
    464       if (nMML.widthBug || span.style.width.match(/%/)) {
    465         //
    466         //  FF doesn't get width of <math> right, so get it from <mrow>
    467         //
    468         var W = span.firstChild.firstChild.scrollWidth;
    469         if (W > zW) {zW = W; span.parentNode.style.width = span.style.minWidth = zW + "px";}
    470       }
    471       if (this.msieIE8HeightBug) {span.style.position = ""}
    472       return {Y:-EVENT.getBBox(span.parentNode).h, mW:mW, mH:mH, zW:zW, zH:zH}
    473     },
    474 
    475     NAMEDSPACE: {
    476       negativeveryverythinmathspace:  "-.0556em",
    477       negativeverythinmathspace:      "-.1111em",
    478       negativethinmathspace:          "-.1667em",
    479       negativemediummathspace:        "-.2222em",
    480       negativethickmathspace:         "-.2778em",
    481       negativeverythickmathspace:     "-.3333em",
    482       negativeveryverythickmathspace: "-.3889em",
    483       veryverythinmathspace:          ".0556em",
    484       verythinmathspace:              ".1111em",
    485       thinmathspace:                  ".1667em",
    486       mediummathspace:                ".2222em",
    487       thickmathspace:                 ".2778em",
    488       verythickmathspace:             ".3333em",
    489       veryverythickmathspace:         ".3889em"
    490     }
    491   });
    492 
    493   HUB.Register.StartupHook("mml Jax Ready",function () {
    494 
    495     MML = MathJax.ElementJax.mml;
    496 
    497     MML.mbase.Augment({
    498       //
    499       //  Add a MathML tag of the correct type, and set its attributes
    500       //    then populate it with its children and append it to the parent
    501       //
    502       toNativeMML: function (parent) {
    503 	var tag = this.NativeMMLelement(this.type);
    504 	this.NativeMMLattributes(tag);
    505 	for (var i = 0, m = this.data.length; i < m; i++) {
    506 	  if (this.data[i]) {this.data[i].toNativeMML(tag)}
    507 	    else {tag.appendChild(this.NativeMMLelement("mrow"))}
    508 	}
    509 	parent.appendChild(tag);
    510       },
    511       //
    512       //  Look for attributes that are different from the defaults
    513       //    and set those in the tag's attribute list
    514       //
    515       NativeMMLattributes: function (tag) {
    516 	var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults);
    517         var names = (this.attrNames||MML.copyAttributeNames),
    518             skip = MML.skipAttributes, copy = MML.copyAttributes;
    519         if (!this.attrNames) {
    520           for (var id in defaults) {if (!skip[id] && !copy[id] && defaults.hasOwnProperty(id)) {
    521 	    if (this[id] != null && this[id] !== defaults[id]) 
    522               tag.setAttribute(id,this.NativeMMLattribute(this[id]));
    523           }}
    524         }
    525 	for (var i = 0, m = names.length; i < m; i++) {
    526           if (copy[names[i]] === 1 && !defaults.hasOwnProperty(names[i])) continue;
    527           var value = (this.attr||{})[names[i]]; if (value == null) {value = this[names[i]]}
    528           if (value != null) {tag.setAttribute(names[i],this.NativeMMLattribute(value))}
    529 	}
    530         this.NativeMMLclass(tag);
    531       },
    532       NativeMMLclass: function (tag) {
    533         var CLASS = []; if (this["class"]) {CLASS.push(this["class"])}
    534         if (this.isa(MML.TeXAtom)) {
    535           var TEXCLASS = ["ORD","OP","BIN","REL","OPEN","CLOSE","PUNCT","INNER","VCENTER"][this.texClass];
    536           if (TEXCLASS) {
    537             CLASS.push("MJX-TeXAtom-"+TEXCLASS)
    538             if (TEXCLASS === "OP" && !this.movablelimits) CLASS.push("MJX-fixedlimits");
    539           }
    540         }
    541         if (this.mathvariant && this.NativeMMLvariants[this.mathvariant])
    542           {CLASS.push("MJX"+this.mathvariant)}
    543         if (this.variantForm) {CLASS.push("MJX-variant")}
    544         if (CLASS.length) {tag.setAttribute("class",CLASS.join(" "))}
    545       },
    546       NativeMMLattribute: function (value) {
    547 	value = String(value);
    548 	if (nMML.NAMEDSPACE[value]) {value = nMML.NAMEDSPACE[value]} // MP doesn't do negative spaces
    549 	else if (value.match(/^\s*(([-+])?(\d+(\.\d*)?|\.\d+))\s*mu\s*$/))
    550           {value = (RegExp.$2||"")+((1/18)*RegExp.$3).toFixed(3).replace(/\.?0+$/,"")+"em"} // FIXME:  should take scriptlevel into account
    551 	else if (this.NativeMMLvariants[value]) {value = this.NativeMMLvariants[value]}
    552 	return value;
    553       },
    554       NativeMMLvariants: {
    555         "-tex-caligraphic":      MML.VARIANT.SCRIPT,
    556         "-tex-caligraphic-bold": MML.VARIANT.BOLDSCRIPT,
    557         "-tex-oldstyle":         MML.VARIANT.NORMAL,
    558         "-tex-oldstyle-bold":    MML.VARIANT.BOLD,
    559         "-tex-mathit":           MML.VARIANT.ITALIC
    560       },
    561       //
    562       //  Create a MathML element
    563       //
    564       NativeMMLelement: function (type) {
    565         var math = ( HUB.Browser.mpNamespace ? document.createElement("m:"+type) :
    566                    (document.createElementNS ? document.createElementNS(nMML.MMLnamespace,type) :
    567                                                document.createElement(type)));
    568         math.isMathJax = true;
    569         return math;
    570       }
    571     });
    572     
    573     MML.mrow.Augment({
    574       //
    575       //  Make inferred rows not include an mrow tag
    576       //
    577       toNativeMML: function (parent) {
    578         var i, m;
    579 	if (this.inferred  && this.parent.inferRow) {
    580 	  for (i = 0, m = this.data.length; i < m; i++) {
    581 	    if (this.data[i]) {this.data[i].toNativeMML(parent)}
    582 	      else {parent.appendChild(this.NativeMMLelement("mrow"))}
    583 	  }
    584 	} else if (nMML.stretchyMoBug && (this.open || this.close)) {
    585           //
    586           // This element contains opening and/or closing fences. Opera is not
    587           // able to stretch <mo> operators, so let's use an <mfenced> element
    588           // instead.
    589           //
    590           var mfenced = this.NativeMMLelement("mfenced");
    591           this.NativeMMLattributes(mfenced);
    592           i = 0, m = this.data.length;
    593           if (this.open) { mfenced.setAttribute("open", this.open); i++; }
    594           if (this.close) { mfenced.setAttribute("close", this.close); m--; }
    595           var tag = mfenced;
    596           if (m - i + 1 > 1) {
    597             //
    598             // If there are several children, put them in an <mrow>
    599             //
    600             tag = this.NativeMMLelement("mrow");
    601 	    parent.appendChild(mfenced);
    602             parent = mfenced;
    603           }
    604           for (; i < m; i++) {
    605 	    if (this.data[i]) {this.data[i].toNativeMML(tag)}
    606 	    else {tag.appendChild(this.NativeMMLelement("mrow"))}
    607 	  }
    608 	  parent.appendChild(tag);
    609         } else {
    610 	  this.SUPER(arguments).toNativeMML.call(this,parent);
    611 	}
    612       }
    613     });
    614 
    615     MML.msubsup.Augment({
    616       //
    617       //  Use proper version of msub, msup, or msubsup, depending on
    618       //  which items are present
    619       //
    620       toNativeMML: function (parent) {
    621 	var type = this.type;
    622 	if (this.data[this.sup] == null) {type = "msub"}
    623 	if (this.data[this.sub] == null) {type = "msup"}
    624 	var tag = this.NativeMMLelement(type);
    625 	this.NativeMMLattributes(tag);
    626 	if (this.data[0]) {delete this.data[0].inferred}
    627 	for (var i = 0, m = this.data.length; i < m; i++)
    628 	  {if (this.data[i]) {this.data[i].toNativeMML(tag)}}
    629 	parent.appendChild(tag);
    630       }
    631     });
    632 
    633     MML.munderover.Augment({
    634       //
    635       //  Use proper version of munder, mover, or munderover, depending on
    636       //  which items are present.  Handle movablelimits on TeXAtom base.
    637       //
    638       toNativeMML: function (parent) {
    639 	var type = this.type;
    640         var base = this.data[this.base];
    641         if (base && base.isa(MML.TeXAtom) && base.movablelimits && !base.Get("displaystyle")) {
    642           type = "msubsup";
    643           if (this.data[this.under] == null) {type = "msup"}
    644           if (this.data[this.over] == null)  {type = "msub"}
    645         } else {
    646           if (this.data[this.under] == null) {type = "mover"}
    647           if (this.data[this.over] == null)  {type = "munder"}
    648         }
    649 	var tag = this.NativeMMLelement(type);
    650 	this.NativeMMLattributes(tag);
    651 	if (this.data[0]) {delete this.data[0].inferred}
    652 	for (var i = 0, m = this.data.length; i < m; i++)
    653 	  {if (this.data[i]) {this.data[i].toNativeMML(tag)}}
    654 	parent.appendChild(tag);
    655       }
    656     });
    657 
    658     if (!isMSIE) {
    659       var SPLIT = HUB.SplitList;
    660       MML.mtable.Augment({
    661         toNativeMML: function (parent) {
    662           var i, m;
    663           if (nMML.tableSpacingBug) {
    664             //
    665             // Parse the rowspacing/columnspacing. For convenience, we convert
    666             // them to a left/top padding value that will be applied to each
    667             // cell. The leftmost/topmost cells will use "0px".
    668             //
    669             var values = this.getValues("rowspacing", "columnspacing");
    670             this.nMMLtopPadding  = SPLIT("0px "+values.rowspacing);
    671             this.nMMLleftPadding = SPLIT("0px "+values.columnspacing);
    672             //
    673             // Transmit the top padding to each row.
    674             // If this.parent.nMML.topPadding does not contain enough value,
    675             // repeat the last one.
    676             //
    677             var tp = this.nMMLtopPadding, M = tp.length;
    678             for (i = 0, m = this.data.length; i < m; i++) {
    679               if (this.data[i])
    680                 {this.data[i].nMMLtopPadding = tp[i < M ? i : M-1]}
    681             }
    682           }
    683           if (nMML.tableLabelBug) {
    684             //
    685             //  Look for labeled rows so we know how to handle them
    686             //
    687             for (i = 0, m = this.data.length; i < m; i++) {
    688               if (this.data[i] && this.data[i].isa(MML.mlabeledtr)) {
    689                 var align = HUB.config.displayAlign.charAt(0),
    690                     side = this.Get("side").charAt(0);
    691                 this.nMMLhasLabels = true;
    692                 this.nMMLlaMatch = (align === side);
    693                 this.nMMLforceWidth =
    694                   (align === "c" || !!((this.width||"").match("%")));
    695                 break;
    696               }
    697             }
    698           }
    699           //
    700           //  Firefox < 13 doesn't handle width, so put it in styles instead
    701           //
    702           if (this.width && this.ffTableWidthBug) {
    703             var styles = (this.style||"").replace(/;\s*$/,"").split(";");
    704             if (styles[0] === "") {styles.shift()}
    705             styles.push("width:"+this.width);
    706             this.style = styles.join(";");
    707           }
    708           this.SUPER(arguments).toNativeMML.call(this,parent);
    709           //
    710           if (this.nMMLhasLabels) {
    711             var mtable = parent.firstChild;
    712             //
    713             //  Add column attributes on the left when extra columns where inserted
    714             //
    715             if (this.nMMLforceWidth || side !== "r") {
    716               var n = (align !== "l" ? 1 : 0) + (side === "l" ? 1 : 0);
    717               if (n) {
    718                 var attr = {columnalign:"left", columnwidth:"auto",
    719                             columnspacing:"0px", columnlines:"none"};
    720                 for (var id in attr) {if (attr.hasOwnProperty(id) && this[id]) {
    721                   var cols = [attr[id],attr[id]].slice(2-n).join(" ")+" ";
    722                   mtable.setAttribute(id,cols+mtable.getAttribute(id));
    723                 }}
    724               }
    725             }
    726             //
    727             //  Force the table width to 100% when needed
    728             //
    729             if (this.nMMLforceWidth || !this.nMMLlaMatch)
    730               {mtable.setAttribute("width","100%")}
    731           }
    732         }
    733       });
    734       MML.mtr.Augment({
    735         toNativeMML: function (parent) {
    736           this.SUPER(arguments).toNativeMML.call(this,parent);
    737           var mtr = parent.lastChild;
    738           if (nMML.tableSpacingBug) {
    739             //
    740             // Set the row/column spacing. If this.parent.nMMLleftPadding does
    741             // not contain enough value, repeat the last one.
    742             //
    743             var lp = this.parent.nMMLleftPadding, M = lp.length;
    744             for (var mtd = mtr.firstChild, i = 0; mtd; mtd = mtd.nextSibling, i++) {
    745               CELLSPACING(mtd,this.nMMLtopPadding,lp[i < M ? i : M-1]);
    746             }
    747           }
    748 
    749           if (nMML.tableLabelBug) {
    750             var forceWidth = this.parent.nMMLforceWidth,
    751                 side = this.parent.Get("side").charAt(0),
    752                 align = HUB.config.displayAlign.charAt(0);
    753 
    754             if (this.parent.nMMLhasLabels && mtr.firstChild) {
    755               //
    756               //  If we add a label or padding column on the left of mlabeledtr,
    757               //    mirror that here and remove padding from first table mtd
    758               //    so the spacing is consistent with unlabeled equations
    759               //
    760               if (forceWidth || side !== "r") {
    761                 NOPADDING("Left",mtr.firstChild);
    762                 if (align !== "l") {
    763                   mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild)
    764                      .setAttribute("style","padding:0");
    765                 }
    766                 if (side === "l") {
    767                   mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild)
    768                      .setAttribute("style","padding:0");
    769                 }
    770               }
    771               //
    772               //  If columns were added on the right, remove mtd padding
    773               //    so that spacing is consistent with unlabled equations
    774               //
    775               if (forceWidth || side !== "l") {NOPADDING("Right",mtr.lastChild)}
    776             }
    777           }
    778         }
    779       });
    780       MML.mlabeledtr.Augment({
    781         toNativeMML: function (parent) {
    782           var mtr = this.NativeMMLelement("mtr");
    783           this.NativeMMLattributes(mtr);
    784           //
    785           //  Add row data
    786           //
    787           for (var i = 1, m = this.data.length; i < m; i++) {
    788             if (this.data[i]) {this.data[i].toNativeMML(mtr)}
    789               else {mtr.appendChild(this.NativeMMLelement("mtd"))}
    790           }
    791 
    792           if (nMML.tableSpacingBug) {
    793             //
    794             // Set the row/column spacing. If this.parent.nMMLleftPadding does
    795             // not contain enough value, repeat the last one.
    796             //
    797             var lp = this.parent.nMMLleftPadding, M = lp.length; i = 0;
    798             for (var mtd = mtr.firstChild; mtd; mtd = mtd.nextSibling, i++) {
    799               CELLSPACING(mtd,this.nMMLtopPadding,lp[i < M ? i : M-1]);
    800             }
    801           }
    802 
    803           if (nMML.tableLabelBug && this.data[0]) {
    804             var side = this.parent.Get("side").charAt(0),
    805                 align = HUB.config.displayAlign.charAt(0),
    806                 indent = HUB.config.displayIndent;
    807             //
    808             // Create label and either set the column width (if label is on the
    809             // same side as the alignment), or use mpadded to hide the label
    810             // width
    811             //
    812             this.data[0].toNativeMML(mtr);
    813             var label = mtr.lastChild, pad = label;
    814             if (side === align) {
    815               label.setAttribute("style","width:"+indent);
    816               label.setAttribute("columnalign",HUB.config.displayAlign);
    817             } else {
    818               pad = this.NativeMMLelement("mpadded");
    819               pad.setAttribute("style","width:0");
    820               pad.setAttribute("width","0px");
    821               pad.appendChild(label.firstChild);
    822               label.appendChild(pad);
    823             }
    824             NOPADDING("",label); mtr.removeChild(label);
    825             //
    826             //  Get spacing to use for separation of label from main table
    827             //
    828             var width = 100, forceWidth = this.parent.nMMLforceWidth;
    829             if ((this.parent.width||"").match(/%/)) {
    830               width -= parseFloat(this.parent.width)
    831             };
    832             var w = width;
    833             //
    834             //  Add spacing (and possibly label) at the left if needed
    835             //
    836             if (forceWidth || side !== "r") {
    837               NOPADDING("Left",mtr.firstChild);
    838               if (align !== "l") {
    839                 if (align === "c") {w /= 2}; width -= w;
    840                 mtr.insertBefore(this.NativeMMLelement("mtd"),mtr.firstChild)
    841                    .setAttribute("style","padding:0;width:"+w+"%");
    842               }
    843               if (side === "l") {mtr.insertBefore(label,mtr.firstChild)}
    844             }
    845             //
    846             //  Add spacing (and possibly label) at the right if needed
    847             //
    848             if (forceWidth || side !== "l") {
    849               NOPADDING("Right",mtr.lastChild);
    850               if (align !== "r") {
    851                 mtr.appendChild(this.NativeMMLelement("mtd"))
    852                    .setAttribute("style","padding:0;width:"+width+"%");
    853               }
    854               if (side === "r") {
    855                 if (side !== align) {pad.setAttribute("lspace","-1width")}
    856                 mtr.appendChild(label);
    857               }
    858             }
    859           }
    860           //
    861           //  Add row to table
    862           //
    863           parent.appendChild(mtr);
    864         }
    865       });
    866       
    867       MML.mtd.Augment({
    868         toNativeMML: function (parent) {
    869           var tag = parent.appendChild(this.NativeMMLelement(this.type));
    870           this.NativeMMLattributes(tag);
    871           if (nMML.mtdWidthBug) {
    872             nMML.adjustWidths.push(tag);
    873             tag = tag.appendChild(this.NativeMMLelement("mrow"));
    874           }
    875           for (var i = 0, m = this.data.length; i < m; i++) {
    876             if (this.data[i]) {this.data[i].toNativeMML(tag)}
    877              else {tag.appendChild(this.NativeMMLelement("mrow"))}
    878           }
    879         }
    880       });
    881       
    882       MML.mspace.Augment({
    883         toNativeMML: function (parent) {
    884           this.SUPER(arguments).toNativeMML.call(this,parent);
    885           if (nMML.spaceWidthBug && this.width) {
    886             var mspace = parent.lastChild;
    887             var width = mspace.getAttribute("width");
    888             var style = (mspace.getAttribute("style") || "").replace(/;?\s*/,"; ");
    889             mspace.setAttribute("style",style+"width:"+width);
    890           }
    891         }
    892       });
    893 
    894       var fontDir = AJAX.fileURL(MathJax.OutputJax.fontDir+"/HTML-CSS/TeX/otf");
    895 
    896       /*
    897        *  Add fix for mathvariant issues
    898        */
    899       nMML.Augment({
    900         config: {
    901           styles: {
    902             '[class="MJX-tex-oldstyle"]':             {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB"},
    903             '[class="MJX-tex-oldstyle-bold"]':        {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB", "font-weight":"bold"},
    904             '[class="MJX-tex-caligraphic"]':          {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB"},
    905             '[class="MJX-tex-caligraphic-bold"]':     {"font-family":"MathJax_Caligraphic, MathJax_Caligraphic-WEB", "font-weight":"bold"},
    906 
    907             '@font-face /*1*/': {
    908               "font-family": "MathJax_Caligraphic-WEB",
    909               "src": "url('"+fontDir+"/MathJax_Caligraphic-Regular.otf')"
    910             },
    911             '@font-face /*2*/': {
    912               "font-family": "MathJax_Caligraphic-WEB", "font-weight":"bold",
    913               "src": "url('"+fontDir+"/MathJax_Caligraphic-Bold.otf')"
    914             }
    915           }
    916         }
    917       });
    918       if (!this.handlesVariants) {
    919         nMML.Augment({
    920           config: {
    921             styles: {
    922               '[mathvariant="double-struck"]':          {"font-family":"MathJax_AMS, MathJax_AMS-WEB"},
    923               '[mathvariant="script"]':                 {"font-family":"MathJax_Script, MathJax_Script-WEB"},
    924               '[mathvariant="fraktur"]':                {"font-family":"MathJax_Fraktur, MathJax_Fraktur-WEB"},
    925               '[mathvariant="bold-script"]':            {"font-family":"MathJax_Script, MathJax_Caligraphic-WEB", "font-weight":"bold"},
    926               '[mathvariant="bold-fraktur"]':           {"font-family":"MathJax_Fraktur, MathJax_Fraktur-WEB", "font-weight":"bold"},
    927               '[mathvariant="monospace"]':              {"font-family":"monospace"},
    928               '[mathvariant="sans-serif"]':             {"font-family":"sans-serif"},
    929               '[mathvariant="bold-sans-serif"]':        {"font-family":"sans-serif", "font-weight":"bold"},
    930               '[mathvariant="sans-serif-italic"]':      {"font-family":"sans-serif", "font-style":"italic"},
    931               '[mathvariant="sans-serif-bold-italic"]': {"font-family":"sans-serif", "font-style":"italic", "font-weight":"bold"},
    932 
    933               '@font-face /*3*/': {
    934                 "font-family": "MathJax_AMS-WEB",
    935                 "src": "url('"+fontDir+"/MathJax_AMS-Regular.otf')"
    936               },
    937               '@font-face /*4*/': {
    938                 "font-family": "MathJax_Script-WEB",
    939                 "src": "url('"+fontDir+"/MathJax_Script-Regular.otf')"
    940               },
    941               '@font-face /*5*/': {
    942                 "font-family": "MathJax_Fraktur-WEB",
    943                 "src": "url('"+fontDir+"/MathJax_Fraktur-Regular.otf')"
    944               },
    945               '@font-face /*6*/': {
    946                 "font-family": "MathJax_Fraktur-WEB", "font-weight":"bold",
    947                 "src": "url('"+fontDir+"/MathJax_Fraktur-Bold.otf')"
    948               }
    949             }
    950           }
    951         });
    952       }
    953     }
    954     
    955     MML.math.Augment({
    956       toNativeMML: function (parent,jax) {
    957         var tag = this.NativeMMLelement(this.type), math = tag;
    958         var annotate = (jax ? MathJax.InputJax[jax.inputJax].annotationEncoding : null);
    959         var i, m;
    960         nMML.adjustWidths = [];
    961         //
    962         //  Some browsers don't seem to add the xmlns attribute, so do it by hand.
    963         //
    964         tag.setAttribute("xmlns",nMML.MMLnamespace);
    965         this.NativeMMLattributes(tag);
    966         //
    967         //  Use an extra <mrow> in FF so that we can get the correct width
    968         //    (the math element doesn't always have an accurate one, see below)
    969         //
    970         if (nMML.widthBug) {tag = tag.appendChild(this.NativeMMLelement("mrow"))}
    971         //
    972         //  Addannotation if the input jax provides an annotation encoding
    973         //
    974         if (annotate) {
    975           tag = tag.appendChild(this.NativeMMLelement("semantics"))
    976           tag.appendChild(this.NativeMMLelement("mrow"));
    977           var annotation = tag.appendChild(this.NativeMMLelement("annotation"));
    978           annotation.appendChild(document.createTextNode(jax.originalText));
    979           annotation.setAttribute("encoding",annotate);
    980           tag = tag.firstChild; // mrow
    981         }
    982         //
    983         //  Add the children
    984         //
    985         for (i = 0, m = this.data.length; i < m; i++) {
    986           if (this.data[i]) {this.data[i].toNativeMML(tag)}
    987             else {tag.appendChild(this.NativeMMLelement("mrow"))}
    988         }
    989         //
    990         //  Look for a top-level mtable and if it has labels
    991         //    Make sure the containers have 100% width, when needed.
    992         //    If the label is on the same side as alignment,
    993         //      override the margin set by the stylesheet.
    994         //
    995         var mtable = ((this.data[0]||{data:[]}).data[0]||{});
    996         if (mtable.nMMLhasLabels) {
    997           if (mtable.nMMLforceWidth || !mtable.nMMLlaMatch) {
    998             tag.setAttribute("style","width:100%")  // mrow node
    999             if (annotate) tag.parentNode.setAttribute("style","width:100%"); // semantics node
   1000           };
   1001           if (mtable.nMMLlaMatch) {
   1002             if (parent.parentNode.parentNode.nodeName.toLowerCase() === "div") {
   1003               parent.parentNode.parentNode.style
   1004                 .setProperty("margin-"+HUB.config.displayAlign,"0px","important");
   1005             }
   1006           }
   1007         }
   1008         //
   1009         //  Check if container must have width set to 100%
   1010         //
   1011         var fullWidth = nMML.isFullWidth(math);
   1012         if (fullWidth) {parent.style.width = parent.parentNode.style.width = "100%"}
   1013         //
   1014         //  Add the math to the page
   1015         //
   1016         parent.appendChild(math);
   1017         //
   1018         //  Firefox can't seem to get the width of <math> elements right, so
   1019         //  use an <mrow> to get the actual width and set the style on the 
   1020         //  parent element to match.  Even if we set the <math> width properly,
   1021         //  it doesn't seem to propagate up to the <span> correctly.
   1022         //
   1023         if (nMML.widthBug &&!fullWidth) {
   1024           //
   1025           //  Convert size to ex's so that it scales properly if the print media
   1026           //    has a different font size.
   1027           //
   1028           parent.style.width = (math.firstChild.scrollWidth/nMML.ex/nMML.scale).toFixed(3) + "ex";
   1029           //
   1030           //  Save size for later when we check if Web fonts have arrived
   1031           //
   1032           if (jax) {jax.NativeMML.scrollWidth = math.firstChild.scrollWidth}
   1033         }
   1034         if (nMML.adjustWidths.length) {
   1035           //
   1036           //  Firefox gets the widths of <mtd> elements wrong, so run
   1037           //  through them (now that the math is part of the page) and
   1038           //  fix them up.  Use ex's so that they print properly (see above).
   1039           //
   1040           var mtd = [];
   1041           for (i = 0, m = nMML.adjustWidths.length; i < m; i++) {
   1042             tag = nMML.adjustWidths[i];
   1043             var style = tag.getAttribute("style") || "";
   1044             if (!style.match(/(^|;)\s*min-width:/)) {
   1045               var width = tag.firstChild.scrollWidth;
   1046               mtd.push(width);
   1047               width = (width/nMML.ex).toFixed(3)+"ex";
   1048               style = style.replace(/;?\s*$/,"; ");
   1049               tag.setAttribute("style",style+"min-width:"+width);
   1050             }
   1051           }
   1052           //
   1053           //  Save the lists so that we can check them later for web font downloads
   1054           //
   1055           if (!jax) {jax = HUB.getJaxFor(parent)}
   1056           if (jax) {jax.NativeMML.mtds = mtd}
   1057           math.MathJaxMtds = nMML.adjustWidths;
   1058           nMML.adjustWidths = []; // clear it so we don't hold onto the DOM elements
   1059         }
   1060       }
   1061     });
   1062 
   1063     MML.mfenced.Augment({
   1064       toNativeMML: function (parent) {
   1065         if (!nMML.mfencedBug) {
   1066 	  this.SUPER(arguments).toNativeMML.call(this,parent);
   1067           return;
   1068         }
   1069 
   1070         //
   1071         // Some browsers do not handle <mfenced> very well. The MathML spec
   1072         // suggests this equivalent construction instead, so let's use it:
   1073         // <mrow> open, child1, sep1, child2, ... sep(N-1), childN, close</mrow>
   1074         // Opera is a bit special: it does not support stretchy <mo>, does not
   1075         // parse mfenced@open/mfenced@close very well, does not support
   1076         // mfenced@separators and only displays the first child of the <mfenced>
   1077         // element... For this browser, we will use this construction:
   1078         // <mfenced open="open" close="close">
   1079         //   <mrow>child1, sep1, child2, sep2, ..., sep(N-1), childN</mrow>
   1080         // </mfenced>
   1081         //
   1082         var isOpera = HUB.Browser.isOpera;
   1083         var i, m, operator;
   1084 
   1085         //
   1086         // parse the open, close and separators attributes.
   1087         //
   1088         var values = this.getValues("open","close","separators");
   1089         values.open = values.open.replace(/^\s+/,"").replace(/\s+$/,"");
   1090         values.close = values.close.replace(/^\s+/,"").replace(/\s+$/,"");
   1091         values.separators = values.separators.replace(/\s+/g,"").split("");
   1092         if (values.separators.length == 0) {
   1093           //
   1094           // No separators specified, do not use separators at all.
   1095           //
   1096           values.separators = null;
   1097         } else if (values.separators.length < this.data.length-1) {
   1098           //
   1099           // There are not enough separators, repeat the last one.
   1100           //
   1101           var s = values.separators[values.separators.length-1];
   1102           for (i = this.data.length-1-values.separators.length; i > 0; i--)
   1103             {values.separators.push(s)}
   1104         }
   1105 
   1106         //
   1107         // Create an <mrow> container and attach the attributes of the
   1108         // <mfenced> element to it. Note: removeAttribute does not raise any
   1109         // exception when the attributes is absent.
   1110         //
   1111         var tag = this.NativeMMLelement(isOpera ? this.type : "mrow");
   1112         this.NativeMMLattributes(tag);
   1113         tag.removeAttribute("separators");
   1114         if (isOpera) {
   1115           tag.setAttribute("open", values.open);
   1116           tag.setAttribute("close", values.close);
   1117           if (this.data.length > 1) {
   1118             parent.appendChild(tag); parent = tag;
   1119             tag = this.NativeMMLelement("mrow");
   1120           }
   1121         } else {
   1122           tag.removeAttribute("open");
   1123           tag.removeAttribute("close");
   1124         }
   1125 
   1126         if (!isOpera) {
   1127           //
   1128           // Append the opening fence
   1129           // 
   1130           operator = this.NativeMMLelement("mo");
   1131           operator.setAttribute("fence", "true");
   1132           operator.textContent = values.open;
   1133           tag.appendChild(operator);
   1134         }
   1135 
   1136         //
   1137         // Append the content of the <mfenced>
   1138         //
   1139         for (i = 0, m = this.data.length; i < m; i++) {
   1140           if (values.separators && i > 0) {
   1141             operator = this.NativeMMLelement("mo");
   1142             operator.setAttribute("separator", "true");
   1143             operator.textContent = values.separators[i-1];
   1144             tag.appendChild(operator);
   1145           }
   1146 	  if (this.data[i]) {this.data[i].toNativeMML(tag)}
   1147             else {tag.appendChild(this.NativeMMLelement("mrow"))}
   1148         }
   1149 
   1150         if (!isOpera) {
   1151           //
   1152           // Append the closing fence
   1153           //
   1154           operator = this.NativeMMLelement("mo");
   1155           operator.setAttribute("fence", "true");
   1156           operator.textContent = values.close;
   1157           tag.appendChild(operator);
   1158         }
   1159 
   1160         // finally, append the new element to the parent.
   1161         parent.appendChild(tag);
   1162       }
   1163     });
   1164 
   1165     MML.TeXAtom.Augment({
   1166       //
   1167       //  Convert TeXatom to an mrow
   1168       //
   1169       toNativeMML: function (parent) {
   1170 	// FIXME:  Handle spacing using mpadded?
   1171 	var tag = this.NativeMMLelement("mrow");
   1172 	this.NativeMMLattributes(tag);
   1173 	this.data[0].toNativeMML(tag);
   1174 	parent.appendChild(tag);
   1175       }
   1176     });
   1177 
   1178     MML.chars.Augment({
   1179       //
   1180       //  Add a text node
   1181       //
   1182       toNativeMML: function (parent) {
   1183 	parent.appendChild(document.createTextNode(this.toString()));
   1184       }
   1185     });
   1186 
   1187     MML.entity.Augment({
   1188       //
   1189       //  Add a text node
   1190       //
   1191       toNativeMML: function (parent) {
   1192 	parent.appendChild(document.createTextNode(this.toString()));
   1193       }
   1194     });
   1195     
   1196     MML.xml.Augment({
   1197       //
   1198       //  Insert the XML verbatim
   1199       //
   1200       toNativeMML: function (parent) {
   1201         for (var i = 0, m = this.data.length; i < m; i++)
   1202           {parent.appendChild(this.data[i].cloneNode(true))}
   1203       }
   1204     });
   1205 
   1206     MML.mi.Augment({
   1207       toNativeMML: function (parent) {
   1208         this.SUPER(arguments).toNativeMML.call(this,parent);
   1209         if (nMML.miItalicBug) {
   1210           if (this.Get("mathvariant") === MML.VARIANT.NORMAL) {
   1211             //
   1212             // When not explicitly specified, mathvariant is set to "italic"
   1213             // with single char mi and to "normal" with multiple char mi.
   1214             // Some browsers always set the default to "italic", so let's
   1215             // attach an explicit mathvariant="normal" attribute.
   1216             //
   1217             var mi = parent.lastChild;
   1218             mi.setAttribute("mathvariant",MML.VARIANT.NORMAL);
   1219           }
   1220         }
   1221       }
   1222     });
   1223 
   1224     MML.mo.Augment({
   1225       toNativeMML: function (parent) {
   1226         this.SUPER(arguments).toNativeMML.call(this,parent);
   1227         if (nMML.webkitMoSpacingBug) {
   1228           //
   1229           // WebKit does not support lspace/rspace values around operators
   1230           // (neither explicit nor given by the operator dictionary) and uses
   1231           // constant values instead. So let's modify the CSS properties here.
   1232           //
   1233 
   1234           var lspace = 0, rspace = 0, p = this.parent;
   1235           if (p && p.type === "mrow" && (p.inferred || !p.isEmbellished())) {
   1236             //
   1237             // Retrieve the values of lspace/rspace and convert named spaces.
   1238             // Other values (except unitless) will be parsed by the CSS engine.
   1239             //
   1240             var values = this.getValues("lspace", "rspace");
   1241             lspace = values.lspace, rspace = values.rspace;
   1242             if (nMML.NAMEDSPACE[lspace]) {lspace = nMML.NAMEDSPACE[lspace]}
   1243             if (nMML.NAMEDSPACE[rspace]) {rspace = nMML.NAMEDSPACE[rspace]}
   1244           }
   1245 
   1246           //
   1247           // Now update -webkit-margin-start and -webkit-margin-end.
   1248           //
   1249           var mo = parent.lastChild;
   1250           var span = HTML.Element("span");
   1251           span.style.cssText = (mo.getAttribute("style")||"");
   1252           span.style.setProperty("-webkit-margin-start", lspace);
   1253           span.style.setProperty("-webkit-margin-end", rspace);
   1254           mo.setAttribute("style",span.style.cssText);
   1255         }
   1256       }
   1257     });
   1258 
   1259     MML.mmultiscripts.Augment({
   1260       toNativeMML: function (parent) {
   1261         //
   1262         // Some browsers do not implement the mmultiscripts element.
   1263         // Try to emulate the support using basic script elements.
   1264         //
   1265         if (!nMML.mmultiscriptsBug || this.data.length === 0) {
   1266           this.SUPER(arguments).toNativeMML.call(this,parent);
   1267           return;
   1268         }
   1269 
   1270         //
   1271         // The children of the mmultiscripts will be wrapped in an mrow so that
   1272         // attributes and properties set on the original mmultiscripts will
   1273         // be reflected on this mrow element.
   1274         //
   1275         var tag = this.NativeMMLelement("mrow");
   1276         this.NativeMMLattributes(tag);
   1277 
   1278         //
   1279         // Create the base
   1280         //
   1281         if (this.data[0]) {this.data[0].toNativeMML(tag)}
   1282         else {tag.appendChild(this.NativeMMLelement("mrow"))}
   1283         var base = tag.removeChild(tag.lastChild);
   1284 
   1285         //
   1286         // Process the postscript pairs
   1287         //
   1288         var m = this.data.length, i, msubsup;
   1289         for (i = 1; i < m; i+=2) {
   1290           if (this.data[i].type === "mprescripts") break;
   1291 
   1292           msubsup = this.NativeMMLelement("msubsup");
   1293           msubsup.appendChild(base);
   1294 
   1295           //
   1296           // append the subscript
   1297           //
   1298           if (this.data[i]) {this.data[i].toNativeMML(msubsup)}
   1299           else {msubsup.appendChild(this.NativeMMLelement("mrow"))}
   1300 
   1301           //
   1302           // append the supscript
   1303           //
   1304           if (i+1 < m && this.data[i+1]) {this.data[i+1].toNativeMML(msubsup)}
   1305           else {msubsup.appendChild(this.NativeMMLelement("mrow"))}
   1306 
   1307           base = msubsup;
   1308         }
   1309 
   1310         tag.appendChild(base);
   1311 
   1312         //
   1313         // Process the prescript pairs
   1314         //
   1315         for (i++; i < m; i+=2) {
   1316           msubsup = this.NativeMMLelement("msubsup");
   1317           msubsup.appendChild(this.NativeMMLelement("mrow"));
   1318 
   1319           //
   1320           // append the presubscript
   1321           //
   1322           if (this.data[i]) {this.data[i].toNativeMML(msubsup)}
   1323           else {msubsup.appendChild(this.NativeMMLelement("mrow"))}
   1324 
   1325           //
   1326           // append the presupscript
   1327           //
   1328           if (i+1 < m && this.data[i+1]) {this.data[i+1].toNativeMML(msubsup)}
   1329           else {msubsup.appendChild(this.NativeMMLelement("mrow"))}
   1330 
   1331           tag.insertBefore(msubsup, base);
   1332         }
   1333 
   1334         parent.appendChild(tag);
   1335       }
   1336     });
   1337 
   1338     HUB.Register.StartupHook("TeX mathchoice Ready",function () {
   1339       MML.TeXmathchoice.Augment({
   1340 	//
   1341 	//  Get the MathML for the selected choice
   1342 	//
   1343 	toNativeMML: function (parent) {this.Core().toNativeMML(parent)}
   1344       });
   1345     });
   1346 
   1347     //
   1348     //  Loading isn't complete until the element jax is modified,
   1349     //  but can't call loadComplete within the callback for "mml Jax Ready"
   1350     //  (it would call NativeMML's Require routine, asking for the mml jax again)
   1351     //  so wait until after the mml jax has finished processing.
   1352     //
   1353     setTimeout(MathJax.Callback(["loadComplete",nMML,"jax.js"]),0);
   1354   });
   1355   
   1356 
   1357   //
   1358   //  Determine browser characteristics
   1359   //
   1360   HUB.Browser.Select({
   1361     MSIE: function (browser) {
   1362       var mode = (document.documentMode || 0);
   1363       nMML.msieIE8HeightBug = (mode === 8);
   1364     },
   1365     Opera: function (browser) {
   1366       nMML.stretchyMoBug = true;
   1367       nMML.tableLabelBug = true;
   1368       nMML.mfencedBug = true;
   1369       nMML.miBug = true;
   1370       nMML.mmultiscriptsBug = true;
   1371     },
   1372     Firefox: function (browser) {
   1373       var is29 = browser.versionAtLeast("29.0");
   1374       nMML.ffTableWidthBug = !browser.versionAtLeast("13.0"); // <mtable width="xx"> not implemented
   1375       nMML.forceReflow = !is29;    // <mtable> with alignments set don't display properly without a reflow
   1376       nMML.widthBug = !is29;       // <math> elements don't always get the correct width
   1377       nMML.mtdWidthBug = true;     // <mtd> widths not properly determined
   1378       nMML.handlesVariants = is29; // FF >=29 handles all math variants
   1379 
   1380       // In Firefox < 20, the intrinsic width of <mspace> is not computed
   1381       // correctly and thus the element is displayed incorrectly in <mtable>.
   1382       nMML.spaceWidthBug = !browser.versionAtLeast("20.0");
   1383 
   1384       // mtable@rowspacing/mtable@columnspacing not supported.
   1385       nMML.tableSpacingBug = !browser.versionAtLeast("33.0");
   1386       nMML.tableLabelBug = true;   // mlabeledtr is not implemented.
   1387       nMML.mfencedBug = true;      // mfenced not displayed correctly
   1388     },
   1389     Chrome: function (browser) {
   1390       nMML.tableSpacingBug = true;
   1391       nMML.tableLabelBug = true;
   1392       nMML.mfencedBug = true;
   1393     },
   1394     Safari: function (browser) {
   1395       nMML.tableSpacingBug = true;
   1396       nMML.tableLabelBug = true;
   1397       nMML.mfencedBug = true;
   1398       nMML.miItalicBug = true;
   1399       nMML.webkitMoSpacingBug = true;
   1400       nMML.spaceWidthBug = true;
   1401       nMML.mmultiscriptsBug = true;
   1402     }
   1403   });
   1404   
   1405 
   1406   HUB.Register.StartupHook("End Cookie",function () {
   1407     if (HUB.config.menuSettings.zoom !== "None")
   1408       {AJAX.Require("[MathJax]/extensions/MathZoom.js")}
   1409   });
   1410 
   1411 })(MathJax.OutputJax.NativeMML, MathJax.Hub, MathJax.Ajax, MathJax.HTML);