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);