PageRenderTime 69ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/talk2/mathjax/unpacked/jax/output/SVG/jax.js

https://github.com/williamstein/mazur-explicit-formula
JavaScript | 1999 lines | 1715 code | 98 blank | 186 comment | 569 complexity | c9d9c3d84e766618fc0a0c2e243f0b31 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, MIT

Large files files are truncated, but you can click here to view the full file

  1. /*************************************************************
  2. *
  3. * MathJax/jax/output/SVG/jax.js
  4. *
  5. * Implements the SVG OutputJax that displays mathematics using
  6. * SVG (or VML in IE) to position the characters from math fonts
  7. * in their proper locations.
  8. *
  9. * ---------------------------------------------------------------------
  10. *
  11. * Copyright (c) 2011-2012 Design Science, Inc.
  12. *
  13. * Licensed under the Apache License, Version 2.0 (the "License");
  14. * you may not use this file except in compliance with the License.
  15. * You may obtain a copy of the License at
  16. *
  17. * http://www.apache.org/licenses/LICENSE-2.0
  18. *
  19. * Unless required by applicable law or agreed to in writing, software
  20. * distributed under the License is distributed on an "AS IS" BASIS,
  21. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22. * See the License for the specific language governing permissions and
  23. * limitations under the License.
  24. */
  25. (function (AJAX,HUB,HTML,SVG) {
  26. var MML;
  27. var SVGNS = "http://www.w3.org/2000/svg";
  28. var XLINKNS = "http://www.w3.org/1999/xlink";
  29. SVG.Augment({
  30. config: {
  31. styles: {
  32. ".MathJax_SVG": {
  33. "display": "inline",
  34. "font-style": "normal",
  35. "font-weight": "normal",
  36. "line-height": "normal",
  37. "font-size": "100%",
  38. "font-size-adjust":"none",
  39. "text-indent": 0,
  40. "text-align": "left",
  41. "text-transform": "none",
  42. "letter-spacing": "normal",
  43. "word-spacing": "normal",
  44. "word-wrap": "normal",
  45. "white-space": "nowrap",
  46. "float": "none",
  47. "direction": "ltr",
  48. border: 0, padding: 0, margin: 0
  49. },
  50. ".MathJax_SVG_Display": {
  51. position: "relative",
  52. display: "block",
  53. width: "100%"
  54. },
  55. ".MathJax_SVG svg a > g, .MathJax_SVG_Display svg a > g": {
  56. fill: "blue", stroke: "blue"
  57. },
  58. ".MathJax_SVG_Processing": {
  59. visibility: "hidden", position:"absolute", top:0, left:0,
  60. width:0, height: 0, overflow:"hidden", display:"block"
  61. },
  62. ".MathJax_SVG_Processed": {display:"none!important"},
  63. ".MathJax_SVG_ExBox": {
  64. display:"block", overflow:"hidden",
  65. width:"1px", height:"60ex"
  66. },
  67. "#MathJax_SVG_Tooltip": {
  68. position: "absolute", left: 0, top: 0,
  69. width: "auto", height: "auto",
  70. display: "none"
  71. }
  72. }
  73. },
  74. hideProcessedMath: true, // use display:none until all math is processed
  75. Config: function () {
  76. var settings = HUB.config.menuSettings;
  77. if (settings.scale) {this.config.scale = settings.scale}
  78. this.SUPER(arguments).Config.apply(this,arguments);
  79. this.fontInUse = this.config.font; this.fontDir += "/" + this.config.font;
  80. if (!this.require) {this.require = []}
  81. this.require.push(this.fontDir+"/fontdata.js");
  82. this.require.push(MathJax.OutputJax.extensionDir+"/MathEvents.js");
  83. },
  84. Startup: function () {
  85. // Set up event handling
  86. EVENT = MathJax.Extension.MathEvents.Event;
  87. TOUCH = MathJax.Extension.MathEvents.Touch;
  88. HOVER = MathJax.Extension.MathEvents.Hover;
  89. this.ContextMenu = EVENT.ContextMenu;
  90. this.Mousedown = EVENT.AltContextMenu;
  91. this.Mouseover = HOVER.Mouseover;
  92. this.Mouseout = HOVER.Mouseout;
  93. this.Mousemove = HOVER.Mousemove;
  94. // Make hidden div for doing tests and storing global SVG <defs>
  95. this.hiddenDiv = HTML.Element("div",{
  96. style:{visibility:"hidden", overflow:"hidden", position:"absolute", top:0,
  97. height:"1px", width: "auto", padding:0, border:0, margin:0,
  98. textAlign:"left", textIndent:0, textTransform:"none",
  99. lineHeight:"normal", letterSpacing:"normal", wordSpacing:"normal"}
  100. });
  101. if (!document.body.firstChild) {document.body.appendChild(this.hiddenDiv)}
  102. else {document.body.insertBefore(this.hiddenDiv,document.body.firstChild)}
  103. this.hiddenDiv = HTML.addElement(this.hiddenDiv,"div",{id:"MathJax_SVG_Hidden"});
  104. // Determine pixels-per-inch and em-size
  105. var div = HTML.addElement(this.hiddenDiv,"div",{style:{width:"5in"}});
  106. this.pxPerInch = div.offsetWidth/5; this.hiddenDiv.removeChild(div);
  107. // Used for measuring text sizes
  108. this.textSVG = this.Element("svg");
  109. // Global defs for font glyphs
  110. DEFS = this.addElement(this.addElement(this.hiddenDiv.parentNode,"svg"),"defs",{id:"MathJax_SVG_glyphs"});
  111. GLYPHS = {};
  112. // Used in preTranslate to get scaling factors
  113. this.ExSpan = HTML.Element("span",
  114. {style:{position:"absolute","font-size-adjust":"none"}},
  115. [["span",{className:"MathJax_SVG_ExBox"}]]
  116. );
  117. // Used in preTranslate to get linebreak width
  118. this.linebreakSpan = HTML.Element("span",null,
  119. [["hr",{style: {width:"auto", size:1, padding:0, border:0, margin:0}}]]);
  120. // Set up styles
  121. return AJAX.Styles(this.config.styles,["InitializeSVG",this]);
  122. },
  123. //
  124. // Handle initialization that requires styles to be set up
  125. //
  126. InitializeSVG: function () {
  127. //
  128. // Get the default sizes (need styles in place to do this)
  129. //
  130. document.body.appendChild(this.ExSpan);
  131. document.body.appendChild(this.linebreakSpan);
  132. this.defaultEx = this.ExSpan.firstChild.offsetHeight/60;
  133. this.defaultWidth = this.linebreakSpan.firstChild.offsetWidth;
  134. document.body.removeChild(this.linebreakSpan);
  135. document.body.removeChild(this.ExSpan);
  136. },
  137. preTranslate: function (state) {
  138. var scripts = state.jax[this.id], i, m = scripts.length,
  139. script, prev, span, div, test, jax, ex, em, maxwidth, relwidth = false, cwidth,
  140. linebreak = this.config.linebreaks.automatic, width = this.config.linebreaks.width;
  141. if (linebreak) {
  142. relwidth = (width.match(/^\s*(\d+(\.\d*)?%\s*)?container\s*$/) != null);
  143. if (relwidth) {width = width.replace(/\s*container\s*/,"")}
  144. else {maxwidth = this.defaultWidth}
  145. if (width === "") {width = "100%"}
  146. } else {maxwidth = 100000} // a big width, so no implicit line breaks
  147. //
  148. // Loop through the scripts
  149. //
  150. for (i = 0; i < m; i++) {
  151. script = scripts[i]; if (!script.parentNode) continue;
  152. //
  153. // Remove any existing output
  154. //
  155. prev = script.previousSibling;
  156. if (prev && String(prev.className).match(/^MathJax(_SVG)?(_Display)?( MathJax(_SVG)?_Processing)?$/))
  157. {prev.parentNode.removeChild(prev)}
  158. //
  159. // Add the span, and a div if in display mode,
  160. // then set the role and mark it as being processed
  161. //
  162. jax = script.MathJax.elementJax; if (!jax) continue;
  163. jax.SVG = {display: (jax.root.Get("display") === "block")}
  164. span = div = HTML.Element("span",{
  165. style: {"font-size": this.config.scale+"%", display:"inline-block"},
  166. className:"MathJax_SVG", id:jax.inputID+"-Frame", isMathJax:true, jaxID:this.id,
  167. oncontextmenu:EVENT.Menu, onmousedown: EVENT.Mousedown,
  168. onmouseover:EVENT.Mouseover, onmouseout:EVENT.Mouseout, onmousemove:EVENT.Mousemove,
  169. onclick:EVENT.Click, ondblclick:EVENT.DblClick
  170. });
  171. if (HUB.Browser.noContextMenu) {
  172. span.ontouchstart = TOUCH.start;
  173. span.ontouchend = TOUCH.end;
  174. }
  175. if (jax.SVG.display) {
  176. div = HTML.Element("div",{className:"MathJax_SVG_Display"});
  177. div.appendChild(span);
  178. }
  179. //
  180. // Mark math for screen readers
  181. // (screen readers don't know about role="math" yet, so use "textbox" instead)
  182. //
  183. div.setAttribute("role","textbox"); div.setAttribute("aria-readonly","true");
  184. div.className += " MathJax_SVG_Processing";
  185. script.parentNode.insertBefore(div,script);
  186. //
  187. // Add the test span for determining scales and linebreak widths
  188. //
  189. script.parentNode.insertBefore(this.ExSpan.cloneNode(true),script);
  190. div.parentNode.insertBefore(this.linebreakSpan.cloneNode(true),div);
  191. }
  192. //
  193. // Determine the scaling factors for each script
  194. // (this only requires one reflow rather than a reflow for each equation)
  195. //
  196. for (i = 0; i < m; i++) {
  197. script = scripts[i]; if (!script.parentNode) continue;
  198. test = script.previousSibling; div = test.previousSibling;
  199. jax = script.MathJax.elementJax; if (!jax) continue;
  200. ex = test.firstChild.offsetHeight/60;
  201. cwidth = div.previousSibling.firstChild.offsetWidth;
  202. if (relwidth) {maxwidth = cwidth}
  203. if (ex === 0 || ex === "NaN") {
  204. // can't read width, so move to hidden div for processing
  205. // (this will cause a reflow for each math element that is hidden)
  206. this.hiddenDiv.appendChild(div);
  207. jax.SVG.isHidden = true;
  208. ex = this.defaultEx; cwidth = this.defaultWidth;
  209. if (relwidth) {maxwidth = this.defaultWidth}
  210. }
  211. jax.SVG.ex = ex; jax.SVG.cwidth = cwidth;
  212. jax.SVG.em = em = ex / SVG.TeX.x_height * 1000; // scale ex to x_height
  213. jax.SVG.lineWidth = (linebreak ? this.length2em(width,1,maxwidth/em) : 1000000);
  214. }
  215. //
  216. // Remove the test spans used for determining scales and linebreak widths
  217. //
  218. for (i = 0; i < m; i++) {
  219. script = scripts[i]; if (!script.parentNode) continue;
  220. test = scripts[i].previousSibling; span = test.previousSibling;
  221. jax = scripts[i].MathJax.elementJax; if (!jax) continue;
  222. if (!jax.SVG.isHidden) {span = span.previousSibling}
  223. span.parentNode.removeChild(span);
  224. test.parentNode.removeChild(test);
  225. }
  226. //
  227. // Set state variables used for displaying equations in chunks
  228. //
  229. state.SVGeqn = state.SVGlast = 0; state.SVGi = -1;
  230. state.SVGchunk = this.config.EqnChunk;
  231. state.SVGdelay = false;
  232. },
  233. Translate: function (script,state) {
  234. if (!script.parentNode) return;
  235. //
  236. // If we are supposed to do a chunk delay, do it
  237. //
  238. if (state.SVGdelay) {
  239. state.SVGdelay = false;
  240. HUB.RestartAfter(MathJax.Callback.Delay(this.config.EqnChunkDelay));
  241. }
  242. //
  243. // Get the data about the math
  244. //
  245. var jax = script.MathJax.elementJax, math = jax.root,
  246. span = document.getElementById(jax.inputID+"-Frame"),
  247. div = (jax.SVG.display ? span.parentNode : span);
  248. //
  249. // Set the font metrics
  250. //
  251. this.em = MML.mbase.prototype.em = jax.SVG.em; this.ex = jax.SVG.ex;
  252. this.linebreakWidth = jax.SVG.lineWidth * 1000; this.cwidth = jax.SVG.cwidth;
  253. //
  254. // Typeset the math
  255. //
  256. this.mathDiv = div;
  257. span.appendChild(this.textSVG);
  258. this.initSVG(math,span);
  259. math.setTeXclass();
  260. try {math.toSVG(span,div)} catch (err) {
  261. if (err.restart) {while (span.firstChild) {span.removeChild(span.firstChild)}}
  262. throw err;
  263. }
  264. span.removeChild(this.textSVG);
  265. //
  266. // Put it in place, and remove the processing marker
  267. //
  268. if (jax.SVG.isHidden) {script.parentNode.insertBefore(div,script)}
  269. div.className = div.className.split(/ /)[0];
  270. //
  271. // Check if we are hiding the math until more is processed
  272. //
  273. if (this.hideProcessedMath) {
  274. //
  275. // Hide the math and don't let its preview be removed
  276. //
  277. div.className += " MathJax_SVG_Processed";
  278. if (script.MathJax.preview) {
  279. jax.SVG.preview = script.MathJax.preview;
  280. delete script.MathJax.preview;
  281. }
  282. //
  283. // Check if we should show this chunk of equations
  284. //
  285. state.SVGeqn += (state.i - state.SVGi); state.SVGi = state.i;
  286. if (state.SVGeqn >= state.SVGlast + state.SVGchunk) {
  287. this.postTranslate(state);
  288. state.SVGchunk = Math.floor(state.SVGchunk*this.config.EqnChunkFactor);
  289. state.SVGdelay = true; // delay if there are more scripts
  290. }
  291. }
  292. },
  293. postTranslate: function (state) {
  294. var scripts = state.jax[this.id];
  295. if (!this.hideProcessedMath) return;
  296. //
  297. // Reveal this chunk of math
  298. //
  299. for (var i = state.SVGlast, m = state.SVGeqn; i < m; i++) {
  300. var script = scripts[i];
  301. if (script && script.MathJax.elementJax) {
  302. //
  303. // Remove the processed marker
  304. //
  305. script.previousSibling.className = script.previousSibling.className.split(/ /)[0];
  306. var data = script.MathJax.elementJax.SVG;
  307. //
  308. // Remove the preview, if any
  309. //
  310. if (data.preview) {
  311. data.preview.innerHTML = "";
  312. script.MathJax.preview = data.preview;
  313. delete data.preview;
  314. }
  315. }
  316. }
  317. //
  318. // Save our place so we know what is revealed
  319. //
  320. state.SVGlast = state.SVGeqn;
  321. },
  322. //
  323. // Return the containing HTML element rather than the SVG element, since
  324. // most browsers can't position to an SVG element properly.
  325. //
  326. hashCheck: function (target) {
  327. if (target && target.nodeName === "g")
  328. {do {target = target.parentNode} while (target && target.firstChild.nodeName !== "svg")}
  329. return target;
  330. },
  331. getJaxFromMath: function (math) {
  332. if (math.parentNode.className === "MathJax_SVG_Display") {math = math.parentNode}
  333. do {math = math.nextSibling} while (math && math.nodeName.toLowerCase() !== "script");
  334. return HUB.getJaxFor(math);
  335. },
  336. getHoverSpan: function (jax,math) {
  337. math.style.position = "relative"; // make sure inline containers have position set
  338. return math.firstChild;
  339. },
  340. getHoverBBox: function (jax,span,math) {
  341. var bbox = EVENT.getBBox(span.parentNode);
  342. bbox.h += 2; bbox.d -= 2; // bbox seems to be a bit off, so compensate (FIXME)
  343. return bbox;
  344. },
  345. Zoom: function (jax,span,math,Mw,Mh) {
  346. //
  347. // Re-render at larger size
  348. //
  349. span.className = "MathJax_SVG";
  350. //
  351. // get em size (taken from this.preTranslate)
  352. //
  353. var emex = span.appendChild(this.ExSpan.cloneNode(true));
  354. var ex = emex.firstChild.offsetHeight/60;
  355. this.em = MML.mbase.prototype.em = ex / SVG.TeX.x_height * 1000;
  356. this.cwidth = .85*SVG.defaultWidth;
  357. emex.parentNode.removeChild(emex);
  358. this.idPostfix = "-zoom"; jax.root.toSVG(span,span); this.idPostfix = "";
  359. if (this.operaZoomRefresh)
  360. {setTimeout(function () {span.firstChild.style.border="1px solid transparent"},1)}
  361. //
  362. // Get height and width of zoomed math and original math
  363. //
  364. span.style.position = math.style.position = "absolute";
  365. var zW = span.offsetWidth, zH = span.offsetHeight,
  366. mH = math.offsetHeight, mW = math.offsetWidth;
  367. span.style.position = math.style.position = "";
  368. //
  369. return {Y:-EVENT.getBBox(span).h, mW:mW, mH:mH, zW:zW, zH:zH};
  370. },
  371. initSVG: function (math,span) {},
  372. Remove: function (jax) {
  373. var span = document.getElementById(jax.inputID+"-Frame");
  374. if (span) {
  375. if (jax.SVG.display) {span = span.parentNode}
  376. span.parentNode.removeChild(span);
  377. }
  378. delete jax.SVG;
  379. },
  380. Em: function (m) {
  381. if (Math.abs(m) < .0006) {return "0em"}
  382. return m.toFixed(3).replace(/\.?0+$/,"") + "em";
  383. },
  384. Ex: function (m) {
  385. m = m / this.TeX.x_height;
  386. if (Math.abs(m) < .0006) {return "0ex"}
  387. return m.toFixed(3).replace(/\.?0+$/,"") + "ex";
  388. },
  389. Percent: function (m) {
  390. return (100*m).toFixed(1).replace(/\.?0+$/,"") + "%";
  391. },
  392. length2em: function (length,mu,size) {
  393. if (typeof(length) !== "string") {length = length.toString()}
  394. if (length === "") {return ""}
  395. if (length === MML.SIZE.NORMAL) {return 1000}
  396. if (length === MML.SIZE.BIG) {return 2000}
  397. if (length === MML.SIZE.SMALL) {return 710}
  398. if (length === "infinity") {return SVG.BIGDIMEN}
  399. if (length.match(/mathspace$/)) {return 1000*SVG.MATHSPACE[length]}
  400. var match = length.match(/^\s*([-+]?(?:\.\d+|\d+(?:\.\d*)?))?(pt|em|ex|mu|px|pc|in|mm|cm|%)?/);
  401. var m = parseFloat(match[1]||"1") * 1000, unit = match[2];
  402. if (size == null) {size = 1000}; if (mu == null) {mu = 1}
  403. if (unit === "em") {return m}
  404. if (unit === "ex") {return m * SVG.TeX.x_height/1000}
  405. if (unit === "%") {return m / 100 * size / 1000}
  406. if (unit === "px") {return m / SVG.em}
  407. if (unit === "pt") {return m / 10} // 10 pt to an em
  408. if (unit === "pc") {return m * 1.2} // 12 pt to a pc
  409. if (unit === "in") {return m * this.pxPerInch / SVG.em}
  410. if (unit === "cm") {return m * this.pxPerInch / SVG.em / 2.54} // 2.54 cm to an inch
  411. if (unit === "mm") {return m * this.pxPerInch / SVG.em / 25.4} // 10 mm to a cm
  412. if (unit === "mu") {return m / 18 * mu}
  413. return m*size / 1000; // relative to given size (or 1em as default)
  414. },
  415. thickness2em: function (length,mu) {
  416. var thick = SVG.TeX.rule_thickness;
  417. if (length === MML.LINETHICKNESS.MEDIUM) {return thick}
  418. if (length === MML.LINETHICKNESS.THIN) {return .67*thick}
  419. if (length === MML.LINETHICKNESS.THICK) {return 1.67*thick}
  420. return this.length2em(length,mu,thick);
  421. },
  422. getPadding: function (span) {
  423. var padding = {top:0, right:0, bottom:0, left:0}, has = false;
  424. for (var id in padding) {if (padding.hasOwnProperty(id)) {
  425. var pad = span.style["padding"+id.charAt(0).toUpperCase()+id.substr(1)];
  426. if (pad) {padding[id] = this.length2em(pad); has = true;}
  427. }}
  428. return (has ? padding : false);
  429. },
  430. getBorders: function (span) {
  431. var border = {top:0, right:0, bottom:0, left:0}, has = false;
  432. for (var id in border) {if (border.hasOwnProperty(id)) {
  433. var ID = "border"+id.charAt(0).toUpperCase()+id.substr(1);
  434. var style = span.style[ID+"Style"];
  435. if (style) {
  436. has = true;
  437. border[id] = this.length2em(span.style[ID+"Width"]);
  438. border[id+"Style"] = span.style[ID+"Style"];
  439. border[id+"Color"] = span.style[ID+"Color"];
  440. if (border[id+"Color"] === "initial") {border[id+"Color"] = ""}
  441. }
  442. }}
  443. return (has ? border : false);
  444. },
  445. Element: function (type,def) {
  446. var obj = (typeof(type) === "string" ? document.createElementNS(SVGNS,type) : type);
  447. obj.isMathJax = true;
  448. if (def) {for (var id in def) {if (def.hasOwnProperty(id)) {obj.setAttribute(id,def[id].toString())}}}
  449. return obj;
  450. },
  451. addElement: function (parent,type,def) {return parent.appendChild(this.Element(type,def))},
  452. TextNode: HTML.TextNode,
  453. addText: HTML.addText,
  454. ucMatch: HTML.ucMatch,
  455. HandleVariant: function (variant,scale,text) {
  456. var svg = BBOX.G();
  457. var n, N, c, font, VARIANT, i, m, id, M, RANGES;
  458. if (!variant) {variant = this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]}
  459. if (variant.forceFamily) {
  460. text = BBOX.TEXT(scale,text,variant.font);
  461. if (variant.h != null) {text.h = variant.h}; if (variant.d != null) {text.d = variant.d}
  462. svg.Add(text); text = "";
  463. }
  464. VARIANT = variant;
  465. for (i = 0, m = text.length; i < m; i++) {
  466. variant = VARIANT;
  467. n = text.charCodeAt(i); c = text.charAt(i);
  468. if (n >= 0xD800 && n < 0xDBFF) {
  469. i++; n = (((n-0xD800)<<10)+(text.charCodeAt(i)-0xDC00))+0x10000;
  470. if (this.FONTDATA.RemapPlane1) {
  471. var nv = this.FONTDATA.RemapPlane1(n,variant);
  472. n = nv.n; variant = nv.variant;
  473. }
  474. } else {
  475. RANGES = this.FONTDATA.RANGES;
  476. for (id = 0, M = RANGES.length; id < M; id++) {
  477. if (RANGES[id].name === "alpha" && variant.noLowerCase) continue;
  478. N = variant["offset"+RANGES[id].offset];
  479. if (N && n >= RANGES[id].low && n <= RANGES[id].high) {
  480. if (RANGES[id].remap && RANGES[id].remap[n]) {
  481. n = N + RANGES[id].remap[n];
  482. } else {
  483. n = n - RANGES[id].low + N;
  484. if (RANGES[id].add) {n += RANGES[id].add}
  485. }
  486. if (variant["variant"+RANGES[id].offset])
  487. {variant = this.FONTDATA.VARIANT[variant["variant"+RANGES[id].offset]]}
  488. break;
  489. }
  490. }
  491. }
  492. if (variant.remap && variant.remap[n]) {
  493. if (variant.remap[n] instanceof Array) {
  494. var remap = variant.remap[n];
  495. n = remap[0]; variant = this.FONTDATA.VARIANT[remap[1]];
  496. } else if (typeof(variant.remap[n]) === "string") {
  497. text = variant.remap[n]+text.substr(i+1);
  498. i = 0; m = text.length; n = text.charCodeAt(0);
  499. } else {
  500. n = variant.remap[n];
  501. if (variant.remap.variant) {variant = this.FONTDATA.VARIANT[variant.remap.variant]}
  502. }
  503. }
  504. if (this.FONTDATA.REMAP[n] && !variant.noRemap) {
  505. n = this.FONTDATA.REMAP[n];
  506. if (n instanceof Array) {variant = this.FONTDATA.VARIANT[n[1]]; n = n[0]}
  507. if (typeof(n) === "string") {
  508. text = n+text.substr(i+1);
  509. i = 0; m = text.length;
  510. n = n.charCodeAt(0);
  511. }
  512. }
  513. font = this.lookupChar(variant,n); c = font[n];
  514. if (c) {
  515. if (c[5] && c[5].space) {svg.w += c[2]} else {
  516. c = [scale,font.id+"-"+n.toString(16).toUpperCase()].concat(c);
  517. svg.Add(BBOX.GLYPH.apply(BBOX,c),svg.w,0);
  518. }
  519. } else if (this.FONTDATA.DELIMITERS[n]) {
  520. c = this.createDelimiter(n,0,1,font);
  521. svg.Add(c,svg.w,(this.FONTDATA.DELIMITERS[n].dir === "V" ? c.d: 0));
  522. } else {
  523. if (n <= 0xFFFF) {c = String.fromCharCode(n)} else {
  524. N = n - 0x10000;
  525. c = String.fromCharCode((N>>10)+0xD800)
  526. + String.fromCharCode((N&0x3FF)+0xDC00);
  527. }
  528. var box = BBOX.TEXT(scale,c,{
  529. "font-family":variant.defaultFamily||SVG.config.undefinedFamily,
  530. "font-style":(variant.italic?"italic":""),
  531. "font-weight":(variant.bold?"bold":"")
  532. })
  533. if (variant.h != null) {box.h = variant.h}; if (variant.d != null) {box.d = variant.d}
  534. c = BBOX.G(); c.Add(box); svg.Add(c,svg.w,0);
  535. HUB.signal.Post(["SVG Jax - unknown char",n,variant]);
  536. }
  537. }
  538. if (text.length == 1 && font.skew && font.skew[n]) {svg.skew = font.skew[n]*1000}
  539. if (svg.element.childNodes.length === 1) {
  540. svg.element = svg.element.firstChild;
  541. svg.removeable = false; svg.scale = scale;
  542. }
  543. return svg;
  544. },
  545. lookupChar: function (variant,n) {
  546. var i, m;
  547. if (!variant.FONTS) {
  548. var FONTS = this.FONTDATA.FONTS;
  549. var fonts = (variant.fonts || this.FONTDATA.VARIANT.normal.fonts);
  550. if (!(fonts instanceof Array)) {fonts = [fonts]}
  551. if (variant.fonts != fonts) {variant.fonts = fonts}
  552. variant.FONTS = [];
  553. for (i = 0, m = fonts.length; i < m; i++) {
  554. if (FONTS[fonts[i]]) {variant.FONTS.push(FONTS[fonts[i]])}
  555. }
  556. }
  557. for (i = 0, m = variant.FONTS.length; i < m; i++) {
  558. var font = variant.FONTS[i];
  559. if (typeof(font) === "string") {delete variant.FONTS; this.loadFont(font)}
  560. if (font[n]) {return font} else {this.findBlock(font,n)}
  561. }
  562. return {id:"unknown"};
  563. },
  564. findBlock: function (font,c) {
  565. if (font.Ranges) {
  566. // FIXME: do binary search?
  567. for (var i = 0, m = font.Ranges.length; i < m; i++) {
  568. if (c < font.Ranges[i][0]) return;
  569. if (c <= font.Ranges[i][1]) {
  570. var file = font.Ranges[i][2];
  571. for (var j = font.Ranges.length-1; j >= 0; j--)
  572. {if (font.Ranges[j][2] == file) {font.Ranges.splice(j,1)}}
  573. this.loadFont(font.directory+"/"+file+".js");
  574. }
  575. }
  576. }
  577. },
  578. loadFont: function (file) {
  579. HUB.RestartAfter(AJAX.Require(this.fontDir+"/"+file));
  580. },
  581. createDelimiter: function (code,HW,scale,font) {
  582. if (!scale) {scale = 1};
  583. var svg = BBOX.G();
  584. if (!code) {
  585. svg.Clean(); delete svg.element;
  586. svg.w = svg.r = this.TeX.nulldelimiterspace * scale;
  587. return svg;
  588. }
  589. if (!(HW instanceof Array)) {HW = [HW,HW]}
  590. var hw = HW[1]; HW = HW[0];
  591. var delim = {alias: code};
  592. while (delim.alias) {
  593. code = delim.alias; delim = this.FONTDATA.DELIMITERS[code];
  594. if (!delim) {delim = {HW: [0,this.FONTDATA.VARIANT[MML.VARIANT.NORMAL]]}}
  595. }
  596. if (delim.load) {HUB.RestartAfter(AJAX.Require(this.fontDir+"/fontdata-"+delim.load+".js"))}
  597. for (var i = 0, m = delim.HW.length; i < m; i++) {
  598. if (delim.HW[i][0]*scale >= HW-10-SVG.config.blacker || (i == m-1 && !delim.stretch)) {
  599. if (delim.HW[i][2]) {scale *= delim.HW[i][2]}
  600. if (delim.HW[i][3]) {code = delim.HW[i][3]}
  601. return this.createChar(scale,[code,delim.HW[i][1]],font).With({stretched: true});
  602. }
  603. }
  604. if (delim.stretch) {this["extendDelimiter"+delim.dir](svg,hw,delim.stretch,scale,font)}
  605. return svg;
  606. },
  607. createChar: function (scale,data,font) {
  608. var text = "", variant = {fonts: [data[1]], noRemap:true};
  609. if (font && font === MML.VARIANT.BOLD) {variant.fonts = [data[1]+"-bold",data[1]]}
  610. if (typeof(data[1]) !== "string") {variant = data[1]}
  611. if (data[0] instanceof Array) {
  612. for (var i = 0, m = data[0].length; i < m; i++) {text += String.fromCharCode(data[0][i])}
  613. } else {text = String.fromCharCode(data[0])}
  614. if (data[4]) {scale = scale*data[4]}
  615. var svg = this.HandleVariant(variant,scale,text);
  616. if (data[2]) {svg.x = data[2]*1000}
  617. if (data[3]) {svg.y = data[3]*1000}
  618. if (data[5]) {svg.h += data[5]*1000}
  619. if (data[6]) {svg.d += data[6]*1000}
  620. return svg;
  621. },
  622. extendDelimiterV: function (svg,H,delim,scale,font) {
  623. var top = this.createChar(scale,(delim.top||delim.ext),font);
  624. var bot = this.createChar(scale,(delim.bot||delim.ext),font);
  625. var h = top.h + top.d + bot.h + bot.d;
  626. var y = -top.h; svg.Add(top,0,y); y -= top.d;
  627. if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); h += mid.h + mid.d}
  628. if (delim.min && H < h*delim.min) {H = h*delim.min}
  629. if (H > h) {
  630. var ext = this.createChar(scale,delim.ext,font);
  631. var k = (delim.mid ? 2 : 1), eH = (H-h) / k, s = (eH+100) / (ext.h+ext.d);
  632. while (k-- > 0) {
  633. var g = SVG.Element("g",{transform:"translate("+ext.y+","+(y-s*ext.h+50+ext.y)+") scale(1,"+s+")"});
  634. g.appendChild(ext.element.cloneNode(false)); svg.element.appendChild(g); y -= eH;
  635. if (delim.mid && k) {svg.Add(mid,0,y-mid.h); y -= (mid.h+mid.d)}
  636. }
  637. } else if (delim.mid) {
  638. y += (h - H)/2; svg.Add(mid,0,y-mid.h); y += -(mid.h + mid.d) + (h - H)/2;
  639. } else {
  640. y += (h - H);
  641. }
  642. svg.Add(bot,0,y-bot.h); svg.Clean();
  643. svg.scale = scale;
  644. svg.isMultiChar = true;
  645. },
  646. extendDelimiterH: function (svg,W,delim,scale,font) {
  647. var left = this.createChar(scale,(delim.left||delim.rep),font);
  648. var right = this.createChar(scale,(delim.right||delim.rep),font);
  649. svg.Add(left,-left.l,0);
  650. var w = (left.r - left.l) + (right.r - right.l), x = left.r - left.l;
  651. if (delim.mid) {var mid = this.createChar(scale,delim.mid,font); w += mid.w}
  652. if (delim.min && W < w*delim.min) {W = w*delim.min}
  653. if (W > w) {
  654. var rep = this.createChar(scale,delim.rep,font), fuzz = delim.fuzz || 0;
  655. var k = (delim.mid ? 2 : 1), rW = (W-w) / k, s = (rW+fuzz) / (rep.r-rep.l);
  656. while (k-- > 0) {
  657. var g = SVG.Element("g",{transform:"translate("+(x-fuzz/2-s*rep.l+rep.x)+","+rep.y+") scale("+s+",1)"});
  658. g.appendChild(rep.element.cloneNode(false)); svg.element.appendChild(g); x += rW;
  659. if (delim.mid && k) {svg.Add(mid,x,0); x += mid.w}
  660. }
  661. } else if (delim.mid) {
  662. x -= (w - W)/2; svg.Add(mid,x,0); x += mid.w - (w - W)/2;
  663. } else {
  664. x -= (w - W);
  665. }
  666. svg.Add(right,x-right.l,0); svg.Clean();
  667. svg.scale = scale;
  668. svg.isMultiChar = true;
  669. },
  670. MATHSPACE: {
  671. veryverythinmathspace: 1/18,
  672. verythinmathspace: 2/18,
  673. thinmathspace: 3/18,
  674. mediummathspace: 4/18,
  675. thickmathspace: 5/18,
  676. verythickmathspace: 6/18,
  677. veryverythickmathspace: 7/18,
  678. negativeveryverythinmathspace: -1/18,
  679. negativeverythinmathspace: -2/18,
  680. negativethinmathspace: -3/18,
  681. negativemediummathspace: -4/18,
  682. negativethickmathspace: -5/18,
  683. negativeverythickmathspace: -6/18,
  684. negativeveryverythickmathspace: -7/18
  685. },
  686. //
  687. // Units are em/1000 so quad is 1em
  688. //
  689. TeX: {
  690. x_height: 430.554,
  691. quad: 1000,
  692. num1: 676.508,
  693. num2: 393.732,
  694. num3: 443.73,
  695. denom1: 685.951,
  696. denom2: 344.841,
  697. sup1: 412.892,
  698. sup2: 362.892,
  699. sup3: 288.888,
  700. sub1: 150,
  701. sub2: 247.217,
  702. sup_drop: 386.108,
  703. sub_drop: 50,
  704. delim1: 2390,
  705. delim2: 1000,
  706. axis_height: 250,
  707. rule_thickness: 60,
  708. big_op_spacing1: 111.111,
  709. big_op_spacing2: 166.666,
  710. big_op_spacing3: 200,
  711. big_op_spacing4: 600,
  712. big_op_spacing5: 100,
  713. scriptspace: 100,
  714. nulldelimiterspace: 120,
  715. delimiterfactor: 901,
  716. delimitershortfall: 100, // originally 300,
  717. min_rule_thickness: 1.25, // in pixels
  718. min_root_space: 1.5 // in pixels
  719. },
  720. BIGDIMEN: 10000000,
  721. NBSP: "\u00A0"
  722. });
  723. var BBOX = SVG.BBOX = MathJax.Object.Subclass({
  724. type: "g", removeable: true,
  725. Init: function (def) {
  726. this.h = this.d = -SVG.BIGDIMEN; this.H = this.D = 0;
  727. this.w = this.r = 0; this.l = SVG.BIGDIMEN;
  728. this.x = this.y = 0; this.scale = 1; this.n = 0;
  729. if (this.type) {this.element = SVG.Element(this.type,def)}
  730. },
  731. With: function (def) {return HUB.Insert(this,def)},
  732. Add: function (svg,dx,dy,forcew,infront) {
  733. if (dx) {svg.x += dx}; if (dy) {svg.y += dy};
  734. if (svg.element) {
  735. if (svg.removeable && svg.element.childNodes.length === 1 && svg.n === 1) {
  736. var child = svg.element.firstChild;
  737. if (child.nodeName === "use" || child.nodeName === "rect") {
  738. svg.element = child; svg.scale = svg.childScale;
  739. var x = svg.childX, y = svg.childY;
  740. svg.x += x; svg.y += y;
  741. svg.h -= y; svg.d += y; svg.H -= y; svg.D +=y;
  742. svg.w -= x; svg.r -= x; svg.l += x;
  743. svg.removeable = false;
  744. }
  745. }
  746. if (Math.abs(svg.x) < 1 && Math.abs(svg.y) < 1) {
  747. svg.remove = svg.removeable;
  748. } else {
  749. if (svg.element.nodeName === "g") {
  750. if (!svg.element.firstChild) {svg.remove = svg.removeable}
  751. else {svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")")}
  752. } else if (svg.element.nodeName === "line" ||
  753. svg.element.nodeName === "polygon" ||
  754. svg.element.nodeName === "path" ||
  755. svg.element.nodeName === "a") {
  756. svg.element.setAttribute("transform","translate("+Math.floor(svg.x)+","+Math.floor(svg.y)+")");
  757. } else {
  758. svg.element.setAttribute("x",Math.floor(svg.x/svg.scale));
  759. svg.element.setAttribute("y",Math.floor(svg.y/svg.scale));
  760. }
  761. }
  762. if (svg.remove) {
  763. this.n += svg.n;
  764. while (svg.element.firstChild) {
  765. if (infront && this.element.firstChild) {
  766. this.element.insertBefore(svg.element.firstChild,this.element.firstChild);
  767. } else {
  768. this.element.appendChild(svg.element.firstChild);
  769. }
  770. }
  771. } else {
  772. if (infront) {this.element.insertBefore(svg.element,this.element.firstChild)}
  773. else {this.element.appendChild(svg.element)}
  774. }
  775. delete svg.element;
  776. }
  777. if (svg.hasIndent) {this.hasIndent = svg.hasIndent}
  778. if (svg.d - svg.y > this.d) {this.d = svg.d - svg.y; if (this.d > this.D) {this.D = this.d}}
  779. if (svg.y + svg.h > this.h) {this.h = svg.y + svg.h; if (this.h > this.H) {this.H = this.h}}
  780. if (svg.D - svg.y > this.D) {this.D = svg.D - svg.y}
  781. if (svg.y + svg.H > this.H) {this.H = svg.y + svg.H}
  782. if (svg.x + svg.l < this.l) {this.l = svg.x + svg.l}
  783. if (svg.x + svg.r > this.r) {this.r = svg.x + svg.r}
  784. if (forcew || svg.x + svg.w + (svg.X||0) > this.w) {this.w = svg.x + svg.w + (svg.X||0)}
  785. this.childScale = svg.scale; this.childX = svg.x; this.childY = svg.y; this.n++;
  786. return svg;
  787. },
  788. Align: function (svg,align,dx,dy) {
  789. dx = ({left: dx, center: (this.w - svg.w)/2, right: this.w - svg.w - dx})[align] || 0;
  790. this.Add(svg,dx,dy);
  791. },
  792. Clean: function () {
  793. if (this.h === -SVG.BIGDIMEN) {this.h = this.d = this.l = 0}
  794. return this;
  795. }
  796. });
  797. BBOX.ROW = BBOX.Subclass({
  798. Init: function () {
  799. this.SUPER(arguments).Init.call(this);
  800. this.svg = []; this.sh = this.sd = 0;
  801. },
  802. Check: function (data) {
  803. var svg = data.toSVG(); this.svg.push(svg);
  804. if (data.SVGcanStretch("Vertical")) {svg.mml = data}
  805. if (svg.h > this.sh) {this.sh = svg.h}
  806. if (svg.d > this.sd) {this.sd = svg.d}
  807. },
  808. Stretch: function () {
  809. for (var i = 0, m = this.svg.length; i < m; i++)
  810. {
  811. var svg = this.svg[i], mml = svg.mml;
  812. if (mml) {
  813. svg = mml.SVGstretchV(this.sh,this.sd);
  814. mml.SVGdata.HW = this.sh; mml.SVGdata.D = this.sd;
  815. }
  816. if (svg.ic) {this.ic = svg.ic} else {delete this.ic}
  817. this.Add(svg,this.w,0,true);
  818. }
  819. delete this.svg;
  820. }
  821. });
  822. BBOX.RECT = BBOX.Subclass({
  823. type: "rect", removeable: false,
  824. Init: function (h,d,w,def) {
  825. if (def == null) {def = {stroke:"none"}}
  826. def.width = Math.floor(w); def.height = Math.floor(h+d);
  827. this.SUPER(arguments).Init.call(this,def);
  828. this.w = this.r = w; this.h = this.H = h+d; this.d = this.D = this.l = 0; this.y = -d;
  829. }
  830. });
  831. BBOX.FRAME = BBOX.Subclass({
  832. type: "rect", removeable: false,
  833. Init: function (h,d,w,t,dash,color,def) {
  834. if (def == null) {def = {}}; def.fill = "none";
  835. def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,"");
  836. def.width = Math.floor(w-t); def.height = Math.floor(h+d-t);
  837. def.transform = "translate("+Math.floor(t/2)+","+Math.floor(-d+t/2)+")";
  838. if (dash === "dashed")
  839. {def["stroke-dasharray"] = [Math.floor(6*SVG.em),Math.floor(6*SVG.em)].join(" ")}
  840. this.SUPER(arguments).Init.call(this,def);
  841. this.w = this.r = w; this.h = this.H = h;
  842. this.d = this.D = d; this.l = 0;
  843. }
  844. });
  845. BBOX.HLINE = BBOX.Subclass({
  846. type: "line", removeable: false,
  847. Init: function (w,t,dash,color,def) {
  848. if (def == null) {def = {}}
  849. if (color && color !== "") {def.stroke = color}
  850. def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,"");
  851. def.x1 = 0; def.y1 = def.y2 = t/2; def.x2 = Math.floor(w);
  852. if (dash === "dashed") {
  853. var n = Math.floor(w/(6*t)), m = Math.floor(w/(2*n+1));
  854. def["stroke-dasharray"] = m+" "+m;
  855. }
  856. this.SUPER(arguments).Init.call(this,def);
  857. this.w = this.r = w; this.l = 0; this.h = this.H = t; this.d = this.D = 0;
  858. }
  859. });
  860. BBOX.VLINE = BBOX.Subclass({
  861. type: "line", removeable: false,
  862. Init: function (h,t,dash,color,def) {
  863. if (def == null) {def = {}}
  864. if (color && color !== "") {def.stroke = color}
  865. def["stroke-width"] = t.toFixed(2).replace(/\.?0+$/,"");
  866. def.x1 = def.x2 = t/2; def.y1 = 0; def.y2 = Math.floor(h);
  867. if (dash === "dashed") {
  868. var n = Math.floor(h/(6*t)), m = Math.floor(h/(2*n+1));
  869. def["stroke-dasharray"] = m+" "+m;
  870. }
  871. this.SUPER(arguments).Init.call(this,def);
  872. this.w = this.r = t; this.l = 0; this.h = this.H = h; this.d = this.D = 0;
  873. }
  874. });
  875. BBOX.TEXT = BBOX.Subclass({
  876. type: "text", removeable: false,
  877. Init: function (scale,text,def) {
  878. if (!def) {def = {}}; def.stroke = "none";
  879. this.SUPER(arguments).Init.call(this,def);
  880. SVG.addText(this.element,text);
  881. SVG.textSVG.appendChild(this.element);
  882. var bbox = this.element.getBBox();
  883. SVG.textSVG.removeChild(this.element);
  884. scale *= 1000/SVG.em;
  885. this.element.setAttribute("transform","scale("+scale+") matrix(1 0 0 -1 0 0)");
  886. this.w = this.r = bbox.width*scale; this.l = 0;
  887. this.h = this.H = -bbox.y*scale;
  888. this.d = this.D = (bbox.height + bbox.y)*scale;
  889. }
  890. });
  891. BBOX.G = BBOX;
  892. BBOX.NULL = BBOX.Subclass({
  893. Init: function () {
  894. this.SUPER(arguments).Init.apply(this,arguments);
  895. this.Clean();
  896. }
  897. });
  898. var GLYPHS, DEFS; // data for which glyphs are used
  899. BBOX.GLYPH = BBOX.Subclass({
  900. type: "path", removeable: false,
  901. Init: function (scale,id,h,d,w,l,r,p) {
  902. var def, t = SVG.config.blacker;
  903. if (!GLYPHS[id]) {
  904. def = {id:id, "stroke-width":t};
  905. if (p !== "") {def.d = "M"+p+"Z"}
  906. this.SUPER(arguments).Init.call(this,def);
  907. DEFS.appendChild(this.element); GLYPHS[id] = true;
  908. }
  909. def = {}; if (scale !== 1) {def.transform = "scale("+scale+")"}
  910. this.element = SVG.Element("use",def);
  911. this.element.setAttributeNS(XLINKNS,"href","#"+id);
  912. this.h = (h+t) * scale; this.d = (d+t) * scale; this.w = (w+t/2) *scale;
  913. this.l = (l+t/2) * scale; this.r = (r+t/2) * scale;
  914. this.H = Math.max(0,this.h); this.D = Math.max(0,this.d);
  915. this.x = this.y = 0; this.scale = scale;
  916. }
  917. });
  918. HUB.Register.StartupHook("mml Jax Ready",function () {
  919. MML = MathJax.ElementJax.mml;
  920. MML.mbase.Augment({
  921. SVG: BBOX,
  922. toSVG: function () {
  923. this.SVGgetStyles();
  924. var variant = this.SVGgetVariant();
  925. var svg = this.SVG();
  926. svg.scale = this.SVGgetScale(); this.SVGhandleSpace(svg);
  927. for (var i = 0, m = this.data.length; i < m; i++) {
  928. if (this.data[i]) {
  929. var child = svg.Add(this.data[i].toSVG(variant,svg.scale),svg.w,0,true);
  930. if (child.skew) {svg.skew = child.skew}
  931. }
  932. }
  933. svg.Clean(); var text = this.data.join("");
  934. if (svg.skew && text.length !== 1) {delete svg.skew}
  935. if (svg.r > svg.w && text.length === 1 && !variant.noIC)
  936. {svg.ic = svg.r - svg.w; svg.w = svg.r}
  937. this.SVGhandleColor(svg);
  938. this.SVGsaveData(svg);
  939. return svg;
  940. },
  941. SVGdataStretched: function (i,HW,D) {
  942. this.SVGdata = {HW:HW, D:D};
  943. if (D != null) {return this.data[i].SVGstretchV(HW,D)}
  944. if (HW != null) {return this.data[i].SVGstretchH(HW)}
  945. return this.data[i].toSVG();
  946. },
  947. SVGsaveData: function (svg) {
  948. if (!this.SVGdata) {this.SVGdata = {}}
  949. this.SVGdata.w = svg.w, this.SVGdata.x = svg.x;
  950. this.SVGdata.h = svg.h, this.SVGdata.d = svg.d;
  951. if (svg.X != null) {this.SVGdata.X = svg.X}
  952. if (this["class"]) {svg.removeable = false; SVG.Element(svg.element,{"class":this["class"]})}
  953. // FIXME: if an element is split by linebreaking, the ID will be the same on both parts
  954. // FIXME: if an element has an id, its zoomed copy will have the same ID
  955. if (this.id) {svg.removeable = false; SVG.Element(svg.element,{"id":this.id})}
  956. if (this.href) {
  957. var a = SVG.Element("a");
  958. a.setAttributeNS(XLINKNS,"href",this.href);
  959. a.onclick = this.SVGlink;
  960. SVG.addElement(a,"rect",{width:svg.w, height:svg.h+svg.d, y:-svg.d,
  961. fill:"none", stroke:"none", "pointer-events":"all"});
  962. if (svg.type === "svg") {
  963. // for svg element, put <a> inside the main <g> element
  964. var g = svg.element.firstChild;
  965. while (g.firstChild) {a.appendChild(g.firstChild)}
  966. g.appendChild(a);
  967. } else {
  968. // if removeable, move contents of <g> to <a>, otherise move element to <a>
  969. if (svg.removeable && svg.element.nodeName === "g") {
  970. while (svg.element.firstChild) {a.appendChild(svg.element.firstChild)}
  971. } else {a.appendChild(svg.element)}
  972. svg.element = a;
  973. }
  974. svg.removeable = false;
  975. }
  976. if (SVG.config.addMMLclasses) {
  977. svg.removeable = false;
  978. svg.element.setAttribute("className","mjx-svg-"+this.type);
  979. }
  980. var style = this.style;
  981. if (style && svg.element) {
  982. svg.element.style.cssText = style;
  983. if (svg.element.style.fontSize) {svg.element.style.fontSize = ""} // handled by scale
  984. svg.element.style.border = svg.element.style.padding = "";
  985. if (svg.removeable) {svg.removeable = svg.element.style.cssText === ""}
  986. }
  987. },
  988. //
  989. // WebKit currently scrolls to the BOTTOM of an svg element if it contains the
  990. // target of the link, so implement link by hand, to the containing span element.
  991. //
  992. SVGlink: function () {
  993. var href = this.href.animVal;
  994. if (href.charAt(0) === "#") {
  995. var target = SVG.hashCheck(document.getElementById(href.substr(1)));
  996. if (target && target.scrollIntoView)
  997. {setTimeout(function () {target.parentNode.scrollIntoView(true)},1)}
  998. }
  999. document.location = href;
  1000. },
  1001. SVGgetStyles: function () {
  1002. if (this.style) {
  1003. var span = HTML.Element("span");
  1004. span.style.cssText = this.style;
  1005. this.styles = {border:SVG.getBorders(span), padding:SVG.getPadding(span)}
  1006. if (span.style.fontSize) {this.styles.fontSize = span.style.fontSize}
  1007. if (span.style.color) {this.styles.color = span.style.color}
  1008. if (span.style.backgroundColor) {this.styles.background = span.style.backgroundColor}
  1009. if (span.style.fontStyle) {this.styles.fontStyle = span.style.fontStyle}
  1010. if (span.style.fontWeight) {this.styles.fontWeight = span.style.fontWeight}
  1011. if (span.style.fontFamily) {this.styles.fontFamily = span.style.fontFamily}
  1012. if (this.styles.fontWeight && this.styles.fontWeight.match(/^\d+$/))
  1013. {this.styles.fontWeight = (parseInt(this.styles.fontWeight) > 600 ? "bold" : "normal")}
  1014. }
  1015. },
  1016. SVGhandleSpace: function (svg) {
  1017. if (this.useMMLspacing) {
  1018. if (this.type !== "mo") return;
  1019. var values = this.getValues("scriptlevel","lspace","rspace");
  1020. if (values.scriptlevel <= 0 || this.hasValue("lspace") || this.hasValue("rspace")) {
  1021. var mu = this.SVGgetMu(svg);
  1022. values.lspace = Math.max(0,SVG.length2em(values.lspace,mu));
  1023. values.rspace = Math.max(0,SVG.length2em(values.rspace,mu));
  1024. var core = this, parent = this.Parent();
  1025. while (parent && parent.isEmbellished() && parent.Core() === core)
  1026. {core = parent; parent = parent.Parent()}
  1027. if (values.lspace) {svg.x += values.lspace}
  1028. if (values.rspace) {svg.X = values.rspace}
  1029. }
  1030. } else {
  1031. var space = this.texSpacing();
  1032. if (space !== "") {svg.x += SVG.length2em(space,this.SVGgetScale())/svg.scale}
  1033. }
  1034. },
  1035. SVGhandleColor: function (svg) {
  1036. var values = this.getValues("mathcolor","color");
  1037. if (this.styles && this.styles.color && !values.color) {values.color = this.styles.color}
  1038. if (values.color && !this.mathcolor) {values.mathcolor = values.color}
  1039. if (values.mathcolor) {
  1040. SVG.Element(svg.element,{fill:values.mathcolor,stroke:values.mathcolor})
  1041. svg.removeable = false;
  1042. }
  1043. var borders = (this.styles||{}).border, padding = (this.styles||{}).padding,
  1044. bleft = ((borders||{}).left||0), pleft = ((padding||{}).left||0), id;
  1045. values.background = (this.mathbackground || this.background ||
  1046. (this.styles||{}).background || MML.COLOR.TRANSPARENT);
  1047. if (bleft + pleft) {
  1048. //
  1049. // Make a box and move the contents of svg to it,
  1050. // then add it back into svg, but offset by the left amount
  1051. //
  1052. var dup = BBOX(); for (id in svg) {if (svg.hasOwnProperty(id)) {dup[id] = svg[id]}}
  1053. dup.x = 0; dup.y = 0;
  1054. svg.element = SVG.Element("g"); svg.removeable = true;
  1055. svg.Add(dup,bleft+pleft,0);
  1056. }
  1057. //
  1058. // Adjust size by padding and dashed borders (left is taken care of above)
  1059. //
  1060. if (padding) {svg.w += padding.right; svg.h += padding.top; svg.d += padding.bottom}
  1061. if (borders) {svg.w += borders.right; svg.h += borders.top; svg.d += borders.bottom}
  1062. //
  1063. // Add background color
  1064. //
  1065. if (values.background !== MML.COLOR.TRANSPARENT) {
  1066. if (svg.element.nodeName !== "g" && svg.element.nodeName !== "svg") {
  1067. var g = SVG.Element("g"); g.appendChild(svg.element);
  1068. svg.element = g; svg.removeable = true;
  1069. }
  1070. svg.Add(BBOX.RECT(svg.h,svg.d,svg.w,{fill:values.background,stroke:"none"}),0,0,false,true)
  1071. }
  1072. //
  1073. // Add borders
  1074. //
  1075. if (borders) {
  1076. var dd = 5; // fuzz factor to avoid anti-alias problems at edges
  1077. var sides = {
  1078. left: ["V",svg.h+svg.d,-dd,-svg.d],
  1079. right: ["V",svg.h+svg.d,svg.w-borders.right+dd,-svg.d],
  1080. top: ["H",svg.w,0,svg.h-borders.top+dd],
  1081. bottom:["H",svg.w,0,-svg.d-dd]
  1082. }
  1083. for (id in sides) {if (sides.hasOwnProperty(id)) {
  1084. if (borders[id]) {
  1085. var side = sides[id], box = BBOX[side[0]+"LINE"];
  1086. svg.Add(box(side[1],borders[id],borders[id+"Style"],borders[id+"Color"]),side[2],side[3]);
  1087. }
  1088. }}
  1089. }
  1090. },
  1091. SVGhandleVariant: function (variant,scale,text) {
  1092. return SVG.HandleVariant(variant,scale,text);
  1093. },
  1094. SVGgetVariant: function () {
  1095. var values = this.getValues("mathvariant","fontfamily","fontweight","fontstyle");
  1096. var variant = values.mathvariant; if (this.variantForm) {variant = "-TeX-variant"}
  1097. values.hasVariant = this.Get("mathvariant",true); // null if not explicitly specified
  1098. if (!values.hasVariant) {
  1099. values.family = values.fontfamily;
  1100. values.weight = values.fontweight;
  1101. values.style = values.fontstyle;
  1102. }
  1103. if (this.styles) {
  1104. if (!values.style && this.styles.fontStyle) {values.style = this.styles.fontStyle}
  1105. if (!values.weight && this.styles.fontWeight) {values.weight = this.styles.fontWeight}
  1106. if (!values.family && this.styles.fontFamily) {values.family = this.styles.fontFamily}
  1107. }
  1108. if (values.family && !values.hasVariant) {
  1109. if (!values.weight && values.mathvariant.match(/bold/)) {values.weight = "bold"}
  1110. if (!values.style && values.mathvariant.match(/italic/)) {values.style = "italic"}
  1111. variant = {forceFamily: true, font: {"font-family":values.family}};
  1112. if (values.style) {variant.font["font-style"] = values.style}
  1113. if (values.weight) {variant.font["font-weight"] = values.weight}
  1114. return variant;
  1115. }
  1116. if (values.weight === "bold") {
  1117. variant = {
  1118. normal:MML.VARIANT.BOLD, italic:MML.VARIANT.BOLDITALIC,
  1119. fraktur:MML.VARIANT.BOLDFRAKTUR, script:MML.VARIANT.BOLDSCRIPT,
  1120. "sans-serif":MML.VARIANT.BOLDSANSSERIF,
  1121. "sans-serif-italic":MML.VARIANT.SANSSERIFBOLDITALIC
  1122. }[variant]||variant;
  1123. } else if (values.weight === "normal") {
  1124. variant = {
  1125. bold:MML.VARIANT.normal, "bold-italic":MML.VARIANT.ITALIC,
  1126. "bold-fraktur":MML.VARIANT.FRAKTUR, "bold-script":MML.VARIANT.SCRIPT,
  1127. "bold-sans-serif":MML.VARIANT.SANSSERIF,
  1128. "sans-serif-bold-italic":MML.VARIANT.SANSSERIFITALIC
  1129. }[variant]||variant;
  1130. }
  1131. if (values.style === "italic") {
  1132. variant = {
  1133. normal:MML.VARIANT.ITALIC, bold:MML.VARIANT.BOLDITALIC,
  1134. "sans-serif":MML.VARIANT.SANSSERIFITALIC,
  1135. "bold-sans-serif":MML.VARIANT.SANSSERIFBOLDITALIC
  1136. }[variant]||variant;
  1137. } else if (values.style === "normal") {
  1138. variant = {
  1139. italic:MML.VARIANT.NORMAL, "bold-italic":MML.VARIANT.BOLD,
  1140. "sans-serif-italic":MML.VARIANT.SANSSERIF,
  1141. "sans-serif-bold-italic":MML.VARIANT.BOLDSANSSERIF
  1142. }[variant]||variant;
  1143. }
  1144. return SVG.FONTDATA.VARIANT[variant];
  1145. },
  1146. SVGgetScale: function () {
  1147. var scale = 1,
  1148. values = this.getValues("mathsize","scriptlevel","fontsize");
  1149. if ((this.styles||{}).fontSize && !values.fontsize) {values.fontsize = this.styles.fontSize}
  1150. if (values.fontsize && !this.mathsize) {values.mathsize = values.fontsize}
  1151. if (values.scriptlevel !== 0) {
  1152. if (values.scrip

Large files files are truncated, but you can click here to view the full file