temml 0.10.12 → 0.10.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/temml.mjs CHANGED
@@ -968,6 +968,7 @@ defineSymbol(math, rel, "\u21c1", "\\rightharpoondown", true);
968
968
  defineSymbol(math, rel, "\u2196", "\\nwarrow", true);
969
969
  defineSymbol(math, rel, "\u21cc", "\\rightleftharpoons", true);
970
970
  defineSymbol(math, mathord, "\u21af", "\\lightning", true);
971
+ defineSymbol(math, mathord, "\u220E", "\\QED", true);
971
972
  defineSymbol(math, mathord, "\u2030", "\\permil", true);
972
973
  defineSymbol(text, textord, "\u2030", "\\permil");
973
974
 
@@ -1114,6 +1115,7 @@ defineSymbol(math, rel, "\u2ab7", "\\precapprox", true);
1114
1115
  defineSymbol(math, rel, "\u22b2", "\\vartriangleleft");
1115
1116
  defineSymbol(math, rel, "\u22b4", "\\trianglelefteq");
1116
1117
  defineSymbol(math, rel, "\u22a8", "\\vDash", true);
1118
+ defineSymbol(math, rel, "\u22ab", "\\VDash", true);
1117
1119
  defineSymbol(math, rel, "\u22aa", "\\Vvdash", true);
1118
1120
  defineSymbol(math, rel, "\u2323", "\\smallsmile");
1119
1121
  defineSymbol(math, rel, "\u2322", "\\smallfrown");
