katex 0.11.1 → 0.12.0

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 (116) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +1 -1
  3. package/README.md +5 -5
  4. package/contrib/auto-render/auto-render.js +12 -3
  5. package/contrib/copy-tex/README.md +3 -5
  6. package/contrib/mathtex-script-type/README.md +12 -14
  7. package/contrib/mhchem/README.md +1 -1
  8. package/contrib/render-a11y-string/render-a11y-string.js +10 -0
  9. package/dist/README.md +5 -5
  10. package/dist/contrib/auto-render.js +14 -3
  11. package/dist/contrib/auto-render.min.js +1 -1
  12. package/dist/contrib/auto-render.mjs +14 -3
  13. package/dist/contrib/mhchem.min.js +1 -1
  14. package/dist/contrib/render-a11y-string.js +12 -0
  15. package/dist/contrib/render-a11y-string.min.js +1 -1
  16. package/dist/contrib/render-a11y-string.mjs +12 -0
  17. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  18. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  19. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  20. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  21. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  22. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  23. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  24. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  25. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  26. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  27. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  28. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  29. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  30. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  31. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  32. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  33. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  34. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  35. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  36. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  37. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  38. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  39. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  40. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  41. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  42. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  43. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  44. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  45. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  46. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  47. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  48. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  49. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  50. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  51. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  52. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  53. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  54. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  55. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  56. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  57. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  58. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  59. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  60. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  61. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  62. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  63. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  64. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  65. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  66. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  67. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  68. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  69. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  70. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  71. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  72. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  73. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  74. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  75. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  76. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  77. package/dist/katex.css +25 -2
  78. package/dist/katex.js +1140 -1257
  79. package/dist/katex.min.css +1 -1
  80. package/dist/katex.min.js +1 -1
  81. package/dist/katex.mjs +1119 -1229
  82. package/package.json +9 -9
  83. package/src/MacroExpander.js +39 -10
  84. package/src/Parser.js +28 -24
  85. package/src/Settings.js +19 -0
  86. package/src/Token.js +2 -0
  87. package/src/buildCommon.js +16 -82
  88. package/src/buildHTML.js +29 -30
  89. package/src/buildMathML.js +5 -1
  90. package/src/buildTree.js +3 -2
  91. package/src/defineFunction.js +1 -4
  92. package/src/delimiter.js +14 -3
  93. package/src/environments/array.js +11 -9
  94. package/src/functions/accent.js +6 -7
  95. package/src/functions/accentunder.js +2 -2
  96. package/src/functions/def.js +184 -0
  97. package/src/functions/delimsizing.js +5 -4
  98. package/src/functions/font.js +1 -1
  99. package/src/functions/genfrac.js +8 -15
  100. package/src/functions/horizBrace.js +6 -7
  101. package/src/functions/html.js +102 -0
  102. package/src/functions/lap.js +4 -7
  103. package/src/functions/op.js +5 -6
  104. package/src/functions/operatorname.js +5 -6
  105. package/src/functions/supsub.js +3 -5
  106. package/src/functions/utils/assembleSupSub.js +0 -2
  107. package/src/functions.js +2 -0
  108. package/src/katex.less +29 -1
  109. package/src/macros.js +97 -56
  110. package/src/parseNode.js +14 -56
  111. package/src/svgGeometry.js +4 -0
  112. package/src/symbols.js +36 -53
  113. package/src/unicodeAccents.js +3 -1
  114. package/src/unicodeSymbols.js +30 -321
  115. package/src/wide-character.js +2 -2
  116. package/src/unicodeMake.js +0 -70
