www

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

jax.js (89561B)


      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/SVG/jax.js
      7  *
      8  *  Implements the SVG OutputJax that displays mathematics using
      9  *  SVG (or VML in IE) to position the characters from math fonts
     10  *  in their proper locations.
     11  *  
     12  *  ---------------------------------------------------------------------
     13  *  
     14  *  Copyright (c) 2011-2015 The MathJax Consortium
     15  * 
     16  *  Licensed under the Apache License, Version 2.0 (the "License");
     17  *  you may not use this file except in compliance with the License.
     18  *  You may obtain a copy of the License at
     19  * 
     20  *      http://www.apache.org/licenses/LICENSE-2.0
     21  * 
     22  *  Unless required by applicable law or agreed to in writing, software
     23  *  distributed under the License is distributed on an "AS IS" BASIS,
     24  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     25  *  See the License for the specific language governing permissions and
     26  *  limitations under the License.
     27  */
     28 
     29 
     30 (function (AJAX,HUB,HTML,SVG) {
     31   var MML;
     32 
     33   var SVGNS   = "http://www.w3.org/2000/svg";
     34   var XLINKNS = "http://www.w3.org/1999/xlink";
     35 
     36   SVG.Augment({
     37     HFUZZ: 2,     // adjustments for height and depth of final svg element
     38     DFUZZ: 2,     //   to get baselines right (fragile).
     39     
     40     config: {
     41       styles: {
     42         ".MathJax_SVG": {
     43           "display":         "inline",
     44           "font-style":      "normal",
     45           "font-weight":     "normal",
     46           "line-height":     "normal",
     47           "font-size":       "100%",
     48           "font-size-adjust":"none",
     49           "text-indent":     0,
     50           "text-align":      "left",
     51           "text-transform":  "none",
     52           "letter-spacing":  "normal",
     53           "word-spacing":    "normal",
     54           "word-wrap":       "normal",
     55           "white-space":     "nowrap",
     56           "float":           "none",
     57           "direction":       "ltr",
     58           "max-width": "none", "max-height": "none",
     59           "min-width": 0, "min-height": 0,
     60           border: 0, padding: 0, margin: 0
     61         },
     62 
     63         ".MathJax_SVG_Display": {
     64           position: "relative",
     65           display: "block!important",
     66           "text-indent": 0,
     67           "max-width": "none", "max-height": "none",
     68           "min-width": 0, "min-height": 0,
     69           width: "100%"
     70         },
     71         
     72         ".MathJax_SVG *": {
     73           transition: "none",
     74           "-webkit-transition": "none",
     75           "-moz-transition": "none",
     76           "-ms-transition": "none",
     77           "-o-transition": "none"
     78         },
     79         
     80         ".mjx-svg-href": {
     81           fill: "blue", stroke: "blue"
     82         },
     83 
     84         ".MathJax_SVG_Processing": {
     85           visibility: "hidden", position:"absolute", top:0, left:0,
     86           width:0, height: 0, overflow:"hidden", display:"block!important"
     87         },
     88         ".MathJax_SVG_Processed": {display:"none!important"},
     89         
     90         ".MathJax_SVG_ExBox": {
     91           display:"block!important", overflow:"hidden",
     92           width:"1px", height:"60ex",
     93           "min-height": 0, "max-height":"none",
     94           padding:0, border: 0, margin: 0
     95         },
     96         
     97         "#MathJax_SVG_Tooltip": {
     98           position: "absolute", left: 0, top: 0,
     99           width: "auto", height: "auto",
    100           display: "none"
    101         }
    102       }
    103     },
    104 
    105     hideProcessedMath: true,           // use display:none until all math is processed
    106 
    107     fontNames: ["TeX","STIX","STIX-Web","Asana-Math",
    108                 "Gyre-Termes","Gyre-Pagella","Latin-Modern","Neo-Euler"],
    109 
    110 
    111     Config: function () {
    112       this.SUPER(arguments).Config.apply(this,arguments);
    113       var settings = HUB.config.menuSettings, config = this.config, font = settings.font;
    114       if (settings.scale) {config.scale = settings.scale}
    115       if (font && font !== "Auto") {
    116         font = font.replace(/(Local|Web|Image)$/i,"");
    117         font = font.replace(/([a-z])([A-Z])/,"$1-$2");
    118         this.fontInUse = font;
    119       } else {
    120         this.fontInUse = config.font || "TeX";
    121       }
    122       if (this.fontNames.indexOf(this.fontInUse) < 0) {this.fontInUse = "TeX"}
    123       this.fontDir += "/" + this.fontInUse;
    124       if (!this.require) {this.require = []}
    125       this.require.push(this.fontDir+"/fontdata.js");
    126       this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js");
    127     },
    128 
    129     Startup: function () {
    130       //  Set up event handling
    131       EVENT = MathJax.Extension.MathEvents.Event;
    132       TOUCH = MathJax.Extension.MathEvents.Touch;
    133       HOVER = MathJax.Extension.MathEvents.Hover;
    134       this.ContextMenu = EVENT.ContextMenu;
    135       this.Mousedown   = EVENT.AltContextMenu;
    136       this.Mouseover   = HOVER.Mouseover;
    137       this.Mouseout    = HOVER.Mouseout;
    138       this.Mousemove   = HOVER.Mousemove;
    139 
    140       // Make hidden div for doing tests and storing global SVG <defs>
    141       this.hiddenDiv = HTML.Element("div",{
    142         style:{visibility:"hidden", overflow:"hidden", position:"absolute", top:0,
    143                height:"1px", width: "auto", padding:0, border:0, margin:0,
    144                textAlign:"left", textIndent:0, textTransform:"none",
    145                lineHeight:"normal", letterSpacing:"normal", wordSpacing:"normal"}
    146       });
    147       if (!document.body.firstChild) {document.body.appendChild(this.hiddenDiv)}
    148         else {document.body.insertBefore(this.hiddenDiv,document.body.firstChild)}
    149       this.hiddenDiv = HTML.addElement(this.hiddenDiv,"div",{id:"MathJax_SVG_Hidden"});
    150 
    151       // Determine pixels-per-inch and em-size
    152       var div = HTML.addElement(this.hiddenDiv,"div",{style:{width:"5in"}});
    153       this.pxPerInch = div.offsetWidth/5; this.hiddenDiv.removeChild(div);
    154       
    155       // Used for measuring text sizes
    156       this.textSVG = this.Element("svg");
    157       
    158       // Global defs for font glyphs
    159       BBOX.GLYPH.defs = this.addElement(this.addElement(this.hiddenDiv.parentNode,"svg"),
    160                                           "defs",{id:"MathJax_SVG_glyphs"});
    161 
    162        // Used in preTranslate to get scaling factors
    163       this.ExSpan = HTML.Element("span",
    164         {style:{position:"absolute","font-size-adjust":"none"}},
    165         [["span",{className:"MathJax_SVG_ExBox"}]]
    166       );
    167 
    168       // Used in preTranslate to get linebreak width
    169       this.linebreakSpan = HTML.Element("span",null,
    170         [["hr",{style: {width:"auto", size:1, padding:0, border:0, margin:0}}]]);
    171 
    172       // Set up styles
    173       return AJAX.Styles(this.config.styles,["InitializeSVG",this]);
    174     },
    175     
    176     //
    177     //  Handle initialization that requires styles to be set up
    178     //
    179     InitializeSVG: function () {
    180       //
    181       //  Get the default sizes (need styles in place to do this)
    182       //
    183       document.body.appendChild(this.ExSpan);
    184       document.body.appendChild(this.linebreakSpan);
    185       this.defaultEx    = this.ExSpan.firstChild.offsetHeight/60;
    186       this.defaultWidth = this.linebreakSpan.firstChild.offsetWidth;
    187       document.body.removeChild(this.linebreakSpan);
    188       document.body.removeChild(this.ExSpan);
    189     },
    190 
    191     preTranslate: function (state) {
    192       var scripts = state.jax[this.id], i, m = scripts.length,
    193           script, prev, span, div, test, jax, ex, em, maxwidth, relwidth = false, cwidth,
    194           linebreak = this.config.linebreaks.automatic, width = this.config.linebreaks.width;
    195       if (linebreak) {
    196         relwidth = (width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/) != null);
    197         if (relwidth) {width = width.replace(/\s*container\s*/,"")}
    198           else {maxwidth = this.defaultWidth}
    199         if (width === "") {width = "100%"}
    200       } else {maxwidth = 100000} // a big width, so no implicit line breaks
    201       //
    202       //  Loop through the scripts
    203       //
    204       for (i = 0; i < m; i++) {
    205         script = scripts[i]; if (!script.parentNode) continue;
    206         //
    207         //  Remove any existing output
    208         //
    209         prev = script.previousSibling;
    210         if (prev && String(prev.className).match(/^MathJax(_SVG)?(_Display)?( MathJax(_SVG)?_Processing)?$/))
    211           {prev.parentNode.removeChild(prev)}
    212         //
    213         //  Add the span, and a div if in display mode,
    214         //  then set the role and mark it as being processed
    215         //
    216         jax = script.MathJax.elementJax; if (!jax) continue;
    217         jax.SVG = {display: (jax.root.Get("display") === "block")}
    218         span = div = HTML.Element("span",{
    219           style: {"font-size": this.config.scale+"%", display:"inline-block"},
    220  	  className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
    221           oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
    222           onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove,
    223 	  onclick:EVENT.Click, ondblclick:EVENT.DblClick,
    224           // Added for keyboard accessible menu.
    225           onkeydown: EVENT.Keydown, tabIndex: HUB.getTabOrder(jax)
    226         });
    227 	if (HUB.Browser.noContextMenu) {
    228 	  span.ontouchstart = TOUCH.start;
    229 	  span.ontouchend = TOUCH.end;
    230 	}
    231         if (jax.SVG.display) {
    232           div = HTML.Element("div",{className:"MathJax_SVG_Display"});
    233           div.appendChild(span);
    234         }
    235         div.className += " MathJax_SVG_Processing";
    236         script.parentNode.insertBefore(div,script);
    237         //
    238         //  Add the test span for determining scales and linebreak widths
    239         //
    240         script.parentNode.insertBefore(this.ExSpan.cloneNode(true),script);
    241         div.parentNode.insertBefore(this.linebreakSpan.cloneNode(true),div);
    242       }
    243       //
    244       //  Determine the scaling factors for each script
    245       //  (this only requires one reflow rather than a reflow for each equation)
    246       //
    247       for (i = 0; i < m; i++) {
    248         script = scripts[i]; if (!script.parentNode) continue;
    249         test = script.previousSibling; div = test.previousSibling;
    250         jax = script.MathJax.elementJax; if (!jax) continue;
    251         ex = test.firstChild.offsetHeight/60;
    252         cwidth = div.previousSibling.firstChild.offsetWidth;
    253         if (relwidth) {maxwidth = cwidth}
    254         if (ex === 0 || ex === "NaN") {
    255           // can't read width, so move to hidden div for processing
    256           // (this will cause a reflow for each math element that is hidden)
    257           this.hiddenDiv.appendChild(div);
    258           jax.SVG.isHidden = true;
    259           ex = this.defaultEx; cwidth = this.defaultWidth;
    260           if (relwidth) {maxwidth = cwidth}
    261         }
    262         jax.SVG.ex = ex;
    263         jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height
    264         jax.SVG.cwidth = cwidth/em * 1000;
    265         jax.SVG.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/em*1000) : SVG.BIGDIMEN);
    266       }
    267       //
    268       //  Remove the test spans used for determining scales and linebreak widths
    269       //
    270       for (i = 0; i < m; i++) {
    271         script = scripts[i]; if (!script.parentNode) continue;
    272         test = scripts[i].previousSibling; span = test.previousSibling;
    273         jax = scripts[i].MathJax.elementJax; if (!jax) continue;
    274         if (!jax.SVG.isHidden) {span = span.previousSibling}
    275         span.parentNode.removeChild(span);
    276         test.parentNode.removeChild(test);
    277       }
    278       //
    279       //  Set state variables used for displaying equations in chunks
    280       //
    281       state.SVGeqn = state.SVGlast = 0; state.SVGi = -1;
    282       state.SVGchunk = this.config.EqnChunk;
    283       state.SVGdelay = false;
    284     },
    285 
    286     Translate: function (script,state) {
    287       if (!script.parentNode) return;
    288 
    289       //
    290       //  If we are supposed to do a chunk delay, do it
    291       //  
    292       if (state.SVGdelay) {
    293         state.SVGdelay = false;
    294         HUB.RestartAfter(MathJax.Callback.Delay(this.config.EqnChunkDelay));
    295       }
    296 
    297       //
    298       //  Get the data about the math
    299       //
    300       var jax = script.MathJax.elementJax, math = jax.root,
    301           span = document.getElementById(jax.inputID+"-Frame"),
    302           div = (jax.SVG.display ? (span||{}).parentNode : span),
    303           localCache = (SVG.config.useFontCache && !SVG.config.useGlobalCache);
    304       if (!div) return;
    305       //
    306       //  Set the font metrics
    307       //
    308       this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex;
    309       this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth;
    310       //
    311       //  Typeset the math
    312       //
    313       this.mathDiv = div;
    314       span.appendChild(this.textSVG);
    315       if (localCache) {SVG.resetGlyphs()}
    316       this.initSVG(math,span);
    317       math.setTeXclass();
    318       try {math.toSVG(span,div)} catch (err) {
    319         if (err.restart) {while (span.firstChild) {span.removeChild(span.firstChild)}}
    320         if (localCache) {BBOX.GLYPH.n--}
    321         throw err;
    322       }
    323       span.removeChild(this.textSVG);
    324       //
    325       //  Put it in place, and remove the processing marker
    326       //
    327       if (jax.SVG.isHidden) {script.parentNode.insertBefore(div,script)}
    328       div.className = div.className.split(/ /)[0];
    329       //
    330       //  Check if we are hiding the math until more is processed
    331       //
    332       if (this.hideProcessedMath) {
    333         //
    334         //  Hide the math and don't let its preview be removed
    335         //
    336         div.className += " MathJax_SVG_Processed";
    337         if (script.MathJax.preview) {
    338           jax.SVG.preview = script.MathJax.preview;
    339           delete script.MathJax.preview;
    340         }
    341         //
    342         //  Check if we should show this chunk of equations
    343         //
    344         state.SVGeqn += (state.i - state.SVGi); state.SVGi = state.i;
    345         if (state.SVGeqn >= state.SVGlast + state.SVGchunk) {
    346           this.postTranslate(state,true);
    347           state.SVGchunk = Math.floor(state.SVGchunk*this.config.EqnChunkFactor);
    348           state.SVGdelay = true;  // delay if there are more scripts
    349         }
    350       }
    351     },
    352 
    353     postTranslate: function (state,partial) {
    354       var scripts = state.jax[this.id];
    355       if (!this.hideProcessedMath) return;
    356       //
    357       //  Reveal this chunk of math
    358       //
    359       for (var i = state.SVGlast, m = state.SVGeqn; i < m; i++) {
    360         var script = scripts[i];
    361         if (script && script.MathJax.elementJax) {
    362           //
    363           //  Remove the processed marker
    364           //
    365           script.previousSibling.className = script.previousSibling.className.split(/ /)[0];
    366           var data = script.MathJax.elementJax.SVG;
    367           //
    368           //  Remove the preview, if any
    369           //
    370           if (data.preview) {
    371             data.preview.innerHTML = "";
    372             script.MathJax.preview = data.preview;
    373             delete data.preview;
    374           }
    375         }
    376       }
    377       //
    378       //  Save our place so we know what is revealed
    379       //
    380       state.SVGlast = state.SVGeqn;
    381     },
    382     
    383     resetGlyphs: function (reset) {
    384       if (this.config.useFontCache) {
    385         var GLYPH = BBOX.GLYPH;
    386         if (this.config.useGlobalCache) {
    387           GLYPH.defs = document.getElementById("MathJax_SVG_glyphs");
    388           GLYPH.defs.innerHTML = "";
    389         } else {
    390           GLYPH.defs = this.Element("defs");
    391           GLYPH.n++;
    392         }
    393         GLYPH.glyphs = {};
    394         if (reset) {GLYPH.n = 0}
    395       }
    396     },
    397 
    398     //
    399     //  Return the containing HTML element rather than the SVG element, since
    400     //  most browsers can't position to an SVG element properly.
    401     //
    402     hashCheck: function (target) {
    403       if (target && target.nodeName.toLowerCase() === "g")
    404         {do {target = target.parentNode} while (target && target.firstChild.nodeName !== "svg")}
    405       return target;
    406     },
    407 
    408     getJaxFromMath: function (math) {
    409       if (math.parentNode.className === "MathJax_SVG_Display") {math = math.parentNode}
    410       do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script");
    411       return HUB.getJaxFor(math);
    412     },
    413     getHoverSpan: function (jax,math) {
    414       math.style.position = "relative"; // make sure inline containers have position set
    415       return math.firstChild;
    416     },
    417     getHoverBBox: function (jax,span,math) {
    418       var bbox = EVENT.getBBox(span.parentNode);
    419       bbox.h += 2; bbox.d -= 2; // bbox seems to be a bit off, so compensate (FIXME)
    420       return bbox;
    421     },
    422     
    423     Zoom: function (jax,span,math,Mw,Mh) {
    424       //
    425       //  Re-render at larger size
    426       //
    427       span.className = "MathJax_SVG";
    428 
    429       //
    430       //  get em size (taken from this.preTranslate)
    431       //
    432       var emex = span.appendChild(this.ExSpan.cloneNode(true));
    433       var ex = emex.firstChild.offsetHeight/60;
    434       this.em = MML.mbase.prototype.em = ex / SVG.TeX.x_height * 1000; this.ex = ex;
    435       this.linebreakWidth = jax.SVG.lineWidth; this.cwidth = jax.SVG.cwidth;
    436       emex.parentNode.removeChild(emex);
    437 
    438       span.appendChild(this.textSVG);
    439       this.mathDIV = span; this.zoomScale = parseInt(HUB.config.menuSettings.zscale) / 100;
    440       var tw = jax.root.data[0].SVGdata.tw; if (tw && tw < this.cwidth) this.cwidth = tw;
    441       this.idPostfix = "-zoom"; jax.root.toSVG(span,span); this.idPostfix = "";
    442       this.zoomScale = 1;
    443       span.removeChild(this.textSVG);
    444       
    445       //
    446       //  Don't allow overlaps on any edge
    447       //
    448       var svg = span.getElementsByTagName("svg")[0].style;
    449       svg.marginTop = svg.marginRight = svg.marginLeft = 0;
    450       if (svg.marginBottom.charAt(0) === "-")
    451         span.style.marginBottom = svg.marginBottom.substr(1);
    452       
    453       if (this.operaZoomRefresh)
    454         {setTimeout(function () {span.firstChild.style.border="1px solid transparent"},1)}
    455       //
    456       // WebKit bug (issue #749)
    457       //  
    458       if (span.offsetWidth < span.firstChild.offsetWidth) {
    459         span.style.minWidth = span.firstChild.offsetWidth + "px";
    460         math.style.minWidth = math.firstChild.offsetWidth + "px";
    461       }
    462       //
    463       //  Get height and width of zoomed math and original math
    464       //
    465       span.style.position = math.style.position = "absolute";
    466       var zW = span.offsetWidth, zH = span.offsetHeight,
    467           mH = math.offsetHeight, mW = math.offsetWidth;
    468       span.style.position = math.style.position = "";
    469       //
    470       return {Y:-EVENT.getBBox(span).h, mW:mW, mH:mH, zW:zW, zH:zH};
    471     },
    472 
    473     initSVG: function (math,span) {},
    474     
    475     Remove: function (jax) {
    476       var span = document.getElementById(jax.inputID+"-Frame");
    477       if (span) {
    478         if (jax.SVG.display) {span = span.parentNode}
    479         span.parentNode.removeChild(span);
    480       }
    481       delete jax.SVG;
    482     },
    483     
    484     Em: function (m) {
    485       if (Math.abs(m) < .0006) return "0";
    486       return m.toFixed(3).replace(/\.?0+$/,"") + "em";
    487     },
    488     Ex: function (m) {
    489       m = m / this.TeX.x_height;
    490       if (Math.abs(m) < .0006) return "0";
    491       return m.toFixed(3).replace(/\.?0+$/,"") + "ex";
    492     },
    493     Percent: function (m) {
    494       return (100*m).toFixed(1).replace(/\.?0+$/,"") + "%";
    495     },
    496     Fixed: function (m,n) {
    497       if (Math.abs(m) < .0006) return "0";
    498       return m.toFixed(n||3).replace(/\.?0+$/,"");
    499     },
    500     length2em: function (length,mu,size) {
    501       if (typeof(length) !== "string") {length = length.toString()}
    502       if (length === "") {return ""}
    503       if (length === MML.SIZE.NORMAL) {return 1000}
    504       if (length === MML.SIZE.BIG)    {return 2000}
    505       if (length === MML.SIZE.SMALL)  {return  710}
    506       if (length === "infinity")      {return SVG.BIGDIMEN}
    507       if (length.match(/mathspace$/)) {return 1000*SVG.MATHSPACE[length]}
    508       var emFactor = (this.zoomScale || 1) / SVG.em;
    509       var match = length.match(/^\s*([-+]?(?:\.\d+|\d+(?:\.\d*)?))?(pt|em|ex|mu|px|pc|in|mm|cm|%)?/);
    510       var m = parseFloat(match[1]||"1") * 1000, unit = match[2];
    511       if (size == null) {size = 1000};  if (mu == null) {mu = 1}
    512       if (unit === "em") {return m}
    513       if (unit === "ex") {return m * SVG.TeX.x_height/1000}
    514       if (unit === "%")  {return m / 100 * size / 1000}
    515       if (unit === "px") {return m * emFactor}
    516       if (unit === "pt") {return m / 10}                               // 10 pt to an em
    517       if (unit === "pc") {return m * 1.2}                              // 12 pt to a pc
    518       if (unit === "in") {return m * this.pxPerInch * emFactor}
    519       if (unit === "cm") {return m * this.pxPerInch * emFactor / 2.54} // 2.54 cm to an inch
    520       if (unit === "mm") {return m * this.pxPerInch * emFactor / 25.4} // 10 mm to a cm
    521       if (unit === "mu") {return m / 18 * mu}
    522       return m*size / 1000;  // relative to given size (or 1em as default)
    523     },
    524     thickness2em: function (length,mu) {
    525       var thick = SVG.TeX.rule_thickness;
    526       if (length === MML.LINETHICKNESS.MEDIUM) {return thick}
    527       if (length === MML.LINETHICKNESS.THIN)   {return .67*thick}
    528       if (length === MML.LINETHICKNESS.THICK)  {return 1.67*thick}
    529       return this.length2em(length,mu,thick);
    530     },
    531 
    532     getPadding: function (styles) {
    533       var padding = {top:0, right:0, bottom:0, left:0}, has = false;
    534       for (var id in padding) {if (padding.hasOwnProperty(id)) {
    535         var pad = styles["padding"+id.charAt(0).toUpperCase()+id.substr(1)];
    536         if (pad) {padding[id] = this.length2em(pad); has = true;}
    537       }}
    538       return (has ? padding : false);
    539     },
    540     getBorders: function (styles) {
    541       var border = {top:0, right:0, bottom:0, left:0}, has = false;
    542       for (var id in border) {if (border.hasOwnProperty(id)) {
    543         var ID = "border"+id.charAt(0).toUpperCase()+id.substr(1);
    544         var style = styles[ID+"Style"];
    545         if (style && style !== "none") {
    546           has = true;
    547           border[id] = this.length2em(styles[ID+"Width"]);
    548           border[id+"Style"] = styles[ID+"Style"];
    549           border[id+"Color"] = styles[ID+"Color"];
    550           if (border[id+"Color"] === "initial") {border[id+"Color"] = ""}
    551         } else {delete border[id]}
    552       }}
    553       return (has ? border : false);
    554     },
    555 
    556     Element: function (type,def) {
    557       var obj = (typeof(type) === "string" ? document.createElementNS(SVGNS,type) : type);
    558       obj.isMathJax = true;
    559       if (def) {for (var id in def) {if (def.hasOwnProperty(id)) {obj.setAttribute(id,def[id].toString())}}}
    560       return obj;
    561     },
    562     addElement: function (parent,type,def) {return parent.appendChild(this.Element(type,def))},
    563     TextNode: HTML.TextNode,
    564     addText: HTML.addText,
    565     ucMatch: HTML.ucMatch,
    566 
    567     HandleVariant: function (variant,scale,text) {
    568       var svg = BBOX.G();
    569       var n, N, c, font, VARIANT, i, m, id, M, RANGES;
    570       if (!variant) {variant = this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]}
    571       if (variant.forceFamily) {
    572         text = BBOX.TEXT(scale,text,variant.font);
    573         if (variant.h != null) {text.h = variant.h}; if (variant.d != null) {text.d = variant.d}
    574         svg.Add(text); text = "";
    575       }
    576       VARIANT = variant;
    577       for (i = 0, m = text.length; i < m; i++) {
    578         variant = VARIANT;
    579         n = text.charCodeAt(i); c = text.charAt(i);
    580         if (n >= 0xD800 && n < 0xDBFF) {
    581           i++; n = (((n-0xD800)<<10)+(text.charCodeAt(i)-0xDC00))+0x10000;
    582           if (this.FONTDATA.RemapPlane1) {
    583             var nv = this.FONTDATA.RemapPlane1(n,variant);
    584             n = nv.n; variant = nv.variant;
    585           }
    586         } else {
    587           RANGES = this.FONTDATA.RANGES;
    588           for (id = 0, M = RANGES.length; id < M; id++) {
    589             if (RANGES[id].name === "alpha" && variant.noLowerCase) continue;
    590             N = variant["offset"+RANGES[id].offset];
    591             if (N && n >= RANGES[id].low && n <= RANGES[id].high) {
    592               if (RANGES[id].remap && RANGES[id].remap[n]) {
    593                 n = N + RANGES[id].remap[n];
    594               } else {
    595                 n = n - RANGES[id].low + N;
    596                 if (RANGES[id].add) {n += RANGES[id].add}
    597               }
    598               if (variant["variant"+RANGES[id].offset])
    599                 {variant = this.FONTDATA.VARIANT[variant["variant"+RANGES[id].offset]]}
    600               break;
    601             }
    602           }
    603         }
    604         if (variant.remap && variant.remap[n]) {
    605           n = variant.remap[n];
    606           if (variant.remap.variant) {variant = this.FONTDATA.VARIANT[variant.remap.variant]}
    607         } else if (this.FONTDATA.REMAP[n] && !variant.noRemap) {
    608           n = this.FONTDATA.REMAP[n];
    609         }
    610         if (n instanceof Array) {variant = this.FONTDATA.VARIANT[n[1]]; n = n[0]}
    611         if (typeof(n) === "string") {
    612           text = n+text.substr(i+1);
    613           m = text.length; i = -1;
    614           continue;
    615         }
    616         font = this.lookupChar(variant,n); c = font[n];
    617         if (c) {
    618           if ((c[5] && c[5].space) || (c[5] === "" && c[0]+c[1] === 0)) {svg.w += c[2]} else {
    619             c = [scale,font.id+"-"+n.toString(16).toUpperCase()].concat(c);
    620             svg.Add(BBOX.GLYPH.apply(BBOX,c),svg.w,0);
    621           }
    622         } else if (this.FONTDATA.DELIMITERS[n]) {
    623           c = this.createDelimiter(n,0,1,font);
    624           svg.Add(c,svg.w,(this.FONTDATA.DELIMITERS[n].dir === "V" ? c.d: 0));
    625         } else {
    626           if (n <= 0xFFFF) {c = String.fromCharCode(n)} else {
    627             N = n - 0x10000;
    628             c = String.fromCharCode((N>>10)+0xD800)
    629               + String.fromCharCode((N&0x3FF)+0xDC00);
    630           }
    631           var box = BBOX.TEXT(scale*100/SVG.config.scale,c,{
    632             "font-family":variant.defaultFamily||SVG.config.undefinedFamily,
    633             "font-style":(variant.italic?"italic":""),
    634             "font-weight":(variant.bold?"bold":"")
    635           })
    636           if (variant.h != null) {box.h = variant.h}; if (variant.d != null) {box.d = variant.d}
    637           c = BBOX.G(); c.Add(box); svg.Add(c,svg.w,0);
    638           HUB.signal.Post(["SVG Jax - unknown char",n,variant]);
    639         }
    640       }
    641       if (text.length == 1 && font.skew && font.skew[n]) {svg.skew = font.skew[n]*1000}
    642       if (svg.element.childNodes.length === 1 && !svg.element.firstChild.getAttribute("x")) {
    643         svg.element = svg.element.firstChild;
    644         svg.removeable = false; svg.scale = scale;
    645       }
    646       return svg;
    647     },
    648         
    649     lookupChar: function (variant,n) {
    650       var i, m;
    651       if (!variant.FONTS) {
    652         var FONTS = this.FONTDATA.FONTS;
    653         var fonts = (variant.fonts || this.FONTDATA.VARIANT.normal.fonts);
    654         if (!(fonts instanceof Array)) {fonts = [fonts]}
    655         if (variant.fonts != fonts) {variant.fonts = fonts}
    656         variant.FONTS = [];
    657         for (i = 0, m = fonts.length; i < m; i++) {
    658           if (FONTS[fonts[i]]) {variant.FONTS.push(FONTS[fonts[i]])}
    659         }
    660       }
    661       for (i = 0, m = variant.FONTS.length; i < m; i++) {
    662         var font = variant.FONTS[i];
    663         if (typeof(font) === "string") {delete variant.FONTS; this.loadFont(font)}
    664         if (font[n]) {return font} else {this.findBlock(font,n)}
    665       }
    666       return {id:"unknown"};
    667     },
    668 
    669     findBlock: function (font,c) {
    670       if (font.Ranges) {
    671         // FIXME:  do binary search?
    672         for (var i = 0, m = font.Ranges.length; i < m; i++) {
    673           if (c <  font.Ranges[i][0]) return;
    674           if (c <= font.Ranges[i][1]) {
    675             var file = font.Ranges[i][2];
    676             for (var j = font.Ranges.length-1; j >= 0; j--)
    677               {if (font.Ranges[j][2] == file) {font.Ranges.splice(j,1)}}
    678             this.loadFont(font.directory+"/"+file+".js");
    679           }
    680         }
    681       }
    682     },
    683       
    684     loadFont: function (file) {
    685       HUB.RestartAfter(AJAX.Require(this.fontDir+"/"+file));
    686     },
    687 
    688     createDelimiter: function (code,HW,scale,font) {
    689       if (!scale) {scale = 1};
    690       var svg = BBOX.G();
    691       if (!code) {
    692         svg.Clean(); delete svg.element;
    693         svg.w = svg.r = this.TeX.nulldelimiterspace * scale;
    694         return svg;
    695       }
    696       if (!(HW instanceof Array)) {HW = [HW,HW]}
    697       var hw = HW[1]; HW = HW[0];
    698       var delim = {alias: code};
    699       while (delim.alias) {
    700         code = delim.alias; delim = this.FONTDATA.DELIMITERS[code];
    701         if (!delim) {delim = {HW: [0,this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]]}}
    702       }
    703       if (delim.load) {HUB.RestartAfter(AJAX.Require(this.fontDir+"/fontdata-"+delim.load+".js"))}
    704       for (var i = 0, m = delim.HW.length; i < m; i++) {
    705         if (delim.HW[i][0]*scale >= HW-10-SVG.config.blacker || (i == m-1 && !delim.stretch)) {
    706           if (delim.HW[i][2]) {scale *= delim.HW[i][2]}
    707           if (delim.HW[i][3]) {code = delim.HW[i][3]}
    708           return this.createChar(scale,[code,delim.HW[i][1]],font).With({stretched: true});
    709         }
    710       }
    711       if (delim.stretch) {this["extendDelimiter"+delim.dir](svg,hw,delim.stretch,scale,font)}
    712       return svg;
    713     },
    714     createChar: function (scale,data,font) {
    715       var text = "", variant = {fonts: [data[1]], noRemap:true};
    716       if (font && font === MML.VARIANT.BOLD) {variant.fonts = [data[1]+"-bold",data[1]]}
    717       if (typeof(data[1]) !== "string") {variant = data[1]}
    718       if (data[0] instanceof Array) {
    719         for (var i = 0, m = data[0].length; i < m; i++) {text += String.fromCharCode(data[0][i])}
    720       } else {text = String.fromCharCode(data[0])}
    721       if (data[4]) {scale = scale*data[4]}
    722       var svg = this.HandleVariant(variant,scale,text);
    723       if (data[2]) {svg.x = data[2]*1000}
    724       if (data[3]) {svg.y = data[3]*1000}
    725       if (data[5]) {svg.h += data[5]*1000}
    726       if (data[6]) {svg.d += data[6]*1000}
    727       return svg;
    728     },
    729     extendDelimiterV: function (svg,H,delim,scale,font) {
    730       var top = this.createChar(scale,(delim.top||delim.ext),font);
    731       var bot = this.createChar(scale,(delim.bot||delim.ext),font);
    732       var h = top.h + top.d + bot.h + bot.d;
    733       var y = -top.h; svg.Add(top,0,y); y -= top.d;
    734       if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); h += mid.h + mid.d}
    735       if (delim.min && H < h*delim.min) {H = h*delim.min}
    736       if (H > h) {
    737         var ext = this.createChar(scale,delim.ext,font);
    738         var k = (delim.mid ? 2 : 1), eH = (H-h) / k, s = (eH+100) / (ext.h+ext.d);
    739         while (k-- > 0) {
    740           var g = SVG.Element("g",{transform:"translate("+ext.y+","+(y-s*ext.h+50+ext.y)+") scale(1,"+s+")"});
    741           g.appendChild(ext.element.cloneNode(false)); svg.element.appendChild(g); y -= eH;
    742           if (delim.mid && k) {svg.Add(mid,0,y-mid.h); y -= (mid.h+mid.d)}
    743         }
    744       } else if (delim.mid) {
    745         y += (h - H)/2; svg.Add(mid,0,y-mid.h); y += -(mid.h + mid.d) + (h - H)/2;
    746       } else {
    747         y += (h - H);
    748       }
    749       svg.Add(bot,0,y-bot.h); svg.Clean();
    750       svg.scale = scale;
    751       svg.isMultiChar = true;
    752     },
    753     extendDelimiterH: function (svg,W,delim,scale,font) {
    754       var left  = this.createChar(scale,(delim.left||delim.rep),font);
    755       var right = this.createChar(scale,(delim.right||delim.rep),font);
    756       svg.Add(left,-left.l,0);
    757       var w = (left.r - left.l) + (right.r - right.l), x = left.r - left.l;
    758       if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); w += mid.w}
    759       if (delim.min && W < w*delim.min) {W = w*delim.min}
    760       if (W > w) {
    761         var rep = this.createChar(scale,delim.rep,font), fuzz = delim.fuzz || 0;
    762         var k = (delim.mid ? 2 : 1), rW = (W-w) / k, s = (rW+fuzz) / (rep.r-rep.l);
    763         while (k-- > 0) {
    764           var g = SVG.Element("g",{transform:"translate("+(x-fuzz/2-s*rep.l+rep.x)+","+rep.y+") scale("+s+",1)"});
    765           g.appendChild(rep.element.cloneNode(false)); svg.element.appendChild(g); x += rW;
    766           if (delim.mid && k) {svg.Add(mid,x,0); x += mid.w}
    767         }
    768       } else if (delim.mid) {
    769         x -= (w - W)/2; svg.Add(mid,x,0); x += mid.w - (w - W)/2;
    770       } else {
    771         x -= (w - W);
    772       }
    773       svg.Add(right,x-right.l,0); svg.Clean();
    774       svg.scale = scale;
    775       svg.isMultiChar = true;
    776     },
    777 
    778 
    779     MATHSPACE: {
    780       veryverythinmathspace:  1/18,
    781       verythinmathspace:      2/18,
    782       thinmathspace:          3/18,
    783       mediummathspace:        4/18,
    784       thickmathspace:         5/18,
    785       verythickmathspace:     6/18,
    786       veryverythickmathspace: 7/18,
    787       negativeveryverythinmathspace:  -1/18,
    788       negativeverythinmathspace:      -2/18,
    789       negativethinmathspace:          -3/18,
    790       negativemediummathspace:        -4/18,
    791       negativethickmathspace:         -5/18,
    792       negativeverythickmathspace:     -6/18,
    793       negativeveryverythickmathspace: -7/18
    794     },
    795 
    796     //
    797     //  Units are em/1000 so quad is 1em
    798     //
    799     TeX: {
    800       x_height:         430.554,
    801       quad:            1000,
    802       num1:             676.508,
    803       num2:             393.732,
    804       num3:             443.73,
    805       denom1:           685.951,
    806       denom2:           344.841,
    807       sup1:             412.892,
    808       sup2:             362.892,
    809       sup3:             288.888,
    810       sub1:             150,
    811       sub2:             247.217,
    812       sup_drop:         386.108,
    813       sub_drop:          50,
    814       delim1:          2390,
    815       delim2:          1000,
    816       axis_height:      250,
    817       rule_thickness:    60,
    818       big_op_spacing1:  111.111,
    819       big_op_spacing2:  166.666,
    820       big_op_spacing3:  200,
    821       big_op_spacing4:  600,
    822       big_op_spacing5:  100,
    823 
    824       scriptspace:         100,
    825       nulldelimiterspace:  120,
    826       delimiterfactor:     901,
    827       delimitershortfall:  300,
    828 
    829       min_rule_thickness:  1.25,   // in pixels
    830       min_root_space:      1.5     // in pixels
    831     },
    832 
    833     BIGDIMEN: 10000000,
    834     NBSP:   "\u00A0"
    835   });
    836   
    837   var BBOX = SVG.BBOX = MathJax.Object.Subclass({
    838     type: "g", removeable: true,
    839     Init: function (def) {
    840       this.h = this.d = -SVG.BIGDIMEN; this.H = this.D = 0;
    841       this.w = this.r = 0; this.l = SVG.BIGDIMEN;
    842       this.x = this.y = 0; this.scale = 1; this.n = 0;
    843       if (this.type) {this.element = SVG.Element(this.type,def)}
    844     },
    845     With: function (def) {return HUB.Insert(this,def)},
    846     Add: function (svg,dx,dy,forcew,infront) {
    847       if (dx) {svg.x += dx}; if (dy) {svg.y += dy};
    848       if (svg.element) {
    849         if (svg.removeable && svg.element.childNodes.length === 1 && svg.n === 1) {
    850           var child = svg.element.firstChild, nodeName = child.nodeName.toLowerCase();
    851           if (nodeName === "use" || nodeName === "rect") {
    852             svg.element = child; svg.scale = svg.childScale;
    853             var x = svg.childX, y = svg.childY;
    854             svg.x += x; svg.y += y;
    855             svg.h -= y; svg.d += y; svg.H -= y; svg.D +=y;
    856             svg.w -= x; svg.r -= x; svg.l += x;
    857             svg.removeable = false;
    858             child.setAttribute("x",Math.floor(svg.x/svg.scale));
    859             child.setAttribute("y",Math.floor(svg.y/svg.scale));
    860           }
    861         }
    862         if (Math.abs(svg.x) < 1 && Math.abs(svg.y) < 1) {
    863           svg.remove = svg.removeable;
    864         } else {
    865           nodeName = svg.element.nodeName.toLowerCase();
    866           if (nodeName === "g") {
    867             if (!svg.element.firstChild) {svg.remove = svg.removeable}
    868               else {svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")")}
    869           } else if (nodeName === "line" || nodeName === "polygon" ||
    870                      nodeName === "path" || nodeName === "a") {
    871             var transform = svg.element.getAttribute("transform") || "";
    872             if (transform) transform = " "+transform;
    873             transform = "translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")"+transform;
    874             svg.element.setAttribute("transform",transform);
    875           } else {
    876             svg.element.setAttribute("x",Math.floor(svg.x/svg.scale));
    877             svg.element.setAttribute("y",Math.floor(svg.y/svg.scale));
    878           }
    879         }
    880         if (svg.remove) {
    881           this.n += svg.n;
    882           while (svg.element.firstChild) {
    883             if (infront && this.element.firstChild) {
    884               this.element.insertBefore(svg.element.firstChild,this.element.firstChild);
    885             } else {
    886               this.element.appendChild(svg.element.firstChild);
    887             }
    888           }
    889         } else {
    890           if (infront) {this.element.insertBefore(svg.element,this.element.firstChild)}
    891                   else {this.element.appendChild(svg.element)}
    892         }
    893         delete svg.element;
    894       }
    895       if (svg.hasIndent) {this.hasIndent = svg.hasIndent}
    896       if (svg.tw != null) {this.tw = svg.tw}
    897       if (svg.d - svg.y > this.d) {this.d = svg.d - svg.y; if (this.d > this.D) {this.D = this.d}}
    898       if (svg.y + svg.h > this.h) {this.h = svg.y + svg.h; if (this.h > this.H) {this.H = this.h}}
    899       if (svg.D - svg.y > this.D) {this.D = svg.D - svg.y}
    900       if (svg.y + svg.H > this.H) {this.H = svg.y + svg.H}
    901       if (svg.x + svg.l < this.l) {this.l = svg.x + svg.l}
    902       if (svg.x + svg.r > this.r) {this.r = svg.x + svg.r}
    903       if (forcew || svg.x + svg.w + (svg.X||0) > this.w) {this.w = svg.x + svg.w + (svg.X||0)}
    904       this.childScale = svg.scale; this.childX = svg.x; this.childY = svg.y; this.n++;
    905       return svg;
    906     },
    907     Align: function (svg,align,dx,dy,shift) {
    908       dx = ({left: dx, center: (this.w - svg.w)/2, right: this.w - svg.w - dx})[align] || 0;
    909       var w = this.w; this.Add(svg,dx+(shift||0),dy); this.w = w;
    910     },
    911     Clean: function () {
    912       if (this.h === -SVG.BIGDIMEN) {this.h = this.d = this.l = 0}
    913       return this;
    914     }
    915   });
    916   
    917   BBOX.ROW = BBOX.Subclass({
    918     Init: function () {
    919       this.SUPER(arguments).Init.call(this);
    920       this.svg = []; this.sh = this.sd = 0;
    921     },
    922     Check: function (data) {
    923       var svg = data.toSVG(); this.svg.push(svg);
    924       if (data.SVGcanStretch("Vertical")) {svg.mml = data}
    925       if (svg.h > this.sh) {this.sh = svg.h}
    926       if (svg.d > this.sd) {this.sd = svg.d}
    927     },
    928     Stretch: function () {
    929       for (var i = 0, m = this.svg.length; i < m; i++)
    930       {
    931         var svg = this.svg[i], mml = svg.mml;
    932         if (mml) {
    933           if (mml.forceStretch || mml.SVGdata.h !== this.sh || mml.SVGdata.d !== this.sd) {
    934             svg = mml.SVGstretchV(this.sh,this.sd);
    935           }
    936           mml.SVGdata.HW = this.sh; mml.SVGdata.D = this.sd;
    937         }
    938         if (svg.ic) {this.ic = svg.ic} else {delete this.ic}
    939         this.Add(svg,this.w,0,true);
    940       }
    941       delete this.svg;
    942     }
    943   });
    944   
    945   BBOX.RECT = BBOX.Subclass({
    946     type: "rect", removeable: false,
    947     Init: function (h,d,w,def) {
    948       if (def == null) {def = {stroke:"none"}}
    949       def.width = Math.floor(w); def.height = Math.floor(h+d);
    950       this.SUPER(arguments).Init.call(this,def);
    951       this.w = this.r = w; this.h = this.H = h+d; this.d = this.D = this.l = 0; this.y = -d;
    952     }
    953   });
    954   
    955   BBOX.FRAME = BBOX.Subclass({
    956     type: "rect", removeable: false,
    957     Init: function (h,d,w,t,dash,color,def) {
    958       if (def == null) {def = {}}; def.fill = "none";
    959       def["stroke-width"] = SVG.Fixed(t,2);
    960       def.width = Math.floor(w-t); def.height = Math.floor(h+d-t);
    961       def.transform = "translate("+Math.floor(t/2)+","+Math.floor(-d+t/2)+")";
    962       if (dash === "dashed")
    963         {def["stroke-dasharray"] = [Math.floor(6*SVG.em),Math.floor(6*SVG.em)].join(" ")}
    964       this.SUPER(arguments).Init.call(this,def);
    965       this.w = this.r = w; this.h = this.H = h;
    966       this.d = this.D = d; this.l = 0;
    967     }
    968   });
    969   
    970   BBOX.HLINE = BBOX.Subclass({
    971     type: "line", removeable: false,
    972     Init: function (w,t,dash,color,def) {
    973       if (def == null) {def = {"stroke-linecap":"square"}}
    974       if (color && color !== "") {def.stroke = color}
    975       def["stroke-width"] = SVG.Fixed(t,2);
    976       def.x1 = def.y1 = def.y2 = Math.floor(t/2); def.x2 = Math.floor(w-t/2);
    977       if (dash === "dashed") {
    978         var n = Math.floor(Math.max(0,w-t)/(6*t)), m = Math.floor(Math.max(0,w-t)/(2*n+1));
    979         def["stroke-dasharray"] = m+" "+m;
    980       }
    981       if (dash === "dotted") {
    982         def["stroke-dasharray"] = [1,Math.max(150,Math.floor(2*t))].join(" ");
    983         def["stroke-linecap"] = "round";
    984       }
    985       this.SUPER(arguments).Init.call(this,def);
    986       this.w = this.r = w; this.l = 0; this.h = this.H = t; this.d = this.D = 0;
    987     }
    988   });
    989 
    990   BBOX.VLINE = BBOX.Subclass({
    991     type: "line", removeable: false,
    992     Init: function (h,t,dash,color,def) {
    993       if (def == null) {def = {"stroke-linecap":"square"}}
    994       if (color && color !== "") {def.stroke = color}
    995       def["stroke-width"] = SVG.Fixed(t,2);
    996       def.x1 = def.x2 = def.y1 = Math.floor(t/2); def.y2 = Math.floor(h-t/2);
    997       if (dash === "dashed") {
    998         var n = Math.floor(Math.max(0,h-t)/(6*t)), m = Math.floor(Math.max(0,h-t)/(2*n+1));
    999         def["stroke-dasharray"] = m+" "+m;
   1000       }
   1001       if (dash === "dotted") {
   1002         def["stroke-dasharray"] = [1,Math.max(150,Math.floor(2*t))].join(" ");
   1003         def["stroke-linecap"] = "round";
   1004       }
   1005       this.SUPER(arguments).Init.call(this,def);
   1006       this.w = this.r = t; this.l = 0; this.h = this.H = h; this.d = this.D = 0;
   1007     }
   1008   });
   1009 
   1010   BBOX.TEXT = BBOX.Subclass({
   1011     type: "text", removeable: false,
   1012     Init: function (scale,text,def) {
   1013       if (!def) {def = {}}; def.stroke = "none";
   1014       if (def["font-style"] === "") delete def["font-style"];
   1015       if (def["font-weight"] === "") delete def["font-weight"];
   1016       this.SUPER(arguments).Init.call(this,def);
   1017       SVG.addText(this.element,text);
   1018       SVG.textSVG.appendChild(this.element);
   1019       var bbox = this.element.getBBox();
   1020       SVG.textSVG.removeChild(this.element);
   1021       scale *= 1000/SVG.em;
   1022       this.element.setAttribute("transform","scale("+SVG.Fixed(scale)+") matrix(1 0 0 -1 0 0)");
   1023       this.w = this.r = bbox.width*scale; this.l = 0;
   1024       this.h = this.H = -bbox.y*scale;
   1025       this.d = this.D = (bbox.height + bbox.y)*scale;
   1026     }
   1027   });
   1028   
   1029   BBOX.G = BBOX;
   1030   
   1031   BBOX.NULL = BBOX.Subclass({
   1032     Init: function () {
   1033       this.SUPER(arguments).Init.apply(this,arguments);
   1034       this.Clean();
   1035     }
   1036   });
   1037   
   1038   BBOX.GLYPH = BBOX.Subclass({
   1039     type: "path", removeable: false,
   1040     Init: function (scale,id,h,d,w,l,r,p) {
   1041       var def, t = SVG.config.blacker, GLYPH = BBOX.GLYPH;
   1042       var cache = SVG.config.useFontCache;
   1043       var transform = (scale === 1 ? null : "scale("+SVG.Fixed(scale)+")");
   1044       if (cache && !SVG.config.useGlobalCache) {id = "E"+GLYPH.n+"-"+id}
   1045       if (!cache || !GLYPH.glyphs[id]) {
   1046         def = {"stroke-width":t};
   1047         if (cache) {def.id = id} else if (transform) {def.transform = transform}
   1048         def.d = (p ? "M"+p+"Z" : "");
   1049         this.SUPER(arguments).Init.call(this,def);
   1050         if (cache) {GLYPH.defs.appendChild(this.element); GLYPH.glyphs[id] = true;}
   1051       }
   1052       if (cache) {
   1053         def = {}; if (transform) {def.transform = transform}
   1054         this.element = SVG.Element("use",def);
   1055         this.element.setAttributeNS(XLINKNS,"href","#"+id);
   1056       }
   1057       this.h = (h+t) * scale; this.d = (d+t) * scale; this.w = (w+t/2) *scale;
   1058       this.l = (l+t/2) * scale; this.r = (r+t/2) * scale;
   1059       this.H = Math.max(0,this.h); this.D = Math.max(0,this.d);
   1060       this.x = this.y = 0; this.scale = scale;
   1061     }
   1062   },{
   1063     glyphs: {},            // which glpyhs have been used
   1064     defs: null,            // the SVG <defs> element where glyphs are stored
   1065     n: 0                   // the ID for local <defs> for self-contained SVG elements
   1066   });
   1067   
   1068   HUB.Register.StartupHook("mml Jax Ready",function () {
   1069 
   1070     MML = MathJax.ElementJax.mml;
   1071 
   1072     MML.mbase.Augment({
   1073       SVG: BBOX,
   1074       toSVG: function () {
   1075         this.SVGgetStyles();
   1076 	var variant = this.SVGgetVariant();
   1077         var svg = this.SVG(); this.SVGgetScale(svg);
   1078         this.SVGhandleSpace(svg);
   1079         for (var i = 0, m = this.data.length; i < m; i++) {
   1080           if (this.data[i]) {
   1081             var child = svg.Add(this.data[i].toSVG(variant,svg.scale),svg.w,0,true);
   1082             if (child.skew) {svg.skew = child.skew}
   1083           }
   1084         }
   1085         svg.Clean(); var text = this.data.join("");
   1086         if (svg.skew && text.length !== 1) {delete svg.skew}
   1087         if (svg.r > svg.w && text.length === 1 && !variant.noIC)
   1088           {svg.ic = svg.r - svg.w; svg.w = svg.r}
   1089 	this.SVGhandleColor(svg);
   1090         this.SVGsaveData(svg);
   1091 	return svg;
   1092       },
   1093       
   1094       SVGchildSVG: function (i) {
   1095         return (this.data[i] ? this.data[i].toSVG() : BBOX());
   1096       },
   1097       
   1098       SVGdataStretched: function (i,HW,D) {
   1099         this.SVGdata = {HW:HW, D:D};
   1100         if (!this.data[i]) {return BBOX()}
   1101         if (D  != null) {return this.data[i].SVGstretchV(HW,D)}
   1102         if (HW != null) {return this.data[i].SVGstretchH(HW)}
   1103         return this.data[i].toSVG();
   1104       },
   1105       
   1106       SVGsaveData: function (svg) {
   1107         if (!this.SVGdata) {this.SVGdata = {}}
   1108         this.SVGdata.w = svg.w, this.SVGdata.x = svg.x;
   1109         this.SVGdata.h = svg.h, this.SVGdata.d = svg.d;
   1110         if (svg.y) {this.SVGdata.h += svg.y; this.SVGdata.d -= svg.y}
   1111         if (svg.X != null) {this.SVGdata.X = svg.X}
   1112         if (svg.tw != null) {this.SVGdata.tw = svg.tw}
   1113         if (svg.skew) {this.SVGdata.skew = svg.skew}
   1114         if (svg.ic) {this.SVGdata.ic = svg.ic}
   1115         if (this["class"]) {svg.removeable = false; SVG.Element(svg.element,{"class":this["class"]})}
   1116         // FIXME:  if an element is split by linebreaking, the ID will be the same on both parts
   1117         // FIXME:  if an element has an id, its zoomed copy will have the same ID
   1118         if (this.id) {svg.removeable = false; SVG.Element(svg.element,{"id":this.id})}
   1119         if (this.href) {
   1120 	  var a = SVG.Element("a",{"class":"mjx-svg-href"});
   1121 	  a.setAttributeNS(XLINKNS,"href",this.href);
   1122           a.onclick = this.SVGlink;
   1123           SVG.addElement(a,"rect",{width:svg.w, height:svg.h+svg.d, y:-svg.d,
   1124                                    fill:"none", stroke:"none", "pointer-events":"all"});
   1125           if (svg.type === "svg") {
   1126             // for svg element, put <a> inside the main <g> element
   1127             var g = svg.element.firstChild;
   1128             while (g.firstChild) {a.appendChild(g.firstChild)}
   1129             g.appendChild(a);
   1130           } else {
   1131             a.appendChild(svg.element); svg.element = a;
   1132           }
   1133           svg.removeable = false;
   1134         }
   1135         if (SVG.config.addMMLclasses) {
   1136           this.SVGaddClass(svg.element,"mjx-svg-"+this.type);
   1137           svg.removeable = false;
   1138         }
   1139         var style = this.style;
   1140         if (style && svg.element) {
   1141           svg.element.style.cssText = style;
   1142           if (svg.element.style.fontSize) {svg.element.style.fontSize = ""} // handled by scale
   1143           svg.element.style.border = svg.element.style.padding = "";
   1144           if (svg.removeable) {svg.removeable = (svg.element.style.cssText === "")}
   1145         }
   1146         this.SVGaddAttributes(svg);
   1147       },
   1148       SVGaddClass: function (node,name) {
   1149         var classes = node.getAttribute("class");
   1150         node.setAttribute("class",(classes ? classes+" " : "")+name);
   1151       },
   1152       SVGaddAttributes: function (svg) {
   1153         //
   1154         //  Copy RDFa, aria, and other tags from the MathML to the HTML-CSS
   1155         //  output spans Don't copy those in the MML.nocopyAttributes list,
   1156         //  the ignoreMMLattributes configuration list, or anything tha
   1157         //  already exists as a property of the span (e.g., no "onlick", etc.)
   1158         //  If a name in the ignoreMMLattributes object is set to false, then
   1159         //  the attribute WILL be copied.
   1160         //
   1161         if (this.attrNames) {
   1162           var copy = this.attrNames, skip = MML.nocopyAttributes, ignore = HUB.config.ignoreMMLattributes;
   1163           var defaults = (this.type === "mstyle" ? MML.math.prototype.defaults : this.defaults);
   1164           for (var i = 0, m = copy.length; i < m; i++) {
   1165             var id = copy[i];
   1166             if (ignore[id] == false || (!skip[id] && !ignore[id] &&
   1167                 defaults[id] == null && typeof(svg.element[id]) === "undefined")) {
   1168               svg.element.setAttribute(id,this.attr[id]);
   1169               svg.removeable = false;
   1170             }
   1171           }
   1172         }
   1173       },
   1174       //
   1175       //  WebKit currently scrolls to the BOTTOM of an svg element if it contains the
   1176       //  target of the link, so implement link by hand, to the containing span element.
   1177       //  
   1178       SVGlink: function () {
   1179         var href = this.href.animVal;
   1180         if (href.charAt(0) === "#") {
   1181           var target = SVG.hashCheck(document.getElementById(href.substr(1)));
   1182           if (target && target.scrollIntoView) 
   1183             {setTimeout(function () {target.parentNode.scrollIntoView(true)},1)}
   1184         }
   1185         document.location = href;
   1186       },
   1187       
   1188       SVGgetStyles: function () {
   1189         if (this.style) {
   1190           var span = HTML.Element("span");
   1191           span.style.cssText = this.style;
   1192           this.styles = this.SVGprocessStyles(span.style);
   1193         }
   1194       },
   1195       SVGprocessStyles: function (style) {
   1196         var styles = {border:SVG.getBorders(style), padding:SVG.getPadding(style)};
   1197         if (!styles.border) {delete styles.border}
   1198         if (!styles.padding) {delete styles.padding}
   1199         if (style.fontSize) {styles.fontSize = style.fontSize}
   1200         if (style.color) {styles.color = style.color}
   1201         if (style.backgroundColor) {styles.background = style.backgroundColor}
   1202         if (style.fontStyle)  {styles.fontStyle = style.fontStyle}
   1203         if (style.fontWeight) {styles.fontWeight = style.fontWeight}
   1204         if (style.fontFamily) {styles.fontFamily = style.fontFamily}
   1205         if (styles.fontWeight && styles.fontWeight.match(/^\d+$/))
   1206           {styles.fontWeight = (parseInt(styles.fontWeight) > 600 ? "bold" : "normal")}
   1207         return styles;
   1208       },
   1209             
   1210       SVGhandleSpace: function (svg) {
   1211 	if (this.useMMLspacing) {
   1212 	  if (this.type !== "mo") return;
   1213 	  var values = this.getValues("scriptlevel","lspace","rspace");
   1214 	  if (values.scriptlevel <= 0 || this.hasValue("lspace") || this.hasValue("rspace")) {
   1215             var mu = this.SVGgetMu(svg);
   1216 	    values.lspace = Math.max(0,SVG.length2em(values.lspace,mu));
   1217 	    values.rspace = Math.max(0,SVG.length2em(values.rspace,mu));
   1218 	    var core = this, parent = this.Parent();
   1219 	    while (parent && parent.isEmbellished() && parent.Core() === core)
   1220 	      {core = parent; parent = parent.Parent()}
   1221 	    if (values.lspace) {svg.x += values.lspace}
   1222 	    if (values.rspace) {svg.X = values.rspace}
   1223 	  }
   1224 	} else {
   1225 	  var space = this.texSpacing();
   1226           this.SVGgetScale();
   1227 	  if (space !== "") {svg.x += SVG.length2em(space,this.scale)*this.mscale}
   1228         }
   1229       },
   1230 
   1231       SVGhandleColor: function (svg) {
   1232         var values = this.getValues("mathcolor","color");
   1233         if (this.styles && this.styles.color && !values.color) {values.color = this.styles.color}
   1234         if (values.color && !this.mathcolor) {values.mathcolor = values.color}
   1235         if (values.mathcolor) {
   1236           SVG.Element(svg.element,{fill:values.mathcolor,stroke:values.mathcolor})
   1237           svg.removeable = false;
   1238         }
   1239         var borders = (this.styles||{}).border, padding = (this.styles||{}).padding,
   1240             bleft = ((borders||{}).left||0), pleft = ((padding||{}).left||0), id;
   1241 	values.background = (this.mathbackground || this.background ||
   1242                              (this.styles||{}).background || MML.COLOR.TRANSPARENT);
   1243         if (bleft + pleft) {
   1244           //
   1245           //  Make a box and move the contents of svg to it,
   1246           //    then add it back into svg, but offset by the left amount
   1247           //
   1248           var dup = BBOX(); for (id in svg) {if (svg.hasOwnProperty(id)) {dup[id] = svg[id]}}
   1249           dup.x = 0; dup.y = 0;
   1250           svg.element = SVG.Element("g"); svg.removeable = true;
   1251           svg.Add(dup,bleft+pleft,0);
   1252         }
   1253         //
   1254         //  Adjust size by padding and dashed borders (left is taken care of above)
   1255         //
   1256         if (padding) {svg.w += padding.right||0; svg.h += padding.top||0; svg.d += padding.bottom||0}
   1257         if (borders) {svg.w += borders.right||0; svg.h += borders.top||0; svg.d += borders.bottom||0}
   1258         //
   1259         //  Add background color
   1260         //
   1261 	if (values.background !== MML.COLOR.TRANSPARENT) {
   1262           var nodeName = svg.element.nodeName.toLowerCase();
   1263           if (nodeName !== "g" && nodeName !== "svg") {
   1264             var g = SVG.Element("g"); g.appendChild(svg.element);
   1265             svg.element = g; svg.removeable = true;
   1266           }
   1267           svg.Add(BBOX.RECT(svg.h,svg.d,svg.w,{fill:values.background,stroke:"none"}),0,0,false,true)
   1268         }
   1269         //
   1270         //  Add borders
   1271         //
   1272         if (borders) {
   1273           var dd = 5; // fuzz factor to avoid anti-alias problems at edges
   1274           var sides = {
   1275             left:  ["V",svg.h+svg.d,-dd,-svg.d],
   1276             right: ["V",svg.h+svg.d,svg.w-borders.right+dd,-svg.d],
   1277             top:   ["H",svg.w,0,svg.h-borders.top+dd],
   1278             bottom:["H",svg.w,0,-svg.d-dd]
   1279           }
   1280           for (id in sides) {if (sides.hasOwnProperty(id)) {
   1281             if (borders[id]) {
   1282               var side = sides[id], box = BBOX[side[0]+"LINE"];
   1283               svg.Add(box(side[1],borders[id],borders[id+"Style"],borders[id+"Color"]),side[2],side[3]);
   1284             }
   1285           }}
   1286         }
   1287       },
   1288       
   1289       SVGhandleVariant: function (variant,scale,text) {
   1290         return SVG.HandleVariant(variant,scale,text);
   1291       },
   1292 
   1293       SVGgetVariant: function () {
   1294 	var values = this.getValues("mathvariant","fontfamily","fontweight","fontstyle");
   1295 	var variant = values.mathvariant;
   1296         if (this.variantForm) variant = "-"+SVG.fontInUse+"-variant";
   1297         values.hasVariant = this.Get("mathvariant",true);  // null if not explicitly specified
   1298         if (!values.hasVariant) {
   1299           values.family = values.fontfamily;
   1300           values.weight = values.fontweight;
   1301           values.style  = values.fontstyle;
   1302         }
   1303         if (this.styles) {
   1304           if (!values.style  && this.styles.fontStyle)  {values.style = this.styles.fontStyle}
   1305           if (!values.weight && this.styles.fontWeight) {values.weight = this.styles.fontWeight}
   1306           if (!values.family && this.styles.fontFamily) {values.family = this.styles.fontFamily}
   1307         }
   1308         if (values.family && !values.hasVariant) {
   1309 	  if (!values.weight && values.mathvariant.match(/bold/)) {values.weight = "bold"}
   1310           if (!values.style && values.mathvariant.match(/italic/)) {values.style = "italic"}
   1311           variant = {forceFamily: true, font: {"font-family":values.family}};
   1312           if (values.style) {variant.font["font-style"] = values.style}
   1313           if (values.weight) {variant.font["font-weight"] = values.weight}
   1314 	  return variant;
   1315 	}
   1316         if (values.weight === "bold") {
   1317           variant = {
   1318             normal:MML.VARIANT.BOLD, italic:MML.VARIANT.BOLDITALIC,
   1319             fraktur:MML.VARIANT.BOLDFRAKTUR, script:MML.VARIANT.BOLDSCRIPT,
   1320             "sans-serif":MML.VARIANT.BOLDSANSSERIF,
   1321             "sans-serif-italic":MML.VARIANT.SANSSERIFBOLDITALIC
   1322           }[variant]||variant;
   1323         } else if (values.weight === "normal") {
   1324           variant = {
   1325             bold:MML.VARIANT.normal, "bold-italic":MML.VARIANT.ITALIC,
   1326             "bold-fraktur":MML.VARIANT.FRAKTUR, "bold-script":MML.VARIANT.SCRIPT,
   1327             "bold-sans-serif":MML.VARIANT.SANSSERIF,
   1328             "sans-serif-bold-italic":MML.VARIANT.SANSSERIFITALIC
   1329           }[variant]||variant;
   1330         }
   1331         if (values.style === "italic") {
   1332           variant = {
   1333             normal:MML.VARIANT.ITALIC, bold:MML.VARIANT.BOLDITALIC,
   1334             "sans-serif":MML.VARIANT.SANSSERIFITALIC,
   1335             "bold-sans-serif":MML.VARIANT.SANSSERIFBOLDITALIC
   1336           }[variant]||variant;
   1337         } else if (values.style === "normal") {
   1338           variant = {
   1339             italic:MML.VARIANT.NORMAL, "bold-italic":MML.VARIANT.BOLD,
   1340             "sans-serif-italic":MML.VARIANT.SANSSERIF,
   1341             "sans-serif-bold-italic":MML.VARIANT.BOLDSANSSERIF
   1342           }[variant]||variant;
   1343         }
   1344         if (!(variant in SVG.FONTDATA.VARIANT)) {
   1345           // If the mathvariant value is invalid or not supported by this
   1346           // font, fallback to normal. See issue 363.
   1347           variant = "normal";
   1348         }
   1349 	return SVG.FONTDATA.VARIANT[variant];
   1350       },
   1351       
   1352       SVGgetScale: function (svg) {
   1353         var scale = 1;
   1354         if (this.mscale) {
   1355           scale = this.scale;
   1356         } else {
   1357           var values = this.getValues("scriptlevel","fontsize");
   1358           values.mathsize = (this.isToken ? this : this.Parent()).Get("mathsize");
   1359           if ((this.styles||{}).fontSize && !values.fontsize) {values.fontsize = this.styles.fontSize}
   1360           if (values.fontsize && !this.mathsize) {values.mathsize = values.fontsize}
   1361           if (values.scriptlevel !== 0) {
   1362             if (values.scriptlevel > 2) {values.scriptlevel = 2}
   1363             scale = Math.pow(this.Get("scriptsizemultiplier"),values.scriptlevel);
   1364             values.scriptminsize = SVG.length2em(this.Get("scriptminsize"))/1000;
   1365             if (scale < values.scriptminsize) {scale = values.scriptminsize}
   1366           }
   1367           this.scale = scale; this.mscale = SVG.length2em(values.mathsize)/1000;
   1368         }
   1369         if (svg) {svg.scale = scale; if (this.isToken) {svg.scale *= this.mscale}}
   1370 	return scale * this.mscale;
   1371       },
   1372       SVGgetMu: function (svg) {
   1373 	var mu = 1, values = this.getValues("scriptlevel","scriptsizemultiplier");
   1374         if (svg.scale && svg.scale !== 1) {mu = 1/svg.scale}
   1375 	if (values.scriptlevel !== 0) {
   1376 	  if (values.scriptlevel > 2) {values.scriptlevel = 2}
   1377 	  mu = Math.sqrt(Math.pow(values.scriptsizemultiplier,values.scriptlevel));
   1378 	}
   1379 	return mu;
   1380       },
   1381 
   1382       SVGnotEmpty: function (data) {
   1383 	while (data) {
   1384 	  if ((data.type !== "mrow" && data.type !== "texatom") ||
   1385 	       data.data.length > 1) {return true}
   1386 	  data = data.data[0];
   1387 	}
   1388 	return false;
   1389       },
   1390       
   1391       SVGcanStretch: function (direction) {
   1392         var can = false;
   1393 	if (this.isEmbellished()) {
   1394           var core = this.Core();
   1395           if (core && core !== this) {
   1396             can = core.SVGcanStretch(direction);
   1397             if (can && core.forceStretch) {this.forceStretch = true}
   1398           }
   1399         }
   1400         return can;
   1401       },
   1402       SVGstretchV: function (h,d) {return this.toSVG(h,d)},
   1403       SVGstretchH: function (w) {return this.toSVG(w)},
   1404       
   1405       SVGlineBreaks: function () {return false}
   1406       
   1407     },{
   1408       SVGemptySVG: function () {
   1409         var svg = this.SVG();
   1410         svg.Clean();
   1411         this.SVGsaveData(svg);
   1412 	return svg;
   1413       },
   1414       SVGautoload: function () {
   1415 	var file = SVG.autoloadDir+"/"+this.type+".js";
   1416 	HUB.RestartAfter(AJAX.Require(file));
   1417       },
   1418       SVGautoloadFile: function (name) {
   1419 	var file = SVG.autoloadDir+"/"+name+".js";
   1420 	HUB.RestartAfter(AJAX.Require(file));
   1421       }
   1422     });
   1423 
   1424     MML.chars.Augment({
   1425       toSVG: function (variant,scale,remap,chars) {
   1426         var text = this.data.join("").replace(/[\u2061-\u2064]/g,""); // remove invisibles
   1427         if (remap) {text = remap(text,chars)}
   1428 	return this.SVGhandleVariant(variant,scale,text);
   1429       }
   1430     });
   1431     MML.entity.Augment({
   1432       toSVG: function (variant,scale,remap,chars) {
   1433         var text = this.toString().replace(/[\u2061-\u2064]/g,""); // remove invisibles
   1434         if (remap) {text = remap(text,chars)}
   1435 	return this.SVGhandleVariant(variant,scale,text);
   1436       }
   1437     });
   1438 
   1439     MML.mo.Augment({
   1440       toSVG: function (HW,D) {
   1441         this.SVGgetStyles();
   1442         var svg = this.svg = this.SVG();
   1443         var scale = this.SVGgetScale(svg);
   1444         this.SVGhandleSpace(svg);
   1445         if (this.data.length == 0) {svg.Clean(); this.SVGsaveData(svg); return svg}
   1446         //
   1447         //  Stretch the operator, if that is requested
   1448         //
   1449         if (D != null) {return this.SVGstretchV(HW,D)}
   1450         else if (HW != null) {return this.SVG.strechH(HW)}
   1451         //
   1452         //  Get the variant, and check for operator size
   1453         //
   1454         var variant = this.SVGgetVariant();
   1455 	var values = this.getValues("largeop","displaystyle");
   1456 	if (values.largeop)
   1457 	  {variant = SVG.FONTDATA.VARIANT[values.displaystyle ? "-largeOp" : "-smallOp"]}
   1458         //
   1459         //  Get character translation for superscript and accents
   1460         //
   1461         var parent = this.CoreParent(),
   1462             isScript = (parent && parent.isa(MML.msubsup) && this !== parent.data[0]),
   1463             mapchars = (isScript?this.remapChars:null);
   1464         if (this.data.join("").length === 1 && parent && parent.isa(MML.munderover) &&
   1465             this.CoreText(parent.data[parent.base]).length === 1) {
   1466           var over = parent.data[parent.over], under = parent.data[parent.under];
   1467           if (over && this === over.CoreMO() && parent.Get("accent")) {mapchars = SVG.FONTDATA.REMAPACCENT}
   1468           else if (under && this === under.CoreMO() && parent.Get("accentunder")) {mapchars = SVG.FONTDATA.REMAPACCENTUNDER}
   1469         }
   1470         //
   1471         //  Primes must come from another font
   1472         //
   1473         if (isScript && this.data.join("").match(/['`"\u00B4\u2032-\u2037\u2057]/))
   1474           {variant = SVG.FONTDATA.VARIANT["-"+SVG.fontInUse+"-variant"]}
   1475         //
   1476         //  Typeset contents
   1477         //
   1478 	for (var i = 0, m = this.data.length; i < m; i++) {
   1479           if (this.data[i]) {
   1480             var text = this.data[i].toSVG(variant,scale,this.remap,mapchars), x = svg.w;
   1481             if (x === 0 && -text.l > 10*text.w) {x += -text.l} // initial combining character doesn't combine
   1482             svg.Add(text,x,0,true);
   1483             if (text.skew) {svg.skew = text.skew}
   1484           }
   1485         }
   1486         svg.Clean();
   1487 	if (this.data.join("").length !== 1) {delete svg.skew}
   1488         //
   1489         //  Handle large operator centering
   1490         //
   1491 	if (values.largeop) {
   1492 	  svg.y = SVG.TeX.axis_height - (svg.h - svg.d)/2/scale;
   1493 	  if (svg.r > svg.w) {svg.ic = svg.r - svg.w; svg.w = svg.r}
   1494 	}
   1495         //
   1496         //  Finish up
   1497         //
   1498 	this.SVGhandleColor(svg);
   1499         this.SVGsaveData(svg);
   1500 	return svg;
   1501       },
   1502       SVGcanStretch: function (direction) {
   1503 	if (!this.Get("stretchy")) {return false}
   1504 	var c = this.data.join("");
   1505 	if (c.length > 1) {return false}
   1506         var parent = this.CoreParent();
   1507         if (parent && parent.isa(MML.munderover) && 
   1508             this.CoreText(parent.data[parent.base]).length === 1) {
   1509           var over = parent.data[parent.over], under = parent.data[parent.under];
   1510           if (over && this === over.CoreMO() && parent.Get("accent")) {c = SVG.FONTDATA.REMAPACCENT[c]||c}
   1511           else if (under && this === under.CoreMO() && parent.Get("accentunder")) {c = SVG.FONTDATA.REMAPACCENTUNDER[c]||c}
   1512         }
   1513 	c = SVG.FONTDATA.DELIMITERS[c.charCodeAt(0)];
   1514         var can = (c && c.dir == direction.substr(0,1));
   1515         if (!can) {delete this.svg}
   1516         this.forceStretch = can && (this.Get("minsize",true) || this.Get("maxsize",true));
   1517         return can;
   1518       },
   1519       SVGstretchV: function (h,d) {
   1520         var svg = this.svg || this.toSVG();
   1521 	var values = this.getValues("symmetric","maxsize","minsize");
   1522 	var axis = SVG.TeX.axis_height*svg.scale, mu = this.SVGgetMu(svg), H;
   1523 	if (values.symmetric) {H = 2*Math.max(h-axis,d+axis)} else {H = h + d}
   1524 	values.maxsize = SVG.length2em(values.maxsize,mu,svg.h+svg.d);
   1525 	values.minsize = SVG.length2em(values.minsize,mu,svg.h+svg.d);
   1526 	H = Math.max(values.minsize,Math.min(values.maxsize,H));
   1527         if (H != values.minsize)
   1528           {H = [Math.max(H*SVG.TeX.delimiterfactor/1000,H-SVG.TeX.delimitershortfall),H]}
   1529 	svg = SVG.createDelimiter(this.data.join("").charCodeAt(0),H,svg.scale);
   1530 	if (values.symmetric) {H = (svg.h + svg.d)/2 + axis}
   1531 	  else {H = (svg.h + svg.d) * h/(h + d)}
   1532         svg.y = H - svg.h;
   1533 	this.SVGhandleSpace(svg);
   1534 	this.SVGhandleColor(svg);
   1535         delete this.svg.element;
   1536         this.SVGsaveData(svg);
   1537         svg.stretched = true;
   1538 	return svg;
   1539       },
   1540       SVGstretchH: function (w) {
   1541         var svg = this.svg || this.toSVG(), mu = this.SVGgetMu(svg);
   1542 	var values = this.getValues("maxsize","minsize","mathvariant","fontweight");
   1543         // FIXME:  should take style="font-weight:bold" into account as well
   1544 	if ((values.fontweight === "bold" || parseInt(values.fontweight) >= 600) &&
   1545             !this.Get("mathvariant",true)) {values.mathvariant = MML.VARIANT.BOLD}
   1546 	values.maxsize = SVG.length2em(values.maxsize,mu,svg.w);
   1547 	values.minsize = SVG.length2em(values.minsize,mu,svg.w);
   1548 	w = Math.max(values.minsize,Math.min(values.maxsize,w));
   1549         svg = SVG.createDelimiter(this.data.join("").charCodeAt(0),w,svg.scale,values.mathvariant);
   1550 	this.SVGhandleSpace(svg);
   1551 	this.SVGhandleColor(svg);
   1552         delete this.svg.element;
   1553         this.SVGsaveData(svg);
   1554         svg.stretched = true;
   1555 	return svg;
   1556       }
   1557     });
   1558 
   1559     MML.mtext.Augment({
   1560       toSVG: function () {
   1561         if (SVG.config.mtextFontInherit || this.Parent().type === "merror") {
   1562           this.SVGgetStyles();
   1563           var svg = this.SVG(), scale = this.SVGgetScale(svg);
   1564           this.SVGhandleSpace(svg);
   1565           var variant = this.SVGgetVariant(), def = {direction:this.Get("dir")};
   1566           if (variant.bold)   {def["font-weight"] = "bold"}
   1567           if (variant.italic) {def["font-style"] = "italic"}
   1568           variant = this.Get("mathvariant");
   1569           if (variant === "monospace") {def["class"] = "MJX-monospace"}
   1570             else if (variant.match(/sans-serif/)) {def["class"] = "MJX-sans-serif"}
   1571           svg.Add(BBOX.TEXT(scale*100/SVG.config.scale,this.data.join(""),def)); svg.Clean();
   1572           this.SVGhandleColor(svg);
   1573           this.SVGsaveData(svg);
   1574           return svg;
   1575         } else {
   1576           return this.SUPER(arguments).toSVG.call(this);
   1577         }
   1578       }
   1579     });
   1580     
   1581     MML.merror.Augment({
   1582       toSVG: function (HW,D) {
   1583         this.SVGgetStyles();
   1584         var svg = this.SVG(), scale = SVG.length2em(this.styles.fontSize||1)/1000;
   1585         this.SVGhandleSpace(svg);
   1586         var def = (scale !== 1 ? {transform:"scale("+SVG.Fixed(scale)+")"} : {});
   1587         var bbox = BBOX(def);
   1588         bbox.Add(this.SVGchildSVG(0)); bbox.Clean();
   1589         if (scale !== 1) {
   1590           bbox.removeable = false;
   1591           var adjust = ["w","h","d","l","r","D","H"];
   1592           for (var i = 0, m = adjust.length; i < m; i++) {bbox[adjust[i]] *= scale}
   1593         }
   1594         svg.Add(bbox); svg.Clean();
   1595         this.SVGhandleColor(svg);
   1596         this.SVGsaveData(svg);
   1597         return svg;
   1598       },
   1599       SVGgetStyles: function () {
   1600         var span = HTML.Element("span",{style: SVG.config.merrorStyle});
   1601         this.styles = this.SVGprocessStyles(span.style);
   1602         if (this.style) {
   1603           span.style.cssText = this.style;
   1604           HUB.Insert(this.styles,this.SVGprocessStyles(span.style));
   1605         }
   1606       }
   1607     });
   1608 
   1609     MML.ms.Augment({toSVG: MML.mbase.SVGautoload});
   1610 
   1611     MML.mglyph.Augment({toSVG: MML.mbase.SVGautoload});
   1612 
   1613     MML.mspace.Augment({
   1614       toSVG: function () {
   1615         this.SVGgetStyles();
   1616 	var values = this.getValues("height","depth","width");
   1617 	values.mathbackground = this.mathbackground;
   1618 	if (this.background && !this.mathbackground) {values.mathbackground = this.background}
   1619         var svg = this.SVG(); this.SVGgetScale(svg);
   1620         var scale = this.mscale, mu = this.SVGgetMu(svg); 
   1621 	svg.h = SVG.length2em(values.height,mu) * scale;
   1622         svg.d = SVG.length2em(values.depth,mu)  * scale;
   1623 	svg.w = svg.r = SVG.length2em(values.width,mu) * scale;
   1624         if (svg.w < 0) {svg.x = svg.w; svg.w = svg.r = 0}
   1625         if (svg.h < -svg.d) {svg.d = -svg.h}
   1626         svg.l = 0; svg.Clean();
   1627         this.SVGhandleColor(svg);
   1628         this.SVGsaveData(svg);
   1629         return svg;
   1630       }
   1631     });
   1632 
   1633     MML.mphantom.Augment({
   1634       toSVG: function (HW,D) {
   1635         this.SVGgetStyles();
   1636         var svg = this.SVG(); this.SVGgetScale(svg);
   1637 	if (this.data[0] != null) {
   1638           this.SVGhandleSpace(svg); svg.Add(this.SVGdataStretched(0,HW,D)); svg.Clean();
   1639           while (svg.element.firstChild) {svg.element.removeChild(svg.element.firstChild)}
   1640 	}
   1641 	this.SVGhandleColor(svg);
   1642         this.SVGsaveData(svg);
   1643         if (svg.removeable && !svg.element.firstChild) {delete svg.element}
   1644 	return svg;
   1645       }
   1646     });
   1647 
   1648     MML.mpadded.Augment({
   1649       toSVG: function (HW,D) {
   1650         this.SVGgetStyles();
   1651         var svg = this.SVG();
   1652 	if (this.data[0] != null) {
   1653           this.SVGgetScale(svg); this.SVGhandleSpace(svg);
   1654           var pad = this.SVGdataStretched(0,HW,D), mu = this.SVGgetMu(svg);
   1655 	  var values = this.getValues("height","depth","width","lspace","voffset"), X = 0, Y = 0;
   1656 	  if (values.lspace)  {X = this.SVGlength2em(pad,values.lspace,mu)}
   1657 	  if (values.voffset) {Y = this.SVGlength2em(pad,values.voffset,mu)}
   1658           var h = pad.h, d = pad.d, w = pad.w, y = pad.y; // these can change durring the Add() 
   1659           svg.Add(pad,X,Y); svg.Clean();
   1660           svg.h = h+y; svg.d = d-y; svg.w = w; svg.removeable = false;
   1661 	  if (values.height !== "") {svg.h = this.SVGlength2em(svg,values.height,mu,"h",0)}
   1662 	  if (values.depth  !== "") {svg.d = this.SVGlength2em(svg,values.depth,mu,"d",0)}
   1663 	  if (values.width  !== "") {svg.w = this.SVGlength2em(svg,values.width,mu,"w",0)}
   1664 	  if (svg.h > svg.H) {svg.H = svg.h}; if (svg.d > svg.D) {svg.D = svg.d}
   1665 	}
   1666 	this.SVGhandleColor(svg);
   1667         this.SVGsaveData(svg);
   1668 	return svg;
   1669       },
   1670       SVGlength2em: function (svg,length,mu,d,m) {
   1671 	if (m == null) {m = -SVG.BIGDIMEN}
   1672 	var match = String(length).match(/width|height|depth/);
   1673 	var size = (match ? svg[match[0].charAt(0)] : (d ? svg[d] : 0));
   1674 	var v = SVG.length2em(length,mu,size/this.mscale)*this.mscale;
   1675 	if (d && String(length).match(/^\s*[-+]/))
   1676 	  {return Math.max(m,svg[d]+v)} else {return v}
   1677       }
   1678     });
   1679 
   1680     MML.mrow.Augment({
   1681       SVG: BBOX.ROW,
   1682       toSVG: function (h,d) {
   1683         this.SVGgetStyles();
   1684 	var svg = this.SVG();
   1685         this.SVGhandleSpace(svg);
   1686         if (d != null) {svg.sh = h; svg.sd = d}
   1687 	for (var i = 0, m = this.data.length; i < m; i++)
   1688           {if (this.data[i]) {svg.Check(this.data[i])}}
   1689         svg.Stretch(); svg.Clean();
   1690         if (this.data.length === 1 && this.data[0]) {
   1691           var data = this.data[0].SVGdata;
   1692           if (data.skew) {svg.skew = data.skew}
   1693         }
   1694         if (this.SVGlineBreaks(svg)) {svg = this.SVGmultiline(svg)}
   1695 	this.SVGhandleColor(svg);
   1696         this.SVGsaveData(svg);
   1697 	return svg;
   1698       },
   1699       SVGlineBreaks: function (svg) {
   1700         if (!this.parent.linebreakContainer) {return false}
   1701         return (SVG.config.linebreaks.automatic &&
   1702                 svg.w > SVG.linebreakWidth) || this.hasNewline();
   1703       },
   1704       SVGmultiline: function (span) {MML.mbase.SVGautoloadFile("multiline")},
   1705       SVGstretchH: function (w) {
   1706 	var svg = this.SVG();
   1707         this.SVGhandleSpace(svg);
   1708 	for (var i = 0, m = this.data.length; i < m; i++)
   1709           {svg.Add(this.SVGdataStretched(i,w),svg.w,0)}
   1710         svg.Clean();
   1711         this.SVGhandleColor(svg);
   1712         this.SVGsaveData(svg);
   1713         return svg;
   1714       }
   1715     });
   1716 
   1717     MML.mstyle.Augment({
   1718       toSVG: function () {
   1719         this.SVGgetStyles();
   1720         var svg = this.SVG();
   1721 	if (this.data[0] != null) {
   1722           this.SVGhandleSpace(svg);
   1723           var math = svg.Add(this.data[0].toSVG()); svg.Clean();
   1724           if (math.ic) {svg.ic = math.ic}
   1725 	  this.SVGhandleColor(svg);
   1726 	}
   1727         this.SVGsaveData(svg);
   1728 	return svg;
   1729       },
   1730       SVGstretchH: function (w) {
   1731 	return (this.data[0] != null ? this.data[0].SVGstretchH(w) : BBOX.NULL());
   1732       },
   1733       SVGstretchV: function (h,d) {
   1734 	return (this.data[0] != null ? this.data[0].SVGstretchV(h,d) : BBOX.NULL());
   1735       }
   1736     });
   1737 
   1738     MML.mfrac.Augment({
   1739       toSVG: function () {
   1740         this.SVGgetStyles();
   1741         var svg = this.SVG(), scale = this.SVGgetScale(svg);
   1742         var frac = BBOX(); frac.scale = svg.scale; this.SVGhandleSpace(frac);
   1743         var num = this.SVGchildSVG(0), den = this.SVGchildSVG(1);
   1744 	var values = this.getValues("displaystyle","linethickness","numalign","denomalign","bevelled");
   1745 	var isDisplay = values.displaystyle;
   1746 	var a = SVG.TeX.axis_height * scale;
   1747 	if (values.bevelled) {
   1748 	  var delta = (isDisplay ? 400 : 150);
   1749 	  var H = Math.max(num.h+num.d,den.h+den.d)+2*delta;
   1750           var bevel = SVG.createDelimiter(0x2F,H);
   1751           frac.Add(num,0,(num.d-num.h)/2+a+delta);
   1752           frac.Add(bevel,num.w-delta/2,(bevel.d-bevel.h)/2+a);
   1753 	  frac.Add(den,num.w+bevel.w-delta,(den.d-den.h)/2+a-delta);
   1754 	} else {
   1755 	  var W = Math.max(num.w,den.w);
   1756 	  var t = SVG.thickness2em(values.linethickness,this.scale)*this.mscale, p,q, u,v;
   1757 	  var mt = SVG.TeX.min_rule_thickness/SVG.em * 1000;
   1758 	  if (isDisplay) {u = SVG.TeX.num1; v = SVG.TeX.denom1}
   1759 	    else {u = (t === 0 ? SVG.TeX.num3 : SVG.TeX.num2); v = SVG.TeX.denom2}
   1760 	  u *= scale; v *= scale;
   1761 	  if (t === 0) {// \atop
   1762 	    p = Math.max((isDisplay ? 7 : 3) * SVG.TeX.rule_thickness, 2*mt); // force to at least 2 px
   1763 	    q = (u - num.d) - (den.h - v);
   1764 	    if (q < p) {u += (p - q)/2; v += (p - q)/2}
   1765             frac.w = W; t = 0;
   1766 	  } else {// \over
   1767 	    p = Math.max((isDisplay ? 2 : 0) * mt + t, t/2 + 1.5*mt);  // force to be at least 1.5px
   1768 	    q = (u - num.d) - (a + t/2); if (q < p) {u += p - q}
   1769 	    q = (a - t/2) - (den.h - v); if (q < p) {v += p - q}
   1770 	    frac.Add(BBOX.RECT(t/2,t/2,W+2*t),0,a);
   1771 	  }
   1772           frac.Align(num,values.numalign,t,u);
   1773           frac.Align(den,values.denomalign,t,-v);
   1774 	}
   1775         frac.Clean(); svg.Add(frac,0,0); svg.Clean();
   1776 	this.SVGhandleColor(svg);
   1777         this.SVGsaveData(svg);
   1778 	return svg;
   1779       },
   1780       SVGcanStretch: function (direction) {return false},
   1781       SVGhandleSpace: function (svg) {
   1782       	if (!this.texWithDelims && !this.useMMLspacing) {
   1783           //
   1784           //  Add nulldelimiterspace around the fraction
   1785           //   (TeXBook pg 150 and Appendix G rule 15e)
   1786           //
   1787           svg.x = svg.X = SVG.TeX.nulldelimiterspace * this.mscale;
   1788         }
   1789         this.SUPER(arguments).SVGhandleSpace.call(this,svg);
   1790       }
   1791     });
   1792 
   1793     MML.msqrt.Augment({
   1794       toSVG: function () {
   1795         this.SVGgetStyles();
   1796         var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg);
   1797 	var base = this.SVGchildSVG(0), rule, surd;
   1798 	var t = SVG.TeX.rule_thickness * scale, p,q, H, x = 0;
   1799 	if (this.Get("displaystyle")) {p = SVG.TeX.x_height * scale} else {p = t}
   1800 	q = Math.max(t + p/4,1000*SVG.TeX.min_root_space/SVG.em);
   1801         H = base.h + base.d + q + t;
   1802 	surd = SVG.createDelimiter(0x221A,H,scale);
   1803 	if (surd.h + surd.d > H) {q = ((surd.h+surd.d) - (H-t)) / 2}
   1804         rule = BBOX.RECT(t,0,base.w);
   1805 	H = base.h + q + t;
   1806 	x = this.SVGaddRoot(svg,surd,x,surd.h+surd.d-H,scale);
   1807         svg.Add(surd,x,H-surd.h);
   1808         svg.Add(rule,x+surd.w,H-rule.h);
   1809 	svg.Add(base,x+surd.w,0);
   1810         svg.Clean();
   1811         svg.h += t; svg.H += t;
   1812 	this.SVGhandleColor(svg);
   1813         this.SVGsaveData(svg);
   1814 	return svg;
   1815       },
   1816       SVGaddRoot: function (svg,surd,x,d,scale) {return x}
   1817     });
   1818 
   1819     MML.mroot.Augment({
   1820       toSVG: MML.msqrt.prototype.toSVG,
   1821       SVGaddRoot: function (svg,surd,x,d,scale) {
   1822         var dx = (surd.isMultiChar ? .55 : .65) * surd.w;
   1823 	if (this.data[1]) {
   1824           var root = this.data[1].toSVG(); root.x = 0;
   1825           var h = this.SVGrootHeight(surd.h+surd.d,scale,root)-d;
   1826           var w = Math.min(root.w,root.r); // remove extra right-hand padding, if any
   1827           x = Math.max(w,dx);
   1828           svg.Add(root,x-w,h);
   1829         } else {dx = x}
   1830 	return x - dx;
   1831       },
   1832       SVGrootHeight: function (d,scale,root) {
   1833 	return .45*(d-900*scale) + 600*scale + Math.max(0,root.d-75);
   1834       }
   1835     });
   1836 
   1837     MML.mfenced.Augment({
   1838       SVG: BBOX.ROW,
   1839       toSVG: function () {
   1840         this.SVGgetStyles();
   1841         var svg = this.SVG();
   1842 	this.SVGhandleSpace(svg);
   1843 	if (this.data.open) {svg.Check(this.data.open)}
   1844 	if (this.data[0] != null) {svg.Check(this.data[0])}
   1845 	for (var i = 1, m = this.data.length; i < m; i++) {
   1846 	  if (this.data[i]) {
   1847 	    if (this.data["sep"+i]) {svg.Check(this.data["sep"+i])}
   1848 	    svg.Check(this.data[i]);
   1849 	  }
   1850 	}
   1851 	if (this.data.close) {svg.Check(this.data.close)}
   1852         svg.Stretch(); svg.Clean();
   1853 	this.SVGhandleColor(svg);
   1854         this.SVGsaveData(svg);
   1855 	return svg;
   1856       }
   1857     });
   1858 
   1859     MML.menclose.Augment({toSVG: MML.mbase.SVGautoload});
   1860     MML.maction.Augment({toSVG: MML.mbase.SVGautoload});
   1861 
   1862     MML.semantics.Augment({
   1863       toSVG: function () {
   1864         this.SVGgetStyles();
   1865         var svg = this.SVG();
   1866 	if (this.data[0] != null) {
   1867           this.SVGhandleSpace(svg);
   1868 	  svg.Add(this.data[0].toSVG()); svg.Clean();
   1869 	} else {svg.Clean()}
   1870         this.SVGsaveData(svg);
   1871 	return svg;
   1872       },
   1873       SVGstretchH: function (w) {
   1874 	return (this.data[0] != null ? this.data[0].SVGstretchH(w) : BBOX.NULL());
   1875       },
   1876       SVGstretchV: function (h,d) {
   1877 	return (this.data[0] != null ? this.data[0].SVGstretchV(h,d) : BBOX.NULL());
   1878       }
   1879     });
   1880 
   1881     MML.munderover.Augment({
   1882       toSVG: function (HW,D) {
   1883         this.SVGgetStyles();
   1884 	var values = this.getValues("displaystyle","accent","accentunder","align");
   1885         var base = this.data[this.base];
   1886 	if (!values.displaystyle && base != null &&
   1887 	    (base.movablelimits || base.CoreMO().Get("movablelimits")))
   1888 	      {return MML.msubsup.prototype.toSVG.call(this)}
   1889         var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg);
   1890 	var boxes = [], stretch = [], box, i, m, W = -SVG.BIGDIMEN, WW = W;
   1891 	for (i = 0, m = this.data.length; i < m; i++) {
   1892 	  if (this.data[i] != null) {
   1893 	    if (i == this.base) {
   1894               boxes[i] = this.SVGdataStretched(i,HW,D);
   1895 	      stretch[i] = (D != null || HW == null) && this.data[i].SVGcanStretch("Horizontal");
   1896 	    } else {
   1897               boxes[i] = this.data[i].toSVG(); boxes[i].x = 0; delete boxes[i].X;
   1898 	      stretch[i] = this.data[i].SVGcanStretch("Horizontal");
   1899 	    }
   1900 	    if (boxes[i].w > WW) {WW = boxes[i].w}
   1901 	    if (!stretch[i] && WW > W) {W = WW}
   1902 	  }
   1903 	}
   1904 	if (D == null && HW != null) {W = HW} else if (W == -SVG.BIGDIMEN) {W = WW}
   1905         for (i = WW = 0, m = this.data.length; i < m; i++) {if (this.data[i]) {
   1906           if (stretch[i]) {
   1907             boxes[i] = this.data[i].SVGstretchH(W);
   1908             if (i !== this.base) {boxes[i].x = 0; delete boxes[i].X}
   1909           }
   1910           if (boxes[i].w > WW) {WW = boxes[i].w}
   1911         }}
   1912         var t = SVG.TeX.rule_thickness * this.mscale;
   1913 	var x, y, z1, z2, z3, dw, k, delta = 0;
   1914 	base = boxes[this.base] || {w:0, h:0, d:0, H:0, D:0, l:0, r:0, y:0, scale:scale};
   1915         if (base.ic) {delta = 1.3*base.ic + .05} // adjust faked IC to be more in line with expeted results
   1916 	for (i = 0, m = this.data.length; i < m; i++) {
   1917 	  if (this.data[i] != null) {
   1918 	    box = boxes[i];
   1919 	    z3 = SVG.TeX.big_op_spacing5 * scale;
   1920 	    var accent = (i != this.base && values[this.ACCENTS[i]]);
   1921 	    if (accent && box.w <= 1) {
   1922               box.x = -box.l;
   1923               boxes[i] = BBOX.G().With({removeable: false});
   1924               boxes[i].Add(box); boxes[i].Clean();
   1925               boxes[i].w = -box.l; box = boxes[i];
   1926             }
   1927 	    dw = {left:0, center:(WW-box.w)/2, right:WW-box.w}[values.align];
   1928 	    x = dw; y = 0;
   1929 	    if (i == this.over) {
   1930 	      if (accent) {
   1931 		k = t * scale; z3 = 0;
   1932 		if (base.skew) {
   1933                   x += base.skew; svg.skew = base.skew;
   1934                   if (x+box.w > WW) {svg.skew += (WW-box.w-x)/2}
   1935                 }
   1936 	      } else {
   1937 		z1 = SVG.TeX.big_op_spacing1 * scale;
   1938 		z2 = SVG.TeX.big_op_spacing3 * scale;
   1939 		k = Math.max(z1,z2-Math.max(0,box.d));
   1940 	      }
   1941 	      k = Math.max(k,1500/SVG.em);
   1942 	      x += delta/2; y = base.y + base.h + box.d + k;
   1943 	      box.h += z3; if (box.h > box.H) {box.H = box.h}
   1944 	    } else if (i == this.under) {
   1945 	      if (accent) {
   1946 		k = 3*t * scale; z3 = 0;
   1947 	      } else {
   1948 		z1 = SVG.TeX.big_op_spacing2 * scale;
   1949 		z2 = SVG.TeX.big_op_spacing4 * scale;
   1950 		k = Math.max(z1,z2-box.h);
   1951 	      }
   1952 	      k = Math.max(k,1500/SVG.em);
   1953 	      x -= delta/2; y = base.y -(base.d + box.h + k);
   1954 	      box.d += z3; if (box.d > box.D) {box.D = box.d}
   1955 	    }
   1956 	    svg.Add(box,x,y);
   1957 	  }
   1958 	}
   1959         svg.Clean();
   1960 	this.SVGhandleColor(svg);
   1961         this.SVGsaveData(svg);
   1962 	return svg;
   1963       }
   1964     });
   1965 
   1966     MML.msubsup.Augment({
   1967       toSVG: function (HW,D) {
   1968         this.SVGgetStyles();
   1969         var svg = this.SVG(), scale = this.SVGgetScale(svg); this.SVGhandleSpace(svg);
   1970 	var mu = this.SVGgetMu(svg);
   1971         var base = svg.Add(this.SVGdataStretched(this.base,HW,D));
   1972 	var sscale = (this.data[this.sup] || this.data[this.sub] || this).SVGgetScale();
   1973 	var x_height = SVG.TeX.x_height * scale, s = SVG.TeX.scriptspace * scale;
   1974 	var sup, sub;
   1975 	if (this.SVGnotEmpty(this.data[this.sup])) {
   1976 	  sup = this.data[this.sup].toSVG();
   1977 	  sup.w += s; sup.r = Math.max(sup.w,sup.r);
   1978 	}
   1979 	if (this.SVGnotEmpty(this.data[this.sub])) {
   1980 	  sub = this.data[this.sub].toSVG();
   1981 	  sub.w += s; sub.r = Math.max(sub.w,sub.r);
   1982 	}
   1983 	var q = SVG.TeX.sup_drop * sscale, r = SVG.TeX.sub_drop * sscale;
   1984 	var u = base.h+(base.y||0) - q, v = base.d-(base.y||0) + r, delta = 0, p;
   1985 	if (base.ic) {
   1986           base.w -= base.ic;       // remove IC (added by mo and mi)
   1987           delta = 1.3*base.ic+.05; // adjust faked IC to be more in line with expeted results
   1988         }
   1989 	if (this.data[this.base] &&
   1990 	   (this.data[this.base].type === "mi" || this.data[this.base].type === "mo")) {
   1991 	  if (this.data[this.base].data.join("").length === 1 && base.scale === 1 &&
   1992 	      !base.stretched && !this.data[this.base].Get("largeop")) {u = v = 0}
   1993 	}
   1994 	var min = this.getValues("subscriptshift","superscriptshift");
   1995 	min.subscriptshift   = (min.subscriptshift === ""   ? 0 : SVG.length2em(min.subscriptshift,mu));
   1996 	min.superscriptshift = (min.superscriptshift === "" ? 0 : SVG.length2em(min.superscriptshift,mu));
   1997         var x = base.w + base.x;
   1998 	if (!sup) {
   1999 	  if (sub) {
   2000 	    v = Math.max(v,SVG.TeX.sub1*scale,sub.h-(4/5)*x_height,min.subscriptshift);
   2001             svg.Add(sub,x,-v); this.data[this.sub].SVGdata.dy = -v;
   2002 	  }
   2003 	} else {
   2004 	  if (!sub) {
   2005 	    values = this.getValues("displaystyle","texprimestyle");
   2006 	    p = SVG.TeX[(values.displaystyle ? "sup1" : (values.texprimestyle ? "sup3" : "sup2"))];
   2007 	    u = Math.max(u,p*scale,sup.d+(1/4)*x_height,min.superscriptshift);
   2008             svg.Add(sup,x+delta,u);
   2009             this.data[this.sup].SVGdata.dx = delta; 
   2010             this.data[this.sup].SVGdata.dy = u;
   2011 	  } else {
   2012 	    v = Math.max(v,SVG.TeX.sub2*scale);
   2013 	    var t = SVG.TeX.rule_thickness * scale;
   2014 	    if ((u - sup.d) - (sub.h - v) < 3*t) {
   2015 	      v = 3*t - u + sup.d + sub.h;
   2016 	      q = (4/5)*x_height - (u - sup.d);
   2017 	      if (q > 0) {u += q; v -= q}
   2018 	    }
   2019             svg.Add(sup,x+delta,Math.max(u,min.superscriptshift));
   2020 	    svg.Add(sub,x,-Math.max(v,min.subscriptshift));
   2021             this.data[this.sup].SVGdata.dx = delta; 
   2022             this.data[this.sup].SVGdata.dy = Math.max(u,min.superscriptshift);
   2023             this.data[this.sub].SVGdata.dy = -Math.max(v,min.subscriptshift);
   2024 	  }
   2025 	}
   2026         svg.Clean();
   2027 	this.SVGhandleColor(svg);
   2028         this.SVGsaveData(svg);
   2029 	return svg;
   2030       }
   2031     });
   2032 
   2033     MML.mmultiscripts.Augment({toSVG: MML.mbase.SVGautoload});
   2034     MML.mtable.Augment({toSVG: MML.mbase.SVGautoload});
   2035     MML["annotation-xml"].Augment({toSVG: MML.mbase.SVGautoload});
   2036 
   2037     MML.math.Augment({
   2038       SVG: BBOX.Subclass({type:"svg", removeable: false}),
   2039       toSVG: function (span,div) {
   2040         var CONFIG = SVG.config;
   2041         //
   2042         //  All the data should be in an inferrerd row
   2043         //
   2044         if (this.data[0]) {
   2045           this.SVGgetStyles();
   2046 	  MML.mbase.prototype.displayAlign = HUB.config.displayAlign;
   2047 	  MML.mbase.prototype.displayIndent = HUB.config.displayIndent;
   2048           if (String(HUB.config.displayIndent).match(/^0($|[a-z%])/i))
   2049             MML.mbase.prototype.displayIndent = "0";
   2050           //
   2051           //  Put content in a <g> with defaults and matrix that flips y axis.
   2052           //  Put that in an <svg> with xlink defined.
   2053           //
   2054           var box = BBOX.G(); box.Add(this.data[0].toSVG(),0,0,true); box.Clean();
   2055           this.SVGhandleColor(box);
   2056           SVG.Element(box.element,{
   2057             stroke:"currentColor", fill:"currentColor", "stroke-width":0,
   2058             transform: "matrix(1 0 0 -1 0 0)"
   2059           });
   2060           box.removeable = false;
   2061           var svg = this.SVG();
   2062           svg.element.setAttribute("xmlns:xlink",XLINKNS);
   2063           if (CONFIG.useFontCache && !CONFIG.useGlobalCache)
   2064             {svg.element.appendChild(BBOX.GLYPH.defs)}
   2065           svg.Add(box); svg.Clean();
   2066           this.SVGsaveData(svg);
   2067           //
   2068           //  If this element is not the top-level math element
   2069           //    remove the transform and return the svg object
   2070           //    (issue #614).
   2071           //
   2072           if (!span) {
   2073             svg.element = svg.element.firstChild;  // remove <svg> element
   2074             svg.element.removeAttribute("transform");
   2075             svg.removable = true;
   2076             return svg;
   2077           }
   2078           //
   2079           //  Style the <svg> to get the right size and placement
   2080           //
   2081           var l = Math.max(-svg.l,0), r = Math.max(svg.r-svg.w,0);
   2082           var style = svg.element.style, px = SVG.TeX.x_height/SVG.ex;
   2083           var H = (Math.ceil(svg.H/px)+1)*px+SVG.HFUZZ,  // round to pixels and add padding
   2084               D = (Math.ceil(svg.D/px)+1)*px+SVG.DFUZZ;
   2085           svg.element.setAttribute("width",SVG.Ex(l+svg.w+r));
   2086           svg.element.setAttribute("height",SVG.Ex(H+D));
   2087           style.verticalAlign = SVG.Ex(-D);
   2088           if (l) style.marginLeft = SVG.Ex(-l);
   2089           if (r) style.marginRight = SVG.Ex(-r);
   2090           svg.element.setAttribute("viewBox",SVG.Fixed(-l,1)+" "+SVG.Fixed(-H,1)+" "+
   2091                                              SVG.Fixed(l+svg.w+r,1)+" "+SVG.Fixed(H+D,1));
   2092           //
   2093           //  If there is extra height or depth, hide that
   2094           //
   2095 	  if (svg.H > svg.h) style.marginTop = SVG.Ex(svg.h-H);
   2096 	  if (svg.D > svg.d) {
   2097 	    style.marginBottom = SVG.Ex(svg.d-D);
   2098 	    style.verticalAlign = SVG.Ex(-svg.d);
   2099 	  }
   2100           //
   2101           //  Add it to the MathJax span
   2102           //
   2103           var alttext = this.Get("alttext");
   2104           if (alttext && !svg.element.getAttribute("aria-label")) svg.element.setAttribute("aria-label",alttext);
   2105           if (!svg.element.getAttribute("role")) svg.element.setAttribute("role","img");
   2106           svg.element.setAttribute("focusable","false");
   2107           span.appendChild(svg.element);
   2108           svg.element = null;
   2109           //
   2110           //  Handle indentalign and indentshift for single-line displays
   2111           //
   2112           if (!this.isMultiline && this.Get("display") === "block" && !svg.hasIndent) {
   2113             var values = this.getValues("indentalignfirst","indentshiftfirst","indentalign","indentshift");
   2114             if (values.indentalignfirst !== MML.INDENTALIGN.INDENTALIGN) {values.indentalign = values.indentalignfirst}
   2115             if (values.indentalign === MML.INDENTALIGN.AUTO) {values.indentalign = this.displayAlign}
   2116             if (values.indentshiftfirst !== MML.INDENTSHIFT.INDENTSHIFT) {values.indentshift = values.indentshiftfirst}
   2117             if (values.indentshift === "auto") {values.indentshift = "0"}
   2118             var shift = SVG.length2em(values.indentshift,1,SVG.cwidth);
   2119             if (this.displayIndent !== "0") {
   2120               var indent = SVG.length2em(this.displayIndent,1,SVG.cwidth);
   2121               shift += (values.indentalign === MML.INDENTALIGN.RIGHT ? -indent : indent);
   2122             }
   2123             div.style.textAlign = values.indentalign;
   2124             if (shift) {
   2125               HUB.Insert(style,({
   2126                 left: {marginLeft: SVG.Ex(shift)},
   2127                 right: {marginRight: SVG.Ex(-shift), marginLeft: SVG.Ex(Math.max(0,shift-(l+svg.w+r)))},
   2128                 center: {marginLeft: SVG.Ex(shift), marginRight: SVG.Ex(-shift)}
   2129               })[values.indentalign]);
   2130             }
   2131           }
   2132         }
   2133 	return span;
   2134       }
   2135     });
   2136 
   2137     MML.TeXAtom.Augment({
   2138       toSVG: function (HW,D) {
   2139         this.SVGgetStyles();
   2140         var svg = this.SVG();
   2141         this.SVGhandleSpace(svg);
   2142 	if (this.data[0] != null) {
   2143           var box = this.SVGdataStretched(0,HW,D), y = 0;
   2144           if (this.texClass === MML.TEXCLASS.VCENTER)
   2145             {y = SVG.TeX.axis_height - (box.h+box.d)/2 + box.d}
   2146           svg.Add(box,0,y);
   2147           svg.ic = box.ic; svg.skew = box.skew;
   2148 	}
   2149 	this.SVGhandleColor(svg);
   2150         this.SVGsaveData(svg);
   2151 	return svg;
   2152       }
   2153     });
   2154 
   2155     //
   2156     //  Make sure these don't generate output
   2157     //
   2158     MML.maligngroup.Augment({toSVG: MML.mbase.SVGemptySVG});
   2159     MML.malignmark.Augment({toSVG: MML.mbase.SVGemptySVG});
   2160     MML.mprescripts.Augment({toSVG: MML.mbase.SVGemptySVG});
   2161     MML.none.Augment({toSVG: MML.mbase.SVGemptySVG});
   2162 
   2163     //
   2164     //  Loading isn't complete until the element jax is modified,
   2165     //  but can't call loadComplete within the callback for "mml Jax Ready"
   2166     //  (it would call SVG's Require routine, asking for the mml jax again)
   2167     //  so wait until after the mml jax has finished processing.
   2168     //  
   2169     //  We also need to wait for the onload handler to run, since the loadComplete
   2170     //  will call Config and Startup, which need to modify the body.
   2171     //
   2172     HUB.Register.StartupHook("onLoad",function () {
   2173       setTimeout(MathJax.Callback(["loadComplete",SVG,"jax.js"]),0);
   2174     });
   2175   });
   2176 
   2177   HUB.Browser.Select({
   2178     Opera: function (browser) {
   2179       SVG.Augment({
   2180         operaZoomRefresh: true          // Opera needs a kick to redraw zoomed equations
   2181       });
   2182     }
   2183   });
   2184     
   2185   HUB.Register.StartupHook("End Cookie", function () {
   2186     if (HUB.config.menuSettings.zoom !== "None")
   2187       {AJAX.Require("[MathJax]/extensions/MathZoom.js")}
   2188   });
   2189   
   2190   if (!document.createElementNS) {
   2191     //
   2192     //  Try to handle SVG in IE8 and below, but fail
   2193     //  (but don't crash on loading the file, so no delay for loadComplete)
   2194     //
   2195     if (!document.namespaces.svg) {document.namespaces.add("svg",SVGNS)}
   2196     SVG.Augment({
   2197       Element: function (type,def) {
   2198         var obj = (typeof(type) === "string" ? document.createElement("svg:"+type) : type);
   2199         obj.isMathJax = true;
   2200         if (def) {for (var id in def) {if (def.hasOwnProperty(id)) {obj.setAttribute(id,def[id].toString())}}}
   2201         return obj;
   2202       }
   2203     });
   2204   }
   2205   
   2206 })(MathJax.Ajax, MathJax.Hub, MathJax.HTML, MathJax.OutputJax.SVG);