katex 0.10.0-rc → 0.10.2

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.
Files changed (144) hide show
  1. package/CHANGELOG.md +214 -126
  2. package/README.md +18 -17
  3. package/cli.js +5 -1
  4. package/contrib/auto-render/README.md +1 -1
  5. package/contrib/auto-render/auto-render.js +4 -1
  6. package/contrib/auto-render/test/auto-render-spec.js +17 -0
  7. package/contrib/copy-tex/README.md +8 -2
  8. package/contrib/copy-tex/copy-tex.js +0 -1
  9. package/contrib/copy-tex/copy-tex.webpack.js +6 -0
  10. package/contrib/mathtex-script-type/README.md +10 -6
  11. package/contrib/mhchem/README.md +19 -0
  12. package/contrib/mhchem/mhchem.js +1695 -0
  13. package/contrib/mhchem/mhchem.patch +235 -0
  14. package/dist/README.md +18 -17
  15. package/dist/contrib/auto-render.js +179 -161
  16. package/dist/contrib/auto-render.min.js +1 -1
  17. package/dist/contrib/auto-render.mjs +215 -0
  18. package/dist/contrib/copy-tex.js +84 -62
  19. package/dist/contrib/copy-tex.min.css +1 -1
  20. package/dist/contrib/copy-tex.min.js +1 -1
  21. package/dist/contrib/copy-tex.mjs +85 -0
  22. package/dist/contrib/mathtex-script-type.js +17 -14
  23. package/dist/contrib/mathtex-script-type.mjs +24 -0
  24. package/dist/contrib/mhchem.js +3241 -0
  25. package/dist/contrib/mhchem.min.js +1 -0
  26. package/dist/contrib/mhchem.mjs +3109 -0
  27. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  28. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  29. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  30. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  31. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  32. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  33. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  34. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  35. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  36. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  37. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  38. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  39. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  40. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  41. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  42. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  43. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  44. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  45. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  46. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  47. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  48. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  49. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  50. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  51. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  52. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  53. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  54. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  55. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  56. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  57. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  58. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  59. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  60. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  61. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  62. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  63. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  64. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  65. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  66. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  67. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  68. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  69. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  70. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  71. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  72. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  73. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  74. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  75. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  76. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  77. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  78. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  79. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  80. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  81. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  82. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  83. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  84. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  85. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  86. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  87. package/dist/katex.css +24 -9
  88. package/dist/katex.js +13295 -12413
  89. package/dist/katex.min.css +1 -1
  90. package/dist/katex.min.js +1 -1
  91. package/dist/katex.mjs +13388 -11826
  92. package/katex.js +1 -2
  93. package/package.json +60 -48
  94. package/src/Lexer.js +25 -25
  95. package/src/MacroExpander.js +0 -1
  96. package/src/Options.js +11 -75
  97. package/src/Parser.js +231 -313
  98. package/src/Settings.js +6 -0
  99. package/src/buildCommon.js +140 -103
  100. package/src/buildHTML.js +125 -121
  101. package/src/buildMathML.js +14 -4
  102. package/src/buildTree.js +16 -10
  103. package/src/delimiter.js +4 -3
  104. package/src/domTree.js +91 -44
  105. package/src/environments/array.js +120 -7
  106. package/src/fontMetrics.js +3 -2
  107. package/src/functions/arrow.js +21 -7
  108. package/src/functions/color.js +2 -37
  109. package/src/functions/delimsizing.js +18 -11
  110. package/src/functions/enclose.js +19 -4
  111. package/src/functions/environment.js +35 -4
  112. package/src/functions/font.js +1 -2
  113. package/src/functions/genfrac.js +35 -20
  114. package/src/functions/href.js +5 -3
  115. package/src/functions/includegraphics.js +146 -0
  116. package/src/functions/mclass.js +1 -0
  117. package/src/functions/op.js +21 -32
  118. package/src/functions/operatorname.js +1 -2
  119. package/src/functions/ordgroup.js +4 -0
  120. package/src/functions/phantom.js +7 -3
  121. package/src/functions/rule.js +20 -9
  122. package/src/functions/sizing.js +2 -4
  123. package/src/functions/smash.js +5 -2
  124. package/src/functions/sqrt.js +1 -4
  125. package/src/functions/styling.js +0 -1
  126. package/src/functions/supsub.js +6 -2
  127. package/src/functions/symbolsOp.js +4 -0
  128. package/src/functions/symbolsSpacing.js +29 -6
  129. package/src/functions/tag.js +20 -4
  130. package/src/functions/text.js +6 -4
  131. package/src/functions/verb.js +16 -4
  132. package/src/functions.js +2 -0
  133. package/src/katex.less +35 -12
  134. package/src/macros.js +161 -36
  135. package/src/mathMLTree.js +17 -19
  136. package/src/parseNode.js +27 -1
  137. package/src/stretchy.js +3 -1
  138. package/src/svgGeometry.js +1 -1
  139. package/src/symbols.js +39 -17
  140. package/src/tree.js +0 -4
  141. package/src/types.js +4 -3
  142. package/src/unicodeMake.js +1 -1
  143. package/src/utils.js +1 -62
  144. package/src/wide-character.js +2 -2