package/src/delimiter.js CHANGED
@@ -375,9 +375,20 @@ const makeStackedDelim = function(
375
375
 
376
376
  // To cover the gap create by the overlaps, insert one more repeat element,
377
377
  // at a position that juts 0.005 above the bottom of the top element.
378
- inners.push({type: "kern", size: shiftOfExtraElement});
379
- inners.push(makeInner(repeat, font, mode));
380
- inners.push(lap);
378
+ if ((repeat === "\u239c" || repeat === "\u239f") && repeatCount === 0) {
379
+ // Parentheses need a short repeat element in order to avoid an overrun.
380
+ // We'll make a 0.3em tall element from a SVG.
381
+ const overlap = buildCommon.svgData.leftParenInner[2] / 2;
382
+ inners.push({type: "kern", size: -overlap});
383
+ const pathName = repeat === "\u239c" ? "leftParenInner" : "rightParenInner";
384
+ const innerSpan = buildCommon.staticSvg(pathName, options);
385
+ inners.push({type: "elem", elem: innerSpan});
386
+ inners.push({type: "kern", size: -overlap});
387
+ } else {
388
+ inners.push({type: "kern", size: shiftOfExtraElement});
389
+ inners.push(makeInner(repeat, font, mode));
390
+ inners.push(lap);
391
+ }
381
392
 
382
393
  // Add the top symbol
383
394
  inners.push(makeInner(top, font, mode));
@@ -6,7 +6,7 @@ import defineFunction from "../defineFunction";
6
6
  import mathMLTree from "../mathMLTree";
7
7
  import ParseError from "../ParseError";
8
8
  import {assertNodeType, assertSymbolNodeType} from "../parseNode";
9
- import {checkNodeType, checkSymbolNodeType} from "../parseNode";
9
+ import {checkSymbolNodeType} from "../parseNode";
10
10
  import {calculateSize} from "../units";
11
11
  import utils from "../utils";
12
12
 
@@ -435,7 +435,7 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
435
435
  let menclose = "";
436
436
  let align = "";
437
437
 
438
- if (group.cols) {
438
+ if (group.cols && group.cols.length > 0) {
439
439
  // Find column alignment, column spacing, and vertical lines.
440
440
  const cols = group.cols;
441
441
  let columnLines = "";
@@ -547,11 +547,10 @@ const alignedHandler = function(context, args) {
547
547
  mode: context.mode,
548
548
  body: [],
549
549
  };
550
- const ordgroup = checkNodeType(args[0], "ordgroup");
551
- if (ordgroup) {
550
+ if (args[0] && args[0].type === "ordgroup") {
552
551
  let arg0 = "";
553
- for (let i = 0; i < ordgroup.body.length; i++) {
554
- const textord = assertNodeType(ordgroup.body[i], "textord");
552
+ for (let i = 0; i < args[0].body.length; i++) {
553
+ const textord = assertNodeType(args[0].body[i], "textord");
555
554
  arg0 += textord.text;
556
555
  }
557
556
  numMaths = Number(arg0);
@@ -738,7 +737,7 @@ defineEnvironment({
738
737
  arraystretch: 0.5,
739
738
  };
740
739
  res = parseArray(context.parser, res, "script");
741
- if (res.body[0].length > 1) {
740
+ if (res.body.length > 0 && res.body[0].length > 1) {
742
741
  throw new ParseError("{subarray} can contain only one column");
743
742
  }
744
743
  return res;
@@ -752,11 +751,14 @@ defineEnvironment({
752
751
  // \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
753
752
  // {dcases} is a {cases} environment where cells are set in \displaystyle,
754
753
  // as defined in mathtools.sty.
754
+ // {rcases} is another mathtools environment. It's brace is on the right side.
755
755
  defineEnvironment({
756
756
  type: "array",
757
757
  names: [
758
758
  "cases",
759
759
  "dcases",
760
+ "rcases",
761
+ "drcases",
760
762
  ],
761
763
  props: {
762
764
  numArgs: 0,
@@ -786,8 +788,8 @@ defineEnvironment({
786
788
  type: "leftright",
787
789
  mode: context.mode,
788
790
  body: [res],
789
- left: "\\{",
790
- right: ".",
791
+ left: context.envName.indexOf("r") > -1 ? "." : "\\{",
792
+ right: context.envName.indexOf("r") > -1 ? "\\}" : ".",
791
793
  rightColor: undefined,
792
794
  };
793
795
  },
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import utils from "../utils";
6
6
  import stretchy from "../stretchy";
7
- import {assertNodeType, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
  import {assertSpan, assertSymbolDomNode} from "../domTree";
9
9
 
10
10
  import * as html from "../buildHTML";
@@ -20,9 +20,8 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
20
20
  let base: AnyParseNode;
21
21
  let group: ParseNode<"accent">;
22
22
 
23
- const supSub: ?ParseNode<"supsub"> = checkNodeType(grp, "supsub");
24
23
  let supSubGroup;
25
- if (supSub) {
24
+ if (grp && grp.type === "supsub") {
26
25
  // If our base is a character box, and we have superscripts and
27
26
  // subscripts, the supsub will defer to us. In particular, we want
28
27
  // to attach the superscripts and subscripts to the inner body (so
@@ -32,18 +31,18 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
32
31
  // rendering that, while keeping track of where the accent is.
33
32
 
34
33
  // The real accent group is the base of the supsub group
35
- group = assertNodeType(supSub.base, "accent");
34
+ group = assertNodeType(grp.base, "accent");
36
35
  // The character box is the base of the accent group
37
36
  base = group.base;
38
37
  // Stick the character box into the base of the supsub group
39
- supSub.base = base;
38
+ grp.base = base;
40
39
 
41
40
  // Rerender the supsub group with its new base, and store that
42
41
  // result.
43
- supSubGroup = assertSpan(html.buildGroup(supSub, options));
42
+ supSubGroup = assertSpan(html.buildGroup(grp, options));
44
43
 
45
44
  // reset original base
46
- supSub.base = group;
45
+ grp.base = group;
47
46
  } else {
48
47
  group = assertNodeType(grp, "accent");
49
48
  base = group.base;
@@ -37,8 +37,8 @@ defineFunction({
37
37
 
38
38
  // Generate the vlist, with the appropriate kerns
39
39
  const vlist = buildCommon.makeVList({
40
- positionType: "bottom",
41
- positionData: accentBody.height + kern,
40
+ positionType: "top",
41
+ positionData: innerGroup.height,
42
42
  children: [
43
43
  {type: "elem", elem: accentBody, wrapperClasses: ["svg-align"]},
44
44
  {type: "kern", size: kern},
@@ -0,0 +1,184 @@
1
+ //@flow
2
+ import defineFunction from "../defineFunction";
3
+ import ParseError from "../ParseError";
4
+ import {assertNodeType} from "../parseNode";
5
+
6
+ const globalMap = {
7
+ "\\global": "\\global",
8
+ "\\long": "\\\\globallong",
9
+ "\\\\globallong": "\\\\globallong",
10
+ "\\def": "\\gdef",
11
+ "\\gdef": "\\gdef",
12
+ "\\edef": "\\xdef",
13
+ "\\xdef": "\\xdef",
14
+ "\\let": "\\\\globallet",
15
+ "\\futurelet": "\\\\globalfuture",
16
+ };
17
+
18
+ const checkControlSequence = (tok) => {
19
+ const name = tok.text;
20
+ if (/^(?:[\\{}$&#^_]|EOF)$/.test(name)) {
21
+ throw new ParseError("Expected a control sequence", tok);
22
+ }
23
+ return name;
24
+ };
25
+
26
+ const getRHS = (parser) => {
27
+ let tok = parser.gullet.popToken();
28
+ if (tok.text === "=") { // consume optional equals
29
+ tok = parser.gullet.popToken();
30
+ if (tok.text === " ") { // consume one optional space
31
+ tok = parser.gullet.popToken();
32
+ }
33
+ }
34
+ return tok;
35
+ };
36
+
37
+ const letCommand = (parser, name, tok, global) => {
38
+ let macro = parser.gullet.macros.get(tok.text);
39
+ if (macro == null) {
40
+ // don't expand it later even if a macro with the same name is defined
41
+ // e.g., \let\foo=\frac \def\frac{\relax} \frac12
42
+ tok.noexpand = true;
43
+ macro = {
44
+ tokens: [tok],
45
+ numArgs: 0,
46
+ // reproduce the same behavior in expansion
47
+ unexpandable: !parser.gullet.isExpandable(tok.text),
48
+ };
49
+ }
50
+ parser.gullet.macros.set(name, macro, global);
51
+ };
52
+
53
+ // <assignment> -> <non-macro assignment>|<macro assignment>
54
+ // <non-macro assignment> -> <simple assignment>|\global<non-macro assignment>
55
+ // <macro assignment> -> <definition>|<prefix><macro assignment>
56
+ // <prefix> -> \global|\long|\outer
57
+ defineFunction({
58
+ type: "internal",
59
+ names: [
60
+ "\\global", "\\long",
61
+ "\\\\globallong", // can’t be entered directly
62
+ ],
63
+ props: {
64
+ numArgs: 0,
65
+ allowedInText: true,
66
+ },
67
+ handler({parser, funcName}) {
68
+ parser.consumeSpaces();
69
+ const token = parser.fetch();
70
+ if (globalMap[token.text]) {
71
+ // KaTeX doesn't have \par, so ignore \long
72
+ if (funcName === "\\global" || funcName === "\\\\globallong") {
73
+ token.text = globalMap[token.text];
74
+ }
75
+ return assertNodeType(parser.parseFunction(), "internal");
76
+ }
77
+ throw new ParseError(`Invalid token after macro prefix`, token);
78
+ },
79
+ });
80
+
81
+ // Basic support for macro definitions: \def, \gdef, \edef, \xdef
82
+ // <definition> -> <def><control sequence><definition text>
83
+ // <def> -> \def|\gdef|\edef|\xdef
84
+ // <definition text> -> <parameter text><left brace><balanced text><right brace>
85
+ defineFunction({
86
+ type: "internal",
87
+ names: ["\\def", "\\gdef", "\\edef", "\\xdef"],
88
+ props: {
89
+ numArgs: 0,
90
+ allowedInText: true,
91
+ },
92
+ handler({parser, funcName}) {
93
+ let arg = parser.gullet.consumeArgs(1)[0];
94
+ if (arg.length !== 1) {
95
+ throw new ParseError("\\gdef's first argument must be a macro name");
96
+ }
97
+ const name = arg[0].text;
98
+ // Count argument specifiers, and check they are in the order #1 #2 ...
99
+ let numArgs = 0;
100
+ arg = parser.gullet.consumeArgs(1)[0];
101
+ while (arg.length === 1 && arg[0].text === "#") {
102
+ arg = parser.gullet.consumeArgs(1)[0];
103
+ if (arg.length !== 1) {
104
+ throw new ParseError(
105
+ `Invalid argument number length "${arg.length}"`);
106
+ }
107
+ if (!(/^[1-9]$/.test(arg[0].text))) {
108
+ throw new ParseError(
109
+ `Invalid argument number "${arg[0].text}"`);
110
+ }
111
+ numArgs++;
112
+ if (parseInt(arg[0].text) !== numArgs) {
113
+ throw new ParseError(
114
+ `Argument number "${arg[0].text}" out of order`);
115
+ }
116
+ arg = parser.gullet.consumeArgs(1)[0];
117
+ }
118
+ if (funcName === "\\edef" || funcName === "\\xdef") {
119
+ arg = parser.gullet.expandTokens(arg);
120
+ arg.reverse(); // to fit in with stack order
121
+ }
122
+ // Final arg is the expansion of the macro
123
+ parser.gullet.macros.set(name, {
124
+ tokens: arg,
125
+ numArgs,
126
+ }, funcName === globalMap[funcName]);
127
+
128
+ return {
129
+ type: "internal",
130
+ mode: parser.mode,
131
+ };
132
+ },
133
+ });
134
+
135
+ // <simple assignment> -> <let assignment>
136
+ // <let assignment> -> \futurelet<control sequence><token><token>
137
+ // | \let<control sequence><equals><one optional space><token>
138
+ // <equals> -> <optional spaces>|<optional spaces>=
139
+ defineFunction({
140
+ type: "internal",
141
+ names: [
142
+ "\\let",
143
+ "\\\\globallet", // can’t be entered directly
144
+ ],
145
+ props: {
146
+ numArgs: 0,
147
+ allowedInText: true,
148
+ },
149
+ handler({parser, funcName}) {
150
+ const name = checkControlSequence(parser.gullet.popToken());
151
+ parser.gullet.consumeSpaces();
152
+ const tok = getRHS(parser);
153
+ letCommand(parser, name, tok, funcName === "\\\\globallet");
154
+ return {
155
+ type: "internal",
156
+ mode: parser.mode,
157
+ };
158
+ },
159
+ });
160
+
161
+ // ref: https://www.tug.org/TUGboat/tb09-3/tb22bechtolsheim.pdf
162
+ defineFunction({
163
+ type: "internal",
164
+ names: [
165
+ "\\futurelet",
166
+ "\\\\globalfuture", // can’t be entered directly
167
+ ],
168
+ props: {
169
+ numArgs: 0,
170
+ allowedInText: true,
171
+ },
172
+ handler({parser, funcName}) {
173
+ const name = checkControlSequence(parser.gullet.popToken());
174
+ const middle = parser.gullet.popToken();
175
+ const tok = parser.gullet.popToken();
176
+ letCommand(parser, name, tok, funcName === "\\\\globalfuture");
177
+ parser.gullet.pushToken(tok);
178
+ parser.gullet.pushToken(middle);
179
+ return {
180
+ type: "internal",
181
+ mode: parser.mode,
182
+ };
183
+ },
184
+ });
@@ -62,11 +62,12 @@ function checkDelimiter(
62
62
  const symDelim = checkSymbolNodeType(delim);
63
63
  if (symDelim && utils.contains(delimiters, symDelim.text)) {
64
64
  return symDelim;
65
- } else {
65
+ } else if (symDelim) {
66
66
  throw new ParseError(
67
- "Invalid delimiter: '" +
68
- (symDelim ? symDelim.text : JSON.stringify(delim)) +
69
- "' after '" + context.funcName + "'", delim);
67
+ `Invalid delimiter '${symDelim.text}' after '${context.funcName}'`,
68
+ delim);
69
+ } else {
70
+ throw new ParseError(`Invalid delimiter type '${delim.type}'`, delim);
70
71
  }
71
72
  }
72
73
 
@@ -95,7 +95,7 @@ defineFunction({
95
95
  // Old font changing functions
96
96
  defineFunction({
97
97
  type: "font",
98
- names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it"],
98
+ names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
99
99
  props: {
100
100
  numArgs: 0,
101
101
  allowedInText: true,
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import delimiter from "../delimiter";
5
5
  import mathMLTree from "../mathMLTree";
6
6
  import Style from "../Style";
7
- import {assertNodeType, assertAtomFamily, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
  import {assert} from "../utils";
9
9
 
10
10
  import * as html from "../buildHTML";
@@ -382,17 +382,10 @@ defineFunction({
382
382
  const denom = args[5];
383
383
 
384
384
  // Look into the parse nodes to get the desired delimiters.
385
- let leftNode = checkNodeType(args[0], "atom");
386
- if (leftNode) {
387
- leftNode = assertAtomFamily(args[0], "open");
388
- }
389
- const leftDelim = leftNode ? delimFromValue(leftNode.text) : null;
390
-
391
- let rightNode = checkNodeType(args[1], "atom");
392
- if (rightNode) {
393
- rightNode = assertAtomFamily(args[1], "close");
394
- }
395
- const rightDelim = rightNode ? delimFromValue(rightNode.text) : null;
385
+ const leftDelim = args[0].type === "atom" && args[0].family === "open"
386
+ ? delimFromValue(args[0].text) : null;
387
+ const rightDelim = args[1].type === "atom" && args[1].family === "close"
388
+ ? delimFromValue(args[1].text) : null;
396
389
 
397
390
  const barNode = assertNodeType(args[2], "size");
398
391
  let hasBarLine;
@@ -409,14 +402,14 @@ defineFunction({
409
402
 
410
403
  // Find out if we want displaystyle, textstyle, etc.
411
404
  let size = "auto";
412
- let styl = checkNodeType(args[3], "ordgroup");
413
- if (styl) {
405
+ let styl = args[3];
406
+ if (styl.type === "ordgroup") {
414
407
  if (styl.body.length > 0) {
415
408
  const textOrd = assertNodeType(styl.body[0], "textord");
416
409
  size = stylArray[Number(textOrd.text)];
417
410
  }
418
411
  } else {
419
- styl = assertNodeType(args[3], "textord");
412
+ styl = assertNodeType(styl, "textord");
420
413
  size = stylArray[Number(styl.text)];
421
414
  }
422
415
 
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import stretchy from "../stretchy";
6
6
  import Style from "../Style";
7
- import {assertNodeType, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
 
9
9
  import * as html from "../buildHTML";
10
10
  import * as mml from "../buildMathML";
@@ -20,15 +20,14 @@ export const htmlBuilder: HtmlBuilderSupSub<"horizBrace"> = (grp, options) => {
20
20
  // Pull out the `ParseNode<"horizBrace">` if `grp` is a "supsub" node.
21
21
  let supSubGroup;
22
22
  let group: ParseNode<"horizBrace">;
23
- const supSub = checkNodeType(grp, "supsub");
24
- if (supSub) {
23
+ if (grp.type === "supsub") {
25
24
  // Ref: LaTeX source2e: }}}}\limits}
26
25
  // i.e. LaTeX treats the brace similar to an op and passes it
27
26
  // with \limits, so we need to assign supsub style.
28
- supSubGroup = supSub.sup ?
29
- html.buildGroup(supSub.sup, options.havingStyle(style.sup()), options) :
30
- html.buildGroup(supSub.sub, options.havingStyle(style.sub()), options);
31
- group = assertNodeType(supSub.base, "horizBrace");
27
+ supSubGroup = grp.sup ?
28
+ html.buildGroup(grp.sup, options.havingStyle(style.sup()), options) :
29
+ html.buildGroup(grp.sub, options.havingStyle(style.sub()), options);
30
+ group = assertNodeType(grp.base, "horizBrace");
32
31
  } else {
33
32
  group = assertNodeType(grp, "horizBrace");
34
33
  }
@@ -0,0 +1,102 @@
1
+ // @flow
2
+ import defineFunction, {ordargument} from "../defineFunction";
3
+ import buildCommon from "../buildCommon";
4
+ import {assertNodeType} from "../parseNode";
5
+ import ParseError from "../ParseError";
6
+
7
+ import * as html from "../buildHTML";
8
+ import * as mml from "../buildMathML";
9
+
10
+ defineFunction({
11
+ type: "html",
12
+ names: ["\\htmlClass", "\\htmlId", "\\htmlStyle", "\\htmlData"],
13
+ props: {
14
+ numArgs: 2,
15
+ argTypes: ["raw", "original"],
16
+ allowedInText: true,
17
+ },
18
+ handler: ({parser, funcName, token}, args) => {
19
+ const value = assertNodeType(args[0], "raw").string;
20
+ const body = args[1];
21
+
22
+ if (parser.settings.strict) {
23
+ parser.settings.reportNonstrict("htmlExtension",
24
+ "HTML extension is disabled on strict mode");
25
+ }
26
+
27
+ let trustContext;
28
+ const attributes = {};
29
+
30
+ switch (funcName) {
31
+ case "\\htmlClass":
32
+ attributes.class = value;
33
+ trustContext = {
34
+ command: "\\htmlClass",
35
+ class: value,
36
+ };
37
+ break;
38
+ case "\\htmlId":
39
+ attributes.id = value;
40
+ trustContext = {
41
+ command: "\\htmlId",
42
+ id: value,
43
+ };
44
+ break;
45
+ case "\\htmlStyle":
46
+ attributes.style = value;
47
+ trustContext = {
48
+ command: "\\htmlStyle",
49
+ style: value,
50
+ };
51
+ break;
52
+ case "\\htmlData": {
53
+ const data = value.split(",");
54
+ for (let i = 0; i < data.length; i++) {
55
+ const keyVal = data[i].split("=");
56
+ if (keyVal.length !== 2) {
57
+ throw new ParseError(
58
+ "Error parsing key-value for \\htmlData");
59
+ }
60
+ attributes["data-" + keyVal[0].trim()] = keyVal[1].trim();
61
+ }
62
+
63
+ trustContext = {
64
+ command: "\\htmlData",
65
+ attributes,
66
+ };
67
+ break;
68
+ }
69
+ default:
70
+ throw new Error("Unrecognized html command");
71
+ }
72
+
73
+ if (!parser.settings.isTrusted(trustContext)) {
74
+ return parser.formatUnsupportedCmd(funcName);
75
+ }
76
+ return {
77
+ type: "html",
78
+ mode: parser.mode,
79
+ attributes,
80
+ body: ordargument(body),
81
+ };
82
+ },
83
+ htmlBuilder: (group, options) => {
84
+ const elements = html.buildExpression(group.body, options, false);
85
+
86
+ const classes = ["enclosing"];
87
+ if (group.attributes.class) {
88
+ classes.push(...group.attributes.class.trim().split(/\s+/));
89
+ }
90
+
91
+ const span = buildCommon.makeSpan(classes, elements, options);
92
+ for (const attr in group.attributes) {
93
+ if (attr !== "class" && group.attributes.hasOwnProperty(attr)) {
94
+ span.setAttribute(attr, group.attributes[attr]);
95
+ }
96
+ }
97
+ return span;
98
+ },
99
+ mathmlBuilder: (group, options) => {
100
+ return mml.buildExpressionRow(group.body, options);
101
+ },
102
+ });
@@ -44,19 +44,16 @@ defineFunction({
44
44
  // two items involved in the lap.
45
45
  // Next, use a strut to set the height of the HTML bounding box.
46
46
  // Otherwise, a tall argument may be misplaced.
47
+ // This code resolved issue #1153
47
48
  const strut = buildCommon.makeSpan(["strut"]);
48
49
  strut.style.height = (node.height + node.depth) + "em";
49
50
  strut.style.verticalAlign = -node.depth + "em";
50
51
  node.children.unshift(strut);
51
52
 
52
53
  // Next, prevent vertical misplacement when next to something tall.
53
- node = buildCommon.makeVList({
54
- positionType: "firstBaseline",
55
- children: [{type: "elem", elem: node}],
56
- }, options);
57
-
58
- // Get the horizontal spacing correct relative to adjacent items.
59
- return buildCommon.makeSpan(["mord"], [node], options);
54
+ // This code resolves issue #1234
55
+ node = buildCommon.makeSpan(["thinbox"], [node], options);
56
+ return buildCommon.makeSpan(["mord", "vbox"], [node], options);
60
57
  },
61
58
  mathmlBuilder: (group, options) => {
62
59
  // mathllap, mathrlap, mathclap
@@ -7,7 +7,7 @@ import * as mathMLTree from "../mathMLTree";
7
7
  import utils from "../utils";
8
8
  import Style from "../Style";
9
9
  import {assembleSupSub} from "./utils/assembleSupSub";
10
- import {assertNodeType, checkNodeType} from "../parseNode";
10
+ import {assertNodeType} from "../parseNode";
11
11
 
12
12
  import * as html from "../buildHTML";
13
13
  import * as mml from "../buildMathML";
@@ -28,14 +28,13 @@ export const htmlBuilder: HtmlBuilderSupSub<"op"> = (grp, options) => {
28
28
  let subGroup;
29
29
  let hasLimits = false;
30
30
  let group: ParseNode<"op">;
31
- const supSub = checkNodeType(grp, "supsub");
32
- if (supSub) {
31
+ if (grp.type === "supsub") {
33
32
  // If we have limits, supsub will pass us its group to handle. Pull
34
33
  // out the superscript and subscript and set the group to the op in
35
34
  // its base.
36
- supGroup = supSub.sup;
37
- subGroup = supSub.sub;
38
- group = assertNodeType(supSub.base, "op");
35
+ supGroup = grp.sup;
36
+ subGroup = grp.sub;
37
+ group = assertNodeType(grp.base, "op");
39
38
  hasLimits = true;
40
39
  } else {
41
40
  group = assertNodeType(grp, "op");
@@ -4,7 +4,7 @@ import buildCommon from "../buildCommon";
4
4
  import mathMLTree from "../mathMLTree";
5
5
  import {SymbolNode} from "../domTree";
6
6
  import {assembleSupSub} from "./utils/assembleSupSub";
7
- import {assertNodeType, checkNodeType} from "../parseNode";
7
+ import {assertNodeType} from "../parseNode";
8
8
 
9
9
  import * as html from "../buildHTML";
10
10
  import * as mml from "../buildMathML";
@@ -21,14 +21,13 @@ export const htmlBuilder: HtmlBuilderSupSub<"operatorname"> = (grp, options) =>
21
21
  let subGroup;
22
22
  let hasLimits = false;
23
23
  let group: ParseNode<"operatorname">;
24
- const supSub = checkNodeType(grp, "supsub");
25
- if (supSub) {
24
+ if (grp.type === "supsub") {
26
25
  // If we have limits, supsub will pass us its group to handle. Pull
27
26
  // out the superscript and subscript and set the group to the op in
28
27
  // its base.
29
- supGroup = supSub.sup;
30
- subGroup = supSub.sub;
31
- group = assertNodeType(supSub.base, "operatorname");
28
+ supGroup = grp.sup;
29
+ subGroup = grp.sub;
30
+ group = assertNodeType(grp.base, "operatorname");
32
31
  hasLimits = true;
33
32
  } else {
34
33
  group = assertNodeType(grp, "operatorname");
@@ -5,7 +5,6 @@ import {SymbolNode} from "../domTree";
5
5
  import mathMLTree from "../mathMLTree";
6
6
  import utils from "../utils";
7
7
  import Style from "../Style";
8
- import {checkNodeType} from "../parseNode";
9
8
 
10
9
  import * as html from "../buildHTML";
11
10
  import * as mml from "../buildMathML";
@@ -197,12 +196,11 @@ defineFunctionBuilders({
197
196
  let isOver;
198
197
  let isSup;
199
198
 
200
- const horizBrace = checkNodeType(group.base, "horizBrace");
201
- if (horizBrace) {
199
+ if (group.base && group.base.type === "horizBrace") {
202
200
  isSup = !!group.sup;
203
- if (isSup === horizBrace.isOver) {
201
+ if (isSup === group.base.isOver) {
204
202
  isBrace = true;
205
- isOver = horizBrace.isOver;
203
+ isOver = group.base.isOver;
206
204
  }
207
205
  }
208
206
 
@@ -17,8 +17,6 @@ export const assembleSupSub = (
17
17
  slant: number,
18
18
  baseShift: number,
19
19
  ): DomSpan => {
20
- // IE 8 clips \int if it is in a display: inline-block. We wrap it
21
- // in a new span so it is an inline, and works.
22
20
  base = buildCommon.makeSpan([], [base]);
23
21
  let sub;
24
22
  let sup;