diff --git a/unpacked/jax/input/MathML/jax.js b/unpacked/jax/input/MathML/jax.js index a97d9ceec..7aaf5ac84 100644 --- a/unpacked/jax/input/MathML/jax.js +++ b/unpacked/jax/input/MathML/jax.js @@ -282,8 +282,11 @@ parseDOM: function (string) {return this.parser.parseFromString(string,"text/xml")}, parseMS: function (string) {return (this.parser.loadXML(string) ? this.parser : null)}, parseDIV: function (string) { - this.div.innerHTML = string.replace(/<([a-z]+)([^>]*)\/>/g,"<$1$2>"); - return this.div; + this.div.innerHTML = + "
"+string.replace(/<([a-z]+)([^>]*)\/>/g,"<$1$2>")+"
"; + var doc = this.div.firstChild; + this.div.innerHTML = ""; + return doc; }, parseError: function (string) {return null}, createMSParser: function() { diff --git a/unpacked/jax/output/HTML-CSS/jax.js b/unpacked/jax/output/HTML-CSS/jax.js index 1f49ecd5d..a095e8f59 100644 --- a/unpacked/jax/output/HTML-CSS/jax.js +++ b/unpacked/jax/output/HTML-CSS/jax.js @@ -520,7 +520,7 @@ if (this.defaultEm) return; var ready = MathJax.Callback(); AJAX.timer.start(AJAX,function (check) { - if (check.time(ready)) {HUB.signal.Post("HTML-CSS Jax - no default em size"); return} + if (check.time(ready)) {HUB.signal.Post(["HTML-CSS Jax - no default em size"]); return} HTMLCSS.getDefaultExEm(); if (HTMLCSS.defaultEm) {ready()} else {setTimeout(check,check.delay)} },this.defaultEmDelay,this.defaultEmTimeout); diff --git a/unpacked/jax/output/SVG/config.js b/unpacked/jax/output/SVG/config.js index 4af24465a..064f8302c 100644 --- a/unpacked/jax/output/SVG/config.js +++ b/unpacked/jax/output/SVG/config.js @@ -41,6 +41,8 @@ MathJax.OutputJax.SVG = MathJax.OutputJax({ undefinedFamily: "STIXGeneral,'Arial Unicode MS',serif", // fonts to use for missing characters addMMLclasses: false, // keep MathML structure and use CSS classes to mark elements + useFontCache: true, // use elements to re-use font paths rather than repeat paths every time + useGlobalCache: true, // store fonts in a global for use in all equations, or one in each equation EqnChunk: (MathJax.Hub.Browser.isMobile ? 10: 50), // number of equations to process before showing them @@ -82,6 +84,10 @@ MathJax.OutputJax.SVG = MathJax.OutputJax({ ".MathJax_SVG .MJX-sans-serif": { "font-family": "sans-serif" }, + + // + // For tooltips + // "#MathJax_SVG_Tooltip": { "background-color": "InfoBackground", color: "InfoText", border: "1px solid black", diff --git a/unpacked/jax/output/SVG/jax.js b/unpacked/jax/output/SVG/jax.js index 396c1e8ad..f6a97437a 100644 --- a/unpacked/jax/output/SVG/jax.js +++ b/unpacked/jax/output/SVG/jax.js @@ -153,8 +153,8 @@ this.textSVG = this.Element("svg"); // Global defs for font glyphs - DEFS = this.addElement(this.addElement(this.hiddenDiv.parentNode,"svg"),"defs",{id:"MathJax_SVG_glyphs"}); - GLYPHS = {}; + BBOX.GLYPH.defs = this.addElement(this.addElement(this.hiddenDiv.parentNode,"svg"), + "defs",{id:"MathJax_SVG_glyphs"}); // Used in preTranslate to get scaling factors this.ExSpan = HTML.Element("span", @@ -166,7 +166,7 @@ this.linebreakSpan = HTML.Element("span",null, [["hr",{style: {width:"auto", size:1, padding:0, border:0, margin:0}}]]); - // Set up styles + // Set up styles return AJAX.Styles(this.config.styles,["InitializeSVG",this]); }, @@ -298,7 +298,8 @@ // var jax = script.MathJax.elementJax, math = jax.root, span = document.getElementById(jax.inputID+"-Frame"), - div = (jax.SVG.display ? (span||{}).parentNode : span); + div = (jax.SVG.display ? (span||{}).parentNode : span), + localCache = (SVG.config.useFontCache && !SVG.config.useGlobalCache); if (!div) return; // // Set the font metrics @@ -310,10 +311,12 @@ // this.mathDiv = div; span.appendChild(this.textSVG); + if (localCache) {SVG.resetGlyphs()} this.initSVG(math,span); math.setTeXclass(); try {math.toSVG(span,div)} catch (err) { if (err.restart) {while (span.firstChild) {span.removeChild(span.firstChild)}} + if (localCache) {BBOX.GLYPH.n--} throw err; } span.removeChild(this.textSVG); @@ -375,13 +378,28 @@ // state.SVGlast = state.SVGeqn; }, + + resetGlyphs: function (reset) { + if (this.config.useFontCache) { + var GLYPH = BBOX.GLYPH; + if (this.config.useGlobalCache) { + GLYPH.defs = document.getElementById("MathJax_SVG_glyphs"); + GLYPH.defs.innerHTML = ""; + } else { + GLYPH.defs = this.Element("defs"); + GLYPH.n++; + } + GLYPH.glyphs = {}; + if (reset) {GLYPH.n = 0} + } + }, // // Return the containing HTML element rather than the SVG element, since // most browsers can't position to an SVG element properly. // hashCheck: function (target) { - if (target && target.nodeName === "g") + if (target && target.nodeName.toLowerCase() === "g") {do {target = target.parentNode} while (target && target.firstChild.nodeName !== "svg")} return target; }, @@ -465,6 +483,10 @@ Percent: function (m) { return (100*m).toFixed(1).replace(/\.?0+$/,"") + "%"; }, + Fixed: function (m,n) { + if (Math.abs(m) < .0006) {return "0"} + return m.toFixed(n||3).replace(/\.?0+$/,""); + }, length2em: function (length,mu,size) { if (typeof(length) !== "string") {length = length.toString()} if (length === "") {return ""} @@ -815,8 +837,8 @@ if (dx) {svg.x += dx}; if (dy) {svg.y += dy}; if (svg.element) { if (svg.removeable && svg.element.childNodes.length === 1 && svg.n === 1) { - var child = svg.element.firstChild; - if (child.nodeName === "use" || child.nodeName === "rect") { + var child = svg.element.firstChild, nodeName = child.nodeName.toLowerCase(); + if (nodeName === "use" || nodeName === "rect") { svg.element = child; svg.scale = svg.childScale; var x = svg.childX, y = svg.childY; svg.x += x; svg.y += y; @@ -828,13 +850,12 @@ if (Math.abs(svg.x) < 1 && Math.abs(svg.y) < 1) { svg.remove = svg.removeable; } else { - if (svg.element.nodeName === "g") { + nodeName = svg.element.nodeName.toLowerCase(); + if (nodeName === "g") { if (!svg.element.firstChild) {svg.remove = svg.removeable} else {svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")")} - } else if (svg.element.nodeName === "line" || - svg.element.nodeName === "polygon" || - svg.element.nodeName === "path" || - svg.element.nodeName === "a") { + } else if (nodeName === "line" || nodeName === "polygon" || + nodeName === "path" || nodeName === "a") { svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")"); } else { svg.element.setAttribute("x",Math.floor(svg.x/svg.scale)); @@ -930,7 +951,7 @@ type: "rect", removeable: false, Init: function (h,d,w,t,dash,color,def) { if (def == null) {def = {}}; def.fill = "none"; - def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); + def["stroke-width"] = SVG.Fixed(t,2); def.width = Math.floor(w-t); def.height = Math.floor(h+d-t); def.transform = "translate("+Math.floor(t/2)+","+Math.floor(-d+t/2)+")"; if (dash === "dashed") @@ -946,7 +967,7 @@ Init: function (w,t,dash,color,def) { if (def == null) {def = {"stroke-linecap":"square"}} if (color && color !== "") {def.stroke = color} - def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); + def["stroke-width"] = SVG.Fixed(t,2); def.x1 = def.y1 = def.y2 = Math.floor(t/2); def.x2 = Math.floor(w-t/2); if (dash === "dashed") { var n = Math.floor(Math.max(0,w-t)/(6*t)), m = Math.floor(Math.max(0,w-t)/(2*n+1)); @@ -966,7 +987,7 @@ Init: function (h,t,dash,color,def) { if (def == null) {def = {"stroke-linecap":"square"}} if (color && color !== "") {def.stroke = color} - def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,""); + def["stroke-width"] = SVG.Fixed(t,2); def.x1 = def.x2 = def.y1 = Math.floor(t/2); def.y2 = Math.floor(h-t/2); if (dash === "dashed") { var n = Math.floor(Math.max(0,h-t)/(6*t)), m = Math.floor(Math.max(0,h-t)/(2*n+1)); @@ -1007,26 +1028,34 @@ } }); - var GLYPHS, DEFS; // data for which glyphs are used - BBOX.GLYPH = BBOX.Subclass({ type: "path", removeable: false, Init: function (scale,id,h,d,w,l,r,p) { - var def, t = SVG.config.blacker; - if (!GLYPHS[id]) { - def = {id:id, "stroke-width":t}; + var def, t = SVG.config.blacker, GLYPH = BBOX.GLYPH; + var cache = SVG.config.useFontCache; + var transform = (scale === 1 ? null : "scale("+SVG.Fixed(scale)+")"); + if (cache && !SVG.config.useGlobalCache) {id = "E"+GLYPH.n+"-"+id} + if (!cache || !GLYPH.glyphs[id]) { + def = {"stroke-width":t}; + if (cache) {def.id = id} else if (transform) {def.transform = transform} if (p !== "") {def.d = "M"+p+"Z"} this.SUPER(arguments).Init.call(this,def); - DEFS.appendChild(this.element); GLYPHS[id] = true; + if (cache) {GLYPH.defs.appendChild(this.element); GLYPH.glyphs[id] = true;} + } + if (cache) { + def = {}; if (transform) {def.transform = transform} + this.element = SVG.Element("use",def); + this.element.setAttributeNS(XLINKNS,"href","#"+id); } - def = {}; if (scale !== 1) {def.transform = "scale("+scale+")"} - this.element = SVG.Element("use",def); - this.element.setAttributeNS(XLINKNS,"href","#"+id); this.h = (h+t) * scale; this.d = (d+t) * scale; this.w = (w+t/2) *scale; this.l = (l+t/2) * scale; this.r = (r+t/2) * scale; this.H = Math.max(0,this.h); this.D = Math.max(0,this.d); this.x = this.y = 0; this.scale = scale; } + },{ + glyphs: {}, // which glpyhs have been used + defs: null, // the SVG element where glyphs are stored + n: 0 // the ID for local for self-contained SVG elements }); HUB.Register.StartupHook("mml Jax Ready",function () { @@ -1197,7 +1226,8 @@ // Add background color // if (values.background !== MML.COLOR.TRANSPARENT) { - if (svg.element.nodeName !== "g" && svg.element.nodeName !== "svg") { + var nodeName = svg.element.nodeName.toLowerCase(); + if (nodeName !== "g" && nodeName !== "svg") { var g = SVG.Element("g"); g.appendChild(svg.element); svg.element = g; svg.removeable = true; } @@ -1988,6 +2018,10 @@ MML.math.Augment({ SVG: BBOX.Subclass({type:"svg", removeable: false}), toSVG: function (span,div) { + var CONFIG = SVG.config; + // + // All the data should be in an inferrerd row + // if (this.data[0]) { this.SVGgetStyles(); MML.mbase.prototype.displayAlign = HUB.config.displayAlign; @@ -2002,7 +2036,10 @@ }).With({removeable: false}); box.Add(this.data[0].toSVG(),0,0,true); box.Clean(); this.SVGhandleColor(box); - var svg = this.SVG(); svg.element.setAttribute("xmlns:xlink",XLINKNS); + var svg = this.SVG(); + svg.element.setAttribute("xmlns:xlink",XLINKNS); + if (CONFIG.useFontCache && !CONFIG.useGlobalCache) + {svg.element.appendChild(BBOX.GLYPH.defs)} svg.Add(box); svg.Clean(); this.SVGsaveData(svg); // @@ -2021,11 +2058,12 @@ // var l = Math.max(-svg.l,0), r = Math.max(svg.r-svg.w,0); var style = svg.element.style; - style.width = SVG.Ex(l+svg.w+r); - style.height = SVG.Ex(svg.H+svg.D+2*SVG.em); + svg.element.setAttribute("width",SVG.Ex(l+svg.w+r)); + svg.element.setAttribute("height",SVG.Ex(svg.H+svg.D+2*SVG.em)); style.verticalAlign = SVG.Ex(-svg.D-3*SVG.em); // remove 2 extra pixels added below plus padding style.marginLeft = SVG.Ex(-l); style.marginRight = SVG.Ex(-r); - svg.element.setAttribute("viewBox",(-l)+" "+(-svg.H-SVG.em)+" "+(l+svg.w+r)+" "+(svg.H+svg.D+2*SVG.em)); + svg.element.setAttribute("viewBox",SVG.Fixed(-l,1)+" "+SVG.Fixed(-svg.H-SVG.em,1)+" "+ + SVG.Fixed(l+svg.w+r,1)+" "+SVG.Fixed(svg.H+svg.D+2*SVG.em,1)); svg.element.style.margin="1px 0px"; // 1px above and below to prevent lines from touching // // If there is extra height or depth, hide that @@ -2033,9 +2071,9 @@ if (svg.H > svg.h || svg.D > svg.d) { var frame = HTML.Element( "span",{style: {display:"inline-block", "white-space":"nowrap", padding:"1px 0px"}, isMathJax:true},[[ - "span",{style: {display:"inline-block", position:"relative", isMathJax:true, + "span",{style: {display:"inline-block", position:"relative", width:SVG.Ex(svg.w), height:SVG.Ex(svg.h+svg.d), - "vertical-align":SVG.Ex(-svg.d)}}]]); + "vertical-align":SVG.Ex(-svg.d)}, isMathJax:true}]]); frame.firstChild.appendChild(svg.element); svg.element = frame; style.verticalAlign = style.margin = ""; style.position = "absolute"; style.bottom = SVG.Ex(svg.d-svg.D); style.left = 0;