@@ -0,0 +1,146 @@
1
+ // @flow
2
+ import defineFunction from "../defineFunction";
3
+ import type {Measurement} from "../units";
4
+ import {calculateSize, validUnit} from "../units";
5
+ import ParseError from "../ParseError";
6
+ import {Img} from "../domTree";
7
+ import mathMLTree from "../mathMLTree";
8
+ import {assertNodeType} from "../parseNode";
9
+ import type {CssStyle} from "../domTree";
10
+
11
+ const sizeData = function(str: string): Measurement {
12
+ if (/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(str)) {
13
+ // str is a number with no unit specified.
14
+ // default unit is bp, per graphix package.
15
+ return {number: +str, unit: "bp"};
16
+ } else {
17
+ const match = (/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(str);
18
+ if (!match) {
19
+ throw new ParseError("Invalid size: '" + str
20
+ + "' in \\includegraphics");
21
+ }
22
+ const data = {
23
+ number: +(match[1] + match[2]), // sign + magnitude, cast to number
24
+ unit: match[3],
25
+ };
26
+ if (!validUnit(data)) {
27
+ throw new ParseError("Invalid unit: '" + data.unit
28
+ + "' in \\includegraphics.");
29
+ }
30
+ return data;
31
+ }
32
+ };
33
+
34
+ defineFunction({
35
+ type: "includegraphics",
36
+ names: ["\\includegraphics"],
37
+ props: {
38
+ numArgs: 1,
39
+ numOptionalArgs: 1,
40
+ argTypes: ["raw", "url"],
41
+ allowedInText: false,
42
+ },
43
+ handler: ({parser}, args, optArgs) => {
44
+ let width = {number: 0, unit: "em"};
45
+ let height = {number: 0.9, unit: "em"}; // sorta character sized.
46
+ let totalheight = {number: 0, unit: "em"};
47
+ let alt = "";
48
+
49
+ if (optArgs[0]) {
50
+ const attributeStr = assertNodeType(optArgs[0], "raw").string;
51
+
52
+ // Parser.js does not parse key/value pairs. We get a string.
53
+ const attributes = attributeStr.split(",");
54
+ for (let i = 0; i < attributes.length; i++) {
55
+ const keyVal = attributes[i].split("=");
56
+ if (keyVal.length === 2) {
57
+ const str = keyVal[1].trim();
58
+ switch (keyVal[0].trim()) {
59
+ case "alt":
60
+ alt = str;
61
+ break;
62
+ case "width":
63
+ width = sizeData(str);
64
+ break;
65
+ case "height":
66
+ height = sizeData(str);
67
+ break;
68
+ case "totalheight":
69
+ totalheight = sizeData(str);
70
+ break;
71
+ default:
72
+ throw new ParseError("Invalid key: '" + keyVal[0] +
73
+ "' in \\includegraphics.");
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ const src = assertNodeType(args[0], "url").url;
80
+
81
+ if (alt === "") {
82
+ // No alt given. Use the file name. Strip away the path.
83
+ alt = src;
84
+ alt = alt.replace(/^.*[\\/]/, '');
85
+ alt = alt.substring(0, alt.lastIndexOf('.'));
86
+ }
87
+
88
+ return {
89
+ type: "includegraphics",
90
+ mode: parser.mode,
91
+ alt: alt,
92
+ width: width,
93
+ height: height,
94
+ totalheight: totalheight,
95
+ src: src,
96
+ };
97
+ },
98
+ htmlBuilder: (group, options) => {
99
+ const height = calculateSize(group.height, options);
100
+ let depth = 0;
101
+
102
+ if (group.totalheight.number > 0) {
103
+ depth = calculateSize(group.totalheight, options) - height;
104
+ depth = Number(depth.toFixed(2));
105
+ }
106
+
107
+ let width = 0;
108
+ if (group.width.number > 0) {
109
+ width = calculateSize(group.width, options);
110
+ }
111
+
112
+ const style: CssStyle = {height: height + depth + "em"};
113
+ if (width > 0) {
114
+ style.width = width + "em";
115
+ }
116
+ if (depth > 0) {
117
+ style.verticalAlign = -depth + "em";
118
+ }
119
+
120
+ const node = new Img(group.src, group.alt, style);
121
+ node.height = height;
122
+ node.depth = depth;
123
+
124
+ return node;
125
+ },
126
+ mathmlBuilder: (group, options) => {
127
+ const node = new mathMLTree.MathNode("mglyph", []);
128
+ node.setAttribute("alt", group.alt);
129
+
130
+ const height = calculateSize(group.height, options);
131
+ let depth = 0;
132
+ if (group.totalheight.number > 0) {
133
+ depth = calculateSize(group.totalheight, options) - height;
134
+ depth = depth.toFixed(2);
135
+ node.setAttribute("valign", "-" + depth + "em");
136
+ }
137
+ node.setAttribute("height", height + depth + "em");
138
+
139
+ if (group.width.number > 0) {
140
+ const width = calculateSize(group.width, options);
141
+ node.setAttribute("width", width + "em");
142
+ }
143
+ node.setAttribute("src", group.src);
144
+ return node;
145
+ },
146
+ });
@@ -99,6 +99,7 @@ defineFunction({
99
99
  mode: baseArg.mode,
100
100
  limits: true,
101
101
  alwaysHandleSupSub: true,
102
+ parentIsSupSub: false,
102
103
  symbol: false,
103
104
  suppressBaseShift: funcName !== "\\stackrel",
104
105
  body: ordargument(baseArg),
@@ -14,6 +14,11 @@ import * as mml from "../buildMathML";
14
14
  import type {HtmlBuilderSupSub, MathMLBuilder} from "../defineFunction";
15
15
  import type {ParseNode} from "../parseNode";
16
16
 
17
+ // Most operators have a large successor symbol, but these don't.
18
+ const noSuccessor = [
19
+ "\\smallint",
20
+ ];
21
+
17
22
  // NOTE: Unlike most `htmlBuilder`s, this one handles not only "op", but also
18
23
  // "supsub" since some of them (like \int) can affect super/subscripting.
19
24
  export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
@@ -37,11 +42,6 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
37
42
 
38
43
  const style = options.style;
39
44
 
40
- // Most operators have a large successor symbol, but these don't.
41
- const noSuccessor = [
42
- "\\smallint",
43
- ];
44
-
45
45
  let large = false;
46
46
  if (style.size === Style.DISPLAY.size &&
47
47
  group.symbol &&
@@ -95,7 +95,8 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
95
95
  base = inner[0];
96
96
  base.classes[0] = "mop"; // replace old mclass
97
97
  } else {
98
- base = buildCommon.makeSpan(["mop"], inner, options);
98
+ base = buildCommon.makeSpan(
99
+ ["mop"], buildCommon.tryCombineChars(inner), options);
99
100
  }
100
101
  } else {
101
102
  // Otherwise, this is a text operator. Build the text from the
@@ -236,12 +237,13 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
236
237
  const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
237
238
  let node;
238
239
 
239
- // TODO(emily): handle big operators using the `largeop` attribute
240
-
241
240
  if (group.symbol) {
242
241
  // This is a symbol. Just add the symbol.
243
242
  node = new mathMLTree.MathNode(
244
243
  "mo", [mml.makeText(group.name, group.mode)]);
244
+ if (utils.contains(noSuccessor, group.name)) {
245
+ node.setAttribute("largeop", "false");
246
+ }
245
247
  } else if (group.body) {
246
248
  // This is an operator with children. Add them.
247
249
  node = new mathMLTree.MathNode(
@@ -253,13 +255,15 @@ const mathmlBuilder: MathMLBuilder<"op"> = (group, options) => {
253
255
  // operators, like \limsup.
254
256
  node = new mathMLTree.MathNode(
255
257
  "mi", [new mathMLTree.TextNode(group.name.slice(1))]);
256
-
257
258
  // Append an <mo>&ApplyFunction;</mo>.
258
259
  // ref: https://www.w3.org/TR/REC-MathML/chap3_2.html#sec3.2.4
259
260
  const operator = new mathMLTree.MathNode("mo",
260
261
  [mml.makeText("\u2061", "text")]);
261
-
262
- return mathMLTree.newDocumentFragment([node, operator]);
262
+ if (group.parentIsSupSub) {
263
+ node = new mathMLTree.MathNode("mo", [node, operator]);
264
+ } else {
265
+ node = mathMLTree.newDocumentFragment([node, operator]);
266
+ }
263
267
  }
264
268
 
265
269
  return node;
@@ -272,7 +276,7 @@ const singleCharBigOps: {[string]: string} = {
272
276
  "\u22c0": "\\bigwedge",
273
277
  "\u22c1": "\\bigvee",
274
278
  "\u22c2": "\\bigcap",
275
- "\u22c3": "\\bigcap",
279
+ "\u22c3": "\\bigcup",
276
280
  "\u2a00": "\\bigodot",
277
281
  "\u2a01": "\\bigoplus",
278
282
  "\u2a02": "\\bigotimes",
@@ -301,6 +305,7 @@ defineFunction({
301
305
  type: "op",
302
306
  mode: parser.mode,
303
307
  limits: true,
308
+ parentIsSupSub: false,
304
309
  symbol: true,
305
310
  name: fName,
306
311
  };
@@ -323,6 +328,7 @@ defineFunction({
323
328
  type: "op",
324
329
  mode: parser.mode,
325
330
  limits: false,
331
+ parentIsSupSub: false,
326
332
  symbol: false,
327
333
  body: ordargument(body),
328
334
  };
@@ -344,26 +350,6 @@ const singleCharIntegrals: {[string]: string} = {
344
350
  "\u2230": "\\oiiint",
345
351
  };
346
352
 
347
- defineFunction({
348
- type: "op",
349
- names: ["\\mathop"],
350
- props: {
351
- numArgs: 1,
352
- },
353
- handler: ({parser}, args) => {
354
- const body = args[0];
355
- return {
356
- type: "op",
357
- mode: parser.mode,
358
- limits: false,
359
- symbol: false,
360
- body: ordargument(body),
361
- };
362
- },
363
- htmlBuilder,
364
- mathmlBuilder,
365
- });
366
-
367
353
  // No limits, not symbols
368
354
  defineFunction({
369
355
  type: "op",
@@ -382,6 +368,7 @@ defineFunction({
382
368
  type: "op",
383
369
  mode: parser.mode,
384
370
  limits: false,
371
+ parentIsSupSub: false,
385
372
  symbol: false,
386
373
  name: funcName,
387
374
  };
@@ -404,6 +391,7 @@ defineFunction({
404
391
  type: "op",
405
392
  mode: parser.mode,
406
393
  limits: true,
394
+ parentIsSupSub: false,
407
395
  symbol: false,
408
396
  name: funcName,
409
397
  };
@@ -431,6 +419,7 @@ defineFunction({
431
419
  type: "op",
432
420
  mode: parser.mode,
433
421
  limits: false,
422
+ parentIsSupSub: false,
434
423
  symbol: true,
435
424
  name: fName,
436
425
  };
@@ -101,8 +101,7 @@ defineFunction({
101
101
  if (isAllString) {
102
102
  // Write a single TextNode instead of multiple nested tags.
103
103
  const word = expression.map(node => node.toText()).join("");
104
- // word has already been escaped by `node.toText()`
105
- expression = [new mathMLTree.TextNode(word, false)];
104
+ expression = [new mathMLTree.TextNode(word)];
106
105
  }
107
106
 
108
107
  const identifier = new mathMLTree.MathNode("mi", expression);
@@ -8,6 +8,10 @@ import * as mml from "../buildMathML";
8
8
  defineFunctionBuilders({
9
9
  type: "ordgroup",
10
10
  htmlBuilder(group, options) {
11
+ if (group.semisimple) {
12
+ return buildCommon.makeFragment(
13
+ html.buildExpression(group.body, options, false));
14
+ }
11
15
  return buildCommon.makeSpan(
12
16
  ["mord"], html.buildExpression(group.body, options, true), options);
13
17
  },
@@ -71,12 +71,15 @@ defineFunction({
71
71
  children: [{type: "elem", elem: node}],
72
72
  }, options);
73
73
 
74
- return node;
74
+ // For spacing, TeX treats \smash as a math group (same spacing as ord).
75
+ return buildCommon.makeSpan(["mord"], [node], options);
75
76
  },
76
77
  mathmlBuilder: (group, options) => {
77
78
  const inner = mml.buildExpression(ordargument(group.body), options);
78
- const node = new mathMLTree.MathNode("mphantom", inner);
79
+ const phantom = new mathMLTree.MathNode("mphantom", inner);
80
+ const node = new mathMLTree.MathNode("mpadded", [phantom]);
79
81
  node.setAttribute("height", "0px");
82
+ node.setAttribute("depth", "0px");
80
83
  return node;
81
84
  },
82
85
  });
@@ -106,7 +109,8 @@ defineFunction({
106
109
  },
107
110
  mathmlBuilder: (group, options) => {
108
111
  const inner = mml.buildExpression(ordargument(group.body), options);
109
- const node = new mathMLTree.MathNode("mphantom", inner);
112
+ const phantom = new mathMLTree.MathNode("mphantom", inner);
113
+ const node = new mathMLTree.MathNode("mpadded", [phantom]);
110
114
  node.setAttribute("width", "0px");
111
115
  return node;
112
116
  },
@@ -30,13 +30,9 @@ defineFunction({
30
30
  const rule = buildCommon.makeSpan(["mord", "rule"], [], options);
31
31
 
32
32
  // Calculate the shift, width, and height of the rule, and account for units
33
- let shift = 0;
34
- if (group.shift) {
35
- shift = calculateSize(group.shift, options);
36
- }
37
-
38
33
  const width = calculateSize(group.width, options);
39
34
  const height = calculateSize(group.height, options);
35
+ const shift = (group.shift) ? calculateSize(group.shift, options) : 0;
40
36
 
41
37
  // Style the rule to the right size
42
38
  rule.style.borderRightWidth = width + "em";
@@ -55,10 +51,25 @@ defineFunction({
55
51
  return rule;
56
52
  },
57
53
  mathmlBuilder(group, options) {
58
- // TODO(emily): Figure out if there's an actual way to draw black boxes
59
- // in MathML.
60
- const node = new mathMLTree.MathNode("mrow");
54
+ const width = calculateSize(group.width, options);
55
+ const height = calculateSize(group.height, options);
56
+ const shift = (group.shift) ? calculateSize(group.shift, options) : 0;
57
+ const color = options.color && options.getColor() || "black";
58
+
59
+ const rule = new mathMLTree.MathNode("mspace");
60
+ rule.setAttribute("mathbackground", color);
61
+ rule.setAttribute("width", width + "em");
62
+ rule.setAttribute("height", height + "em");
63
+
64
+ const wrapper = new mathMLTree.MathNode("mpadded", [rule]);
65
+ if (shift >= 0) {
66
+ wrapper.setAttribute("height", "+" + shift + "em");
67
+ } else {
68
+ wrapper.setAttribute("height", shift + "em");
69
+ wrapper.setAttribute("depth", "+" + (-shift) + "em");
70
+ }
71
+ wrapper.setAttribute("voffset", shift + "em");
61
72
 
62
- return node;
73
+ return wrapper;
63
74
  },
64
75
  });
@@ -2,7 +2,6 @@
2
2
  import buildCommon from "../buildCommon";
3
3
  import defineFunction from "../defineFunction";
4
4
  import mathMLTree from "../mathMLTree";
5
- import utils from "../utils";
6
5
 
7
6
  import * as html from "../buildHTML";
8
7
  import * as mml from "../buildMathML";
@@ -23,7 +22,7 @@ export function sizingGroup(
23
22
  // Add size-resetting classes to the inner list and set maxFontSize
24
23
  // manually. Handle nested size changes.
25
24
  for (let i = 0; i < inner.length; i++) {
26
- const pos = utils.indexOf(inner[i].classes, "sizing");
25
+ const pos = inner[i].classes.indexOf("sizing");
27
26
  if (pos < 0) {
28
27
  Array.prototype.push.apply(inner[i].classes,
29
28
  options.sizingClasses(baseOptions));
@@ -62,14 +61,13 @@ defineFunction({
62
61
  allowedInText: true,
63
62
  },
64
63
  handler: ({breakOnTokenText, funcName, parser}, args) => {
65
- parser.consumeSpaces();
66
64
  const body = parser.parseExpression(false, breakOnTokenText);
67
65
 
68
66
  return {
69
67
  type: "sizing",
70
68
  mode: parser.mode,
71
69
  // Figure out what size to use based on the list of functions above
72
- size: utils.indexOf(sizeFuncs, funcName) + 1,
70
+ size: sizeFuncs.indexOf(funcName) + 1,
73
71
  body,
74
72
  };
75
73
  },
@@ -55,7 +55,7 @@ defineFunction({
55
55
  },
56
56
  htmlBuilder: (group, options) => {
57
57
  const node = buildCommon.makeSpan(
58
- ["mord"], [html.buildGroup(group.body, options)]);
58
+ [], [html.buildGroup(group.body, options)]);
59
59
 
60
60
  if (!group.smashHeight && !group.smashDepth) {
61
61
  return node;
@@ -85,10 +85,13 @@ defineFunction({
85
85
  // makeVList applies "display: table-cell", which prevents the browser
86
86
  // from acting on that line height. So we'll call makeVList now.
87
87
 
88
- return buildCommon.makeVList({
88
+ const smashedNode = buildCommon.makeVList({
89
89
  positionType: "firstBaseline",
90
90
  children: [{type: "elem", elem: node}],
91
91
  }, options);
92
+
93
+ // For spacing, TeX treats \hphantom as a math group (same spacing as ord).
94
+ return buildCommon.makeSpan(["mord"], [smashedNode], options);
92
95
  },
93
96
  mathmlBuilder: (group, options) => {
94
97
  const node = new mathMLTree.MathNode(
@@ -5,7 +5,6 @@ import mathMLTree from "../mathMLTree";
5
5
  import delimiter from "../delimiter";
6
6
  import Style from "../Style";
7
7
 
8
- import {DocumentFragment} from "../tree";
9
8
  import * as html from "../buildHTML";
10
9
  import * as mml from "../buildMathML";
11
10
 
@@ -39,9 +38,7 @@ defineFunction({
39
38
 
40
39
  // Some groups can return document fragments. Handle those by wrapping
41
40
  // them in a span.
42
- if (inner instanceof DocumentFragment) {
43
- inner = buildCommon.makeSpan([], [inner], options);
44
- }
41
+ inner = buildCommon.wrapFragment(inner, options);
45
42
 
46
43
  // Calculate the minimum size for the \surd delimiter
47
44
  const metrics = options.fontMetrics();
@@ -25,7 +25,6 @@ defineFunction({
25
25
  },
26
26
  handler({breakOnTokenText, funcName, parser}, args) {
27
27
  // parse out the implicit body
28
- parser.consumeSpaces();
29
28
  const body = parser.parseExpression(true, breakOnTokenText);
30
29
 
31
30
  // TODO: Refactor to avoid duplicating styleMap in multiple places (e.g.
@@ -201,6 +201,10 @@ defineFunctionBuilders({
201
201
  }
202
202
  }
203
203
 
204
+ if (group.base && group.base.type === "op") {
205
+ group.base.parentIsSupSub = true;
206
+ }
207
+
204
208
  const children = [mml.buildGroup(group.base, options)];
205
209
 
206
210
  if (group.sub) {
@@ -217,7 +221,7 @@ defineFunctionBuilders({
217
221
  } else if (!group.sub) {
218
222
  const base = group.base;
219
223
  if (base && base.type === "op" && base.limits &&
220
- options.style === Style.DISPLAY) {
224
+ (options.style === Style.DISPLAY || base.alwaysHandleSupSub)) {
221
225
  nodeType = "mover";
222
226
  } else {
223
227
  nodeType = "msup";
@@ -225,7 +229,7 @@ defineFunctionBuilders({
225
229
  } else if (!group.sup) {
226
230
  const base = group.base;
227
231
  if (base && base.type === "op" && base.limits &&
228
- options.style === Style.DISPLAY) {
232
+ (options.style === Style.DISPLAY || base.alwaysHandleSupSub)) {
229
233
  nodeType = "munder";
230
234
  } else {
231
235
  nodeType = "msub";
@@ -23,6 +23,10 @@ defineFunctionBuilders({
23
23
  }
24
24
  } else if (group.family === "punct") {
25
25
  node.setAttribute("separator", "true");
26
+ } else if (group.family === "open" || group.family === "close") {
27
+ // Delims built here should not stretch vertically.
28
+ // See delimsizing.js for stretchy delims.
29
+ node.setAttribute("stretchy", "false");
26
30
  }
27
31
  return node;
28
32
  },
@@ -4,13 +4,36 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import ParseError from "../ParseError";
6
6
 
7
+ // A map of CSS-based spacing functions to their CSS class.
8
+ const cssSpace: {[string]: string} = {
9
+ "\\nobreak": "nobreak",
10
+ "\\allowbreak": "allowbreak",
11
+ };
12
+
13
+ // A lookup table to determine whether a spacing function/symbol should be
14
+ // treated like a regular space character. If a symbol or command is a key
15
+ // in this table, then it should be a regular space character. Furthermore,
16
+ // the associated value may have a `className` specifying an extra CSS class
17
+ // to add to the created `span`.
18
+ const regularSpace: {[string]: { className?: string }} = {
19
+ " ": {},
20
+ "\\ ": {},
21
+ "~": {
22
+ className: "nobreak",
23
+ },
24
+ "\\space": {},
25
+ "\\nobreakspace": {
26
+ className: "nobreak",
27
+ },
28
+ };
29
+
7
30
  // ParseNode<"spacing"> created in Parser.js from the "spacing" symbol Groups in
8
31
  // src/symbols.js.
9
32
  defineFunctionBuilders({
10
33
  type: "spacing",
11
34
  htmlBuilder(group, options) {
12
- if (buildCommon.regularSpace.hasOwnProperty(group.text)) {
13
- const className = buildCommon.regularSpace[group.text].className || "";
35
+ if (regularSpace.hasOwnProperty(group.text)) {
36
+ const className = regularSpace[group.text].className || "";
14
37
  // Spaces are generated by adding an actual space. Each of these
15
38
  // things has an entry in the symbols table, so these will be turned
16
39
  // into appropriate outputs.
@@ -23,10 +46,10 @@ defineFunctionBuilders({
23
46
  [buildCommon.mathsym(group.text, group.mode, options)],
24
47
  options);
25
48
  }
26
- } else if (buildCommon.cssSpace.hasOwnProperty(group.text)) {
49
+ } else if (cssSpace.hasOwnProperty(group.text)) {
27
50
  // Spaces based on just a CSS class.
28
51
  return buildCommon.makeSpan(
29
- ["mspace", buildCommon.cssSpace[group.text]],
52
+ ["mspace", cssSpace[group.text]],
30
53
  [], options);
31
54
  } else {
32
55
  throw new ParseError(`Unknown type of space "${group.text}"`);
@@ -35,10 +58,10 @@ defineFunctionBuilders({
35
58
  mathmlBuilder(group, options) {
36
59
  let node;
37
60
 
38
- if (buildCommon.regularSpace.hasOwnProperty(group.text)) {
61
+ if (regularSpace.hasOwnProperty(group.text)) {
39
62
  node = new mathMLTree.MathNode(
40
63
  "mtext", [new mathMLTree.TextNode("\u00a0")]);
41
- } else if (buildCommon.cssSpace.hasOwnProperty(group.text)) {
64
+ } else if (cssSpace.hasOwnProperty(group.text)) {
42
65
  // CSS-based MathML spaces (\nobreak, \allowbreak) are ignored
43
66
  return new mathMLTree.MathNode("mspace");
44
67
  } else {
@@ -4,21 +4,37 @@ import mathMLTree from "../mathMLTree";
4
4
 
5
5
  import * as mml from "../buildMathML";
6
6
 
7
+ const pad = () => {
8
+ const padNode = new mathMLTree.MathNode("mtd", []);
9
+ padNode.setAttribute("width", "50%");
10
+ return padNode;
11
+ };
12
+
7
13
  defineFunctionBuilders({
8
14
  type: "tag",
9
15
  mathmlBuilder(group, options) {
10
16
  const table = new mathMLTree.MathNode("mtable", [
11
- new mathMLTree.MathNode("mlabeledtr", [
17
+ new mathMLTree.MathNode("mtr", [
18
+ pad(),
12
19
  new mathMLTree.MathNode("mtd", [
13
- mml.buildExpressionRow(group.tag, options),
20
+ mml.buildExpressionRow(group.body, options),
14
21
  ]),
22
+ pad(),
15
23
  new mathMLTree.MathNode("mtd", [
16
- mml.buildExpressionRow(group.body, options),
24
+ mml.buildExpressionRow(group.tag, options),
17
25
  ]),
18
26
  ]),
19
27
  ]);
20
- table.setAttribute("side", "right");
28
+ table.setAttribute("width", "100%");
21
29
  return table;
30
+
31
+ // TODO: Left-aligned tags.
32
+ // Currently, the group and options passed here do not contain
33
+ // enough info to set tag alignment. `leqno` is in Settings but it is
34
+ // not passed to Options. On the HTML side, leqno is
35
+ // set by a CSS class applied in buildTree.js. That would have worked
36
+ // in MathML if browsers supported <mlabeledtr>. Since they don't, we
37
+ // need to rewrite the way this function is called.
22
38
  },
23
39
  });
24
40
 
@@ -13,10 +13,12 @@ const textFontFamilies = {
13
13
 
14
14
  const textFontWeights = {
15
15
  "\\textbf": "textbf",
16
+ "\\textmd": "textmd",
16
17
  };
17
18
 
18
19
  const textFontShapes = {
19
20
  "\\textit": "textit",
21
+ "\\textup": "textup",
20
22
  };
21
23
 
22
24
  const optionsWithFont = (group, options) => {
@@ -39,9 +41,9 @@ defineFunction({
39
41
  // Font families
40
42
  "\\text", "\\textrm", "\\textsf", "\\texttt", "\\textnormal",
41
43
  // Font weights
42
- "\\textbf",
44
+ "\\textbf", "\\textmd",
43
45
  // Font Shapes
44
- "\\textit",
46
+ "\\textit", "\\textup",
45
47
  ],
46
48
  props: {
47
49
  numArgs: 1,
@@ -62,8 +64,8 @@ defineFunction({
62
64
  htmlBuilder(group, options) {
63
65
  const newOptions = optionsWithFont(group, options);
64
66
  const inner = html.buildExpression(group.body, newOptions, true);
65
- buildCommon.tryCombineChars(inner);
66
- return buildCommon.makeSpan(["mord", "text"], inner, newOptions);
67
+ return buildCommon.makeSpan(
68
+ ["mord", "text"], buildCommon.tryCombineChars(inner), newOptions);
67
69
  },
68
70
  mathmlBuilder(group, options) {
69
71
  const newOptions = optionsWithFont(group, options);