www

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

jax.js (108246B)


      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/CommonHTML/jax.js
      7  *
      8  *  Implements the CommonHTML OutputJax that displays mathematics
      9  *  using HTML and CSS to position the characters from math fonts
     10  *  in their proper locations.  Unlike the HTML-CSS output jax,
     11  *  this HTML is browswer and OS independent.
     12  *  
     13  *  ---------------------------------------------------------------------
     14  *  
     15  *  Copyright (c) 2013-2015 The MathJax Consortium
     16  * 
     17  *  Licensed under the Apache License, Version 2.0 (the "License");
     18  *  you may not use this file except in compliance with the License.
     19  *  You may obtain a copy of the License at
     20  * 
     21  *      http://www.apache.org/licenses/LICENSE-2.0
     22  * 
     23  *  Unless required by applicable law or agreed to in writing, software
     24  *  distributed under the License is distributed on an "AS IS" BASIS,
     25  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     26  *  See the License for the specific language governing permissions and
     27  *  limitations under the License.
     28  */
     29 
     30 
     31 (function (AJAX,HUB,HTML,CHTML) {
     32   var MML;
     33 
     34   var EVENT, TOUCH, HOVER; // filled in later
     35 
     36   var STRUTHEIGHT = 1,
     37       EFUZZ = .1,                  // overlap needed for stretchy delimiters
     38       HFUZZ = .025, DFUZZ = .025;  // adjustments to bounding box of character boxes
     39 
     40   var STYLES = {
     41     ".mjx-chtml": {
     42       display:           "inline-block",
     43       "line-height":     0,
     44       "text-indent":     0,
     45       "text-align":      "left",
     46       "text-transform":  "none",
     47       "font-style":      "normal",
     48       "font-weight":     "normal",
     49       "font-size":       "100%",
     50       "font-size-adjust":"none",
     51       "letter-spacing":  "normal",
     52       "word-wrap":       "normal",
     53       "word-spacing":    "normal",
     54       "white-space":     "nowrap",
     55       "float":           "none",
     56       "direction":       "ltr",
     57       "max-width":       "none",
     58       "max-height":      "none",
     59       "min-width":       0,
     60       "min-height":      0,
     61       border:            0,
     62       margin:            0,
     63       padding:           "1px 0"
     64     },
     65     ".MJXc-display": {
     66       display:      "block",
     67       "text-align": "center",
     68       "margin":     "1em 0",
     69       padding:      0
     70     },
     71     ".mjx-chtml[tabindex]:focus, body :focus .mjx-chtml[tabindex]": {
     72       display: "inline-table"  // see issues #1282 and #1338
     73     },
     74 
     75     ".mjx-math":   {
     76       "display":         "inline-block",
     77       "border-collapse": "separate",
     78       "border-spacing":  0,
     79     },
     80     ".mjx-math *": {display:"inline-block", "text-align":"left"},
     81 
     82     ".mjx-numerator":   {display:"block", "text-align":"center"},
     83     ".mjx-denominator": {display:"block", "text-align":"center"},
     84     ".MJXc-stacked":    {height:0, position:"relative"},
     85     ".MJXc-stacked > *":  {position: "absolute"},
     86     ".MJXc-bevelled > *": {display:"inline-block"},
     87     
     88     ".mjx-stack":  {display:"inline-block"},
     89     ".mjx-op":     {display:"block"},
     90     ".mjx-under":  {display:"table-cell"},
     91     ".mjx-over":   {display:"block"},
     92     ".mjx-over > *": {"padding-left":"0px!important", "padding-right":"0px!important"},
     93     ".mjx-under > *": {"padding-left":"0px!important", "padding-right":"0px!important"},
     94     
     95     ".mjx-stack > .mjx-sup": {display:"block"},
     96     ".mjx-stack > .mjx-sub": {display:"block"},
     97     ".mjx-prestack > .mjx-presup": {display:"block"},
     98     ".mjx-prestack > .mjx-presub": {display:"block"},
     99     
    100     ".mjx-delim-h > .mjx-char": {display:"inline-block"},
    101     
    102     ".mjx-surd": {"vertical-align":"top"},
    103     
    104     ".mjx-mphantom *": {visibility:"hidden"},
    105 
    106     ".mjx-merror": {
    107       "background-color":"#FFFF88",
    108       color:             "#CC0000",
    109       border:            "1px solid #CC0000",
    110       padding:           "2px 3px",
    111       "font-style":      "normal",
    112       "font-size":       "90%"
    113     },
    114     
    115     ".mjx-annotation-xml": {"line-height":"normal"},
    116     
    117     ".mjx-menclose > svg": {fill:"none", stroke:"currentColor"},
    118 
    119     ".mjx-mtr":    {display:"table-row"},
    120     ".mjx-mlabeledtr": {display:"table-row"},
    121     ".mjx-mtd":    {display:"table-cell", "text-align":"center"},
    122     ".mjx-label":  {display:"block"},
    123 
    124     ".mjx-box":    {display:"inline-block"},
    125     ".mjx-block":  {display:"block"},
    126     ".mjx-span":   {display:"span"},
    127     ".mjx-char":   {display:"block", "white-space":"pre"},
    128     ".mjx-itable": {display:"inline-table"},
    129     ".mjx-row":    {display:"table-row"},
    130     ".mjx-cell":   {display:"table-cell"},
    131     ".mjx-table":  {display:"table", width:"100%"},
    132     ".mjx-line":   {display:"block", height:0},
    133     ".mjx-strut":  {width:0, "padding-top":STRUTHEIGHT+"em"},
    134     ".mjx-vsize":  {width:0},
    135 
    136     ".MJXc-space1": {"margin-left":".167em"},
    137     ".MJXc-space2": {"margin-left":".222em"},
    138     ".MJXc-space3": {"margin-left":".278em"},
    139     
    140     ".mjx-chartest": {
    141       display:"block",
    142       visibility: "hidden",
    143       position:"absolute", top:0,
    144       "line-height":"normal",
    145       "font-size":"500%"
    146     },
    147     ".mjx-chartest .mjx-char": {display:"inline"},
    148     ".mjx-chartest .mjx-box": {"padding-top": "1000px"},
    149 
    150     ".MJXc-processing": {
    151       visibility: "hidden", position:"fixed",
    152       width: 0, height: 0, overflow:"hidden"
    153     },
    154     ".MJXc-processed": {display:"none"},
    155     
    156     ".mjx-test": {
    157       display:           "block",
    158       "font-style":      "normal",
    159       "font-weight":     "normal",
    160       "font-size":       "100%",
    161       "font-size-adjust":"none",
    162       "text-indent":     0,
    163       "text-transform":  "none",
    164       "letter-spacing":  "normal",
    165       "word-spacing":    "normal",
    166       overflow:          "hidden",
    167       height:            "1px"
    168     },
    169     ".mjx-ex-box-test": {
    170       position:  "absolute",
    171       width:"1px", height:"60ex"
    172     },
    173     
    174     "#MathJax_CHTML_Tooltip": {
    175       "background-color": "InfoBackground", color: "InfoText",
    176       border: "1px solid black",
    177       "box-shadow": "2px 2px 5px #AAAAAA",         // Opera 10.5
    178       "-webkit-box-shadow": "2px 2px 5px #AAAAAA", // Safari 3 and Chrome
    179       "-moz-box-shadow": "2px 2px 5px #AAAAAA",    // Forefox 3.5
    180       "-khtml-box-shadow": "2px 2px 5px #AAAAAA",  // Konqueror
    181       padding: "3px 4px",
    182       "z-index": 401,
    183       position: "absolute", left: 0, top: 0,
    184       width: "auto", height: "auto",
    185       display: "none"
    186     }
    187 
    188   };
    189   
    190   
    191   /************************************************************/
    192   
    193   var BIGDIMEN = 1000000;
    194   var LINEBREAKS = {}, CONFIG = MathJax.Hub.config;
    195 
    196   CHTML.Augment({
    197     settings: HUB.config.menuSettings,
    198     config: {styles: STYLES},
    199 
    200     /********************************************/
    201     
    202     Config: function () {
    203       if (!this.require) {this.require = []}
    204       this.SUPER(arguments).Config.call(this); var settings = this.settings;
    205       if (settings.scale) {this.config.scale = settings.scale}
    206       this.require.push(this.fontDir+"/TeX/fontdata.js");
    207       this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js");
    208       LINEBREAKS = this.config.linebreaks;
    209     },
    210 
    211     Startup: function () {
    212       //
    213       //  Set up event handling
    214       //
    215       EVENT = MathJax.Extension.MathEvents.Event;
    216       TOUCH = MathJax.Extension.MathEvents.Touch;
    217       HOVER = MathJax.Extension.MathEvents.Hover;
    218       this.ContextMenu = EVENT.ContextMenu;
    219       this.Mousedown   = EVENT.AltContextMenu;
    220       this.Mouseover   = HOVER.Mouseover;
    221       this.Mouseout    = HOVER.Mouseout;
    222       this.Mousemove   = HOVER.Mousemove;
    223 
    224       //
    225       //  Determine pixels per inch
    226       //
    227       var div = CHTML.addElement(document.body,"mjx-block",{style:{display:"block",width:"5in"}});
    228       this.pxPerInch = div.offsetWidth/5; div.parentNode.removeChild(div);
    229 
    230       //
    231       // Used in preTranslate to get scaling factors and line width
    232       //
    233       this.TestSpan = CHTML.Element("mjx-test",{style:{left:"1em"}},[["mjx-ex-box-test"]]);
    234 
    235       //
    236       //  Set up styles and preload web fonts
    237       //
    238       return AJAX.Styles(this.config.styles,["InitializeCHTML",this]);
    239     },
    240     
    241     InitializeCHTML: function () {
    242       this.getDefaultExEm();
    243       //
    244       //  If the defaultEm size is zero, it might be that a web font hasn't
    245       //  arrived yet, so try to wait for it, but don't wait too long.
    246       //
    247       if (this.defaultEm) return;
    248       var ready = MathJax.Callback();
    249       AJAX.timer.start(AJAX,function (check) {
    250         if (check.time(ready)) {HUB.signal.Post(["CommonHTML Jax - no default em size"]); return}
    251         CHTML.getDefaultExEm();
    252         if (CHTML.defaultEm) {ready()} else {setTimeout(check,check.delay)}
    253       },this.defaultEmDelay,this.defaultEmTimeout);
    254       return ready;
    255     },
    256     defaultEmDelay: 100,      // initial delay when checking for defaultEm
    257     defaultEmTimeout: 1000,   // when to stop looking for defaultEm
    258     getDefaultExEm: function () {
    259       //
    260       //  Get the default sizes (need styles in place to do this)
    261       //
    262       document.body.appendChild(this.TestSpan);
    263       this.defaultEm    = this.getFontSize(this.TestSpan);
    264       this.defaultEx    = this.TestSpan.firstChild.offsetHeight/60;
    265       this.defaultWidth = this.TestSpan.offsetWidth;
    266       document.body.removeChild(this.TestSpan);
    267     },
    268     getFontSize: (window.getComputedStyle ? 
    269       function (node) {
    270         var style = window.getComputedStyle(node);
    271         return parseFloat(style.fontSize);
    272       } :
    273       //
    274       //  IE 8 doesn't do getComputedStyle, so use
    275       //  an alternative approach
    276       //
    277       function (node) {
    278         return node.style.pixelLeft;
    279       }
    280     ),
    281     getMaxWidth: (window.getComputedStyle ?
    282       function (node) {
    283         var style = window.getComputedStyle(node);
    284         if (style.maxWidth !== "none") return parseFloat(style.maxWidth);
    285         return 0;
    286       } :
    287       //
    288       //  IE 8 doesn't do getComputedStyle, so use
    289       //  currentStyle, and a hack to get the pixels for
    290       //  a non-px max-width
    291       //
    292       function (node) {
    293         var max = node.currentStyle.maxWidth;
    294         if (max !== "none") {
    295           if (max.match(/\d*px/)) return parseFloat(max);
    296           var left = node.style.left;
    297           node.style.left = max; max = node.style.pixelLeft;
    298           node.style.left = left;
    299           return max;
    300         }
    301         return 0;
    302       }
    303     ),
    304 
    305     //
    306     //  Load data for a font
    307     //
    308     loadFont: function (font) {
    309       HUB.RestartAfter(AJAX.Require(this.fontDir+"/"+font));
    310     },
    311     //
    312     //  Signal that the font data are loaded
    313     //
    314     fontLoaded: function (font) {
    315       if (!font.match(/-|fontdata/)) font += "-Regular";
    316       if (!font.match(/\.js$/)) font += ".js"
    317       MathJax.Callback.Queue(
    318         ["Post",HUB.Startup.signal,["CommonHTML - font data loaded",font]],
    319         ["loadComplete",AJAX,this.fontDir+"/"+font]
    320       );
    321     },
    322     
    323     Element: function (type,def,content) {
    324       if (type.substr(0,4) === "mjx-") {
    325         if (!def) def = {};
    326         if (def.isMathJax == null) def.isMathJax = true;
    327         if (def.className) def.className = type+" "+def.className; else def.className = type;
    328         type = "span";
    329       }
    330       return this.HTMLElement(type,def,content);
    331     },
    332     addElement: function (node,type,def,content) {
    333       return node.appendChild(this.Element(type,def,content));
    334     },
    335     HTMLElement: HTML.Element,
    336     ucMatch: HTML.ucMatch,
    337     setScript: HTML.setScript,
    338     
    339     getNodesByClass: (document.getElementsByClassName ? 
    340       function (node,type) {return node.getElementsByClassName(type)} :
    341       function (node,type) {
    342         var NODES = [];
    343         var nodes = node.getElementsByTagName("span");
    344         var name = RegExp("\\b"+type+"\\b");
    345         for (var i = 0, m = nodes.length; i < m; i++) {
    346           if (name.test(nodes[i].className)) NODES.push = nodes[i];
    347         }
    348         return NODES;
    349       }
    350     ),
    351     getNode: function (node,type) {
    352       var nodes = this.getNodesByClass(node,type);
    353       if (nodes.length === 1) return nodes[0];
    354       var closest = nodes[0], N = this.getNodeDepth(node,closest);
    355       for (var i = 1, m = nodes.length; i < m; i++) {
    356         var n = this.getNodeDepth(node,nodes[i]);
    357         if (n < N) {closest = nodes[i]; N = n}
    358       }
    359       return closest;
    360     },
    361     getNodeDepth: function (parent,node) {
    362       var n = 0;
    363       while (node && node !== parent) {node = node.parentNode; n++}
    364       return n;
    365     },
    366     
    367 
    368     /********************************************/
    369     
    370     preTranslate: function (state) {
    371       var scripts = state.jax[this.id], i, m = scripts.length,
    372           script, prev, node, jax, ex, em;
    373       //
    374       //  Get linebreaking information
    375       //
    376       var maxwidth = 100000, relwidth = false, cwidth = 0,
    377           linebreak = LINEBREAKS.automatic, width = LINEBREAKS.width;
    378       if (linebreak) {
    379         relwidth = !!width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/);
    380         if (relwidth) {width = width.replace(/\s*container\s*/,"")}
    381           else {maxwidth = this.defaultWidth}
    382         if (width === "") {width = "100%"}
    383       }
    384       //
    385       //  Loop through the scripts
    386       //
    387       for (i = 0; i < m; i++) {
    388         script = scripts[i]; if (!script.parentNode) continue;
    389         //
    390         //  Remove any existing output
    391         //
    392         prev = script.previousSibling;
    393 	if (prev && prev.className && String(prev.className).substr(0,9) === "mjx-chtml")
    394 	  prev.parentNode.removeChild(prev);
    395         //
    396         //  Add the node for the math and mark it as being processed
    397         //
    398         jax = script.MathJax.elementJax; if (!jax) continue;
    399         jax.CHTML = {display: (jax.root.Get("display") === "block")}
    400         node = CHTML.Element("mjx-chtml",{
    401           id:jax.inputID+"-Frame", className:"MathJax_CHTML", isMathJax:true, jaxID:this.id,
    402           oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
    403           onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove,
    404 	  onclick:EVENT.Click, ondblclick:EVENT.DblClick,
    405           // Added for keyboard accessible menu.
    406           onkeydown: EVENT.Keydown, tabIndex: HUB.getTabOrder(jax)
    407         });
    408         if (jax.CHTML.display) {
    409           //
    410           // Zoom box requires an outer container to get the positioning right.
    411           //
    412           var NODE = CHTML.Element("mjx-chtml",{className:"MJXc-display",isMathJax:false});
    413           NODE.appendChild(node); node = NODE;
    414         }
    415         if (HUB.Browser.noContextMenu) {
    416           node.ontouchstart = TOUCH.start;
    417           node.ontouchend = TOUCH.end;
    418         }
    419         //
    420         node.className += " MJXc-processing";
    421         script.parentNode.insertBefore(node,script);
    422         //
    423         //  Add test nodes for determineing scales and linebreak widths
    424         //
    425         script.parentNode.insertBefore(this.TestSpan.cloneNode(true),script);
    426       }
    427       //
    428       //  Determine the scaling factors for each script
    429       //  (this only requires one reflow rather than a reflow for each equation)
    430       //
    431       for (i = 0; i < m; i++) {
    432         script = scripts[i]; if (!script.parentNode) continue;
    433         test = script.previousSibling;
    434         jax = script.MathJax.elementJax; if (!jax) continue;
    435         em = CHTML.getFontSize(test);
    436         ex = test.firstChild.offsetHeight/60;
    437         if (ex === 0 || ex === "NaN") ex = this.defaultEx
    438         node = test;
    439         while (node) {
    440           cwidth = node.offsetWidth; if (cwidth) break;
    441           cwidth = CHTML.getMaxWidth(node); if (cwidth) break;
    442           node = node.parentNode;
    443         }
    444         if (relwidth) maxwidth = cwidth;
    445         scale = (this.config.matchFontHeight ? ex/this.TEX.x_height/em : 1);
    446         scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale);
    447         jax.CHTML.scale = scale/100; jax.CHTML.fontSize = scale+"%";
    448         jax.CHTML.outerEm = em; jax.CHTML.em = this.em = em * scale/100;
    449         jax.CHTML.ex = ex; jax.CHTML.cwidth = cwidth/this.em;
    450         jax.CHTML.lineWidth = (linebreak ? this.length2em(width,maxwidth/this.em,1) : maxwidth);
    451       }
    452       //
    453       //  Remove the test spans used for determining scales and linebreak widths
    454       //
    455       for (i = 0; i < m; i++) {
    456         script = scripts[i]; if (!script.parentNode) continue;
    457         test = scripts[i].previousSibling;
    458         jax = scripts[i].MathJax.elementJax; if (!jax) continue;
    459         test.parentNode.removeChild(test);
    460       }
    461       state.CHTMLeqn = state.CHTMLlast = 0; state.CHTMLi = -1;
    462       state.CHTMLchunk = this.config.EqnChunk;
    463       state.CHTMLdelay = false;
    464     },
    465 
    466     /********************************************/
    467     
    468     Translate: function (script,state) {
    469       if (!script.parentNode) return;
    470 
    471       //
    472       //  If we are supposed to do a chunk delay, do it
    473       //
    474       if (state.CHTMLdelay) {
    475         state.CHTMLdelay = false;
    476         HUB.RestartAfter(MathJax.Callback.Delay(this.config.EqnChunkDelay));
    477       }
    478 
    479       //
    480       //  Get the data about the math
    481       //
    482       var jax = script.MathJax.elementJax, math = jax.root,
    483           node = document.getElementById(jax.inputID+"-Frame");
    484       this.getMetrics(jax);
    485       if (this.scale !== 1) node.style.fontSize = jax.CHTML.fontSize;
    486       //
    487       //  Typeset the math
    488       //
    489       this.initCHTML(math,node);
    490       this.savePreview(script);
    491       this.CHTMLnode = node;
    492       try {
    493         math.setTeXclass();
    494         math.toCommonHTML(node);
    495       } catch (err) {
    496         while (node.firstChild) node.removeChild(node.firstChild);
    497         delete this.CHTMLnode;
    498         this.restorePreview(script);
    499         throw err;
    500       }
    501       delete this.CHTMLnode;
    502       this.restorePreview(script);
    503       //
    504       //  Put it in place, and remove the processing marker
    505       //
    506       if (jax.CHTML.display) node = node.parentNode;
    507       node.className = node.className.replace(/ [^ ]+$/,"");
    508       //
    509       //  Hide the math and don't let its preview be removed
    510       //
    511       node.className += " MJXc-processed";
    512       if (script.MathJax.preview) {
    513         jax.CHTML.preview = script.MathJax.preview;
    514         delete script.MathJax.preview;
    515       }
    516       //
    517       //  Check if we should show this chunk of equations
    518       //
    519       state.CHTMLeqn += (state.i - state.CHTMLi); state.CHTMLi = state.i;
    520       if (state.CHTMLeqn >= state.CHTMLlast + state.CHTMLchunk) {
    521         this.postTranslate(state);
    522         state.CHTMLchunk = Math.floor(state.CHTMLchunk*this.config.EqnChunkFactor);
    523         state.CHTMLdelay = true;  // delay if there are more scripts
    524       }
    525     },
    526 
    527     initCHTML: function (math,node) {},
    528 
    529     //
    530     //  MathML previews can contain the same ID's as the HTML output,
    531     //  which confuses CHTMLnodeElement(), so remove the preview temporarily
    532     //  and restore it after typesetting the math.
    533     //
    534     savePreview: function (script) {
    535       var preview = script.MathJax.preview;
    536       if (preview && preview.parentNode) {
    537         script.MathJax.tmpPreview = document.createElement("span");
    538         preview.parentNode.replaceChild(script.MathJax.tmpPreview,preview);
    539       }
    540     },
    541     restorePreview: function (script) {
    542       var tmpPreview = script.MathJax.tmpPreview;
    543       if (tmpPreview) {
    544         tmpPreview.parentNode.replaceChild(script.MathJax.preview,tmpPreview);
    545         delete script.MathJax.tmpPreview;
    546       }
    547     },
    548     //
    549     //  Get the jax metric information
    550     //
    551     getMetrics: function(jax) {
    552       var data = jax.CHTML;
    553       this.jax = jax;
    554       this.em = data.em;
    555       this.outerEm = data.outerEm;
    556       this.scale = data.scale;
    557       this.cwidth = data.cwidth;
    558       this.linebreakWidth = data.lineWidth;
    559     },
    560 
    561     /********************************************/
    562     
    563     postTranslate: function (state) {
    564       var scripts = state.jax[this.id];
    565       //
    566       //  Reveal this chunk of math
    567       //
    568       for (var i = state.CHTMLlast, m = state.CHTMLeqn; i < m; i++) {
    569         var script = scripts[i];
    570         if (script && script.MathJax.elementJax) {
    571           //
    572           //  Remove the processed marker
    573           //
    574           script.previousSibling.className = script.previousSibling.className.replace(/ [^ ]+$/,"");
    575           var data = script.MathJax.elementJax.CHTML;
    576           //
    577           //  Remove the preview, if any
    578           //
    579           if (data.preview) {
    580             data.preview.innerHTML = "";
    581             script.MathJax.preview = data.preview;
    582             delete data.preview;
    583           }
    584         }
    585       }
    586       //
    587       //  Save our place so we know what is revealed
    588       //
    589       state.CHTMLlast = state.CHTMLeqn;
    590     },
    591 
    592     /********************************************/
    593     
    594     getJaxFromMath: function (math) {
    595       if (math.parentNode.className.match(/MJXc-display/)) math = math.parentNode;
    596       do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script");
    597       return HUB.getJaxFor(math);
    598     },
    599     getHoverSpan: function (jax,math) {return jax.root.CHTMLnodeElement()},
    600     getHoverBBox: function (jax,span,math) {
    601       var bbox = jax.root.CHTML, em = jax.CHTML.outerEm;
    602       var BBOX = {w:bbox.w*em, h:bbox.h*em, d:bbox.d*em};
    603       if (bbox.width) {BBOX.width = bbox.width}
    604       return BBOX;
    605     },
    606     
    607     Zoom: function (jax,span,math,Mw,Mh) {
    608       //
    609       //  Re-render at larger size
    610       //
    611       this.getMetrics(jax);
    612       var node = CHTML.addElement(span,"mjx-chtml",{style:{"font-size":Math.floor(CHTML.scale*100)+"%"},isMathJax:false});
    613       CHTML.CHTMLnode = node;
    614       this.idPostfix = "-zoom"; jax.root.toCommonHTML(node); this.idPostfix = "";
    615       //
    616       //  Adjust margins to prevent overlaps at the edges
    617       //
    618       var style = node.style, bbox = jax.root.CHTML;
    619       if (bbox.t > bbox.h) style.marginTop = CHTML.Em(bbox.t-bbox.h);
    620       if (bbox.b > bbox.d) style.marginBottom = CHTML.Em(bbox.b-bbox.d);
    621       if (bbox.l < 0) style.paddingLeft = CHTML.Em(-bbox.l);
    622       if (bbox.r > bbox.w) style.marginRight = CHTML.Em(bbox.r-bbox.w);
    623       //
    624       //  Get height and width of zoomed math and original math
    625       //
    626       style.position = "absolute";
    627       var zW = node.offsetWidth, zH = node.offsetHeight,
    628           mH = math.firstChild.offsetHeight, mW = math.firstChild.offsetWidth;
    629       node.style.position = "";
    630       //
    631       return {Y:-EVENT.getBBox(span).h, mW:mW, mH:mH, zW:zW, zH:zH};
    632     },
    633 
    634     Remove: function (jax) {
    635       var node = document.getElementById(jax.inputID+"-Frame");
    636       if (node && jax.CHTML.display) node = node.parentNode;
    637       if (node) node.parentNode.removeChild(node);
    638       delete jax.CHTML;
    639     },
    640     
    641     /********************************************/
    642     
    643     ID: 0, idPostfix: "",
    644     GetID: function () {this.ID++; return this.ID},
    645     
    646     /********************************************/
    647 
    648     MATHSPACE: {
    649       veryverythinmathspace:  1/18,
    650       verythinmathspace:      2/18,
    651       thinmathspace:          3/18,
    652       mediummathspace:        4/18,
    653       thickmathspace:         5/18,
    654       verythickmathspace:     6/18,
    655       veryverythickmathspace: 7/18,
    656       negativeveryverythinmathspace:  -1/18,
    657       negativeverythinmathspace:      -2/18,
    658       negativethinmathspace:          -3/18,
    659       negativemediummathspace:        -4/18,
    660       negativethickmathspace:         -5/18,
    661       negativeverythickmathspace:     -6/18,
    662       negativeveryverythickmathspace: -7/18,
    663 
    664       thin: .04,
    665       medium: .06,
    666       thick: .1,
    667 
    668       infinity: BIGDIMEN
    669     },
    670     SPACECLASS: {
    671       thinmathspace:   "MJXc-space1",
    672       mediummathspace: "MJXc-space2",
    673       thickmathspace:  "MJXc-space3"
    674     },
    675     pxPerInch: 96,
    676     em: 16,
    677     
    678     maxStretchyParts: 1000,            // limit the number of parts allowed for
    679                                        // stretchy operators. See issue 366.
    680 
    681     FONTDEF: {},
    682     TEXDEF: {
    683       x_height:         .442,
    684       quad:             1,
    685       num1:             .676508,
    686       num2:             .393732,
    687       num3:             .44373,
    688       denom1:           .685951,
    689       denom2:           .344841,
    690       sup1:             .412892,
    691       sup2:             .362892,
    692       sup3:             .288888,
    693       sub1:             .15,
    694       sub2:             .247217,
    695       sup_drop:         .386108,
    696       sub_drop:         .05,
    697       delim1:          2.39,
    698       delim2:          1.0,
    699       axis_height:      .25,
    700       rule_thickness:   .06,
    701       big_op_spacing1:  .111111,
    702       big_op_spacing2:  .166666,
    703       big_op_spacing3:  .2,
    704       big_op_spacing4:  .45, //.6,  // better spacing for under arrows and braces
    705       big_op_spacing5:  .1,
    706 
    707       surd_height:      .075,
    708       
    709       scriptspace:         .05,
    710       nulldelimiterspace:  .12,
    711       delimiterfactor:     901,
    712       delimitershortfall:   .3,
    713 
    714       min_rule_thickness:  1.25     // in pixels
    715     },
    716     
    717     /********************************************************/
    718     
    719     //
    720     //  Get a unicode character by number (even when it takes two character)
    721     //
    722     unicodeChar: function (n) {
    723       if (n < 0xFFFF) return String.fromCharCode(n);
    724       n -= 0x10000;
    725       return String.fromCharCode((n>>10)+0xD800) + String.fromCharCode((n&0x3FF)+0xDC00);
    726     },
    727     //
    728     //  Get the unicode number of a (possibly multi-character) string
    729     //
    730     getUnicode: function (string) {
    731       var n = string.text.charCodeAt(string.i); string.i++;
    732       if (n >= 0xD800 && n < 0xDBFF) {
    733         n = (((n-0xD800)<<10)+(string.text.charCodeAt(string.i)-0xDC00))+0x10000;
    734         string.i++;
    735       }
    736       return n;
    737     },
    738     //
    739     //  Get the list of actions for a given character in a given variant
    740     //  (processing remaps, multi-character results, and so on).  Results are
    741     //  cached so that future lookups for the same variant/n pair will not
    742     //  require looking through the data again.
    743     //
    744     getCharList: function (variant,n) {
    745       var id, M, list = [], cache = variant.cache, nn = n;
    746       if (cache[n]) return cache[n];
    747       var RANGES = this.FONTDATA.RANGES, VARIANT = this.FONTDATA.VARIANT;
    748       if (n >= RANGES[0].low && n <= RANGES[RANGES.length-1].high) {
    749         for (id = 0, M = RANGES.length; id < M; id++) {
    750           if (RANGES[id].name === "alpha" && variant.noLowerCase) continue;
    751           var N = variant["offset"+RANGES[id].offset];
    752           if (N && n >= RANGES[id].low && n <= RANGES[id].high) {
    753             if (RANGES[id].remap && RANGES[id].remap[n]) {
    754               n = N + RANGES[id].remap[n];
    755             } else {
    756               n = n - RANGES[id].low + N;
    757               if (RANGES[id].add) {n += RANGES[id].add}
    758             }
    759             if (variant["variant"+RANGES[id].offset])
    760               variant = VARIANT[variant["variant"+RANGES[id].offset]];
    761             break;
    762           }
    763         }
    764       }
    765       if (variant.remap && variant.remap[n]) {
    766         n = variant.remap[n];
    767         if (variant.remap.variant) {variant = VARIANT[variant.remap.variant]}
    768       } else if (this.FONTDATA.REMAP[n] && !variant.noRemap) {
    769         n = this.FONTDATA.REMAP[n];
    770       }
    771       if (n instanceof Array) {variant = VARIANT[n[1]]; n = n[0]} 
    772       if (typeof(n) === "string") {
    773         var string = {text:n, i:0, length:n.length};
    774         while (string.i < string.length) {
    775           n = this.getUnicode(string);
    776           var chars = this.getCharList(variant,n);
    777           if (chars) list.push.apply(list,chars);
    778         }
    779       } else {
    780         if (variant.cache[n]) {list = variant.cache[n]}
    781           else {variant.cache[n] = list = [this.lookupChar(variant,n)]}
    782       }
    783       cache[nn] = list;
    784       return list;
    785     },
    786     //
    787     //  After all remapping has been done, look up a character
    788     //  in the fonts for a given variant, chaining to other
    789     //  variants as needed.  Return an undefined character if
    790     //  it isnt' found in the given variant.
    791     //
    792     lookupChar: function (variant,n) {
    793       var VARIANT = variant;
    794       while (variant) {
    795         for (var i = 0, m = variant.fonts.length; i < m; i++) {
    796           var font = this.FONTDATA.FONTS[variant.fonts[i]];
    797           if (typeof(font) === "string") this.loadFont(font);
    798           var C = font[n];
    799           if (C) {
    800             if (C.length === 5) C[5] = {};
    801             if (C.c == null) {
    802               C[0] /= 1000; C[1] /= 1000; C[2] /= 1000; C[3] /= 1000; C[4] /= 1000;
    803               C.c = this.unicodeChar(n);
    804             }
    805             if (C[5].space) return {type:"space", w:C[2], font:font};
    806             return {type:"char", font:font, n:n};
    807           } else if (font.Extra) {
    808             this.findBlock(font,n);
    809           }
    810         }
    811         variant = this.FONTDATA.VARIANT[variant.chain];
    812       }
    813       return this.unknownChar(VARIANT,n);
    814     },
    815     findBlock: function (font,n) {
    816       var extra = font.Extra, name = font.file, file;
    817       for (var i = 0, m = extra.length; i < m; i++) {
    818         if (typeof(extra[i]) === "number") {
    819           if (n === extra[i]) {file = name; break}
    820         } else {
    821           if (n <  extra[i][0]) return;
    822           if (n <= extra[i][1]) {file = name; break}
    823         }
    824       }
    825       //
    826       //  Currently this only loads one extra file, but that
    827       //  might need to be expanded in the future.
    828       //
    829       if (file) {delete font.Extra; this.loadFont(name)}
    830     },
    831     //
    832     //  Create a fake font entry for an unknown character.
    833     //
    834     unknownChar: function (variant,n) {
    835       HUB.signal.Post(["CommonHTML Jax - unknown char",n,variant]);
    836       var id = ""; if (variant.bold) id += "B"; if (variant.italic) id += "I";
    837       var unknown = this.FONTDATA.UNKNOWN[id||"R"]; // cache of previously measured characters
    838       if (!unknown[n]) this.getUnknownChar(unknown,n);
    839       return {type:"unknown", n:n, font:unknown};
    840     },
    841     getUnknownChar: function (unknown,n) {
    842       var c = this.unicodeChar(n);
    843       var HDW = this.getHDW(c,unknown.className);
    844       // ### FIXME:  provide a means of setting the height and depth for individual characters
    845       unknown[n] = [.8,.2,HDW.w,0,HDW.w,{a:Math.max(0,(HDW.h-HDW.d)/2), h:HDW.h, d:HDW.d}];
    846       unknown[n].c = c;
    847     },
    848     styledText: function (variant,text) {
    849       HUB.signal.Post(["CommonHTML Jax - styled text",text,variant]);
    850       var style = variant.style;
    851       var id = "_"+(style["font-family"]||variant.className||"");
    852       if (style["font-weight"]) id += "_"+style["font-weight"];
    853       if (style["font-style"])  id += "_"+style["font-style"];
    854       if (!this.STYLEDTEXT) this.STYLEDTEXT = {};
    855       if (!this.STYLEDTEXT[id]) this.STYLEDTEXT[id] = {className:variant.className||""};
    856       var unknown = this.STYLEDTEXT[id];
    857       if (!unknown["_"+text]) {
    858         var HDW = this.getHDW(text,variant.className||"",style);
    859         unknown["_"+text] = [.8,.2,HDW.w,0,HDW.w,{a:Math.max(0,(HDW.h-HDW.d)/2), h:HDW.h, d:HDW.d}];
    860         unknown["_"+text].c = text;
    861       }
    862       return {type:"unknown", n:"_"+text, font:unknown, style:style, rscale:variant.rscale};
    863     },
    864 
    865     //
    866     //  Get the height, depth, and width of a character
    867     //  (height and depth are of the font, not the character).
    868     //  WARNING:  causes reflow of the page!
    869     //
    870     getHDW: function (c,name,styles) {
    871       var test1 = CHTML.addElement(CHTML.CHTMLnode,"mjx-chartest",{className:name},[["mjx-char",{style:styles},[c]]]);
    872       var test2 = CHTML.addElement(CHTML.CHTMLnode,"mjx-chartest",{className:name},[["mjx-char",{style:styles},[c,["mjx-box"]]]]);
    873       test1.firstChild.style.fontSize = test2.firstChild.style.fontSize = "";
    874       var em = 5*CHTML.em;
    875       var H1 = test1.offsetHeight, H2 = test2.offsetHeight, W = test1.offsetWidth;
    876       CHTML.CHTMLnode.removeChild(test1);
    877       CHTML.CHTMLnode.removeChild(test2);
    878       if (H2 === 0) {
    879         em = 5*CHTML.defaultEm;
    880         var test = document.body.appendChild(document.createElement("div"));
    881         test.appendChild(test1); test.appendChild(test2);
    882         H1 = test1.offsetHeight, H2 = test2.offsetHeight, W = test1.offsetWidth;
    883         document.body.removeChild(test);
    884       }
    885       var d = (H2-1000)/em, w = W/em, h = H1/em - d;
    886       return {h:h, d:d, w:w}
    887     },
    888     
    889 
    890     /********************************************************/
    891     
    892     //
    893     //  Process a character list into a given node and return
    894     //  the updated bounding box.
    895     //
    896     addCharList: function (node,list,bbox) {
    897       var state = {text:"", className:null, a:0};
    898       for (var i = 0, m = list.length; i < m; i++) {
    899         var item = list[i];
    900         if (this.charList[item.type]) (this.charList[item.type])(item,node,bbox,state,m);
    901       }
    902       if (state.text !== "") {
    903         if (node.childNodes.length) {
    904           this.charList.flushText(node,state);
    905         } else {
    906           HTML.addText(node,state.text);
    907           if (node.className) node.className += " "+state.className;
    908             else node.className = state.className;
    909         }
    910       }
    911       bbox.b = (state.flushed ? 0 : bbox.a);
    912     },
    913     //
    914     //  The various item types are processed by these
    915     //  functions.
    916     //
    917     charList: {
    918       //
    919       //  Character from the known fonts
    920       //
    921       "char": function (item,node,bbox,state,m) {
    922         var font = item.font;
    923         if (state.className && font.className !== state.className) this.flushText(node,state);
    924         if (!state.a) state.a = font.centerline/1000;
    925         if (state.a > (bbox.a||0)) bbox.a = state.a;
    926         var C = font[item.n];
    927         state.text += C.c; state.className = font.className;
    928         if (bbox.h < C[0]+HFUZZ) bbox.t = bbox.h = C[0]+HFUZZ;
    929         if (bbox.d < C[1]+DFUZZ) bbox.b = bbox.d = C[1]+DFUZZ;
    930         if (bbox.l > bbox.w+C[3]) bbox.l = bbox.w+C[3];
    931         if (bbox.r < bbox.w+C[4]) bbox.r = bbox.w+C[4];
    932         bbox.w += C[2] * (item.rscale||1);
    933         if (m == 1 && font.skew && font.skew[item.n]) bbox.skew = font.skew[item.n];
    934         if (C[5].rfix) this.flushText(node,state).style.marginRight = CHTML.Em(C[5].rfix/1000);
    935       },
    936       //
    937       //  Space characters (not actually in the fonts)
    938       //
    939       space: function (item,node,bbox,state) {
    940         if (item.w) {
    941           if (state.text === "") state.className = item.font.className;
    942           this.flushText(node,state).style.marginRight = CHTML.Em(item.w);
    943           bbox.w += item.w;
    944         }
    945       },
    946       //
    947       //  An unknown character (one not in the font data)
    948       //
    949       unknown: function (item,node,bbox,state) {
    950         (this["char"])(item,node,bbox,state,0);
    951         var C = item.font[item.n];
    952         if (C[5].a) {
    953           state.a = C[5].a;
    954           if (bbox.a == null || state.a > bbox.a) bbox.a = state.a;
    955         }
    956         node = this.flushText(node,state,item.style);
    957         node.style.width = CHTML.Em(C[2]);
    958       },
    959       //
    960       //  Put the pending text into a box of the class, and
    961       //  reset the data about the text.
    962       //
    963       flushText: function (node,state,style) {
    964         node = CHTML.addElement(node,"mjx-charbox",
    965           {className:state.className,style:style},[state.text]);
    966         if (state.a) node.style.paddingBottom = CHTML.Em(state.a);
    967         state.text = ""; state.className = null; state.a = 0; state.flushed = true;
    968         return node;
    969       }
    970     },
    971 
    972     //
    973     //  Add the given text (in the given variant) into the given node, and
    974     //  update the bounding box of the result.  Make sure the node's DOM
    975     //  bounding box matches the contents.
    976     //
    977     handleText: function (node,text,variant,bbox) {
    978       if (node.childNodes.length === 0) {
    979         CHTML.addElement(node,"mjx-char");
    980         bbox = CHTML.BBOX.empty(bbox);
    981       }
    982       if (typeof(variant) === "string") variant = this.FONTDATA.VARIANT[variant];
    983       if (!variant) variant = this.FONTDATA.VARIANT[MML.VARIANT.NORMAL];
    984       var string = {text:text, i:0, length:text.length}, list = [];
    985       if (variant.style && string.length) {
    986         list.push(this.styledText(variant,text));
    987       } else {
    988         while (string.i < string.length) {
    989           var n = this.getUnicode(string);
    990           list.push.apply(list,this.getCharList(variant,n));
    991         }
    992       }
    993       if (list.length) this.addCharList(node.firstChild,list,bbox);
    994       bbox.clean();
    995       if (bbox.d < 0) {bbox.D = bbox.d; bbox.d = 0}
    996       if (bbox.h - bbox.a) node.firstChild.style[bbox.h - bbox.a < 0 ? "marginTop" : "paddingTop"] = this.EmRounded(bbox.h-bbox.a);
    997       if (bbox.d > -bbox.b) node.firstChild.style.paddingBottom = this.EmRounded(bbox.d+bbox.b);
    998       return bbox;
    999     },
   1000 
   1001     /********************************************************/
   1002 
   1003     createDelimiter: function (node,code,HW,BBOX,font) {
   1004       if (!code) {
   1005         var bbox = this.BBOX.zero();
   1006         bbox.w = bbox.r = this.TEX.nulldelimiterspace;
   1007         CHTML.addElement(node,"mjx-box",{style:{width:bbox.w}});
   1008         return bbox;
   1009       }
   1010       if (!(HW instanceof Array)) HW = [HW,HW];
   1011       var hw = HW[1]; HW = HW[0];
   1012       var delim = {alias: code};
   1013       while (delim.alias) {
   1014         code = delim.alias; delim = this.FONTDATA.DELIMITERS[code];
   1015         if (!delim) {delim = {HW: [0,this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]]}}
   1016       }
   1017       if (delim.load) HUB.RestartAfter(AJAX.Require(this.fontDir+"/TeX/fontdata-"+delim.load+".js"));
   1018       for (var i = 0, m = delim.HW.length; i < m; i++) {
   1019         if (delim.HW[i][0] >= HW-.01 || (i == m-1 && !delim.stretch)) {
   1020           if (delim.HW[i][3]) code = delim.HW[i][3];
   1021           bbox = this.createChar(node,[code,delim.HW[i][1]],(delim.HW[i][2]||1),font);
   1022           bbox.offset = .6 * bbox.w;
   1023           if (BBOX) {bbox.scale = BBOX.scale; BBOX.rscale = BBOX.rscale}
   1024           return bbox;
   1025         }
   1026       }
   1027       if (!delim.stretch) return bbox;
   1028       return this["extendDelimiter"+delim.dir](node,hw,delim.stretch,BBOX,font);
   1029     },
   1030     extendDelimiterV: function (node,H,delim,BBOX,font) {
   1031       node = CHTML.addElement(node,"mjx-delim-v"); var tmp = CHTML.Element("span");
   1032       var top, bot, mid, ext, tbox, bbox, mbox, ebox, k = 1, c;
   1033       tbox = this.createChar(tmp,(delim.top||delim.ext),1,font); top = tmp.removeChild(tmp.firstChild);
   1034       bbox = this.createChar(tmp,(delim.bot||delim.ext),1,font); bot = tmp.removeChild(tmp.firstChild);
   1035       mbox = ebox = CHTML.BBOX.zero();
   1036       var h = tbox.h + tbox.d + bbox.h + bbox.d - EFUZZ;
   1037       node.appendChild(top);
   1038       if (delim.mid) {
   1039         mbox = this.createChar(tmp,delim.mid,1,font); mid = tmp.removeChild(tmp.firstChild);
   1040         h += mbox.h + mbox.d; k = 2;
   1041       }
   1042       if (delim.min && H < h*delim.min) H = h*delim.min;
   1043       if (H > h) {
   1044         ebox = this.createChar(tmp,delim.ext,1,font); ext = tmp.removeChild(tmp.firstChild);
   1045         var eH = ebox.h + ebox.d, eh = eH - EFUZZ;
   1046         var n = Math.min(Math.ceil((H-h)/(k*eh)),this.maxStretchyParts);
   1047         if (delim.fullExtenders) H = n*k*eh + h; else eh = (H-h)/(k*n);
   1048         c = ebox.d + ebox.a - eH/2; // for centering of extenders
   1049         ext.style.margin = ext.style.padding = "";
   1050         ext.style.lineHeight = CHTML.Em(eh);
   1051         ext.style.marginBottom = CHTML.Em(c-EFUZZ/2/k);
   1052         ext.style.marginTop = CHTML.Em(-c-EFUZZ/2/k);
   1053         var TEXT = ext.textContent, text = "\n"+TEXT;
   1054         while (--n > 0) TEXT += text;
   1055         ext.textContent = TEXT;
   1056         node.appendChild(ext);
   1057         if (delim.mid) {
   1058           node.appendChild(mid);
   1059           node.appendChild(ext.cloneNode(true));
   1060         }
   1061       } else {
   1062         c = (H-h-EFUZZ) / k;
   1063         top.style.marginBottom = CHTML.Em(c+parseFloat(top.style.marginBottom||"0"));
   1064         if (delim.mid) node.appendChild(mid);
   1065         bot.style.marginTop = CHTML.Em(c+parseFloat(bot.style.marginTop||"0"));
   1066       }
   1067       node.appendChild(bot);
   1068       var vbox = CHTML.BBOX({
   1069         w:  Math.max(tbox.w,ebox.w,bbox.w,mbox.w),
   1070         l: Math.min(tbox.l,ebox.l,bbox.l,mbox.l),
   1071         r: Math.max(tbox.r,ebox.r,bbox.r,mbox.r),
   1072         h: H-bbox.d, d: bbox.d, t: H-bbox.d, b: bbox.d
   1073       });
   1074       vbox.offset = .5 * vbox.w;
   1075       if (BBOX) {vbox.scale = BBOX.scale; vbox.rscale = BBOX.rscale}
   1076       return vbox;
   1077     },
   1078     extendDelimiterH: function (node,W,delim,BBOX,font) {
   1079       node = CHTML.addElement(node,"mjx-delim-h"); var tmp = CHTML.Element("span");
   1080       var left, right, mid, ext, ext2, lbox, rbox, mbox, ebox, k = 1;
   1081       lbox = this.createChar(tmp,(delim.left||delim.rep),1,font); left = tmp.removeChild(tmp.firstChild);
   1082       rbox = this.createChar(tmp,(delim.right||delim.rep),1,font); right = tmp.removeChild(tmp.firstChild);
   1083       ebox = this.createChar(tmp,delim.rep,1,font); ext = tmp.removeChild(tmp.firstChild);
   1084       left.style.marginLeft = CHTML.Em(-lbox.l);
   1085       right.style.marginRight = CHTML.Em(rbox.r-rbox.w);
   1086       node.appendChild(left); 
   1087       var hbox = CHTML.BBOX.zero(); 
   1088       hbox.h = Math.max(lbox.h,rbox.h,ebox.h);
   1089       hbox.d = Math.max(lbox.D||lbox.d,rbox.D||rbox.d,ebox.D||ebox.d);
   1090       var w = (lbox.r - lbox.l) + (rbox.r - rbox.l) - EFUZZ;
   1091       if (delim.mid) {
   1092         mbox = this.createChar(tmp,delim.mid,1,font);
   1093         mid = tmp.removeChild(tmp.firstChild);
   1094         mid.style.marginleft = CHTML.Em(-mbox.l); mid.style.marginRight = CHTML.Em(mbox.r-mbox.w);
   1095         w += mbox.r - mbox.l + EFUZZ; k = 2;
   1096         if (mbox.h > hbox.h) hbox.h = mbox.h;
   1097         if (mbox.d > hbox.d) hbox.d = mbox.d;
   1098       }
   1099       if (delim.min && W < w*delim.min) W = w*delim.min;
   1100       hbox.w = hbox.r = W;
   1101       if (W > w) {
   1102         var eW = ebox.r-ebox.l, ew = eW - EFUZZ;
   1103         var n = Math.min(Math.ceil((W-w)/(k*ew)),this.maxStretchyParts);
   1104         if (delim.fullExtenders) W = n*k*ew + w; else ew = (W-w)/(k*n);
   1105         var c = (eW - ew + EFUZZ/k) / 2; // for centering of extenders
   1106         ext.style.marginLeft = CHTML.Em(-ebox.l-c);
   1107         ext.style.marginRight = CHTML.Em(ebox.r-ebox.w+c);
   1108         ext.style.letterSpacing = CHTML.Em(-(ebox.w-ew));
   1109         left.style.marginRight = CHTML.Em(lbox.r-lbox.w);
   1110         right.style.marginleft = CHTML.Em(-rbox.l);
   1111         var TEXT = ext.textContent, text = TEXT;
   1112         while (--n > 0) TEXT += text;
   1113         ext.textContent = TEXT;
   1114         node.appendChild(ext);
   1115         if (delim.mid) {
   1116           node.appendChild(mid);
   1117           ext2 = node.appendChild(ext.cloneNode(true));
   1118         }
   1119       } else {
   1120         c = (W-w-EFUZZ/k) / 2;
   1121         left.style.marginRight = CHTML.Em(lbox.r-lbox.w+c);
   1122         if (delim.mid) node.appendChild(mid);
   1123         right.style.marginLeft = CHTML.Em(-rbox.l+c);
   1124       }
   1125       node.appendChild(right);
   1126       this.adjustHeights([left,ext,mid,ext2,right],[lbox,ebox,mbox,ebox,rbox],hbox);
   1127       if (BBOX) {hbox.scale = BBOX.scale; hbox.rscale = BBOX.rscale}
   1128       return hbox;
   1129     },
   1130     adjustHeights: function (nodes,box,bbox) {
   1131       //
   1132       //  To get alignment right in horizontal delimiters, we force all
   1133       //  the elements to the same height and depth
   1134       //
   1135       var T = bbox.h, B = bbox.d;
   1136       if (bbox.d < 0) {B = -bbox.d; bbox.D = bbox.d; bbox.d = 0}
   1137       for (var i = 0, m = nodes.length; i < m; i++) if (nodes[i]) {
   1138         nodes[i].style.paddingTop = CHTML.Em(T-box[i].a);
   1139         nodes[i].style.paddingBottom = CHTML.Em(B+box[i].a);
   1140         nodes[i].style.marginTop = nodes[i].style.marginBottom = 0;
   1141       }
   1142     },
   1143     createChar: function (node,data,scale,font) {
   1144       // ### FIXME: handle cache better (by data[1] and font)
   1145       var text = "", variant = {fonts: [data[1]], noRemap:true, cache:{}};
   1146       if (font && font === MML.VARIANT.BOLD && this.FONTDATA.FONTS[data[1]+"-Bold"])
   1147         variant.fonts = [data[1]+"-Bold",data[1]];
   1148       if (typeof(data[1]) !== "string") variant = data[1];
   1149       if (data[0] instanceof Array) {
   1150         for (var i = 0, m = data[0].length; i < m; i++) text += String.fromCharCode(data[0][i]);
   1151       } else text = String.fromCharCode(data[0]);
   1152       if (data[4]) scale *= data[4];
   1153       var bbox = this.handleText(node,text,variant), style = node.firstChild.style;
   1154       if (scale !== 1) style.fontSize = this.Percent(scale);
   1155       if (data[2]) {  // x offset
   1156         style.paddingLeft = this.Em(data[2]);
   1157         bbox.w += data[2]; bbox.r += data[2];
   1158       }
   1159       if (data[3]) {  // y offset
   1160         style.verticalAlign = this.Em(data[3]);
   1161         bbox.h += data[3]; if (bbox.h < 0) bbox.h = 0;
   1162       }
   1163       if (data[5]) {  // extra height
   1164         style.marginTop = this.Em(data[5]);
   1165         bbox.h += data[5]; bbox.t += data[5];
   1166       }
   1167       if (data[6]) {  // extra depth
   1168         style.marginBottom = this.Em(data[6]);
   1169         bbox.d += data[6]; bbox.b += data[6];
   1170       }
   1171       return bbox;
   1172     },
   1173 
   1174     /********************************************************/
   1175     
   1176     //
   1177     //  ### FIXME: Handle mu's
   1178     //
   1179     length2em: function (length,size,scale) {
   1180       if (typeof(length) !== "string") length = length.toString();
   1181       if (length === "") return "";
   1182       if (length === MML.SIZE.NORMAL) return 1;
   1183       if (length === MML.SIZE.BIG)    return 2;
   1184       if (length === MML.SIZE.SMALL)  return .71;
   1185       if (this.MATHSPACE[length])     return this.MATHSPACE[length];
   1186       var match = length.match(/^\s*([-+]?(?:\.\d+|\d+(?:\.\d*)?))?(pt|em|ex|mu|px|pc|in|mm|cm|%)?/);
   1187       var m = parseFloat(match[1]||"1"), unit = match[2];
   1188       if (size == null) size = 1;  if (!scale) scale = 1;
   1189       scale = 1 /this.em / scale;
   1190       if (unit === "em") return m;
   1191       if (unit === "ex") return m * this.TEX.x_height;
   1192       if (unit === "%")  return m / 100 * size;
   1193       if (unit === "px") return m * scale;
   1194       if (unit === "pt") return m / 10;                 // 10 pt to an em
   1195       if (unit === "pc") return m * 1.2;                // 12 pt to a pc
   1196       scale *= this.pxPerInch;
   1197       if (unit === "in") return m * scale;
   1198       if (unit === "cm") return m * scale / 2.54;       // 2.54 cm to an inch
   1199       if (unit === "mm") return m * scale / 25.4;       // 10 mm to a cm
   1200       if (unit === "mu") return m / 18;                 // 18mu to an em for the scriptlevel
   1201       return m*size;  // relative to given size (or 1em as default)
   1202     },
   1203     thickness2em: function (length,scale) {
   1204       var thick = CHTML.TEX.rule_thickness/(scale||1);
   1205       if (length === MML.LINETHICKNESS.MEDIUM) return thick;
   1206       if (length === MML.LINETHICKNESS.THIN)   return .67*thick;
   1207       if (length === MML.LINETHICKNESS.THICK)  return 1.67*thick;
   1208       return this.length2em(length,thick,scale);
   1209     },
   1210 
   1211     Em: function (m) {
   1212       if (Math.abs(m) < .001) return "0";
   1213       return (m.toFixed(3).replace(/\.?0+$/,""))+"em";
   1214     },
   1215     EmRounded: function (m) {
   1216       m = (Math.round(m*CHTML.em)+.05)/CHTML.em;
   1217       if (Math.abs(m) < .0006) {return "0em"}
   1218       return m.toFixed(3).replace(/\.?0+$/,"") + "em";
   1219     },
   1220     unEm: function (m) {
   1221       return parseFloat(m);
   1222     },
   1223     Px: function (m,M) {
   1224       m *= this.em;
   1225       if (M && m < M) m = M;
   1226       if (Math.abs(m) < .1) return "0";
   1227       return m.toFixed(1).replace(/\.0$/,"")+"px";
   1228     },
   1229     
   1230     Percent: function (m) {
   1231       return (100*m).toFixed(1).replace(/\.?0+$/,"") + "%";
   1232     },
   1233     
   1234     Transform: function (node,trans,origin) {
   1235       var style = node.style;
   1236       style.transform = style.WebkitTransform = style.MozTransform = style["-ms-transform"] = trans;
   1237       if (origin)
   1238         style.transformOrigin = style.WebkitTransformOrigin =
   1239           style.MozTransformOrigin = style["-ms-transform-origin"] = origin;
   1240     },
   1241 
   1242     /********************************************************/
   1243     
   1244     arrayEntry: function (a,i) {return a[Math.max(0,Math.min(i,a.length-1))]},
   1245 
   1246     //
   1247     //  Styles to be removed from style="..." attributes
   1248     //
   1249     removeStyles: ["fontSize","fontFamily","fontWeight","fontStyle","fontVariant","font"]
   1250     
   1251   });
   1252 
   1253   /**********************************************************/
   1254 
   1255   CHTML.BBOX = MathJax.Object.Subclass({
   1256     Init: function (def) {
   1257       for (var id in def) {
   1258         if (def.hasOwnProperty(id)) this[id] = def[id];
   1259       }
   1260     },
   1261     clean: function () {
   1262       if (this.h === -BIGDIMEN) this.h = 0;
   1263       if (this.d === -BIGDIMEN) this.d = 0;
   1264       if (this.l ===  BIGDIMEN) this.l = 0;
   1265       if (this.r === -BIGDIMEN) this.r = 0;
   1266       if (this.t === -BIGDIMEN) this.t = 0;
   1267       if (this.b === -BIGDIMEN) this.b = 0;
   1268       if (this.D && this.d > 0) delete this.D;
   1269     },
   1270     rescale: function (scale) {
   1271       this.w *= scale; this.h *= scale; this.d *= scale;
   1272       this.l *= scale; this.r *= scale; this.t *= scale; this.b *= scale;
   1273       if (this.L) this.L *= scale;
   1274       if (this.R) this.R *= scale;
   1275       if (this.D) this.D *= scale;
   1276     },
   1277     combine: function (cbox,x,y) {
   1278       cbox.X = x; cbox.Y = y;  // save for use with line breaking
   1279       scale = cbox.rscale;
   1280       if (x + scale*cbox.r > this.r) this.r = x + scale*cbox.r;
   1281       if (x + scale*cbox.l < this.l) this.l = x + scale*cbox.l;
   1282       if (x + scale*(cbox.w+(cbox.L||0)+(cbox.R||0)) > this.w)
   1283         this.w  = x + scale*(cbox.w + (cbox.L||0) + (cbox.R||0));
   1284       if (y + scale*cbox.h > this.h) this.h = y + scale*cbox.h;
   1285       if (cbox.D && (this.D == null || scale*cbox.D - y > this.D) && scale*cbox.D > this.d) this.D = scale*cbox.D - y;
   1286         else if (cbox.D == null && this.D) delete this.D;
   1287       if (scale*cbox.d - y > this.d) this.d = scale*cbox.d - y;
   1288       if (y + scale*cbox.t > this.t) this.t = y + scale*cbox.t;
   1289       if (scale*cbox.b - y > this.b) this.b = scale*cbox.b - y;
   1290     },
   1291     append: function (cbox) {
   1292       scale = cbox.rscale; var x = this.w;
   1293       if (x + scale*cbox.r > this.r) this.r = x + scale*cbox.r;
   1294       if (x + scale*cbox.l < this.l) this.l = x + scale*cbox.l;
   1295       this.w += scale*(cbox.w+(cbox.L||0)+(cbox.R||0)) ;
   1296       if (scale*cbox.h > this.h) this.h = scale*cbox.h;
   1297       if (cbox.D && (this.D == null || scale*cbox.D > this.D) && scale*cbox.D > this.d) this.D = scale*cbox.D;
   1298         else if (cbox.D == null && this.D) delete this.D;
   1299       if (scale*cbox.d > this.d) this.d = scale*cbox.d;
   1300       if (scale*cbox.t > this.t) this.t = scale*cbox.t;
   1301       if (scale*cbox.b > this.b) this.b = scale*cbox.b;
   1302     },
   1303     updateFrom: function (cbox) {
   1304       this.h = cbox.h; this.d = cbox.d; this.w = cbox.w; this.r = cbox.r; this.l = cbox.l;
   1305       this.t = cbox.t; this.b = cbox.b;
   1306       if (cbox.D) this.D = cbox.D; else delete this.D;
   1307     },
   1308     adjust: function (m,x,X,M) {
   1309       this[x] += CHTML.length2em(m,1,this.scale);
   1310       if (M == null) {
   1311         if (this[x] > this[X]) this[X] = this[x];
   1312       } else {
   1313         if (this[X] < M) this[X] = M;
   1314       }
   1315     }
   1316   },{
   1317     zero: function () {
   1318       return CHTML.BBOX({h:0, d:0, w:0, l:0, r:0, t:0, b:0, scale:1, rscale:1});
   1319     },
   1320     empty: function (bbox) {
   1321       if (!bbox) bbox = CHTML.BBOX.zero();
   1322       bbox.h = bbox.d = bbox.r = bbox.t = bbox.b = -BIGDIMEN;
   1323       bbox.w = 0;  bbox.l = BIGDIMEN;
   1324       return bbox;
   1325     },
   1326     //
   1327     //  CSS styles that affect BBOXes
   1328     //
   1329     styleAdjust: [
   1330       ["borderTopWidth","h","t"],
   1331       ["borderRightWidth","w","r"],
   1332       ["borderBottomWidth","d","b"],
   1333       ["borderLeftWidth","w","l",0],
   1334       ["paddingTop","h","t"],
   1335       ["paddingRight","w","r"],
   1336       ["paddingBottom","d","b"],
   1337       ["paddingLeft","w","l",0],
   1338     ]
   1339   });
   1340   
   1341   /**********************************************************/
   1342 
   1343   MathJax.Hub.Register.StartupHook("mml Jax Ready",function () {
   1344     MML = MathJax.ElementJax.mml;
   1345 
   1346     /********************************************************/
   1347     
   1348     MML.mbase.Augment({
   1349       toCommonHTML: function (node,options) {
   1350         return this.CHTMLdefaultNode(node,options);
   1351       },
   1352       CHTMLmultiline: function () {MML.mbase.CHTMLautoloadFile("multiline")},
   1353 
   1354       CHTMLdefaultNode: function (node,options) {
   1355         if (!options) options = {};
   1356         node = this.CHTMLcreateNode(node); this.CHTML = CHTML.BBOX.empty();
   1357         this.CHTMLhandleStyle(node);
   1358         this.CHTMLhandleScale(node);
   1359         if (this.isToken) this.CHTMLgetVariant();
   1360         var m = Math.max((options.minChildren||0),this.data.length);
   1361         for (var i = 0; i < m; i++) this.CHTMLaddChild(node,i,options);
   1362         if (!options.noBBox) this.CHTML.clean();
   1363         this.CHTMLhandleSpace(node);
   1364         this.CHTMLhandleBBox(node);
   1365         this.CHTMLhandleColor(node);
   1366         return node;
   1367       },
   1368       CHTMLaddChild: function (node,i,options) {
   1369         var child = this.data[i], cnode;
   1370         if (child) {
   1371           var type = options.childNodes;
   1372           if (type) {
   1373             if (type instanceof Array) type = type[i]||"span";
   1374             node = CHTML.addElement(node,type);
   1375           }
   1376           cnode = child.toCommonHTML(node,options.childOptions);
   1377           if (type && child.CHTML.rscale !== 1) {
   1378             // move scale factor to outer container (which seems to be more accurate)
   1379             node.style.fontSize = node.firstChild.style.fontSize;
   1380             node.firstChild.style.fontSize = "";
   1381           }
   1382           if (!options.noBBox) {
   1383             var bbox = this.CHTML, cbox = child.CHTML;
   1384             bbox.append(cbox);
   1385             if (cbox.ic) {bbox.ic = cbox.ic} else {delete bbox.ic}
   1386             if (cbox.skew) bbox.skew = cbox.skew;
   1387             if (cbox.pwidth) bbox.pwidth = cbox.pwidth;
   1388           }
   1389         } else if (options.forceChild) {cnode = CHTML.addElement(node,"mjx-box")}
   1390         return cnode;
   1391       },
   1392       
   1393       CHTMLchildNode: function (node,i) {
   1394         node = node.childNodes[i];
   1395         if (node.nodeName.toLowerCase() === "a") node = node.firstChild;
   1396         return node;
   1397       },
   1398       CHTMLcoreNode: function (node) {
   1399         return this.CHTMLchildNode(node,this.CoreIndex());
   1400       },
   1401       
   1402       CHTMLstretchChildV: function (i,H,D) {
   1403         var data = this.data[i];
   1404         if (data) {
   1405           var bbox = this.CHTML, dbox = data.CHTML;
   1406           if (dbox.stretch || (dbox.stretch == null && data.CHTMLcanStretch("Vertical",H,D))) {
   1407             var w = dbox.w;
   1408             dbox = data.CHTMLstretchV(H,D);
   1409             bbox.w += dbox.w - w;
   1410             if (bbox.w > bbox.r) bbox.r = bbox.w;
   1411             if (dbox.h > bbox.h) bbox.h = dbox.h;
   1412             if (dbox.d > bbox.d) bbox.d = dbox.d;
   1413             if (dbox.t > bbox.t) bbox.t = dbox.t;
   1414             if (dbox.b > bbox.b) bbox.b = dbox.b;
   1415           }
   1416         }
   1417       },
   1418       CHTMLstretchChildH: function (i,W,node) {
   1419         var data = this.data[i];
   1420         if (data) {
   1421           var bbox = this.CHTML, dbox = data.CHTML;
   1422           if (dbox.stretch || (dbox.stretch == null && data.CHTMLcanStretch("Horizontal",W))) {
   1423             var w = dbox.w;
   1424             dbox = data.CHTMLstretchH(this.CHTMLchildNode(node,i),W);
   1425             bbox.w += dbox.w - w;
   1426             if (bbox.w > bbox.r) bbox.r = bbox.w;
   1427             if (dbox.h > bbox.h) bbox.h = dbox.h;
   1428             if (dbox.d > bbox.d) bbox.d = dbox.d;
   1429             if (dbox.t > bbox.t) bbox.t = dbox.t;
   1430             if (dbox.b > bbox.b) bbox.b = dbox.b;
   1431           }
   1432         }
   1433       },
   1434 
   1435       CHTMLcanStretch: function (direction,H,D) {
   1436         var stretch = false;
   1437         if (this.isEmbellished()) {
   1438           var core = this.Core();
   1439           if (core && core !== this) stretch = core.CHTMLcanStretch(direction,H,D);
   1440         }
   1441         this.CHTML.stretch = stretch;
   1442         return stretch;
   1443       },
   1444       CHTMLstretchV: function (h,d) {
   1445         this.CHTML.updateFrom(this.Core().CHTMLstretchV(h,d));
   1446         return this.CHTML;
   1447       },
   1448       CHTMLstretchH: function (node,w) {
   1449         this.CHTML.updateFrom(this.CHTMLstretchCoreH(node,w));
   1450         return this.CHTML;
   1451       },
   1452       CHTMLstretchCoreH: function (node,w) {
   1453         return this.Core().CHTMLstretchH(this.CHTMLcoreNode(node),w);
   1454       },
   1455 
   1456       CHTMLcreateNode: function (node) {
   1457         if (!this.CHTML) this.CHTML = {};
   1458         this.CHTML = CHTML.BBOX.zero();
   1459         if (this.href) node = CHTML.addElement(node,"a",{href:this.href, isMathJax:true});
   1460         if (!this.CHTMLnodeID) this.CHTMLnodeID = CHTML.GetID();
   1461         var id = (this.id || "MJXc-Node-"+this.CHTMLnodeID)+CHTML.idPostfix;
   1462         return this.CHTMLhandleAttributes(CHTML.addElement(node,"mjx-"+this.type,{id:id}));
   1463       },
   1464       CHTMLnodeElement: function () {
   1465         if (!this.CHTMLnodeID) {return null}
   1466         return document.getElementById((this.id||"MJXc-Node-"+this.CHTMLnodeID)+CHTML.idPostfix);
   1467       },
   1468       
   1469       CHTMLlength2em: function (length,size) {
   1470         return CHTML.length2em(length,size,this.CHTML.scale);
   1471       },
   1472       
   1473       CHTMLhandleAttributes: function (node) {
   1474         if (this["class"]) {
   1475           if (node.className) node.className += " "+this["class"];
   1476             else node.className = this["class"];
   1477         }
   1478         //
   1479         //  Copy RDFa, aria, and other tags from the MathML to the CHTML
   1480         //  output nodes.  Don't copy those in the MML.nocopyAttributes list,
   1481         //  the ignoreMMLattributes configuration list, or anything that
   1482         //  already exists as a property of the node (e.g., no "onlick", etc.)
   1483         //  If a name in the ignoreMMLattributes object is set to false, then
   1484         //  the attribute WILL be copied.
   1485         //
   1486         if (this.attrNames) {
   1487           var copy = this.attrNames, skip = MML.nocopyAttributes, ignore = HUB.config.ignoreMMLattributes;
   1488           var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults);
   1489           for (var i = 0, m = copy.length; i < m; i++) {
   1490             var id = copy[i];
   1491             if (ignore[id] == false || (!skip[id] && !ignore[id] &&
   1492                 defaults[id] == null && typeof(node[id]) === "undefined")) {
   1493               node.setAttribute(id,this.attr[id])
   1494             }
   1495           }
   1496         }
   1497         return node;
   1498       },
   1499 
   1500       CHTMLhandleScale: function (node) {
   1501         var scale = 1, parent = this.parent, pscale = (parent ? parent.CHTML.scale : 1);
   1502         var values = this.getValues("scriptlevel","fontsize");
   1503         values.mathsize = this.Get("mathsize",null,!this.isToken);
   1504         if (values.scriptlevel !== 0) {
   1505           if (values.scriptlevel > 2) values.scriptlevel = 2;
   1506           scale = Math.pow(this.Get("scriptsizemultiplier"),values.scriptlevel);
   1507           values.scriptminsize = CHTML.length2em(this.Get("scriptminsize"),.8,1);
   1508           if (scale < values.scriptminsize) scale = values.scriptminsize;
   1509         }
   1510         if (this.removedStyles && this.removedStyles.fontSize && !values.fontsize)
   1511           values.fontsize = this.removedStyles.fontSize;
   1512         if (values.fontsize && !this.mathsize) values.mathsize = values.fontsize;
   1513         if (values.mathsize !== 1) scale *= CHTML.length2em(values.mathsize,1,1);
   1514         this.CHTML.scale = scale; pscale = this.CHTML.rscale = scale/pscale;
   1515         if (Math.abs(pscale-1) < .001) pscale = 1;
   1516         if (node && pscale !== 1) node.style.fontSize = CHTML.Percent(pscale);
   1517         return scale;
   1518       },
   1519 
   1520       CHTMLhandleStyle: function (node) {
   1521         if (!this.style) return;
   1522         var style = node.style;
   1523         style.cssText = this.style; this.removedStyles = {};
   1524         for (var i = 0, m = CHTML.removeStyles.length; i < m; i++) {
   1525           var id = CHTML.removeStyles[i];
   1526           if (style[id]) {
   1527             this.removedStyles[id] = style[id];
   1528             style[id] = "";
   1529           }
   1530         }
   1531       },
   1532 
   1533       CHTMLhandleBBox: function (node) {
   1534         var BBOX = this.CHTML, style = node.style;
   1535         if (this.data.length === 1 && (this.data[0].CHTML||{}).pwidth) {
   1536           BBOX.pwidth = this.data[0].CHTML.pwidth;
   1537           BBOX.mwidth = this.data[0].CHTML.mwidth;
   1538           style.width = "100%";
   1539         } else if (BBOX.pwidth) {
   1540           BBOX.mwidth = CHTML.Em(BBOX.w);
   1541           style.width = "100%";
   1542         } else if (BBOX.w < 0) {
   1543           style.width = "0px";
   1544           style.marginRight = CHTML.Em(BBOX.w);
   1545         }
   1546         if (!this.style) return;
   1547         // ### FIXME:  adjust for width, height, vertical-align?
   1548         for (var i = 0, m = CHTML.BBOX.styleAdjust.length; i < m; i++) {
   1549           var data = CHTML.BBOX.styleAdjust[i];
   1550           if (data && style[data[0]]) BBOX.adjust(style[data[0]],data[1],data[2],data[3]);
   1551         }
   1552       },
   1553 
   1554       CHTMLhandleColor: function (node) {
   1555         if (this.mathcolor) {node.style.color = this.mathcolor}
   1556           else if (this.color) {node.style.color = this.color}
   1557         if (this.mathbackground) {node.style.backgroundColor = this.mathbackground}
   1558           else if (this.background) {node.style.backgroundColor = this.background}
   1559       },
   1560       
   1561       CHTMLhandleSpace: function (node) {
   1562         if (!this.useMMLspacing) {
   1563           var space = this.texSpacing();
   1564           if (space !== "") {
   1565             this.CHTML.L = this.CHTMLlength2em(space);
   1566             node.className += " "+CHTML.SPACECLASS[space];
   1567           }
   1568         }
   1569       },
   1570 
   1571       CHTMLhandleText: function (node,text,variant) {
   1572         if (node.firstChild && !this.CHTML) this.CHTML = CHTML.BBOX.empty();
   1573         this.CHTML = CHTML.handleText(node,text,variant,this.CHTML);
   1574       },
   1575       
   1576       CHTMLgetVariant: function () {
   1577         var values = this.getValues("mathvariant","fontfamily","fontweight","fontstyle"), style;
   1578         values.hasVariant = this.Get("mathvariant",true);  // null if not explicitly specified
   1579         if (this.removedStyles) {
   1580           style = this.removedStyles;
   1581           if (style.fontFamily) values.family = style.fontFamily;
   1582           if (style.fontWeight) values.weight = style.fontWeight;
   1583           if (style.fontStyle)  values.style  = style.fontStyle;
   1584         }
   1585         if (!values.hasVariant) {
   1586           if (values.fontfamily) values.family = values.fontfamily;
   1587           if (values.fontweight) values.weight = values.fontweight;
   1588           if (values.fontstyle)  values.style  = values.fontstyle;
   1589         }
   1590         if (values.weight && values.weight.match(/^\d+$/))
   1591             values.weight = (parseInt(values.weight) > 600 ? "bold" : "normal");
   1592         var variant = values.mathvariant; if (this.variantForm) variant = "-TeX-variant";
   1593         if (values.family && !values.hasVariant) {
   1594           if (!values.weight && values.mathvariant.match(/bold/)) values.weight = "bold";
   1595           if (!values.style && values.mathvariant.match(/italic/)) values.style = "italic";
   1596           this.CHTMLvariant = {fonts:[], noRemap:true, cache:{}, style: {
   1597             "font-family":values.family, "font-weight":values.weight||"normal", "font-style":values.style||"normal"
   1598           }};
   1599           return;
   1600         }
   1601         if (values.weight === "bold") {
   1602           variant = {
   1603             normal:MML.VARIANT.BOLD, italic:MML.VARIANT.BOLDITALIC,
   1604             fraktur:MML.VARIANT.BOLDFRAKTUR, script:MML.VARIANT.BOLDSCRIPT,
   1605             "sans-serif":MML.VARIANT.BOLDSANSSERIF,
   1606             "sans-serif-italic":MML.VARIANT.SANSSERIFBOLDITALIC
   1607           }[variant]||variant;
   1608         } else if (values.weight === "normal") {
   1609           variant = {
   1610             bold:MML.VARIANT.normal, "bold-italic":MML.VARIANT.ITALIC,
   1611             "bold-fraktur":MML.VARIANT.FRAKTUR, "bold-script":MML.VARIANT.SCRIPT,
   1612             "bold-sans-serif":MML.VARIANT.SANSSERIF,
   1613             "sans-serif-bold-italic":MML.VARIANT.SANSSERIFITALIC
   1614           }[variant]||variant;
   1615         }
   1616         if (values.style === "italic") {
   1617           variant = {
   1618             normal:MML.VARIANT.ITALIC, bold:MML.VARIANT.BOLDITALIC,
   1619             "sans-serif":MML.VARIANT.SANSSERIFITALIC,
   1620             "bold-sans-serif":MML.VARIANT.SANSSERIFBOLDITALIC
   1621           }[variant]||variant;
   1622         } else if (values.style === "normal") {
   1623           variant = {
   1624             italic:MML.VARIANT.NORMAL, "bold-italic":MML.VARIANT.BOLD,
   1625             "sans-serif-italic":MML.VARIANT.SANSSERIF,
   1626             "sans-serif-bold-italic":MML.VARIANT.BOLDSANSSERIF
   1627           }[variant]||variant;
   1628         }
   1629         this.CHTMLvariant = CHTML.FONTDATA.VARIANT[variant] ||
   1630                             CHTML.FONTDATA.VARIANT[MML.VARIANT.NORMAL];
   1631       },
   1632 
   1633       CHTMLbboxFor: function (n) {
   1634         if (this.data[n] && this.data[n].CHTML) return this.data[n].CHTML;
   1635         return CHTML.BBOX.zero();
   1636       },
   1637       //
   1638       //  Debugging function to see if internal BBox matches actual bbox
   1639       //
   1640       CHTMLdrawBBox: function (node,bbox) {
   1641         if (!bbox) bbox = this.CHTML;
   1642         var box = CHTML.Element("mjx-box",
   1643           {style:{opacity:.25,"margin-left":CHTML.Em(-(bbox.w+(bbox.R||0)))}},[
   1644           ["mjx-box",{style:{
   1645             height:CHTML.Em(bbox.h),width:CHTML.Em(bbox.w),
   1646             "background-color":"red"
   1647           }}],
   1648           ["mjx-box",{style:{
   1649             height:CHTML.Em(bbox.d),width:CHTML.Em(bbox.w),
   1650             "margin-left":CHTML.Em(-bbox.w),"vertical-align":CHTML.Em(-bbox.d),
   1651             "background-color":"green"
   1652           }}]
   1653         ]);
   1654         if (node.nextSibling) {node.parentNode.insertBefore(box,node.nextSibling)}
   1655           else {node.parentNode.appendChild(box)}
   1656       },
   1657 
   1658       CHTMLnotEmpty: function (mml) {
   1659         while (mml && mml.data.length < 2 && (mml.type === "mrow" || mml.type === "texatom"))
   1660           mml = mml.data[0];
   1661         return !!mml;
   1662       }
   1663 
   1664     },{
   1665       //
   1666       //  Autoload files based on node type or file name
   1667       //
   1668       CHTMLautoload: function () {
   1669 	var file = CHTML.autoloadDir+"/"+this.type+".js";
   1670 	HUB.RestartAfter(AJAX.Require(file));
   1671       },
   1672       CHTMLautoloadFile: function (name) {
   1673 	var file = CHTML.autoloadDir+"/"+name+".js";
   1674 	HUB.RestartAfter(AJAX.Require(file));
   1675       },
   1676       //
   1677       //  For use with embellished operators
   1678       //
   1679       CHTMLstretchV: function (h,d) {
   1680         this.Core().CHTMLstretchV(h,d);
   1681         this.toCommonHTML(this.CHTMLnodeElement(),true);
   1682         return this.CHTML;
   1683       },
   1684       CHTMLstretchH: function (node,w) {
   1685         this.CHTMLstretchCoreH(node,w);
   1686         this.toCommonHTML(node,true);
   1687         return this.CHTML;
   1688       }      
   1689     });
   1690 
   1691     /********************************************************/
   1692     
   1693     MML.chars.Augment({
   1694       toCommonHTML: function (node,options) {
   1695         if (options == null) options = {};
   1696         var text = this.toString();
   1697         if (options.remap) text = options.remap(text,options.remapchars);
   1698         this.CHTMLhandleText(node,text,options.variant||this.parent.CHTMLvariant);
   1699       }
   1700     });
   1701     MML.entity.Augment({
   1702       toCommonHTML: function (node,options) {
   1703         if (options == null) options = {};
   1704         var text = this.toString();
   1705         if (options.remapchars) text = options.remap(text,options.remapchars);
   1706         this.CHTMLhandleText(node,text,options.variant||this.parent.CHTMLvariant);
   1707       }
   1708     });
   1709 
   1710     /********************************************************/
   1711     
   1712     MML.math.Augment({
   1713       toCommonHTML: function (node) {
   1714         node = this.CHTMLdefaultNode(node);
   1715         if (this.CHTML.w < 0) {
   1716           node.parentNode.style.width = "0px";
   1717           node.parentNode.style.marginRight = CHTML.Em(this.CHTML.w);
   1718         }
   1719         var alttext = this.Get("alttext");
   1720         if (alttext && !node.getAttribute("aria-label")) node.setAttribute("aria-label",alttext);
   1721         if (!node.getAttribute("role")) node.setAttribute("role","math");
   1722         if (this.CHTML.pwidth) {
   1723           node.parentNode.style.width = this.CHTML.pwidth;
   1724           node.parentNode.style.minWidth = this.CHTML.mwidth||CHTML.Em(this.CHTML.w);
   1725         } else if (!this.isMultiline && this.Get("display") === "block") {
   1726           var values = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
   1727           if (values.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) values.indentalign = values.indentalignfirst;
   1728           if (values.indentalign === MML.INDENTALIGN.AUTO) values.indentalign = CONFIG.displayAlign;
   1729           if (values.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) values.indentshift = values.indentshiftfirst;
   1730           if (values.indentshift === "auto") values.indentshift = "0";
   1731           var shift = this.CHTMLlength2em(values.indentshift,CHTML.cwidth);
   1732           if (CONFIG.displayIndent !== "0") {
   1733             var indent = this.CHTMLlength2em(CONFIG.displayIndent,CHTML.cwidth);
   1734             shift += (values.indentalign === MML.INDENTALIGN.RIGHT ? -indent : indent);
   1735           }
   1736           var styles = node.parentNode.parentNode.style;
   1737           styles.textAlign = values.indentalign;
   1738           // ### FIXME: make percentage widths respond to changes in container
   1739           if (shift) {
   1740             shift *= CHTML.em/CHTML.outerEm;
   1741             HUB.Insert(styles,({
   1742               left: {marginLeft: CHTML.Em(shift)},
   1743               right: {marginRight: CHTML.Em(-shift)},
   1744               center: {marginLeft: CHTML.Em(shift), marginRight: CHTML.Em(-shift)}
   1745             })[values.indentalign]);
   1746           }
   1747         }
   1748         return node;
   1749       }
   1750     });
   1751     
   1752     /********************************************************/
   1753     
   1754     MML.mi.Augment({
   1755       toCommonHTML: function (node) {
   1756         node = this.CHTMLdefaultNode(node);
   1757         var bbox = this.CHTML, text = this.data.join("");
   1758         if (bbox.skew != null && text.length !== 1) delete bbox.skew;
   1759         if (bbox.r > bbox.w && text.length === 1 && !this.CHTMLvariant.noIC) {
   1760           bbox.ic = bbox.r - bbox.w; bbox.w = bbox.r;
   1761           node.lastChild.style.paddingRight = CHTML.Em(bbox.ic);
   1762         }
   1763         return node;
   1764       }
   1765     });
   1766 
   1767     /********************************************************/
   1768     
   1769     MML.mn.Augment({
   1770       toCommonHTML: function (node) {
   1771         node = this.CHTMLdefaultNode(node);
   1772         var bbox = this.CHTML, text = this.data.join("");
   1773         if (bbox.skew != null && text.length !== 1) delete bbox.skew;
   1774         if (bbox.r > bbox.w && text.length === 1 && !this.CHTMLvariant.noIC) {
   1775           bbox.ic = bbox.r - bbox.w; bbox.w = bbox.r;
   1776           node.lastChild.style.paddingRight = CHTML.Em(bbox.ic);
   1777         }
   1778         return node;
   1779       }
   1780     });
   1781 
   1782     /********************************************************/
   1783     
   1784     MML.mo.Augment({
   1785       toCommonHTML: function (node) {
   1786         node = this.CHTMLcreateNode(node);
   1787         this.CHTMLhandleStyle(node);
   1788         this.CHTMLhandleScale(node);
   1789         this.CHTMLgetVariant();
   1790         CHTML.BBOX.empty(this.CHTML);
   1791         
   1792         var values = this.getValues("displaystyle","largeop");
   1793         values.variant = this.CHTMLvariant;
   1794         values.text = this.data.join("");
   1795         if (values.text == "") {
   1796           if (this.fence) node.style.width = CHTML.Em(CHTML.TEX.nulldelimiterspace);
   1797         } else {
   1798           this.CHTMLadjustAccent(values);
   1799           this.CHTMLadjustVariant(values);
   1800 
   1801           for (var i = 0, m = this.data.length; i < m; i++) {
   1802             this.CHTMLaddChild(node,i,{childOptions:{
   1803               variant: values.mathvariant,
   1804               remap: this.remap,
   1805               remapchars: values.remapchars
   1806             }});
   1807           }
   1808           if (values.text.length !== 1) delete this.CHTML.skew;
   1809             else if (this.CHTML.w === 0 && this.CHTML.l < 0) this.CHTMLfixCombiningChar(node);
   1810           if (values.largeop) this.CHTMLcenterOp(node);
   1811         }
   1812 
   1813         this.CHTML.clean();
   1814         this.CHTMLhandleBBox(node);
   1815         this.CHTMLhandleSpace(node);
   1816         this.CHTMLhandleColor(node);
   1817 
   1818         return node;
   1819       },
   1820       CHTMLhandleSpace: function (node) {
   1821         if (this.useMMLspacing) {
   1822           var values = this.getValues("scriptlevel","lspace","rspace");
   1823           values.lspace = Math.max(0,this.CHTMLlength2em(values.lspace));
   1824           values.rspace = Math.max(0,this.CHTMLlength2em(values.rspace));
   1825           if (values.scriptlevel > 0) {
   1826             if (!this.hasValue("lspace")) values.lspace = .15;
   1827             if (!this.hasValue("rspace")) values.rspace = .15;
   1828           }
   1829           var core = this, parent = this.Parent();
   1830           while (parent && parent.isEmbellished() && parent.Core() === core)
   1831             {core = parent; parent = parent.Parent(); node = core.CHTMLnodeElement()}
   1832           if (values.lspace) node.style.paddingLeft =  CHTML.Em(values.lspace);
   1833           if (values.rspace) node.style.paddingRight = CHTML.Em(values.rspace);
   1834           this.CHTML.L = values.lspace; this.CHTML.R = values.rspace;
   1835         } else {
   1836           this.SUPER(arguments).CHTMLhandleSpace.apply(this,arguments);
   1837         }
   1838       },
   1839       CHTMLadjustAccent: function (data) {
   1840         var parent = this.CoreParent(); data.parent = parent;
   1841         if (data.text.length === 1 && parent && parent.isa(MML.munderover) && 
   1842             this.CoreText(parent.data[parent.base]).length === 1) {
   1843           var over = parent.data[parent.over], under = parent.data[parent.under];
   1844           if (over && this === over.CoreMO() && parent.Get("accent")) {
   1845             data.remapchars = CHTML.FONTDATA.REMAPACCENT;
   1846           } else if (under && this === under.CoreMO() && parent.Get("accentunder")) {
   1847             data.remapchars = CHTML.FONTDATA.REMAPACCENTUNDER;
   1848           }
   1849         }
   1850       },
   1851       CHTMLadjustVariant: function (data) {
   1852         var parent = data.parent,
   1853             isScript = (parent && parent.isa(MML.msubsup) && this !== parent.data[parent.base]);
   1854         if (data.largeop) data.mathvariant = (data.displaystyle ? "-largeOp" : "-smallOp");
   1855         if (isScript) {
   1856           data.remapchars = this.remapChars;
   1857           if (data.text.match(/['`"\u00B4\u2032-\u2037\u2057]/))
   1858             data.mathvariant = "-TeX-variant";  // ### FIXME: handle other fonts
   1859         }
   1860       },
   1861       CHTMLfixCombiningChar: function (node) {
   1862         //
   1863         //  IE doesn't display combining chararacters unless they combine with
   1864         //  something, so put them over a space and remove the space's width
   1865         //
   1866         node = node.firstChild;
   1867         var space = CHTML.Element("mjx-span",{style:{width:".25em","margin-left":"-.25em"}});
   1868         node.insertBefore(space,node.firstChild);
   1869       },
   1870       CHTMLcenterOp: function (node) {
   1871         var bbox = this.CHTML;
   1872         var p = (bbox.h - bbox.d)/2 - CHTML.TEX.axis_height;
   1873         if (Math.abs(p) > .001) node.style.verticalAlign = CHTML.Em(-p);
   1874         bbox.h -= p; bbox.d += p;
   1875         if (bbox.r > bbox.w) {
   1876           bbox.ic = bbox.r - bbox.w; bbox.w = bbox.r;
   1877           node.style.paddingRight = CHTML.Em(bbox.ic);
   1878         }
   1879       },
   1880       CHTMLcanStretch: function (direction,H,D) {
   1881         if (!this.Get("stretchy")) return false;
   1882         var c = this.data.join(""); if (c.length !== 1) return false;
   1883         var values = {text: c};
   1884         this.CHTMLadjustAccent(values);
   1885         if (values.remapchars) c = values.remapchars[c]||c;
   1886         c = CHTML.FONTDATA.DELIMITERS[c.charCodeAt(0)];
   1887         var stretch = (c && c.dir === direction.substr(0,1));
   1888         if (stretch) {
   1889           stretch = (this.CHTML.h !== H || this.CHTML.d !== D ||
   1890             !!this.Get("minsize",true) || !!this.Get("maxsize",true));
   1891           if (stretch) this.CHTML.stretch = true;
   1892         }
   1893         return stretch;
   1894       },
   1895       CHTMLstretchV: function (h,d) {
   1896         var node = this.CHTMLnodeElement(), bbox = this.CHTML;
   1897         var values = this.getValues("symmetric","maxsize","minsize");
   1898         //
   1899         //  Determine the height needed
   1900         //
   1901         var H, a = CHTML.TEX.axis_height;
   1902         if (values.symmetric) {H = 2*Math.max(h-a,d+a)} else {H = h + d}
   1903         values.maxsize = this.CHTMLlength2em(values.maxsize,bbox.h+bbox.d);
   1904         values.minsize = this.CHTMLlength2em(values.minsize,bbox.h+bbox.d);
   1905         H = Math.max(values.minsize,Math.min(values.maxsize,H));
   1906         //
   1907         //  If we are not already stretched to this height
   1908         //
   1909         if (H !== bbox.sH) {
   1910           //
   1911           //  Get a delimiter of the proper height and save the height
   1912           //
   1913           if (H != values.minsize)
   1914             {H = [Math.max(H*CHTML.TEX.delimiterfactor/1000,H-CHTML.TEX.delimitershortfall),H]}
   1915           while (node.firstChild) node.removeChild(node.firstChild);
   1916           this.CHTML = bbox = CHTML.createDelimiter(node,this.data.join("").charCodeAt(0),H,bbox);
   1917           bbox.sH = (H instanceof Array ? H[1] : H);
   1918           //
   1919           //  Reposition as needed
   1920           //
   1921           if (values.symmetric) {H = (bbox.h + bbox.d)/2 + a}
   1922             else {H = (bbox.h + bbox.d) * h/(h + d)}
   1923           H -= bbox.h;
   1924           if (Math.abs(H) > .05) {
   1925             node.style.verticalAlign = CHTML.Em(H);
   1926             bbox.h += H; bbox.d -= H; bbox.t += H; bbox.b -= H;
   1927           }
   1928         }
   1929         return this.CHTML;
   1930       },
   1931       CHTMLstretchH: function (node,W) {
   1932         var bbox = this.CHTML;
   1933         var values = this.getValues("maxsize","minsize","mathvariant","fontweight");
   1934         if ((values.fontweight === "bold" || (this.removedStyles||{}).fontWeight === "bold" ||
   1935             parseInt(values.fontweight) >= 600) && !this.Get("mathvariant",true))
   1936                 values.mathvariant = MML.VARIANT.BOLD;
   1937         values.maxsize = this.CHTMLlength2em(values.maxsize,bbox.w);
   1938         values.minsize = this.CHTMLlength2em(values.minsize,bbox.w);
   1939         W = Math.max(values.minsize,Math.min(values.maxsize,W));
   1940         if (W !== bbox.sW) {
   1941           while (node.firstChild) node.removeChild(node.firstChild);
   1942           this.CHTML = bbox = CHTML.createDelimiter(node,this.data.join("").charCodeAt(0),W,bbox,values.mathvariant);
   1943           bbox.sW = W;
   1944         }
   1945         return this.CHTML;
   1946       }
   1947 
   1948     });
   1949 
   1950     /********************************************************/
   1951 
   1952     MML.mtext.Augment({
   1953       CHTMLgetVariant: function () {
   1954         if (CHTML.config.mtextFontInherit || this.Parent().type === "merror") {
   1955           var scale = (CHTML.config.scale/100)/CHTML.scale;
   1956           var variant = {cache:{}, fonts:[], className:"MJXc-font-inherit", rscale:scale,
   1957                          style:{"font-size":CHTML.Percent(scale)}};
   1958           var name = this.Get("mathvariant");
   1959           if (name.match(/bold/)) variant.style["font-weight"] = "bold";
   1960           if (name.match(/italic|-tex-mathit/)) variant.style["font-style"] = "italic";
   1961           if (name === "monospace") variant.className += " MJXc-monospace-font";
   1962           if (name === "double-struck") variant.className += " MJXc-double-struck-font";
   1963           if (name.match(/fraktur/)) variant.className += " MJXc-fraktur-font";
   1964           if (name.match(/sans-serif/)) variant.className += " MJXc-sans-serif-font";
   1965           if (name.match(/script/)) variant.className += " MJXc-script-font";
   1966           this.CHTMLvariant = variant;
   1967         } else {
   1968           this.SUPER(arguments).CHTMLgetVariant.call(this);
   1969         }
   1970       }
   1971     });
   1972 
   1973     /********************************************************/
   1974     
   1975     MML.merror.Augment({
   1976       toCommonHTML: function (node) {
   1977         node = this.CHTMLdefaultNode(node);
   1978         var bbox = this.CHTML;
   1979         //
   1980         //  Adjust for font-size: 90%
   1981         //
   1982         bbox.rescale(.9);
   1983         //
   1984         //  Adjust for padding and border
   1985         //
   1986         bbox.h += 3/CHTML.em; if (bbox.h > bbox.t) bbox.t = bbox.h;
   1987         bbox.d += 3/CHTML.em; if (bbox.d > bbox.b) bbox.b = bbox.d;
   1988         bbox.w += 8/CHTML.em; bbox.r = bbox.w; bbox.l = 0;
   1989         return node;
   1990       }
   1991     });
   1992     
   1993     /********************************************************/
   1994     
   1995     MML.mspace.Augment({
   1996       toCommonHTML: function (node) {
   1997         node = this.CHTMLcreateNode(node);
   1998         this.CHTMLhandleStyle(node);
   1999         this.CHTMLhandleScale(node);
   2000         var values = this.getValues("height","depth","width");
   2001         var w = this.CHTMLlength2em(values.width),
   2002             h = this.CHTMLlength2em(values.height),
   2003             d = this.CHTMLlength2em(values.depth);
   2004         var bbox = this.CHTML;
   2005         bbox.w = bbox.r = w; bbox.h = bbox.t = h; bbox.d = bbox.b = d; bbox.l = 0;
   2006         if (w < 0) {node.style.marginRight = CHTML.Em(w); w = 0}
   2007         node.style.width = CHTML.Em(w);
   2008         node.style.height = CHTML.Em(Math.max(0,h+d));
   2009         if (d) node.style.verticalAlign = CHTML.Em(-d);
   2010         this.CHTMLhandleBBox(node);
   2011         this.CHTMLhandleColor(node);
   2012         return node;
   2013       }
   2014     });
   2015 
   2016     /********************************************************/
   2017     
   2018     MML.mpadded.Augment({
   2019       toCommonHTML: function (node,stretch) {
   2020         var child;
   2021         if (stretch) {
   2022           node = node.firstChild; child = node.firstChild;
   2023         } else {
   2024           node = this.CHTMLdefaultNode(node,{childNodes:"mjx-box", forceChild:true});
   2025           child = node.firstChild; node = CHTML.addElement(node,"mjx-block");
   2026           node.appendChild(child); CHTML.addElement(node,"mjx-strut"); // force proper alignment of short heights
   2027         }
   2028         var cbox = this.CHTMLbboxFor(0);
   2029         var values = this.getValues("width","height","depth","lspace","voffset");
   2030         var x = 0, y = 0, w = cbox.w, h = cbox.h, d = cbox.d;
   2031         child.style.width = 0; child.style.margin = CHTML.Em(-h)+" 0 "+CHTML.Em(-d);
   2032         if (values.width !== "")  w = this.CHTMLdimen(values.width,"w",w,0);
   2033         if (values.height !== "") h = this.CHTMLdimen(values.height,"h",h,0);
   2034         if (values.depth !== "")  d = this.CHTMLdimen(values.depth,"d",d,0);
   2035         if (values.voffset !== "") {
   2036           y = this.CHTMLdimen(values.voffset);
   2037           if (y) {
   2038             child.style.position = "relative";
   2039             child.style.top = CHTML.Em(-y);
   2040           }
   2041         }
   2042         if (values.lspace !== "") {
   2043           x = this.CHTMLdimen(values.lspace);
   2044           if (x) {
   2045             child.style.position = "relative";
   2046             child.style.left = CHTML.Em(x);
   2047           }
   2048         }
   2049         node.style.width = 0;
   2050         node.style.marginTop = CHTML.Em(h-STRUTHEIGHT);
   2051         node.style.padding = "0 "+CHTML.Em(w)+" "+CHTML.Em(d)+" 0";
   2052         var bbox = CHTML.BBOX({w:w, h:h, d:d, l:0, r:w, t:h, b:d,
   2053                                scale:this.CHTML.scale, rscale:this.CHTML.rscale});
   2054         bbox.combine(cbox,x,y);
   2055         bbox.w = w; bbox.h = h; bbox.d = d;
   2056         this.CHTML = bbox;
   2057         return node.parentNode;
   2058       },
   2059       CHTMLstretchV: MML.mbase.CHTMLstretchV,
   2060       CHTMLstretchH: MML.mbase.CHTMLstretchH,
   2061       CHTMLdimen: function (length,d,D,m) {
   2062         if (m == null) {m = -BIGDIMEN}
   2063         length = String(length);
   2064         var match = length.match(/width|height|depth/);
   2065         var size = (match ? this.CHTML[match[0].charAt(0)] : (d ? this.CHTML[d] : 0));
   2066         var dimen = (this.CHTMLlength2em(length,size)||0);
   2067         if (length.match(/^[-+]/) && D != null) dimen += D;
   2068         if (m != null) dimen = Math.max(m,dimen);
   2069         return dimen;
   2070       }
   2071     });
   2072 
   2073     /********************************************************/
   2074     
   2075     MML.munderover.Augment({
   2076       toCommonHTML: function (node,stretch) {
   2077         var values = this.getValues("displaystyle","accent","accentunder","align");
   2078         var base = this.data[this.base];
   2079         if (!values.displaystyle && base != null &&
   2080             (base.movablelimits || base.CoreMO().Get("movablelimits")))
   2081                 return MML.msubsup.prototype.toCommonHTML.call(this,node,stretch);
   2082         //
   2083         //  Get the nodes for base and limits
   2084         //
   2085         var under, over, nodes = [];
   2086         if (stretch) {
   2087           if (this.data[this.base])  base = CHTML.getNode(node,"mjx-op");
   2088           if (this.data[this.under]) under = CHTML.getNode(node,"mjx-under");
   2089           if (this.data[this.over])  over = CHTML.getNode(node,"mjx-over");
   2090           nodes[0] = base; nodes[1] = under||over; nodes[2] = over;
   2091         } else {
   2092           var types = ["mjx-op","mjx-under","mjx-over"];
   2093           if (this.over === 1) types[1] = types[2];
   2094           node = this.CHTMLdefaultNode(node,{
   2095             childNodes:types, noBBox:true, forceChild:true, minChildren: 2
   2096           });
   2097           nodes[0] = base = node.removeChild(node.firstChild);
   2098           nodes[1] = under = over = node.removeChild(node.firstChild);
   2099           if (node.firstChild) nodes[2] = over = node.removeChild(node.firstChild);
   2100         }
   2101         //
   2102         //  Get the bounding boxes and the maximum width
   2103         //
   2104         var boxes = [], W = this.CHTMLgetBBoxes(boxes,nodes,values);
   2105         var bbox = boxes[this.base], BBOX = this.CHTML;
   2106         BBOX.w = W; BBOX.h = bbox.h; BBOX.d = bbox.d; // modified below
   2107         //
   2108         //  Add over- and under-scripts
   2109         //  
   2110         var stack = base, delta = 0;
   2111         if (bbox.ic) {delta = 1.3*bbox.ic + .05} // make faked IC be closer to expeted results
   2112         if (this.data[this.over]) stack = this.CHTMLaddOverscript(over,boxes,values,delta,base,stretch);
   2113         if (this.data[this.under]) this.CHTMLaddUnderscript(under,boxes,values,delta,node,stack,stretch);
   2114           else if (!stretch) node.appendChild(stack);
   2115         //
   2116         //  Handle horizontal positions
   2117         //
   2118         this.CHTMLplaceBoxes(base,under,over,values,boxes);
   2119         return node;
   2120       },
   2121       //
   2122       //  Get the bounding boxes for the children, stretch
   2123       //  any stretchable elements, and compute the maximum width
   2124       //  
   2125       CHTMLgetBBoxes: function (bbox,nodes,values) {
   2126         var i, m = this.data.length, scale,
   2127             w = -BIGDIMEN,  // maximum width of non-stretchy items
   2128             W = w;          // maximum width of all items
   2129         //
   2130         //  Get the maximum width
   2131         //
   2132         for (i = 0; i < m; i++) {
   2133           bbox[i] = this.CHTMLbboxFor(i); bbox[i].x = bbox[i].y = 0;
   2134           if (this.data[i]) bbox[i].stretch = this.data[i].CHTMLcanStretch("Horizontal");
   2135           scale = (i === this.base ? 1 : bbox[i].rscale);
   2136           if (i !== this.base) {delete bbox[i].L; delete bbox[i].R} // these are overriden by CSS
   2137           W = Math.max(W,scale*(bbox[i].w + (bbox[i].L||0) + (bbox[i].R||0)));
   2138           if (!bbox[i].stretch && W > w) w = W;
   2139         }
   2140         if (w === -BIGDIMEN) w = W;
   2141         //
   2142         //  Stretch those parts that need it
   2143         //
   2144         for (i = 0; i < m; i++) {
   2145           if (bbox[i].stretch) {
   2146             scale = (i === this.base ? 1 : bbox[i].rscale);
   2147             bbox[i] = this.data[i].CHTMLstretchH(nodes[i].firstChild,w/scale);
   2148             bbox[i].x = bbox[i].y = 0;
   2149             W = Math.max(W,scale*(bbox[i].w + (bbox[i].L||0) + (bbox[i].R||0)));
   2150           }
   2151         }
   2152         if (!bbox[this.base]) bbox[this.base] = CHTML.BBOX.empty();
   2153         return W;
   2154       },
   2155       //
   2156       //  Add an overscript
   2157       //
   2158       CHTMLaddOverscript: function (over,boxes,values,delta,base,stretch) {
   2159         var BBOX = this.CHTML;
   2160         var z1, z2, z3 = CHTML.TEX.big_op_spacing5, k;
   2161         var obox = boxes[this.over], bbox = boxes[this.base], scale = obox.rscale;
   2162         //
   2163         //  Put the base and script into a stack
   2164         //
   2165         if (!stretch) {
   2166           var stack = CHTML.Element("mjx-stack");
   2167           stack.appendChild(over); stack.appendChild(base);
   2168         }
   2169         if (obox.D) obox.d = obox.D;
   2170         if (obox.d < 0) {
   2171           //
   2172           // For negative depths, set the height and align to top
   2173           // in order to avoid extra baseline space
   2174           //
   2175           over.firstChild.style.verticalAlign = "top";
   2176           over.style.height = CHTML.Em(obox.h+obox.d);
   2177         }
   2178         //
   2179         //  Determine the spacing
   2180         //
   2181         obox.x = 0;
   2182         if (values.accent) {
   2183           if (obox.w < .001) obox.x += (obox.r - obox.l)/2; // center combining accents
   2184           k = CHTML.TEX.rule_thickness; z3 = 0;
   2185           if (bbox.skew) {
   2186             obox.x += scale*bbox.skew; BBOX.skew = scale*bbox.skew;
   2187             if (obox.x+scale*obox.w > BBOX.w) BBOX.skew += (BBOX.w - (obox.x+scale*obox.w))/2;
   2188           }
   2189         } else {
   2190           z1 = CHTML.TEX.big_op_spacing1;
   2191           z2 = CHTML.TEX.big_op_spacing3;
   2192           k = Math.max(z1,z2-Math.max(0,scale*obox.d));
   2193         }
   2194         obox.x += delta/2; obox.y = BBOX.h + k + z3 + scale*obox.d;
   2195         //
   2196         //  Position the overscript
   2197         //
   2198         if (k) over.style.paddingBottom = CHTML.Em(k/scale);
   2199         if (z3) over.style.paddingTop = CHTML.Em(z3/scale);
   2200         return stack;
   2201       },
   2202       //
   2203       //  Add an underscript
   2204       //
   2205       CHTMLaddUnderscript: function (under,boxes,values,delta,node,stack,stretch) {
   2206         var BBOX = this.CHTML;
   2207         var z1, z2, z3 = CHTML.TEX.big_op_spacing5, k;
   2208         var ubox = boxes[this.under], scale = ubox.rscale;
   2209         //
   2210         //  Create a table for the underscript
   2211         //
   2212         if (!stretch) {
   2213           CHTML.addElement(node,"mjx-itable",{},[
   2214             ["mjx-row",{},[["mjx-cell"]]],
   2215             ["mjx-row"]
   2216           ]);
   2217           node.firstChild.firstChild.firstChild.appendChild(stack);
   2218           node.firstChild.lastChild.appendChild(under);
   2219         }
   2220         if (ubox.D) ubox.d = ubox.D;
   2221         if (ubox.d < 0) {
   2222           //
   2223           // For negative depths, set the height and align to top
   2224           // in order to avoid extra baseline space
   2225           //
   2226           under.firstChild.style.verticalAlign = "top";
   2227           node.firstChild.style.marginBottom = CHTML.Em(ubox.d);
   2228         }
   2229         //
   2230         //  determine the spacing
   2231         //
   2232         if (values.accentunder) {
   2233           k = 2*CHTML.TEX.rule_thickness; z3 = 0;
   2234         } else {
   2235           z1 = CHTML.TEX.big_op_spacing2;
   2236           z2 = CHTML.TEX.big_op_spacing4;
   2237           k = Math.max(z1,z2-scale*ubox.h);
   2238         }
   2239         ubox.x = -delta/2; ubox.y = -(BBOX.d + k + z3 + scale*ubox.h);
   2240         //
   2241         //  Position the underscript
   2242         //
   2243         if (k) under.style.paddingTop = CHTML.Em(k/scale);
   2244         if (z3) under.style.paddingBottom = CHTML.Em(z3/scale);
   2245       },
   2246       //
   2247       //  Center boxes horizontally, taking offsets into account
   2248       //
   2249       CHTMLplaceBoxes: function (base,under,over,values,boxes) {
   2250         var W = this.CHTML.w, i, m = boxes.length, scale;
   2251         var BBOX = CHTML.BBOX.zero();
   2252         BBOX.scale = this.CHTML.scale; BBOX.rscale = this.CHTML.rscale;
   2253         boxes[this.base].x = boxes[this.base].y = 0; var dx = BIGDIMEN;
   2254         for (i = 0; i < m; i++) {
   2255           scale = (i === this.base ? 1 : boxes[i].rscale);
   2256           var w = scale*(boxes[i].w + (boxes[i].L||0) + (boxes[i].R||0));
   2257           boxes[i].x += {left:0, center:(W-w)/2, right:W-w}[values.align];
   2258           if (boxes[i].x < dx) dx = boxes[i].x;
   2259         }
   2260         for (i = 0; i < m; i++) {
   2261           if (this.data[i]) {
   2262             scale = (i === this.base ? 1 : boxes[i].rscale);
   2263             if (boxes[i].x - dx) {
   2264               var node = (i === this.base ? base : i === this.over ? over : under);
   2265               node.style.paddingLeft = CHTML.Em((boxes[i].x-dx)/scale);
   2266             }
   2267             BBOX.combine(boxes[i],boxes[i].x-dx,boxes[i].y);
   2268           }
   2269         }
   2270         this.CHTML = BBOX;
   2271       },
   2272       CHTMLstretchV: MML.mbase.CHTMLstretchV,
   2273       CHTMLstretchH: MML.mbase.CHTMLstretchH,
   2274       CHTMLchildNode: function (node,i) {
   2275         var types = ["mjx-op","mjx-under","mjx-over"];
   2276         if (this.over === 1) types[1] = types[2];
   2277         return CHTML.getNode(node,types[i]);
   2278       }
   2279     });
   2280 
   2281     /********************************************************/
   2282     
   2283     MML.msubsup.Augment({
   2284       toCommonHTML: function (node,stretch) {
   2285         var values = this.getValues(
   2286            "displaystyle","subscriptshift","superscriptshift","texprimestyle"
   2287         );
   2288         //
   2289         //  Get the nodes for base and limits
   2290         //
   2291         var base, sub, sup;
   2292         if (stretch) {
   2293           if (this.data[this.base]) base = CHTML.getNode(node,"mjx-base");
   2294           if (this.data[this.sub])  sub = CHTML.getNode(node,"mjx-sub");
   2295           if (this.data[this.sup])  sup = CHTML.getNode(node,"mjx-sup");
   2296           stack = CHTML.getNode(node,"mjx-stack");
   2297         } else {
   2298           var types = ["mjx-base","mjx-sub","mjx-sup"];
   2299           if (this.sup === 1) types[1] = types[2];
   2300           node = this.CHTMLdefaultNode(node,{
   2301             childNodes:types, noBBox:true, forceChild:true, minChildren: 3
   2302           });
   2303           base = node.childNodes[this.base];
   2304           sub = node.childNodes[this.sub]; sup = node.childNodes[this.sup];
   2305           if (!this.CHTMLnotEmpty(this.data[this.sub])) {node.removeChild(sub); sub = null}
   2306           if (!this.CHTMLnotEmpty(this.data[this.sup])) {node.removeChild(sup); sup = null}
   2307           if (node.childNodes.length === 3) {
   2308             var stack = CHTML.addElement(node,"mjx-stack");
   2309             stack.appendChild(sup); stack.appendChild(sub);
   2310           }
   2311         }
   2312         //
   2313         //  Get the bounding boxes and maximum width of scripts
   2314         //
   2315         var boxes = [], BBOX = CHTML.BBOX.empty(this.CHTML);
   2316         for (var i = 0, m = this.data.length; i < m; i++) boxes[i] = this.CHTMLbboxFor(i);
   2317         var bbox = boxes[this.base] || CHTML.BBOX.empty(),
   2318             sbox = boxes[this.sub], Sbox = boxes[this.sup];
   2319         var sscale = (sub ? sbox.rscale : 1), Sscale = (sup ? Sbox.rscale : 1);
   2320         BBOX.combine(bbox,0,0);
   2321         //
   2322         //  Get initial values for parameters
   2323         //
   2324         var ex = CHTML.TEX.x_height, s = CHTML.TEX.scriptspace;
   2325         var q = CHTML.TEX.sup_drop * Sscale, r = CHTML.TEX.sub_drop * sscale;
   2326         var u = bbox.h - q, v = bbox.d + r, delta = 0, p;
   2327         if (bbox.ic) {
   2328           BBOX.w -= bbox.ic;         // remove IC (added by mo and mi)
   2329           base.style.marginRight = CHTML.Em(-bbox.ic);
   2330           delta = 1.3*bbox.ic + .05; // make faked IC be closer to expeted results
   2331         }
   2332         var bmml = this.data[this.base];
   2333         if (bmml) {
   2334           if ((bmml.type === "mrow" || bmml.type === "mstyle") && bmml.data.length === 1) bmml = bmml.data[0];
   2335           if (bmml.type === "mi" || bmml.type === "mo") {
   2336             if (bmml.data.join("").length === 1 && bbox.rscale === 1 && !bbox.sH &&
   2337                 !bmml.Get("largeop")) {u = v = 0}
   2338           }
   2339         }
   2340         values.subscriptshift   = (values.subscriptshift === ""   ? 0 : this.CHTMLlength2em(values.subscriptshift));
   2341         values.superscriptshift = (values.superscriptshift === "" ? 0 : this.CHTMLlength2em(values.superscriptshift));
   2342         //
   2343         //  Add the super- and subscripts
   2344         //
   2345         var x = BBOX.w; if (sub) sbox.w += s; if (sup) Sbox.w += s;
   2346         if (!sup) {
   2347           if (sub) {
   2348             v = Math.max(v,CHTML.TEX.sub1,sscale*sbox.h-(4/5)*ex,values.subscriptshift);
   2349             sub.style.verticalAlign = CHTML.Em(-v/sscale);
   2350             sub.style.paddingRight = CHTML.Em(s/sscale);
   2351             BBOX.combine(sbox,x,-v);
   2352           }
   2353         } else {
   2354           if (!sub) {
   2355             p = CHTML.TEX[(values.displaystyle ? "sup1" : (values.texprimestyle ? "sup3" : "sup2"))];
   2356             u = Math.max(u,p,Sscale*Sbox.d+(1/4)*ex,values.superscriptshift);
   2357             sup.style.verticalAlign = CHTML.Em(u/Sscale);
   2358             sup.style.paddingLeft = CHTML.Em(delta/Sscale);
   2359             sup.style.paddingRight = CHTML.Em(s/Sscale);
   2360             BBOX.combine(Sbox,x+delta,u);
   2361           } else {
   2362             v = Math.max(v,CHTML.TEX.sub2);
   2363             var t = CHTML.TEX.rule_thickness;
   2364             if ((u - Sscale*Sbox.d) - (sscale*sbox.h - v) < 3*t) {
   2365               v = 3*t - u + Sscale*Sbox.d + sscale*sbox.h;
   2366               q = (4/5)*ex - (u - Sscale*Sbox.d);
   2367               if (q > 0) {u += q; v -= q}
   2368             }
   2369             u = Math.max(u,values.superscriptshift);
   2370             v = Math.max(v,values.subscriptshift);
   2371             sub.style.paddingRight = CHTML.Em(s/sscale);
   2372             sup.style.paddingBottom = CHTML.Em(u/Sscale+v/sscale-Sbox.d-sbox.h/sscale*Sscale);
   2373             sup.style.paddingLeft = CHTML.Em(delta/Sscale);
   2374             sup.style.paddingRight = CHTML.Em(s/Sscale);
   2375             stack.style.verticalAlign = CHTML.Em(-v);
   2376             BBOX.combine(Sbox,x+delta,u);
   2377             BBOX.combine(sbox,x,-v);
   2378           }
   2379         }
   2380         BBOX.clean();
   2381         return node;
   2382       },
   2383       CHTMLstretchV: MML.mbase.CHTMLstretchV,
   2384       CHTMLstretchH: MML.mbase.CHTMLstretchH,
   2385       CHTMLchildNode: function (node,i) {
   2386         var types = ["mjx-base","mjx-sub","mjx-sup"];
   2387         if (this.over === 1) types[1] = types[2];
   2388         return CHTML.getNode(node,types[i]);
   2389       }
   2390     });
   2391 
   2392     /********************************************************/
   2393     
   2394     MML.mfrac.Augment({
   2395       toCommonHTML: function (node) {
   2396         node = this.CHTMLdefaultNode(node,{
   2397           childNodes:["mjx-numerator","mjx-denominator"],
   2398           forceChild:true, noBBox:true, minChildren:2
   2399         });
   2400         var values = this.getValues("linethickness","displaystyle",
   2401                                     "numalign","denomalign","bevelled");
   2402         var isDisplay = values.displaystyle;
   2403         //
   2404         //  Create the table for the fraction and set the alignment
   2405         //
   2406         var num = node.firstChild, denom = node.lastChild;
   2407         var frac = CHTML.addElement(node,"mjx-box");
   2408         frac.appendChild(num); frac.appendChild(denom); node.appendChild(frac);
   2409         if (values.numalign !== "center") num.style.textAlign = values.numalign;
   2410         if (values.denomalign !== "center") denom.style.textAlign = values.denomalign;
   2411         //
   2412         //  Get the bounding boxes for the parts, and determine the placement
   2413         //  of the numerator and denominator
   2414         //
   2415         var nbox = this.CHTMLbboxFor(0), dbox = this.CHTMLbboxFor(1),
   2416             BBOX = CHTML.BBOX.empty(this.CHTML), nscale = nbox.rscale, dscale = dbox.rscale;
   2417         values.linethickness = Math.max(0,CHTML.thickness2em(values.linethickness||"0",BBOX.scale));
   2418         var mt = CHTML.TEX.min_rule_thickness/CHTML.em, a = CHTML.TEX.axis_height;
   2419         var t = values.linethickness, p,q, u,v;
   2420         if (values.bevelled) {
   2421           frac.className += " MJXc-bevelled";
   2422           var delta = (isDisplay ? .4 : .15);
   2423           var H = Math.max(nscale*(nbox.h+nbox.d),dscale*(dbox.h+dbox.d)) + 2*delta;
   2424           var bevel = CHTML.Element("mjx-bevel"); frac.insertBefore(bevel,denom);
   2425           var bbox = CHTML.createDelimiter(bevel,0x2F,H);
   2426           u = nscale*(nbox.d-nbox.h)/2+a+delta;
   2427           v = dscale*(dbox.d-dbox.h)/2+a-delta;
   2428           if (u) num.style.verticalAlign = CHTML.Em(u/nscale);
   2429           if (v) denom.style.verticalAlign = CHTML.Em(v/dscale);
   2430           bevel.style.marginLeft = bevel.style.marginRight = CHTML.Em(-delta/2);
   2431           BBOX.combine(nbox,0,u);
   2432           BBOX.combine(bbox,nscale*nbox.w-delta/2,0);
   2433           BBOX.combine(dbox,nscale*nbox.w+bbox.w-delta,v);
   2434           BBOX.clean();
   2435         } else {
   2436           frac.className += " MJXc-stacked";
   2437           if (isDisplay) {u = CHTML.TEX.num1; v = CHTML.TEX.denom1}
   2438             else {u = (t === 0 ? CHTML.TEX.num3 : CHTML.TEX.num2); v = CHTML.TEX.denom2}
   2439           if (t === 0) { // \atop
   2440             p = Math.max((isDisplay ? 7 : 3) * CHTML.TEX.rule_thickness, 2*mt); // force to at least 2 px
   2441             q = (u - nbox.d*nscale) - (dbox.h*dscale - v);
   2442             if (q < p) {u += (p - q)/2; v += (p - q)/2}
   2443           } else { // \over
   2444             p = Math.max((isDisplay ? 2 : 0) * mt + t, t/2 + 1.5*mt);
   2445             t = Math.max(t,mt);
   2446             q = (u - nbox.d*nscale) - (a + t/2); if (q < p) u += (p - q);
   2447             q = (a - t/2) - (dbox.h*dscale - v); if (q < p) v += (p - q);
   2448             nbox.L = nbox.R = dbox.L = dbox.R = .1;  // account for padding in BBOX width
   2449             var rule = CHTML.addElement(frac,"mjx-line",{style: {
   2450               "border-bottom":CHTML.Px(t*BBOX.scale,1)+" solid", top: CHTML.Em(-t/2-a)
   2451             }});
   2452           }
   2453           //
   2454           //  Determine the new bounding box and place the parts
   2455           //
   2456           BBOX.combine(nbox,0,u);
   2457           BBOX.combine(dbox,0,-v);
   2458           BBOX.clean();
   2459           //
   2460           //  Force elements to the correct width
   2461           //
   2462           frac.style.width = CHTML.Em(BBOX.w);
   2463           num.style.width = CHTML.Em(BBOX.w/nscale);
   2464           denom.style.width = CHTML.Em(BBOX.w/dscale);
   2465           if (rule) rule.style.width = frac.style.width;
   2466           //
   2467           //  Place the numerator and denominator in relation to the baseline
   2468           //
   2469           num.style.top = CHTML.Em(-BBOX.h/nscale);
   2470           denom.style.bottom = CHTML.Em(-BBOX.d/dscale);
   2471           //
   2472           //  Force the size of the surrounding box, since everything is absolutely positioned
   2473           //
   2474           CHTML.addElement(node,"mjx-vsize",{style: {
   2475             height: CHTML.Em(BBOX.h+BBOX.d), verticalAlign: CHTML.Em(-BBOX.d)
   2476           }});
   2477         }
   2478         //
   2479         //  Add nulldelimiterspace around the fraction
   2480         //  (TeXBook pg 150 and Appendix G rule 15e)
   2481         //
   2482         if (!this.texWithDelims && !this.useMMLspacing) {
   2483           var space = CHTML.TEX.nulldelimiterspace;
   2484           frac.style.padding = "0 "+CHTML.Em(space);
   2485           BBOX.l += space; BBOX.r += space; BBOX.w += 2*space;
   2486         }
   2487         //
   2488         //  Return the completed fraction
   2489         //
   2490         return node;
   2491       },
   2492       CHTMLcanStretch: function (direction) {return false}
   2493     });
   2494 
   2495     /********************************************************/
   2496     
   2497     MML.msqrt.Augment({
   2498       toCommonHTML: function (node) {
   2499         node = this.CHTMLdefaultNode(node,{
   2500           childNodes:["mjx-box","mjx-root"], forceChild:true, noBBox:true
   2501         });
   2502         var base = node.firstChild || CHTML.Element("mjx-box");
   2503         var sqrt = CHTML.addElement(node,"mjx-box"); sqrt.appendChild(base);
   2504         var bbox = this.CHTMLbboxFor(0), BBOX = CHTML.BBOX.empty(this.CHTML);
   2505         var t = CHTML.TEX.rule_thickness, T = CHTML.TEX.surd_height, p = t, q, H;
   2506         if (this.Get("displaystyle")) p = CHTML.TEX.x_height;
   2507         q = t + p/4;
   2508         H = bbox.h + bbox.d + q + t;
   2509         var surd = CHTML.Element("mjx-surd"); sqrt.insertBefore(surd,base);
   2510         var sbox = CHTML.createDelimiter(surd,0x221A,[H-.04,H]);
   2511         if (sbox.h + sbox.d > H) q = ((sbox.h+sbox.d) - (H-t))/2;
   2512         H = bbox.h + q + t;
   2513         var x = this.CHTMLaddRoot(node,sbox,sbox.h+sbox.d-H);
   2514         base.style.paddingTop = CHTML.Em(q); 
   2515         base.style.borderTop = CHTML.Px(T*bbox.scale,1)+" solid";
   2516         sqrt.style.paddingTop = CHTML.Em(2*t-T);  // use wider line, but don't affect height
   2517         bbox.h += q + 2*t;
   2518         BBOX.combine(sbox,x,H-sbox.h);
   2519         BBOX.combine(bbox,x+sbox.w,0);
   2520         BBOX.clean();
   2521         return node;
   2522       },
   2523       CHTMLaddRoot: function () {return 0}
   2524     });
   2525 
   2526     /********************************************************/
   2527     
   2528     MML.mroot.Augment({
   2529       toCommonHTML: MML.msqrt.prototype.toCommonHTML,
   2530       CHTMLaddRoot: function (sqrt,sbox,d) {
   2531         if (!this.data[1]) return;
   2532         var BBOX = this.CHTML, bbox = this.data[1].CHTML, root = sqrt.firstChild;
   2533         var scale = bbox.rscale;
   2534         var h = this.CHTMLrootHeight(bbox,sbox,scale)-d;
   2535         var w = Math.min(bbox.w,bbox.r); // remove extra right-hand padding, if any
   2536         var dx = Math.max(w,sbox.offset/scale); 
   2537         if (h) root.style.verticalAlign = CHTML.Em(h/scale);
   2538         if (dx > w) root.firstChild.style.paddingLeft = CHTML.Em(dx-w);
   2539         dx -= sbox.offset/scale;
   2540         root.style.width = CHTML.Em(dx);
   2541         BBOX.combine(bbox,0,h);
   2542         return dx*scale;
   2543       },
   2544       CHTMLrootHeight: function (bbox,sbox,scale) {
   2545         return .45*(sbox.h+sbox.d-.9)+sbox.offset + Math.max(0,bbox.d-.075);
   2546       }
   2547     });
   2548     
   2549     /********************************************************/
   2550     
   2551     MML.mfenced.Augment({
   2552       toCommonHTML: function (node) {
   2553         node = this.CHTMLcreateNode(node);
   2554         this.CHTMLhandleStyle(node);
   2555         this.CHTMLhandleScale(node);
   2556         //
   2557         //  Make row of open, data, sep, ... data, close
   2558         //
   2559         this.CHTMLaddChild(node,"open",{});
   2560         for (var i = 0, m = this.data.length; i < m; i++) {
   2561           this.CHTMLaddChild(node,"sep"+i,{});
   2562           this.CHTMLaddChild(node,i,{});
   2563         }
   2564         this.CHTMLaddChild(node,"close",{});
   2565         //
   2566         //  Check for stretching the elements
   2567         //
   2568         var H = this.CHTML.h, D = this.CHTML.d;
   2569         this.CHTMLstretchChildV("open",H,D);
   2570         for (i = 0, m = this.data.length; i < m; i++) {
   2571           this.CHTMLstretchChildV("sep"+i,H,D);
   2572           this.CHTMLstretchChildV(i,H,D);
   2573         }
   2574         this.CHTMLstretchChildV("close",H,D);
   2575         this.CHTMLhandleSpace(node);
   2576         this.CHTMLhandleBBox(node);
   2577         this.CHTMLhandleColor(node);
   2578         return node;
   2579       }
   2580     });
   2581 
   2582     /********************************************************/
   2583     
   2584     MML.mrow.Augment({
   2585       toCommonHTML: function (node) {
   2586         node = this.CHTMLdefaultNode(node);
   2587         var bbox = this.CHTML, H = bbox.h, D = bbox.d, hasNegative;
   2588         for (var i = 0, m = this.data.length; i < m; i++) {
   2589           this.CHTMLstretchChildV(i,H,D);
   2590           if (this.data[i] && this.data[i].CHTML && this.data[i].CHTML.w < 0) hasNegative = true;
   2591         }
   2592         if (this.CHTMLlineBreaks()) {
   2593           this.CHTMLmultiline(node);
   2594         } else {
   2595           if (hasNegative && bbox.w) node.style.width = CHTML.Em(Math.max(0,bbox.w));
   2596           if (bbox.w < 0) node.style.marginRight = CHTML.Em(bbox.w);
   2597         }
   2598         return node;
   2599       },
   2600       CHTMLlineBreaks: function () {
   2601         if (!this.parent.linebreakContainer) return false;
   2602         return (LINEBREAKS.automatic && this.CHTML.w > CHTML.linebreakWidth) || this.hasNewline();
   2603       },
   2604       CHTMLstretchV: function (h,d) {
   2605         this.CHTMLstretchChildV(this.CoreIndex(),h,d);
   2606         return this.CHTML;
   2607       },
   2608       CHTMLstretchH: function (node,w) {
   2609         this.CHTMLstretchChildH(this.CoreIndex(),w,node);
   2610         return this.CHTML;
   2611       }
   2612     });
   2613 
   2614     /********************************************************/
   2615     
   2616     MML.mstyle.Augment({
   2617       toCommonHTML: function (node) {
   2618         node = this.CHTMLdefaultNode(node);
   2619         if (this.scriptlevel && this.data[0]) this.CHTML.rescale(this.data[0].CHTML.rscale);
   2620         return node;
   2621       }
   2622     });
   2623 
   2624     /********************************************************/
   2625     
   2626     MML.TeXAtom.Augment({
   2627       toCommonHTML: function (node,stretch) {
   2628         if (!stretch) node = this.CHTMLdefaultNode(node);
   2629         if (this.texClass === MML.TEXCLASS.VCENTER) {
   2630           var a = CHTML.TEX.axis_height, BBOX = this.CHTML;
   2631           var v = a-(BBOX.h+BBOX.d)/2+BBOX.d;
   2632           if (Math.abs(v) > .001) {
   2633             node.style.verticalAlign = CHTML.Em(v);
   2634             BBOX.h += v; BBOX.t += v; BBOX.d -= v; BBOX.b -= v;
   2635           }
   2636         }
   2637         return node;
   2638       },
   2639       CHTMLstretchV: function (h,d) {
   2640         this.CHTML.updateFrom(this.Core().CHTMLstretchV(h,d));
   2641         this.toCommonHTML(this.CHTMLnodeElement(),true);
   2642         return this.CHTML;
   2643       },
   2644       CHTMLstretchH: function (node,w) {
   2645         this.CHTML.updateFrom(this.CHTMLstretchCoreH(node,w));
   2646         this.toCommonHTML(node,true);
   2647         return this.CHTML;
   2648       }
   2649     });
   2650 
   2651     /********************************************************/
   2652     
   2653     MML.semantics.Augment({
   2654       toCommonHTML: function (node) {
   2655         node = this.CHTMLcreateNode(node);
   2656 	if (this.data[0]) {
   2657 	  this.data[0].toCommonHTML(node);
   2658 	  this.CHTML.updateFrom(this.data[0].CHTML);
   2659 	}
   2660         return node;
   2661       }
   2662     });
   2663     MML.annotation.Augment({toCommonHTML: function(node) {return this.CHTMLcreateNode(node)}});
   2664     MML["annotation-xml"].Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2665 
   2666     /********************************************************/
   2667 
   2668     MML.ms.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2669     MML.mglyph.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2670     MML.menclose.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2671     MML.maction.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2672     MML.mmultiscripts.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2673     MML.mtable.Augment({toCommonHTML: MML.mbase.CHTMLautoload});
   2674     
   2675     /********************************************************/
   2676     
   2677     //
   2678     //  Loading isn't complete until the element jax is modified,
   2679     //  but can't call loadComplete within the callback for "mml Jax Ready"
   2680     //  (it would call CommonHTML's Require routine, asking for the mml jax again)
   2681     //  so wait until after the mml jax has finished processing.
   2682     //  
   2683     //  We also need to wait for the onload handler to run, since the loadComplete
   2684     //  will call Config and Startup, which need to modify the body.
   2685     //
   2686     MathJax.Hub.Register.StartupHook("onLoad",function () {
   2687       setTimeout(MathJax.Callback(["loadComplete",CHTML,"jax.js"]),0);
   2688     });
   2689   });
   2690 
   2691   MathJax.Hub.Register.StartupHook("End Cookie", function () {  
   2692     if (HUB.config.menuSettings.zoom !== "None")
   2693       {AJAX.Require("[MathJax]/extensions/MathZoom.js")}
   2694   });
   2695     
   2696 })(MathJax.Ajax,MathJax.Hub,MathJax.HTML,MathJax.OutputJax.CommonHTML);