@@ -1763,13 +1765,16 @@ for (let i = 0; i < 10; i++) {
1763
1765
  * much of this module.
1764
1766
  */
1765
1767
 
1768
+ const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃";
1769
+ const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄";
1770
+
1766
1771
  function setLineBreaks(expression, wrapMode, isDisplayMode) {
1767
1772
  const mtrs = [];
1768
1773
  let mrows = [];
1769
1774
  let block = [];
1770
1775
  let numTopLevelEquals = 0;
1771
- let canBeBIN = false; // The first node cannot be an infix binary operator.
1772
1776
  let i = 0;
1777
+ let level = 0;
1773
1778
  while (i < expression.length) {
1774
1779
  while (expression[i] instanceof DocumentFragment) {
1775
1780
  expression.splice(i, 1, ...expression[i].children); // Expand the fragment.
@@ -1792,7 +1797,12 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
1792
1797
  }
1793
1798
  block.push(node);
1794
1799
  if (node.type && node.type === "mo" && node.children.length === 1) {
1795
- if (wrapMode === "=" && node.children[0].text === "=") {
1800
+ const ch = node.children[0].text;
1801
+ if (openDelims.indexOf(ch) > -1) {
1802
+ level += 1;
1803
+ } else if (closeDelims.indexOf(ch) > -1) {
1804
+ level -= 1;
1805
+ } else if (level === 0 && wrapMode === "=" && ch === "=") {
1796
1806
  numTopLevelEquals += 1;
1797
1807
  if (numTopLevelEquals > 1) {
1798
1808
  block.pop();
@@ -1801,59 +1811,48 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
1801
1811
  mrows.push(element);
1802
1812
  block = [node];
1803
1813
  }
1804
- } else if (wrapMode === "tex") {
1805
- // This may be a place for a soft line break.
1806
- if (canBeBIN && !node.attributes.form) {
1807
- // Check if the following node is a \nobreak text node, e.g. "~""
1808
- const next = i < expression.length - 1 ? expression[i + 1] : null;
1809
- let glueIsFreeOfNobreak = true;
1810
- if (
1811
- !(
1812
- next &&
1813
- next.type === "mtext" &&
1814
- next.attributes.linebreak &&
1815
- next.attributes.linebreak === "nobreak"
1816
- )
1817
- ) {
1818
- // We may need to start a new block.
1819
- // First, put any post-operator glue on same line as operator.
1820
- for (let j = i + 1; j < expression.length; j++) {
1821
- const nd = expression[j];
1814
+ } else if (level === 0 && wrapMode === "tex") {
1815
+ // Check if the following node is a \nobreak text node, e.g. "~""
1816
+ const next = i < expression.length - 1 ? expression[i + 1] : null;
1817
+ let glueIsFreeOfNobreak = true;
1818
+ if (
1819
+ !(
1820
+ next &&
1821
+ next.type === "mtext" &&
1822
+ next.attributes.linebreak &&
1823
+ next.attributes.linebreak === "nobreak"
1824
+ )
1825
+ ) {
1826
+ // We may need to start a new block.
1827
+ // First, put any post-operator glue on same line as operator.
1828
+ for (let j = i + 1; j < expression.length; j++) {
1829
+ const nd = expression[j];
1830
+ if (
1831
+ nd.type &&
1832
+ nd.type === "mspace" &&
1833
+ !(nd.attributes.linebreak && nd.attributes.linebreak === "newline")
1834
+ ) {
1835
+ block.push(nd);
1836
+ i += 1;
1822
1837
  if (
1823
- nd.type &&
1824
- nd.type === "mspace" &&
1825
- !(nd.attributes.linebreak && nd.attributes.linebreak === "newline")
1838
+ nd.attributes &&
1839
+ nd.attributes.linebreak &&
1840
+ nd.attributes.linebreak === "nobreak"
1826
1841
  ) {
1827
- block.push(nd);
1828
- i += 1;
1829
- if (
1830
- nd.attributes &&
1831
- nd.attributes.linebreak &&
1832
- nd.attributes.linebreak === "nobreak"
1833
- ) {
1834
- glueIsFreeOfNobreak = false;
1835
- }
1836
- } else {
1837
- break;
1842
+ glueIsFreeOfNobreak = false;
1838
1843
  }
1844
+ } else {
1845
+ break;
1839
1846
  }
1840
1847
  }
1841
- if (glueIsFreeOfNobreak) {
1842
- // Start a new block. (Insert a soft linebreak.)
1843
- const element = new mathMLTree.MathNode("mrow", block);
1844
- mrows.push(element);
1845
- block = [];
1846
- }
1847
- canBeBIN = false;
1848
1848
  }
1849
- const isOpenDelimiter = node.attributes.form && node.attributes.form === "prefix";
1850
- // Any operator that follows an open delimiter is unary.
1851
- canBeBIN = !(node.attributes.separator || isOpenDelimiter);
1852
- } else {
1853
- canBeBIN = true;
1849
+ if (glueIsFreeOfNobreak) {
1850
+ // Start a new block. (Insert a soft linebreak.)
1851
+ const element = new mathMLTree.MathNode("mrow", block);
1852
+ mrows.push(element);
1853
+ block = [];
1854
+ }
1854
1855
  }
1855
- } else {
1856
- canBeBIN = true;
1857
1856
  }
1858
1857
  i += 1;
1859
1858
  }
@@ -2132,7 +2131,10 @@ function buildMathML(tree, texExpression, style, settings) {
2132
2131
  math.setAttribute("display", "block");
2133
2132
  math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
2134
2133
  ? "inline"
2135
- : "block math";
2134
+ : "block math"; // necessary in Chromium.
2135
+ // Firefox and Safari do not recognize display: "block math".
2136
+ // Set a class so that the CSS file can set display: block.
2137
+ math.classes = ["tml-display"];
2136
2138
  }
2137
2139
  return math;
2138
2140
  }
@@ -2416,43 +2418,69 @@ const paddedNode = (group, lspace = 0.3, rspace = 0) => {
2416
2418
  return new mathMLTree.MathNode("mrow", row)
2417
2419
  };
2418
2420
 
2419
- const labelSize = (size, scriptLevel) => (size / emScale(scriptLevel)).toFixed(4);
2421
+ const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel);
2420
2422
 
2421
- const munderoverNode = (name, body, below, style) => {
2422
- const arrowNode = stretchy.mathMLnode(name);
2423
+ const munderoverNode = (fName, body, below, style) => {
2424
+ const arrowNode = stretchy.mathMLnode(fName);
2423
2425
  // Is this the short part of a mhchem equilibrium arrow?
2424
- const isEq = name.slice(1, 3) === "eq";
2425
- const minWidth = name.charAt(1) === "x"
2426
- ? "1.75" // mathtools extensible arrows are 1.75em long
2427
- : name.slice(2, 4) === "cd"
2426
+ const isEq = fName.slice(1, 3) === "eq";
2427
+ const minWidth = fName.charAt(1) === "x"
2428
+ ? "1.75" // mathtools extensible arrows are 1.75em long
2429
+ : fName.slice(2, 4) === "cd"
2428
2430
  ? "3.0" // cd package arrows
2429
2431
  : isEq
2430
2432
  ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow
2431
2433
  : "2.0"; // other mhchem arrows
2432
- arrowNode.setAttribute("minsize", String(minWidth) + "em");
2434
+ // TODO: When Firefox supports minsize, use the next line.
2435
+ //arrowNode.setAttribute("minsize", String(minWidth) + "em")
2433
2436
  arrowNode.setAttribute("lspace", "0");
2434
2437
  arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0"));
2435
2438
 
2436
2439
  // <munderover> upper and lower labels are set to scriptlevel by MathML
2437
- // So we have to adjust our dimensions accordingly.
2440
+ // So we have to adjust our label dimensions accordingly.
2438
2441
  const labelStyle = style.withLevel(style.level < 2 ? 2 : 3);
2439
- const emptyLabelWidth = labelSize(minWidth, labelStyle.level);
2440
- const lspace = labelSize((isEq ? 0 : 0.3), labelStyle.level);
2441
- const rspace = labelSize((isEq ? 0 : 0.3), labelStyle.level);
2442
-
2443
- const upperNode = (body && body.body &&
2442
+ const minArrowWidth = labelSize(minWidth, labelStyle.level);
2443
+ // The dummyNode will be inside a <mover> inside a <mover>
2444
+ // So it will be at scriptlevel 3
2445
+ const dummyWidth = labelSize(minWidth, 3);
2446
+ const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0);
2447
+ const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0);
2448
+ // The arrow is a little longer than the label. Set a spacer length.
2449
+ const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4);
2450
+ let upperNode;
2451
+ let lowerNode;
2452
+
2453
+ const gotUpper = (body && body.body &&
2444
2454
  // \hphantom visible content
2445
- (body.body.body || body.body.length > 0))
2446
- ? paddedNode(buildGroup$1(body, labelStyle), lspace, rspace)
2447
- // Since Firefox does not recognize minsize set on the arrow,
2448
- // create an upper node w/correct width.
2449
- : paddedNode(null, emptyLabelWidth, 0);
2450
- const lowerNode = (below && below.body &&
2451
- (below.body.body || below.body.length > 0))
2452
- ? paddedNode(buildGroup$1(below, labelStyle), lspace, rspace)
2453
- : paddedNode(null, emptyLabelWidth, 0);
2454
- const node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]);
2455
- if (minWidth === "3.0") { node.style.height = "1em"; }
2455
+ (body.body.body || body.body.length > 0));
2456
+ if (gotUpper) {
2457
+ let label = buildGroup$1(body, labelStyle);
2458
+ label = paddedNode(label, space, space);
2459
+ // Since Firefox does not support minsize, stack a invisible node
2460
+ // on top of the label. Its width will serve as a min-width.
2461
+ // TODO: Refactor this after Firefox supports minsize.
2462
+ upperNode = new mathMLTree.MathNode("mover", [label, dummyNode]);
2463
+ }
2464
+ const gotLower = (below && below.body &&
2465
+ (below.body.body || below.body.length > 0));
2466
+ if (gotLower) {
2467
+ let label = buildGroup$1(below, labelStyle);
2468
+ label = paddedNode(label, space, space);
2469
+ lowerNode = new mathMLTree.MathNode("munder", [label, dummyNode]);
2470
+ }
2471
+
2472
+ let node;
2473
+ if (!gotUpper && !gotLower) {
2474
+ node = new mathMLTree.MathNode("mover", [arrowNode, emptyLabel]);
2475
+ } else if (gotUpper && gotLower) {
2476
+ node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]);
2477
+ } else if (gotUpper) {
2478
+ node = new mathMLTree.MathNode("mover", [arrowNode, upperNode]);
2479
+ } else {
2480
+ node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode]);
2481
+ }
2482
+ if (minWidth === "3.0") { node.style.height = "1em"; } // CD environment
2483
+ node.setAttribute("accent", "false"); // Necessary for MS Word
2456
2484
  return node
