katex 0.12.0 → 0.13.3

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 (167) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +1 -1
  3. package/README.md +8 -7
  4. package/contrib/auto-render/auto-render.js +12 -19
  5. package/contrib/auto-render/index.html +9 -3
  6. package/contrib/auto-render/splitAtDelimiters.js +44 -61
  7. package/contrib/auto-render/test/auto-render-spec.js +88 -52
  8. package/contrib/copy-tex/README.md +5 -9
  9. package/contrib/copy-tex/copy-tex.css +0 -3
  10. package/contrib/mathtex-script-type/README.md +5 -5
  11. package/contrib/mhchem/README.md +4 -2
  12. package/contrib/render-a11y-string/render-a11y-string.js +31 -2
  13. package/contrib/render-a11y-string/test/render-a11y-string-spec.js +23 -0
  14. package/dist/README.md +8 -7
  15. package/dist/contrib/auto-render.js +148 -171
  16. package/dist/contrib/auto-render.min.js +1 -1
  17. package/dist/contrib/auto-render.mjs +91 -95
  18. package/dist/contrib/copy-tex.css +3 -3
  19. package/dist/contrib/copy-tex.js +11 -108
  20. package/dist/contrib/copy-tex.min.css +1 -1
  21. package/dist/contrib/copy-tex.min.js +1 -1
  22. package/dist/contrib/copy-tex.mjs +21 -20
  23. package/dist/contrib/mathtex-script-type.js +66 -91
  24. package/dist/contrib/mathtex-script-type.min.js +1 -1
  25. package/dist/contrib/mathtex-script-type.mjs +3 -3
  26. package/dist/contrib/mhchem.js +70 -95
  27. package/dist/contrib/mhchem.min.js +1 -1
  28. package/dist/contrib/mhchem.mjs +2 -2
  29. package/dist/contrib/render-a11y-string.js +97 -92
  30. package/dist/contrib/render-a11y-string.min.js +1 -1
  31. package/dist/contrib/render-a11y-string.mjs +86 -45
  32. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  33. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  34. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  35. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  36. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  37. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  38. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  39. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  40. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  41. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  42. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  43. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  44. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  45. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  46. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  47. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  48. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  49. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  50. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  51. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  52. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  53. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  54. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  55. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  56. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  57. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  58. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  59. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  60. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  61. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  62. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  63. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  64. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  65. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  66. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  67. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  68. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  69. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  70. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  71. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  72. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  73. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  74. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  75. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  76. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  77. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  78. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  79. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  80. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  81. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  82. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  83. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  84. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  85. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  86. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  87. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  88. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  89. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  90. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  91. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  92. package/dist/katex.css +66 -10
  93. package/dist/katex.js +2714 -1950
  94. package/dist/katex.min.css +1 -1
  95. package/dist/katex.min.js +1 -1
  96. package/dist/katex.mjs +3162 -2372
  97. package/katex.js +4 -3
  98. package/package.json +69 -59
  99. package/src/Lexer.js +4 -2
  100. package/src/MacroExpander.js +117 -37
  101. package/src/Options.js +2 -2
  102. package/src/ParseError.js +1 -1
  103. package/src/Parser.js +100 -159
  104. package/src/Settings.js +2 -2
  105. package/src/Style.js +4 -4
  106. package/src/Token.js +1 -1
  107. package/src/buildCommon.js +12 -5
  108. package/src/buildHTML.js +11 -0
  109. package/src/buildMathML.js +6 -0
  110. package/src/defineEnvironment.js +0 -3
  111. package/src/defineFunction.js +15 -22
  112. package/src/delimiter.js +61 -57
  113. package/src/domTree.js +1 -1
  114. package/src/environments/array.js +223 -35
  115. package/src/environments/cd.js +312 -0
  116. package/src/fontMetrics.js +1 -1
  117. package/src/fontMetricsData.js +2076 -0
  118. package/src/fonts/.gitignore +9 -0
  119. package/src/fonts/Makefile +139 -0
  120. package/src/fonts/default.cfg +20 -0
  121. package/src/fonts/generate_fonts.py +61 -0
  122. package/src/fonts/lib/Extra.otf +0 -0
  123. package/src/fonts/lib/Space.ttx +234 -0
  124. package/src/fonts/makeBlacker +49 -0
  125. package/src/fonts/makeFF +2003 -0
  126. package/src/fonts/xbbold.mf +182 -0
  127. package/src/fonts.less +64 -0
  128. package/src/functions/accent.js +3 -2
  129. package/src/functions/arrow.js +8 -2
  130. package/src/functions/color.js +4 -4
  131. package/src/functions/cr.js +7 -25
  132. package/src/functions/def.js +50 -24
  133. package/src/functions/delimsizing.js +8 -0
  134. package/src/functions/enclose.js +80 -12
  135. package/src/functions/environment.js +1 -1
  136. package/src/functions/font.js +3 -4
  137. package/src/functions/genfrac.js +36 -11
  138. package/src/functions/hbox.js +39 -0
  139. package/src/functions/kern.js +1 -0
  140. package/src/functions/mathchoice.js +1 -0
  141. package/src/functions/mclass.js +2 -1
  142. package/src/functions/op.js +3 -7
  143. package/src/functions/operatorname.js +1 -1
  144. package/src/functions/raisebox.js +0 -1
  145. package/src/functions/styling.js +1 -0
  146. package/src/functions/supsub.js +1 -3
  147. package/src/functions/symbolsOrd.js +0 -2
  148. package/src/functions/text.js +2 -3
  149. package/src/functions/vcenter.js +44 -0
  150. package/src/functions.js +3 -0
  151. package/src/katex.less +69 -16
  152. package/src/macros.js +42 -6
  153. package/src/mathMLTree.js +16 -1
  154. package/src/metrics/.gitignore +1 -0
  155. package/src/metrics/README.md +23 -0
  156. package/src/metrics/extract_tfms.py +114 -0
  157. package/src/metrics/extract_ttfs.py +119 -0
  158. package/src/metrics/format_json.py +28 -0
  159. package/src/metrics/mapping.pl +1224 -0
  160. package/src/metrics/parse_tfm.py +211 -0
  161. package/src/parseNode.js +29 -1
  162. package/src/parseTree.js +6 -0
  163. package/src/stretchy.js +12 -5
  164. package/src/svgGeometry.js +33 -4
  165. package/src/symbols.js +5 -3
  166. package/src/types.js +3 -2
  167. package/src/unicodeScripts.js +5 -0
