MatchWebFonts.js (11991B)
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/extensions/MatchWebFonts.js 7 * 8 * Adds code to the output jax so that if web fonts are used on the page, 9 * MathJax will be able to detect their arrival and update the math to 10 * accommodate the change in font. For the NativeMML output, this works 11 * both for web fonts in main text, and for web fonts in the math as well. 12 * 13 * --------------------------------------------------------------------- 14 * 15 * Copyright (c) 2013-2015 The MathJax Consortium 16 * 17 * Licensed under the Apache License, Version 2.0 (the "License"); 18 * you may not use this file except in compliance with the License. 19 * You may obtain a copy of the License at 20 * 21 * http://www.apache.org/licenses/LICENSE-2.0 22 * 23 * Unless required by applicable law or agreed to in writing, software 24 * distributed under the License is distributed on an "AS IS" BASIS, 25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 * See the License for the specific language governing permissions and 27 * limitations under the License. 28 */ 29 30 (function (HUB,AJAX) { 31 var VERSION = "2.6.0"; 32 33 var CONFIG = MathJax.Hub.CombineConfig("MatchWebFonts",{ 34 matchFor: { 35 "HTML-CSS": true, 36 NativeMML: true, 37 SVG: true 38 }, 39 fontCheckDelay: 500, // initial delay for the first check for web fonts 40 fontCheckTimeout: 15 * 1000, // how long to keep looking for fonts (15 seconds) 41 }); 42 43 MathJax.Extension.MatchWebFonts = { 44 version: VERSION, 45 config: CONFIG 46 }; 47 48 HUB.Register.StartupHook("HTML-CSS Jax Ready",function () { 49 var HTMLCSS = MathJax.OutputJax["HTML-CSS"]; 50 var POSTTRANSLATE = HTMLCSS.postTranslate; 51 52 HTMLCSS.Augment({ 53 postTranslate: function (state,partial) { 54 if (!partial && CONFIG.matchFor["HTML-CSS"] && this.config.matchFontHeight) { 55 // 56 // Check for changes in the web fonts that might affect the font 57 // size for math elements. This is a periodic check that goes on 58 // until a timeout is reached. 59 // 60 AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]], 61 CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout); 62 } 63 return POSTTRANSLATE.apply(this,arguments); // do the original function 64 }, 65 66 checkFonts: function (check,scripts) { 67 if (check.time(function () {})) return; 68 var size = [], i, m, retry = false; 69 // 70 // Add the elements used for testing ex and em sizes 71 // 72 for (i = 0, m = scripts.length; i < m; i++) { 73 script = scripts[i]; 74 if (script.parentNode && script.MathJax.elementJax) { 75 script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script); 76 } 77 } 78 // 79 // Check to see if anything has changed 80 // 81 for (i = 0, m = scripts.length; i < m; i++) { 82 script = scripts[i]; if (!script.parentNode) continue; retry = true; 83 var jax = script.MathJax.elementJax; if (!jax) continue; 84 // 85 // Check if ex or mex has changed 86 // 87 var test = script.previousSibling; 88 var ex = test.firstChild.offsetHeight/60; 89 var em = test.lastChild.lastChild.offsetHeight/60; 90 if (ex === 0 || ex === "NaN") {ex = this.defaultEx; em = this.defaultEm} 91 if (ex !== jax.HTMLCSS.ex || em !== jax.HTMLCSS.em) { 92 var scale = ex/this.TeX.x_height/em; 93 scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale)*this.config.scale); 94 if (scale/100 !== jax.scale) {size.push(script); scripts[i] = {}} 95 } 96 } 97 // 98 // Remove markers 99 // 100 scripts = scripts.concat(size); // some scripts have been moved to the size array 101 for (i = 0, m = scripts.length; i < m; i++) { 102 script = scripts[i]; 103 if (script && script.parentNode && script.MathJax.elementJax) { 104 script.parentNode.removeChild(script.previousSibling); 105 } 106 } 107 // 108 // Rerender the changed items 109 // 110 if (size.length) {HUB.Queue(["Rerender",HUB,[size],{}])} 111 // 112 // Try again later 113 // 114 if (retry) {setTimeout(check,check.delay)} 115 } 116 }); 117 }); 118 119 HUB.Register.StartupHook("SVG Jax Ready",function () { 120 var SVG = MathJax.OutputJax.SVG; 121 var POSTTRANSLATE = SVG.postTranslate; 122 123 SVG.Augment({ 124 postTranslate: function (state,partial) { 125 if (!partial && CONFIG.matchFor.SVG) { 126 // 127 // Check for changes in the web fonts that might affect the font 128 // size for math elements. This is a periodic check that goes on 129 // until a timeout is reached. 130 // 131 AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]], 132 CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout); 133 } 134 return POSTTRANSLATE.apply(this,arguments); // do the original function 135 }, 136 137 checkFonts: function (check,scripts) { 138 if (check.time(function () {})) return; 139 var size = [], i, m, retry = false; 140 // 141 // Add the elements used for testing ex and em sizes 142 // 143 for (i = 0, m = scripts.length; i < m; i++) { 144 script = scripts[i]; 145 if (script.parentNode && script.MathJax.elementJax) { 146 script.parentNode.insertBefore(this.ExSpan.cloneNode(true),script); 147 } 148 } 149 // 150 // Check to see if anything has changed 151 // 152 for (i = 0, m = scripts.length; i < m; i++) { 153 script = scripts[i]; if (!script.parentNode) continue; retry = true; 154 var jax = script.MathJax.elementJax; if (!jax) continue; 155 // 156 // Check if ex or mex has changed 157 // 158 var test = script.previousSibling; 159 var ex = test.firstChild.offsetHeight/60; 160 if (ex === 0 || ex === "NaN") {ex = this.defaultEx} 161 if (ex !== jax.SVG.ex) {size.push(script); scripts[i] = {}} 162 } 163 // 164 // Remove markers 165 // 166 scripts = scripts.concat(size); // some scripts have been moved to the size array 167 for (i = 0, m = scripts.length; i < m; i++) { 168 script = scripts[i]; 169 if (script.parentNode && script.MathJax.elementJax) { 170 script.parentNode.removeChild(script.previousSibling); 171 } 172 } 173 // 174 // Rerender the changed items 175 // 176 if (size.length) {HUB.Queue(["Rerender",HUB,[size],{}])} 177 // 178 // Try again later (if not all the scripts are null) 179 // 180 181 if (retry) setTimeout(check,check.delay); 182 } 183 }); 184 }); 185 186 HUB.Register.StartupHook("NativeMML Jax Ready",function () { 187 var nMML = MathJax.OutputJax.NativeMML; 188 var POSTTRANSLATE = nMML.postTranslate; 189 190 nMML.Augment({ 191 postTranslate: function (state) { 192 if (!HUB.Browser.isMSIE && CONFIG.matchFor.NativeMML) { 193 // 194 // Check for changes in the web fonts that might affect the sizes 195 // of math elements. This is a periodic check that goes on until 196 // a timeout is reached. 197 // 198 AJAX.timer.start(AJAX,["checkFonts",this,state.jax[this.id]], 199 CONFIG.fontCheckDelay,CONFIG.fontCheckTimeout); 200 } 201 POSTTRANSLATE.apply(this,arguments); // do the original routine 202 }, 203 204 // 205 // Check to see if web fonts have been loaded that change the ex size 206 // of the surrounding font, the ex size within the math, or the widths 207 // of math elements. We do this by rechecking the ex and mex sizes 208 // (to see if the font scaling needs adjusting) and by checking the 209 // size of the inner mrow of math elements and mtd elements. The 210 // sizes of these have been stored in the NativeMML object of the 211 // element jax so that we can check for them here. 212 // 213 checkFonts: function (check,scripts) { 214 if (check.time(function () {})) return; 215 var adjust = [], mtd = [], size = [], i, m, script; 216 // 217 // Add the elements used for testing ex and em sizes 218 // 219 for (i = 0, m = scripts.length; i < m; i++) { 220 script = scripts[i]; 221 if (script.parentNode && script.MathJax.elementJax) { 222 script.parentNode.insertBefore(this.EmExSpan.cloneNode(true),script); 223 } 224 } 225 // 226 // Check to see if anything has changed 227 // 228 for (i = 0, m = scripts.length; i < m; i++) { 229 script = scripts[i]; if (!script.parentNode) continue; 230 var jax = script.MathJax.elementJax; if (!jax) continue; 231 var span = document.getElementById(jax.inputID+"-Frame"); 232 var math = span.getElementsByTagName("math")[0]; if (!math) continue; 233 jax = jax.NativeMML; 234 // 235 // Check if ex or mex has changed 236 // 237 var test = script.previousSibling; 238 var ex = test.firstChild.offsetWidth/60; 239 var mex = test.lastChild.offsetWidth/60; 240 if (ex === 0 || ex === "NaN") {ex = this.defaultEx; mex = this.defaultMEx} 241 var newEx = (ex !== jax.ex); 242 if (newEx || mex != jax.mex) { 243 var scale = (this.config.matchFontHeight && mex > 1 ? ex/mex : 1); 244 scale = Math.floor(Math.max(this.config.minScaleAdjust/100,scale) * this.config.scale); 245 if (scale/100 !== jax.scale) {size.push([span.style,scale])} 246 jax.scale = scale/100; jax.fontScale = scale+"%"; jax.ex = ex; jax.mex = mex; 247 } 248 249 // 250 // Check width of math elements 251 // 252 if ("scrollWidth" in jax && (newEx || jax.scrollWidth !== math.firstChild.scrollWidth)) { 253 jax.scrollWidth = math.firstChild.scrollWidth; 254 adjust.push([math.parentNode.style,jax.scrollWidth/jax.ex/jax.scale]); 255 } 256 // 257 // Check widths of mtd elements 258 // 259 if (math.MathJaxMtds) { 260 for (var j = 0, n = math.MathJaxMtds.length; j < n; j++) { 261 if (!math.MathJaxMtds[j].parentNode) continue; 262 if (newEx || math.MathJaxMtds[j].firstChild.scrollWidth !== jax.mtds[j]) { 263 jax.mtds[j] = math.MathJaxMtds[j].firstChild.scrollWidth; 264 mtd.push([math.MathJaxMtds[j],jax.mtds[j]/jax.ex]); 265 } 266 } 267 } 268 } 269 // 270 // Remove markers 271 // 272 for (i = 0, m = scripts.length; i < m; i++) { 273 script = scripts[i]; 274 if (script.parentNode && script.MathJax.elementJax) { 275 script.parentNode.removeChild(script.previousSibling); 276 } 277 } 278 // 279 // Adjust scaling factor 280 // 281 for (i = 0, m = size.length; i < m; i++) { 282 size[i][0].fontSize = size[i][1] + "%"; 283 } 284 // 285 // Adjust width of spans containing math elements that have changed 286 // 287 for (i = 0, m = adjust.length; i < m; i++) { 288 adjust[i][0].width = adjust[i][1].toFixed(3)+"ex"; 289 } 290 // 291 // Adjust widths of mtd elements that have changed 292 // 293 for (i = 0, m = mtd.length; i < m; i++) { 294 var style = mtd[i][0].getAttribute("style"); 295 style = style.replace(/(($|;)\s*min-width:).*?ex/,"$1 "+mtd[i][1].toFixed(3)+"ex"); 296 mtd[i][0].setAttribute("style",style); 297 } 298 // 299 // Try again later 300 // 301 setTimeout(check,check.delay); 302 } 303 }); 304 }); 305 306 HUB.Startup.signal.Post("MatchWebFonts Extension Ready"); 307 AJAX.loadComplete("[MathJax]/extensions/MatchWebFonts.js"); 308 309 })(MathJax.Hub,MathJax.Ajax);