2457
2485
  };
2458
2486
 
@@ -2531,7 +2559,7 @@ defineFunction({
2531
2559
  "\\xleftrightharpoons", // mathtools
2532
2560
  "\\xrightleftharpoons", // mathtools
2533
2561
  "\\yieldsLeftRight", // mhchem
2534
- "\\equilibrium", // mhchem
2562
+ "\\equilibrium", // mhchem
2535
2563
  "\\equilibriumRight",
2536
2564
  "\\equilibriumLeft"
2537
2565
  ],
@@ -3635,10 +3663,9 @@ defineFunction({
3635
3663
  // so we have to explicitly set stretchy to true.
3636
3664
  node.setAttribute("stretchy", "true");
3637
3665
  }
3638
-
3639
3666
  node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox.
3640
3667
  node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em");
3641
- // Don't set the maxsize attribute. It's broken in Chromium.
3668
+ node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em");
3642
3669
  return node;
3643
3670
  }
3644
3671
  });
@@ -3773,33 +3800,31 @@ const padding$1 = _ => {
3773
3800
 
3774
3801
  const mathmlBuilder$8 = (group, style) => {
3775
3802
  let node;
3776
- if (group.label.indexOf("colorbox") > -1) {
3777
- // Chrome mpadded +width attribute is broken. Insert <mspace>
3778
- node = new mathMLTree.MathNode("mpadded", [
3803
+ if (group.label.indexOf("colorbox") > -1 || group.label === "\\boxed") {
3804
+ // MathML core does not support +width attribute in <mpadded>.
3805
+ // Firefox does not reliably add side padding.
3806
+ // Insert <mspace>
3807
+ node = new mathMLTree.MathNode("mrow", [
3779
3808
  padding$1(),
3780
3809
  buildGroup$1(group.body, style),
3781
3810
  padding$1()
3782
3811
  ]);
3783
3812
  } else {
3784
- node = new mathMLTree.MathNode("menclose", [buildGroup$1(group.body, style)]);
3813
+ node = new mathMLTree.MathNode("mrow", [buildGroup$1(group.body, style)]);
3785
3814
  }
3786
3815
  switch (group.label) {
3787
3816
  case "\\overline":
3788
- node.setAttribute("notation", "top");
3789
3817
  node.style.padding = "0.1em 0 0 0";
3790
3818
  node.style.borderTop = "0.065em solid";
3791
3819
  break
3792
3820
  case "\\underline":
3793
- node.setAttribute("notation", "bottom");
3794
3821
  node.style.padding = "0 0 0.1em 0";
3795
3822
  node.style.borderBottom = "0.065em solid";
3796
3823
  break
3797
3824
  case "\\cancel":
3798
- node.setAttribute("notation", "updiagonalstrike");
3799
3825
  node.classes.push("cancel");
3800
3826
  break
3801
3827
  case "\\bcancel":
3802
- node.setAttribute("notation", "downdiagonalstrike");
3803
3828
  node.classes.push("bcancel");
3804
3829
  break
3805
3830
  /*
@@ -3810,18 +3835,21 @@ const mathmlBuilder$8 = (group, style) => {
3810
3835
  node.setAttribute("notation", "phasorangle");
3811
3836
  break */
3812
3837
  case "\\angl":
3813
- node.setAttribute("notation", "actuarial");
3814
3838
  node.style.padding = "0.03889em 0.03889em 0 0.03889em";
3815
3839
  node.style.borderTop = "0.049em solid";
3816
3840
  node.style.borderRight = "0.049em solid";
3817
3841
  node.style.marginRight = "0.03889em";
3818
3842
  break
3819
3843
  case "\\sout":
3820
- node.setAttribute("notation", "horizontalstrike");
3821
3844
  node.style["text-decoration"] = "line-through 0.08em solid";
3822
3845
  break
3846
+ case "\\boxed":
3847
+ // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty
3848
+ node.style = { padding: "3pt 0 3pt 0", border: "1px solid" };
3849
+ node.setAttribute("scriptlevel", "0");
3850
+ node.setAttribute("displaystyle", "true");
3851
+ break
3823
3852
  case "\\fbox":
3824
- node.setAttribute("notation", "box");
3825
3853
  node.style = { padding: "3pt", border: "1px solid" };
3826
3854
  break
3827
3855
  case "\\fcolorbox":
@@ -3841,7 +3869,6 @@ const mathmlBuilder$8 = (group, style) => {
3841
3869
  break
3842
3870
  }
3843
3871
  case "\\xcancel":
3844
- node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
3845
3872
  node.classes.push("xcancel");
3846
3873
  break
3847
3874
  }
@@ -3936,7 +3963,7 @@ defineFunction({
3936
3963
 
3937
3964
  defineFunction({
3938
3965
  type: "enclose",
3939
- names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline"],
3966
+ names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", "\\boxed"],
3940
3967
  // , "\\phase", "\\longdiv"
3941
3968
  props: {
3942
3969
  numArgs: 1
@@ -6802,8 +6829,6 @@ defineFunction({
6802
6829
  }
6803
6830
  });
6804
6831
 
6805
- const sign = num => num >= 0 ? "+" : "-";
6806
-
6807
6832
  // \raise, \lower, and \raisebox
6808
6833
 
6809
6834
  const mathmlBuilder = (group, style) => {
@@ -6811,11 +6836,13 @@ const mathmlBuilder = (group, style) => {
6811
6836
  const node = new mathMLTree.MathNode("mpadded", [buildGroup$1(group.body, newStyle)]);
6812
6837
  const dy = calculateSize(group.dy, style);
6813
6838
  node.setAttribute("voffset", dy.number + dy.unit);
6814
- const dyAbs = Math.abs(dy.number);
6815
- // The next two lines do not work in Chromium.
6816
- // TODO: Find some other way to adjust height and depth.
6817
- node.setAttribute("height", sign(dy.number) + dyAbs + dy.unit);
6818
- node.setAttribute("depth", sign(-dy.number) + dyAbs + dy.unit);
6839
+ // Add padding, which acts to increase height in Chromium.
6840
+ // TODO: Figure out some way to change height in Firefox w/o breaking Chromium.
6841
+ if (dy.number > 0) {
6842
+ node.style.padding = dy.number + dy.unit + " 0 0 0";
6843
+ } else {
6844
+ node.style.padding = "0 0 " + Math.abs(dy.number) + dy.unit + " 0";
6845
+ }
6819
6846
  return node
6820
6847
  };
6821
6848
 
@@ -8444,9 +8471,6 @@ defineMacro("\u22ee", "\\vdots");
8444
8471
  //\newcommand{\substack}[1]{\subarray{c}#1\endsubarray}
8445
8472
  defineMacro("\\substack", "\\begin{subarray}{c}#1\\end{subarray}");
8446
8473
 
8447
- // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}}
8448
- defineMacro("\\boxed", "\\fbox{$\\displaystyle{#1}$}");
8449
-
8450
8474
  // \def\iff{\DOTSB\;\Longleftrightarrow\;}
8451
8475
  // \def\implies{\DOTSB\;\Longrightarrow\;}
8452
8476
  // \def\impliedby{\DOTSB\;\Longleftarrow\;}
@@ -8695,7 +8719,7 @@ defineMacro(
8695
8719
  defineMacro(
8696
8720
  "\\Temml",
8697
8721
  // eslint-disable-next-line max-len
8698
- "\\textrm{T}\\kern-0.2em\\lower{0.2em}\\textrm{E}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}"
8722
+ "\\textrm{T}\\kern-0.2em\\lower{0.2em}{\\textrm{E}}\\kern-0.08em{\\textrm{M}\\kern-0.08em\\raise{0.2em}\\textrm{M}\\kern-0.08em\\textrm{L}}"
8699
8723
  );
8700
8724
 
8701
8725
  // \DeclareRobustCommand\hspace{\@ifstar\@hspacer\@hspace}
@@ -12875,7 +12899,7 @@ class Style {
12875
12899
  * https://mit-license.org/
12876
12900
  */
12877
12901
 
12878
- const version = "0.10.12";
12902
+ const version = "0.10.14";
12879
12903
 
12880
12904
  function postProcess(block) {
12881
12905
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.12";
17
+ const version = "0.10.14";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.10.12",
3
+ "version": "0.10.14",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
+ "engines": {
7
+ "node": ">=18.13.0"
8
+ },
6
9
  "exports": {
7
10
  ".": {
8
11
  "require": "./dist/temml.cjs"
@@ -262,7 +262,10 @@ export default function buildMathML(tree, texExpression, style, settings) {
262
262
  math.setAttribute("display", "block");
263
263
  math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
264
264
  ? "inline"
265
- : "block math"
265
+ : "block math" // necessary in Chromium.
266
+ // Firefox and Safari do not recognize display: "block math".
267
+ // Set a class so that the CSS file can set display: block.
268
+ math.classes = ["tml-display"]
266
269
  }
267
270
  return math;
268
271
  }
@@ -18,45 +18,71 @@ const paddedNode = (group, lspace = 0.3, rspace = 0) => {
18
18
  if (lspace !== 0) { row.unshift(padding(lspace)) }
19
19
  if (rspace > 0) { row.push(padding(rspace)) }
20
20
  return new mathMLTree.MathNode("mrow", row)
21
- };
21
+ }
22
22
 
23
- const labelSize = (size, scriptLevel) => (size / emScale(scriptLevel)).toFixed(4)
23
+ const labelSize = (size, scriptLevel) => Number(size) / emScale(scriptLevel);
24
24
 
25
- const munderoverNode = (name, body, below, style) => {
26
- const arrowNode = stretchy.mathMLnode(name);
25
+ const munderoverNode = (fName, body, below, style) => {
26
+ const arrowNode = stretchy.mathMLnode(fName);
27
27
  // Is this the short part of a mhchem equilibrium arrow?
28
- const isEq = name.slice(1, 3) === "eq"
29
- const minWidth = name.charAt(1) === "x"
30
- ? "1.75" // mathtools extensible arrows are 1.75em long
31
- : name.slice(2, 4) === "cd"
28
+ const isEq = fName.slice(1, 3) === "eq"
29
+ const minWidth = fName.charAt(1) === "x"
30
+ ? "1.75" // mathtools extensible arrows are 1.75em long
31
+ : fName.slice(2, 4) === "cd"
32
32
  ? "3.0" // cd package arrows
33
33
  : isEq
34
34
  ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow
35
35
  : "2.0"; // other mhchem arrows
36
- arrowNode.setAttribute("minsize", String(minWidth) + "em");
36
+ // TODO: When Firefox supports minsize, use the next line.
37
+ //arrowNode.setAttribute("minsize", String(minWidth) + "em")
37
38
  arrowNode.setAttribute("lspace", "0")
38
39
  arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0"))
39
40
 
40
41
  // <munderover> upper and lower labels are set to scriptlevel by MathML
41
- // So we have to adjust our dimensions accordingly.
42
+ // So we have to adjust our label dimensions accordingly.
42
43
  const labelStyle = style.withLevel(style.level < 2 ? 2 : 3)
43
- const emptyLabelWidth = labelSize(minWidth, labelStyle.level)
44
- const lspace = labelSize((isEq ? 0 : 0.3), labelStyle.level)
45
- const rspace = labelSize((isEq ? 0 : 0.3), labelStyle.level)
44
+ const minArrowWidth = labelSize(minWidth, labelStyle.level)
45
+ // The dummyNode will be inside a <mover> inside a <mover>
46
+ // So it will be at scriptlevel 3
47
+ const dummyWidth = labelSize(minWidth, 3)
48
+ const emptyLabel = paddedNode(null, minArrowWidth.toFixed(4), 0)
49
+ const dummyNode = paddedNode(null, dummyWidth.toFixed(4), 0)
50
+ // The arrow is a little longer than the label. Set a spacer length.
51
+ const space = labelSize((isEq ? 0 : 0.3), labelStyle.level).toFixed(4)
52
+ let upperNode
53
+ let lowerNode
46
54
 
47
- const upperNode = (body && body.body &&
55
+ const gotUpper = (body && body.body &&
48
56
  // \hphantom visible content
49
57
  (body.body.body || body.body.length > 0))
50
- ? paddedNode(mml.buildGroup(body, labelStyle), lspace, rspace)
51
- // Since Firefox does not recognize minsize set on the arrow,
52
- // create an upper node w/correct width.
53
- : paddedNode(null, emptyLabelWidth, 0)
54
- const lowerNode = (below && below.body &&
58
+ if (gotUpper) {
59
+ let label = mml.buildGroup(body, labelStyle)
60
+ label = paddedNode(label, space, space)
61
+ // Since Firefox does not support minsize, stack a invisible node
62
+ // on top of the label. Its width will serve as a min-width.
63
+ // TODO: Refactor this after Firefox supports minsize.
64
+ upperNode = new mathMLTree.MathNode("mover", [label, dummyNode])
65
+ }
66
+ const gotLower = (below && below.body &&
55
67
  (below.body.body || below.body.length > 0))
56
- ? paddedNode(mml.buildGroup(below, labelStyle), lspace, rspace)
57
- : paddedNode(null, emptyLabelWidth, 0)
58
- const node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]);
59
- if (minWidth === "3.0") { node.style.height = "1em" }
68
+ if (gotLower) {
69
+ let label = mml.buildGroup(below, labelStyle)
70
+ label = paddedNode(label, space, space)
71
+ lowerNode = new mathMLTree.MathNode("munder", [label, dummyNode])
72
+ }
73
+
74
+ let node
75
+ if (!gotUpper && !gotLower) {
76
+ node = new mathMLTree.MathNode("mover", [arrowNode, emptyLabel])
77
+ } else if (gotUpper && gotLower) {
78
+ node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode])
79
+ } else if (gotUpper) {
80
+ node = new mathMLTree.MathNode("mover", [arrowNode, upperNode])
81
+ } else {
82
+ node = new mathMLTree.MathNode("munder", [arrowNode, lowerNode])
83
+ }
84
+ if (minWidth === "3.0") { node.style.height = "1em" } // CD environment
85
+ node.setAttribute("accent", "false") // Necessary for MS Word
60
86
  return node
61
87
  }
62
88
 
@@ -135,7 +161,7 @@ defineFunction({
135
161
  "\\xleftrightharpoons", // mathtools
136
162
  "\\xrightleftharpoons", // mathtools
137
163
  "\\yieldsLeftRight", // mhchem
138
- "\\equilibrium", // mhchem
164
+ "\\equilibrium", // mhchem
139
165
  "\\equilibriumRight",
140
166
  "\\equilibriumLeft"
141
167
  ],
@@ -178,10 +178,9 @@ defineFunction({
178
178
  // so we have to explicitly set stretchy to true.
179
179
  node.setAttribute("stretchy", "true")
180
180
  }
181
-
182
181
  node.setAttribute("symmetric", "true"); // Needed for tall arrows in Firefox.
183
- node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em");
184
- // Don't set the maxsize attribute. It's broken in Chromium.
182
+ node.setAttribute("minsize", sizeToMaxHeight[group.size] + "em")
183
+ node.setAttribute("maxsize", sizeToMaxHeight[group.size] + "em")
185
184
  return node;
186
185
  }
187
186
  });
@@ -12,33 +12,31 @@ const padding = _ => {
12
12
 
13
13
  const mathmlBuilder = (group, style) => {
14
14
  let node
15
- if (group.label.indexOf("colorbox") > -1) {
16
- // Chrome mpadded +width attribute is broken. Insert <mspace>
17
- node = new mathMLTree.MathNode("mpadded", [
15
+ if (group.label.indexOf("colorbox") > -1 || group.label === "\\boxed") {
16
+ // MathML core does not support +width attribute in <mpadded>.
17
+ // Firefox does not reliably add side padding.
18
+ // Insert <mspace>
19
+ node = new mathMLTree.MathNode("mrow", [
18
20
  padding(),
19
21
  mml.buildGroup(group.body, style),
20
22
  padding()
21
23
  ])
22
24
  } else {
23
- node = new mathMLTree.MathNode("menclose", [mml.buildGroup(group.body, style)])
25
+ node = new mathMLTree.MathNode("mrow", [mml.buildGroup(group.body, style)])
24
26
  }
25
27
  switch (group.label) {
26
28
  case "\\overline":
27
- node.setAttribute("notation", "top")
28
29
  node.style.padding = "0.1em 0 0 0"
29
30
  node.style.borderTop = "0.065em solid"
30
31
  break
31
32
  case "\\underline":
32
- node.setAttribute("notation", "bottom")
33
33
  node.style.padding = "0 0 0.1em 0"
34
34
  node.style.borderBottom = "0.065em solid"
35
35
  break
36
36
  case "\\cancel":
37
- node.setAttribute("notation", "updiagonalstrike");
38
37
  node.classes.push("cancel")
39
38
  break
40
39
  case "\\bcancel":
41
- node.setAttribute("notation", "downdiagonalstrike");
42
40
  node.classes.push("bcancel")
43
41
  break
44
42
  /*
@@ -49,18 +47,21 @@ const mathmlBuilder = (group, style) => {
49
47
  node.setAttribute("notation", "phasorangle");
50
48
  break */
51
49
  case "\\angl":
52
- node.setAttribute("notation", "actuarial")
53
50
  node.style.padding = "0.03889em 0.03889em 0 0.03889em"
54
51
  node.style.borderTop = "0.049em solid"
55
52
  node.style.borderRight = "0.049em solid"
56
53
  node.style.marginRight = "0.03889em"
57
54
  break
58
55
  case "\\sout":
59
- node.setAttribute("notation", "horizontalstrike");
60
56
  node.style["text-decoration"] = "line-through 0.08em solid"
61
57
  break
58
+ case "\\boxed":
59
+ // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty
60
+ node.style = { padding: "3pt 0 3pt 0", border: "1px solid" }
61
+ node.setAttribute("scriptlevel", "0")
62
+ node.setAttribute("displaystyle", "true")
63
+ break
62
64
  case "\\fbox":
63
- node.setAttribute("notation", "box");
64
65
  node.style = { padding: "3pt", border: "1px solid" }
65
66
  break
66
67
  case "\\fcolorbox":
@@ -80,7 +81,6 @@ const mathmlBuilder = (group, style) => {
80
81
  break
81
82
  }
82
83
  case "\\xcancel":
83
- node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
84
84
  node.classes.push("xcancel")
85
85
  break
86
86
  }
@@ -175,7 +175,7 @@ defineFunction({
175
175
 
176
176
  defineFunction({
177
177
  type: "enclose",
178
- names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline"],
178
+ names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", "\\boxed"],
179
179
  // , "\\phase", "\\longdiv"
180
180
  props: {
181
181
  numArgs: 1