package/src/Parser.js CHANGED
@@ -143,13 +143,7 @@ export default class Parser {
143
143
  return parse;
144
144
  }
145
145
 
146
- static endOfExpression = ["}", "\\endgroup", "\\end", "\\right", "&"];
147
-
148
- static endOfGroup = {
149
- "[": "]",
150
- "{": "}",
151
- "\\begingroup": "\\endgroup",
152
- }
146
+ static endOfExpression: string[] = ["}", "\\endgroup", "\\end", "\\right", "&"];
153
147
 
154
148
  /**
155
149
  * Parses an "expression", which is a list of atoms.
@@ -253,9 +247,6 @@ export default class Parser {
253
247
  }
254
248
  }
255
249
 
256
- // The greediness of a superscript or subscript
257
- static SUPSUB_GREEDINESS = 1;
258
-
259
250
  /**
260
251
  * Handle a subscript or superscript with nice errors.
261
252
  */
@@ -265,9 +256,8 @@ export default class Parser {
265
256
  const symbolToken = this.fetch();
266
257
  const symbol = symbolToken.text;
267
258
  this.consume();
268
- const group = this.parseGroup(name, false, Parser.SUPSUB_GREEDINESS,
269
- undefined, undefined, true);
270
- // ignore spaces before sup/subscript argument
259
+ this.consumeSpaces(); // ignore spaces before sup/subscript argument
260
+ const group = this.parseGroup(name);
271
261
 
272
262
  if (!group) {
273
263
  throw new ParseError(
@@ -312,7 +302,7 @@ export default class Parser {
312
302
  parseAtom(breakOnTokenText?: BreakToken): ?AnyParseNode {
313
303
  // The body of an atom is an implicit group, so that things like
314
304
  // \left(x\right)^2 work correctly.
315
- const base = this.parseGroup("atom", false, null, breakOnTokenText);
305
+ const base = this.parseGroup("atom", breakOnTokenText);
316
306
 
317
307
  // In text mode, we don't have superscripts or subscripts
318
308
  if (this.mode === "text") {
@@ -409,8 +399,7 @@ export default class Parser {
409
399
  */
410
400
  parseFunction(
411
401
  breakOnTokenText?: BreakToken,
412
- name?: string, // For error reporting.
413
- greediness?: ?number,
402
+ name?: string, // For determining its context
414
403
  ): ?AnyParseNode {
415
404
  const token = this.fetch();
416
405
  const func = token.text;
@@ -420,7 +409,7 @@ export default class Parser {
420
409
  }
421
410
  this.consume(); // consume command token
422
411
 
423
- if (greediness != null && funcData.greediness <= greediness) {
412
+ if (name && name !== "atom" && !funcData.allowedInArgument) {
424
413
  throw new ParseError(
425
414
  "Got function '" + func + "' with no arguments" +
426
415
  (name ? " as " + name : ""), token);
@@ -475,36 +464,28 @@ export default class Parser {
475
464
  return {args: [], optArgs: []};
476
465
  }
477
466
 
478
- const baseGreediness = funcData.greediness;
479
467
  const args = [];
480
468
  const optArgs = [];
481
469
 
482
470
  for (let i = 0; i < totalArgs; i++) {
483
- const argType = funcData.argTypes && funcData.argTypes[i];
471
+ let argType = funcData.argTypes && funcData.argTypes[i];
484
472
  const isOptional = i < funcData.numOptionalArgs;
485
- // Ignore spaces between arguments. As the TeXbook says:
486
- // "After you have said ‘\def\row#1#2{...}’, you are allowed to
487
- // put spaces between the arguments (e.g., ‘\row x n’), because
488
- // TeX doesn’t use single spaces as undelimited arguments."
489
- const consumeSpaces = (i > 0 && !isOptional) ||
490
- // Also consume leading spaces in math mode, as parseSymbol
491
- // won't know what to do with them. This can only happen with
492
- // macros, e.g. \frac\foo\foo where \foo expands to a space symbol.
493
- // In LaTeX, the \foo's get treated as (blank) arguments.
494
- // In KaTeX, for now, both spaces will get consumed.
495
- // TODO(edemaine)
496
- (i === 0 && !isOptional && this.mode === "math");
473
+
474
+ if ((funcData.primitive && argType == null) ||
475
+ // \sqrt expands into primitive if optional argument doesn't exist
476
+ (funcData.type === "sqrt" && i === 1 && optArgs[0] == null)) {
477
+ argType = "primitive";
478
+ }
479
+
497
480
  const arg = this.parseGroupOfType(`argument to '${func}'`,
498
- argType, isOptional, baseGreediness, consumeSpaces);
499
- if (!arg) {
500
- if (isOptional) {
501
- optArgs.push(null);
502
- continue;
503
- }
504
- throw new ParseError(
505
- `Expected group after '${func}'`, this.fetch());
481
+ argType, isOptional);
482
+ if (isOptional) {
483
+ optArgs.push(arg);
484
+ } else if (arg != null) {
485
+ args.push(arg);
486
+ } else { // should be unreachable
487
+ throw new ParseError("Null argument, please report this as a bug");
506
488
  }
507
- (isOptional ? optArgs : args).push(arg);
508
489
  }
509
490
 
510
491
  return {args, optArgs};
@@ -517,65 +498,50 @@ export default class Parser {
517
498
  name: string,
518
499
  type: ?ArgType,
519
500
  optional: boolean,
520
- greediness: ?number,
521
- consumeSpaces: boolean,
522
501
  ): ?AnyParseNode {
523
502
  switch (type) {
524
503
  case "color":
525
- if (consumeSpaces) {
526
- this.consumeSpaces();
527
- }
528
504
  return this.parseColorGroup(optional);
529
505
  case "size":
530
- if (consumeSpaces) {
531
- this.consumeSpaces();
532
- }
533
506
  return this.parseSizeGroup(optional);
534
507
  case "url":
535
- return this.parseUrlGroup(optional, consumeSpaces);
508
+ return this.parseUrlGroup(optional);
536
509
  case "math":
537
510
  case "text":
538
- return this.parseGroup(
539
- name, optional, greediness, undefined, type, consumeSpaces);
511
+ return this.parseArgumentGroup(optional, type);
540
512
  case "hbox": {
541
513
  // hbox argument type wraps the argument in the equivalent of
542
514
  // \hbox, which is like \text but switching to \textstyle size.
543
- const group = this.parseGroup(name, optional, greediness,
544
- undefined, "text", consumeSpaces);
545
- if (!group) {
546
- return group;
547
- }
548
- const styledGroup = {
515
+ const group = this.parseArgumentGroup(optional, "text");
516
+ return group != null ? {
549
517
  type: "styling",
550
518
  mode: group.mode,
551
519
  body: [group],
552
520
  style: "text", // simulate \textstyle
553
- };
554
- return styledGroup;
521
+ } : null;
555
522
  }
556
523
  case "raw": {
557
- if (consumeSpaces) {
558
- this.consumeSpaces();
559
- }
560
- if (optional && this.fetch().text === "{") {
561
- return null;
524
+ const token = this.parseStringGroup("raw", optional);
525
+ return token != null ? {
526
+ type: "raw",
527
+ mode: "text",
528
+ string: token.text,
529
+ } : null;
530
+ }
531
+ case "primitive": {
532
+ if (optional) {
533
+ throw new ParseError("A primitive argument cannot be optional");
562
534
  }
563
- const token = this.parseStringGroup("raw", optional, true);
564
- if (token) {
565
- return {
566
- type: "raw",
567
- mode: "text",
568
- string: token.text,
569
- };
570
- } else {
571
- throw new ParseError("Expected raw group", this.fetch());
535
+ const group = this.parseGroup(name);
536
+ if (group == null) {
537
+ throw new ParseError("Expected group as " + name, this.fetch());
572
538
  }
539
+ return group;
573
540
  }
574
541
  case "original":
575
542
  case null:
576
543
  case undefined:
577
- return this.parseGroup(name, optional, greediness,
578
- undefined, undefined, consumeSpaces);
544
+ return this.parseArgumentGroup(optional);
579
545
  default:
580
546
  throw new ParseError(
581
547
  "Unknown group type as " + name, this.fetch());
@@ -598,49 +564,20 @@ export default class Parser {
598
564
  parseStringGroup(
599
565
  modeName: ArgType, // Used to describe the mode in error messages.
600
566
  optional: boolean,
601
- raw?: boolean,
602
567
  ): ?Token {
603
- const groupBegin = optional ? "[" : "{";
604
- const groupEnd = optional ? "]" : "}";
605
- const beginToken = this.fetch();
606
- if (beginToken.text !== groupBegin) {
607
- if (optional) {
608
- return null;
609
- } else if (raw && beginToken.text !== "EOF" &&
610
- /[^{}[\]]/.test(beginToken.text)) {
611
- this.consume();
612
- return beginToken;
613
- }
568
+ const argToken = this.gullet.scanArgument(optional);
569
+ if (argToken == null) {
570
+ return null;
614
571
  }
615
- const outerMode = this.mode;
616
- this.mode = "text";
617
- this.expect(groupBegin);
618
572
  let str = "";
619
- const firstToken = this.fetch();
620
- let nested = 0; // allow nested braces in raw string group
621
- let lastToken = firstToken;
622
573
  let nextToken;
623
- while ((nextToken = this.fetch()).text !== groupEnd ||
624
- (raw && nested > 0)) {
625
- switch (nextToken.text) {
626
- case "EOF":
627
- throw new ParseError(
628
- "Unexpected end of input in " + modeName,
629
- firstToken.range(lastToken, str));
630
- case groupBegin:
631
- nested++;
632
- break;
633
- case groupEnd:
634
- nested--;
635
- break;
636
- }
637
- lastToken = nextToken;
638
- str += lastToken.text;
574
+ while ((nextToken = this.fetch()).text !== "EOF") {
575
+ str += nextToken.text;
639
576
  this.consume();
640
577
  }
641
- this.expect(groupEnd);
642
- this.mode = outerMode;
643
- return firstToken.range(lastToken, str);
578
+ this.consume(); // consume the end of the argument
579
+ argToken.text = str;
580
+ return argToken;
644
581
  }
645
582
 
646
583
  /**
@@ -652,8 +589,6 @@ export default class Parser {
652
589
  regex: RegExp,
653
590
  modeName: string, // Used to describe the mode in error messages.
654
591
  ): Token {
655
- const outerMode = this.mode;
656
- this.mode = "text";
657
592
  const firstToken = this.fetch();
658
593
  let lastToken = firstToken;
659
594
  let str = "";
@@ -669,7 +604,6 @@ export default class Parser {
669
604
  "Invalid " + modeName + ": '" + firstToken.text + "'",
670
605
  firstToken);
671
606
  }
672
- this.mode = outerMode;
673
607
  return firstToken.range(lastToken, str);
674
608
  }
675
609
 
@@ -678,7 +612,7 @@ export default class Parser {
678
612
  */
679
613
  parseColorGroup(optional: boolean): ?ParseNode<"color-token"> {
680
614
  const res = this.parseStringGroup("color", optional);
681
- if (!res) {
615
+ if (res == null) {
682
616
  return null;
683
617
  }
684
618
  const match = (/^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i).exec(res.text);
@@ -705,7 +639,9 @@ export default class Parser {
705
639
  parseSizeGroup(optional: boolean): ?ParseNode<"size"> {
706
640
  let res;
707
641
  let isBlank = false;
708
- if (!optional && this.fetch().text !== "{") {
642
+ // don't expand before parseStringGroup
643
+ this.gullet.consumeSpaces();
644
+ if (!optional && this.gullet.future().text !== "{") {
709
645
  res = this.parseRegexGroup(
710
646
  /^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/, "size");
711
647
  } else {
@@ -744,11 +680,13 @@ export default class Parser {
744
680
  * Parses an URL, checking escaped letters and allowed protocols,
745
681
  * and setting the catcode of % as an active character (as in \hyperref).
746
682
  */
747
- parseUrlGroup(optional: boolean, consumeSpaces: boolean): ?ParseNode<"url"> {
683
+ parseUrlGroup(optional: boolean): ?ParseNode<"url"> {
748
684
  this.gullet.lexer.setCatcode("%", 13); // active character
749
- const res = this.parseStringGroup("url", optional, true); // get raw string
685
+ this.gullet.lexer.setCatcode("~", 12); // other character
686
+ const res = this.parseStringGroup("url", optional);
750
687
  this.gullet.lexer.setCatcode("%", 14); // comment character
751
- if (!res) {
688
+ this.gullet.lexer.setCatcode("~", 13); // active character
689
+ if (res == null) {
752
690
  return null;
753
691
  }
754
692
  // hyperref package allows backslashes alone in href, but doesn't
@@ -764,52 +702,60 @@ export default class Parser {
764
702
  }
765
703
 
766
704
  /**
767
- * If `optional` is false or absent, this parses an ordinary group,
768
- * which is either a single nucleus (like "x") or an expression
769
- * in braces (like "{x+y}") or an implicit group, a group that starts
770
- * at the current position, and ends right before a higher explicit
705
+ * Parses an argument with the mode specified.
706
+ */
707
+ parseArgumentGroup(optional: boolean, mode?: Mode): ?ParseNode<"ordgroup"> {
708
+ const argToken = this.gullet.scanArgument(optional);
709
+ if (argToken == null) {
710
+ return null;
711
+ }
712
+ const outerMode = this.mode;
713
+ if (mode) { // Switch to specified mode
714
+ this.switchMode(mode);
715
+ }
716
+
717
+ this.gullet.beginGroup();
718
+ const expression = this.parseExpression(false, "EOF");
719
+ // TODO: find an alternative way to denote the end
720
+ this.expect("EOF"); // expect the end of the argument
721
+ this.gullet.endGroup();
722
+ const result = {
723
+ type: "ordgroup",
724
+ mode: this.mode,
725
+ loc: argToken.loc,
726
+ body: expression,
727
+ };
728
+
729
+ if (mode) { // Switch mode back
730
+ this.switchMode(outerMode);
731
+ }
732
+ return result;
733
+ }
734
+
735
+ /**
736
+ * Parses an ordinary group, which is either a single nucleus (like "x")
737
+ * or an expression in braces (like "{x+y}") or an implicit group, a group
738
+ * that starts at the current position, and ends right before a higher explicit
771
739
  * group ends, or at EOF.
772
- * If `optional` is true, it parses either a bracket-delimited expression
773
- * (like "[x+y]") or returns null to indicate the absence of a
774
- * bracket-enclosed group.
775
- * If `mode` is present, switches to that mode while parsing the group,
776
- * and switches back after.
777
740
  */
778
741
  parseGroup(
779
742
  name: string, // For error reporting.
780
- optional?: boolean,
781
- greediness?: ?number,
782
743
  breakOnTokenText?: BreakToken,
783
- mode?: Mode,
784
- consumeSpaces?: boolean,
785
744
  ): ?AnyParseNode {
786
- // Switch to specified mode
787
- const outerMode = this.mode;
788
- if (mode) {
789
- this.switchMode(mode);
790
- }
791
- // Consume spaces if requested, crucially *after* we switch modes,
792
- // so that the next non-space token is parsed in the correct mode.
793
- if (consumeSpaces) {
794
- this.consumeSpaces();
795
- }
796
- // Get first token
797
745
  const firstToken = this.fetch();
798
746
  const text = firstToken.text;
799
747
 
800
748
  let result;
801
749
  // Try to parse an open brace or \begingroup
802
- if (optional ? text === "[" : text === "{" || text === "\\begingroup") {
750
+ if (text === "{" || text === "\\begingroup") {
803
751
  this.consume();
804
- const groupEnd = Parser.endOfGroup[text];
805
- // Start a new group namespace
752
+ const groupEnd = text === "{" ? "}" : "\\endgroup";
753
+
806
754
  this.gullet.beginGroup();
807
755
  // If we get a brace, parse an expression
808
756
  const expression = this.parseExpression(false, groupEnd);
809
757
  const lastToken = this.fetch();
810
- // Check that we got a matching closing brace
811
- this.expect(groupEnd);
812
- // End group namespace
758
+ this.expect(groupEnd); // Check that we got a matching closing brace
813
759
  this.gullet.endGroup();
814
760
  result = {
815
761
  type: "ordgroup",
@@ -822,13 +768,10 @@ export default class Parser {
822
768
  // use-begingroup-instead-of-bgroup
823
769
  semisimple: text === "\\begingroup" || undefined,
824
770
  };
825
- } else if (optional) {
826
- // Return nothing for an optional group
827
- result = null;
828
771
  } else {
829
772
  // If there exists a function with this name, parse the function.
830
773
  // Otherwise, just return a nucleus
831
- result = this.parseFunction(breakOnTokenText, name, greediness) ||
774
+ result = this.parseFunction(breakOnTokenText, name) ||
832
775
  this.parseSymbol();
833
776
  if (result == null && text[0] === "\\" &&
834
777
  !implicitCommands.hasOwnProperty(text)) {
@@ -840,11 +783,6 @@ export default class Parser {
840
783
  this.consume();
841
784
  }
842
785
  }
843
-
844
- // Switch mode back
845
- if (mode) {
846
- this.switchMode(outerMode);
847
- }
848
786
  return result;
849
787
  }
850
788
 
@@ -975,6 +913,7 @@ export default class Parser {
975
913
  text,
976
914
  };
977
915
  }
916
+ // $FlowFixMe
978
917
  symbol = s;
979
918
  } else if (text.charCodeAt(0) >= 0x80) { // no symbol for e.g. ^
980
919
  if (this.settings.strict) {
@@ -1025,10 +964,12 @@ export default class Parser {
1025
964
  label: command,
1026
965
  isStretchy: false,
1027
966
  isShifty: true,
967
+ // $FlowFixMe
1028
968
  base: symbol,
1029
969
  };
1030
970
  }
1031
971
  }
972
+ // $FlowFixMe
1032
973
  return symbol;
1033
974
  }
1034
975
  }
package/src/Settings.js CHANGED
@@ -155,7 +155,7 @@ export default class Settings {
155
155
  * This is for the second category of `errorCode`s listed in the README.
156
156
  */
157
157
  useStrictBehavior(errorCode: string, errorMsg: string,
158
- token?: Token | AnyParseNode) {
158
+ token?: Token | AnyParseNode): boolean {
159
159
  let strict = this.strict;
160
160
  if (typeof strict === "function") {
161
161
  // Allow return value of strict function to be boolean or string
@@ -193,7 +193,7 @@ export default class Settings {
193
193
  * If `context` has a `url` field, a `protocol` field will automatically
194
194
  * get added by this function (changing the specified object).
195
195
  */
196
- isTrusted(context: AnyTrustContext) {
196
+ isTrusted(context: AnyTrustContext): boolean {
197
197
  if (context.url && !context.protocol) {
198
198
  context.protocol = utils.protocolFromUrl(context.url);
199
199
  }
package/src/Style.js CHANGED
@@ -123,8 +123,8 @@ const text = [D, Dc, T, Tc, T, Tc, T, Tc];
123
123
 
124
124
  // We only export some of the styles.
125
125
  export default {
126
- DISPLAY: styles[D],
127
- TEXT: styles[T],
128
- SCRIPT: styles[S],
129
- SCRIPTSCRIPT: styles[SS],
126
+ DISPLAY: (styles[D]: Style),
127
+ TEXT: (styles[T]: Style),
128
+ SCRIPT: (styles[S]: Style),
129
+ SCRIPTSCRIPT: (styles[SS]: Style),
130
130
  };
package/src/Token.js CHANGED
@@ -41,7 +41,7 @@ export class Token {
41
41
  range(
42
42
  endToken: Token, // last token of the range, inclusive
43
43
  text: string, // the text of the newly constructed token
44
- ) {
44
+ ): Token {
45
45
  return new Token(text, SourceLocation.range(this, endToken));
46
46
  }
47
47
  }
@@ -243,6 +243,15 @@ const canCombine = (prev: SymbolNode, next: SymbolNode) => {
243
243
  return false;
244
244
  }
245
245
 
246
+ // If prev and next both are just "mbin"s or "mord"s we don't combine them
247
+ // so that the proper spacing can be preserved.
248
+ if (prev.classes.length === 1) {
249
+ const cls = prev.classes[0];
250
+ if (cls === "mbin" || cls === "mord") {
251
+ return false;
252
+ }
253
+ }
254
+
246
255
  for (const style in prev.style) {
247
256
  if (prev.style.hasOwnProperty(style)
248
257
  && prev.style[style] !== next.style[style]) {
@@ -261,7 +270,7 @@ const canCombine = (prev: SymbolNode, next: SymbolNode) => {
261
270
  };
262
271
 
263
272
  /**
264
- * Combine consequetive domTree.symbolNodes into a single symbolNode.
273
+ * Combine consecutive domTree.symbolNodes into a single symbolNode.
265
274
  * Note: this function mutates the argument.
266
275
  */
267
276
  const tryCombineChars = (chars: HtmlDomNode[]): HtmlDomNode[] => {
@@ -349,7 +358,7 @@ const makeLineSpan = function(
349
358
  className: string,
350
359
  options: Options,
351
360
  thickness?: number,
352
- ) {
361
+ ): DomSpan {
353
362
  const line = makeSpan([className], [], options);
354
363
  line.height = Math.max(
355
364
  thickness || options.fontMetrics().defaultRuleThickness,
@@ -369,7 +378,7 @@ const makeAnchor = function(
369
378
  classes: string[],
370
379
  children: HtmlDomNode[],
371
380
  options: Options,
372
- ) {
381
+ ): Anchor {
373
382
  const anchor = new Anchor(href, classes, children, options);
374
383
 
375
384
  sizeElementFromChildren(anchor);
@@ -728,8 +737,6 @@ const svgData: {
728
737
  oiintSize2: ["oiintSize2", 1.472, 0.659],
729
738
  oiiintSize1: ["oiiintSize1", 1.304, 0.499],
730
739
  oiiintSize2: ["oiiintSize2", 1.98, 0.659],
731
- leftParenInner: ["leftParenInner", 0.875, 0.3],
732
- rightParenInner: ["rightParenInner", 0.875, 0.3],
733
740
  };
734
741
 
735
742
  const staticSvg = function(value: string, options: Options): SvgSpan {
package/src/buildHTML.js CHANGED
@@ -74,6 +74,9 @@ export const buildExpression = function(
74
74
  }
75
75
  }
76
76
 
77
+ // Combine consecutive domTree.symbolNodes into a single symbolNode.
78
+ buildCommon.tryCombineChars(groups);
79
+
77
80
  // If `expression` is a partial group, let the parent handle spacings
78
81
  // to avoid processing groups multiple times.
79
82
  if (!isRealGroup) {
@@ -318,6 +321,12 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
318
321
  // Build the expression contained in the tree
319
322
  const expression = buildExpression(tree, options, "root");
320
323
 
324
+ let eqnNum;
325
+ if (expression.length === 2 && expression[1].hasClass("tag")) {
326
+ // An environment with automatic equation numbers, e.g. {gather}.
327
+ eqnNum = expression.pop();
328
+ }
329
+
321
330
  const children = [];
322
331
 
323
332
  // Create one base node for each chunk between potential line breaks.
@@ -373,6 +382,8 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
373
382
  );
374
383
  tagChild.classes = ["tag"];
375
384
  children.push(tagChild);
385
+ } else if (eqnNum) {
386
+ children.push(eqnNum);
376
387
  }
377
388
 
378
389
  const htmlNode = makeSpan(["katex-html"], children);
@@ -219,6 +219,7 @@ export const buildGroup = function(
219
219
  // Call the groupBuilders function
220
220
  // $FlowFixMe
221
221
  const result: MathDomNode = groupBuilders[group.type](group, options);
222
+ // $FlowFixMe
222
223
  return result;
223
224
  } else {
224
225
  throw new ParseError(
@@ -243,6 +244,11 @@ export default function buildMathML(
243
244
  ): DomSpan {
244
245
  const expression = buildExpression(tree, options);
245
246
 
247
+ // TODO: Make a pass thru the MathML similar to buildHTML.traverseNonSpaceNodes
248
+ // and add spacing nodes. This is necessary only adjacent to math operators
249
+ // like \sin or \lim or to subsup elements that contain math operators.
250
+ // MathML takes care of the other spacing issues.
251
+
246
252
  // Wrap up the expression in an mrow so it is presented in the semantics
247
253
  // tag correctly, unless it's a single <mrow> or <mtable>.
248
254
  let wrapper;
@@ -52,7 +52,6 @@ export type EnvSpec<NODETYPE: NodeType> = {|
52
52
  type: NODETYPE, // Need to use the type to avoid error. See NOTES below.
53
53
  numArgs: number,
54
54
  argTypes?: ArgType[],
55
- greediness: number,
56
55
  allowedInText: boolean,
57
56
  numOptionalArgs: number,
58
57
  handler: EnvHandler,
@@ -99,7 +98,6 @@ export default function defineEnvironment<NODETYPE: NodeType>({
99
98
  const data = {
100
99
  type,
101
100
  numArgs: props.numArgs || 0,
102
- greediness: 1,
103
101
  allowedInText: false,
104
102
  numOptionalArgs: 0,
105
103
  handler,
@@ -108,7 +106,6 @@ export default function defineEnvironment<NODETYPE: NodeType>({
108
106
  // TODO: The value type of _environments should be a type union of all
109
107
  // possible `EnvSpec<>` possibilities instead of `EnvSpec<*>`, which is
110
108
  // an existential type.
111
- // $FlowFixMe
112
109
  _environments[names[i]] = data;
113
110
  }
114
111
  if (htmlBuilder) {