katex 0.10.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 (144) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/LICENSE +1 -1
  3. package/README.md +6 -6
  4. package/cli.js +0 -0
  5. package/contrib/auto-render/auto-render.js +12 -3
  6. package/contrib/copy-tex/README.md +3 -5
  7. package/contrib/mathtex-script-type/README.md +12 -14
  8. package/contrib/mhchem/README.md +3 -1
  9. package/contrib/render-a11y-string/render-a11y-string.js +712 -0
  10. package/contrib/render-a11y-string/test/render-a11y-string-spec.js +526 -0
  11. package/dist/README.md +6 -6
  12. package/dist/contrib/auto-render.js +14 -3
  13. package/dist/contrib/auto-render.min.js +1 -1
  14. package/dist/contrib/auto-render.mjs +14 -3
  15. package/dist/contrib/mhchem.min.js +1 -1
  16. package/dist/contrib/render-a11y-string.js +870 -0
  17. package/dist/contrib/render-a11y-string.min.js +1 -0
  18. package/dist/contrib/render-a11y-string.mjs +753 -0
  19. package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
  20. package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
  21. package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  22. package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  23. package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  24. package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  25. package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  26. package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  27. package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  28. package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  29. package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  30. package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  31. package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  32. package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  33. package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  34. package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
  35. package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
  36. package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
  37. package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  38. package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  39. package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  40. package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
  41. package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
  42. package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
  43. package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
  44. package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
  45. package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
  46. package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  47. package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  48. package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  49. package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
  50. package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
  51. package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
  52. package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  53. package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  54. package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  55. package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  56. package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  57. package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  58. package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  59. package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  60. package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  61. package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
  62. package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
  63. package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
  64. package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
  65. package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
  66. package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  67. package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
  68. package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
  69. package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  70. package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
  71. package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
  72. package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  73. package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
  74. package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
  75. package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  76. package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  77. package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  78. package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  79. package/dist/katex.css +34 -10
  80. package/dist/katex.js +2906 -2115
  81. package/dist/katex.min.css +1 -1
  82. package/dist/katex.min.js +1 -1
  83. package/dist/katex.mjs +2809 -2020
  84. package/package.json +12 -11
  85. package/src/Lexer.js +1 -0
  86. package/src/MacroExpander.js +39 -10
  87. package/src/Options.js +15 -75
  88. package/src/Parser.js +152 -115
  89. package/src/Settings.js +70 -7
  90. package/src/Token.js +2 -0
  91. package/src/buildCommon.js +24 -90
  92. package/src/buildHTML.js +31 -31
  93. package/src/buildMathML.js +52 -9
  94. package/src/buildTree.js +13 -6
  95. package/src/defineFunction.js +7 -22
  96. package/src/delimiter.js +66 -27
  97. package/src/domTree.js +71 -4
  98. package/src/environments/array.js +235 -25
  99. package/src/fontMetrics.js +11 -2
  100. package/src/functions/accent.js +9 -9
  101. package/src/functions/accentunder.js +2 -2
  102. package/src/functions/arrow.js +15 -5
  103. package/src/functions/color.js +9 -38
  104. package/src/functions/def.js +184 -0
  105. package/src/functions/delimsizing.js +32 -8
  106. package/src/functions/enclose.js +33 -6
  107. package/src/functions/font.js +4 -1
  108. package/src/functions/genfrac.js +39 -27
  109. package/src/functions/horizBrace.js +6 -7
  110. package/src/functions/href.js +16 -0
  111. package/src/functions/html.js +102 -0
  112. package/src/functions/includegraphics.js +153 -0
  113. package/src/functions/lap.js +4 -7
  114. package/src/functions/math.js +1 -5
  115. package/src/functions/mclass.js +41 -2
  116. package/src/functions/op.js +27 -111
  117. package/src/functions/operatorname.js +136 -92
  118. package/src/functions/ordgroup.js +1 -1
  119. package/src/functions/overline.js +3 -2
  120. package/src/functions/phantom.js +5 -2
  121. package/src/functions/raisebox.js +4 -16
  122. package/src/functions/rule.js +20 -9
  123. package/src/functions/styling.js +0 -9
  124. package/src/functions/supsub.js +27 -7
  125. package/src/functions/symbolsOp.js +4 -0
  126. package/src/functions/tag.js +20 -4
  127. package/src/functions/text.js +4 -3
  128. package/src/functions/underline.js +3 -2
  129. package/src/functions/utils/assembleSupSub.js +110 -0
  130. package/src/functions.js +3 -0
  131. package/src/katex.less +45 -9
  132. package/src/macros.js +259 -98
  133. package/src/mathMLTree.js +6 -4
  134. package/src/parseNode.js +37 -57
  135. package/src/stretchy.js +3 -1
  136. package/src/svgGeometry.js +136 -44
  137. package/src/symbols.js +52 -69
  138. package/src/tree.js +2 -2
  139. package/src/types.js +2 -1
  140. package/src/unicodeAccents.js +3 -1
  141. package/src/unicodeSymbols.js +30 -321
  142. package/src/utils.js +10 -0
  143. package/src/wide-character.js +2 -2
  144. package/src/unicodeMake.js +0 -70
package/src/Settings.js CHANGED
@@ -16,18 +16,57 @@ export type StrictFunction =
16
16
  (errorCode: string, errorMsg: string, token?: Token | AnyParseNode) =>
17
17
  ?(boolean | string);
18
18
 
19
+ export type TrustContextTypes = {
20
+ "\\href": {|
21
+ command: "\\href",
22
+ url: string,
23
+ protocol?: string,
24
+ |},
25
+ "\\includegraphics": {|
26
+ command: "\\includegraphics",
27
+ url: string,
28
+ protocol?: string,
29
+ |},
30
+ "\\url": {|
31
+ command: "\\url",
32
+ url: string,
33
+ protocol?: string,
34
+ |},
35
+ "\\htmlClass": {|
36
+ command: "\\htmlClass",
37
+ class: string,
38
+ |},
39
+ "\\htmlId": {|
40
+ command: "\\htmlId",
41
+ id: string,
42
+ |},
43
+ "\\htmlStyle": {|
44
+ command: "\\htmlStyle",
45
+ style: string,
46
+ |},
47
+ "\\htmlData": {|
48
+ command: "\\htmlData",
49
+ attributes: {[string]: string},
50
+ |},
51
+ };
52
+ export type AnyTrustContext = $Values<TrustContextTypes>;
53
+ export type TrustFunction = (context: AnyTrustContext) => ?boolean;
54
+
19
55
  export type SettingsOptions = {
20
56
  displayMode?: boolean;
57
+ output?: "html" | "mathml" | "htmlAndMathml";
21
58
  leqno?: boolean;
22
59
  fleqn?: boolean;
23
60
  throwOnError?: boolean;
24
61
  errorColor?: string;
25
62
  macros?: MacroMap;
63
+ minRuleThickness?: number;
26
64
  colorIsTextColor?: boolean;
27
65
  strict?: boolean | "ignore" | "warn" | "error" | StrictFunction;
66
+ trust?: boolean | TrustFunction;
28
67
  maxSize?: number;
29
68
  maxExpand?: number;
30
- allowedProtocols?: string[];
69
+ globalGroup?: boolean;
31
70
  };
32
71
 
33
72
  /**
@@ -40,34 +79,42 @@ export type SettingsOptions = {
40
79
  * math (true), meaning that the math starts in \displaystyle
41
80
  * and is placed in a block with vertical margin.
42
81
  */
43
- class Settings {
82
+ export default class Settings {
44
83
  displayMode: boolean;
84
+ output: "html" | "mathml" | "htmlAndMathml";
45
85
  leqno: boolean;
46
86
  fleqn: boolean;
47
87
  throwOnError: boolean;
48
88
  errorColor: string;
49
89
  macros: MacroMap;
90
+ minRuleThickness: number;
50
91
  colorIsTextColor: boolean;
51
92
  strict: boolean | "ignore" | "warn" | "error" | StrictFunction;
93
+ trust: boolean | TrustFunction;
52
94
  maxSize: number;
53
95
  maxExpand: number;
54
- allowedProtocols: string[];
96
+ globalGroup: boolean;
55
97
 
56
98
  constructor(options: SettingsOptions) {
57
99
  // allow null options
58
100
  options = options || {};
59
101
  this.displayMode = utils.deflt(options.displayMode, false);
102
+ this.output = utils.deflt(options.output, "htmlAndMathml");
60
103
  this.leqno = utils.deflt(options.leqno, false);
61
104
  this.fleqn = utils.deflt(options.fleqn, false);
62
105
  this.throwOnError = utils.deflt(options.throwOnError, true);
63
106
  this.errorColor = utils.deflt(options.errorColor, "#cc0000");
64
107
  this.macros = options.macros || {};
108
+ this.minRuleThickness = Math.max(
109
+ 0,
110
+ utils.deflt(options.minRuleThickness, 0)
111
+ );
65
112
  this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false);
66
113
  this.strict = utils.deflt(options.strict, "warn");
114
+ this.trust = utils.deflt(options.trust, false);
67
115
  this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity));
68
116
  this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000));
69
- this.allowedProtocols = utils.deflt(options.allowedProtocols,
70
- ["http", "https", "mailto", "_relative"]);
117
+ this.globalGroup = utils.deflt(options.globalGroup, false);
71
118
  }
72
119
 
73
120
  /**
@@ -137,6 +184,22 @@ class Settings {
137
184
  return false;
138
185
  }
139
186
  }
140
- }
141
187
 
142
- export default Settings;
188
+ /**
189
+ * Check whether to test potentially dangerous input, and return
190
+ * `true` (trusted) or `false` (untrusted). The sole argument `context`
191
+ * should be an object with `command` field specifying the relevant LaTeX
192
+ * command (as a string starting with `\`), and any other arguments, etc.
193
+ * If `context` has a `url` field, a `protocol` field will automatically
194
+ * get added by this function (changing the specified object).
195
+ */
196
+ isTrusted(context: AnyTrustContext) {
197
+ if (context.url && !context.protocol) {
198
+ context.protocol = utils.protocolFromUrl(context.url);
199
+ }
200
+ const trust = typeof this.trust === "function"
201
+ ? this.trust(context)
202
+ : this.trust;
203
+ return Boolean(trust);
204
+ }
205
+ }
package/src/Token.js CHANGED
@@ -23,6 +23,8 @@ export interface LexerInterface {input: string, tokenRegex: RegExp}
23
23
  export class Token {
24
24
  text: string;
25
25
  loc: ?SourceLocation;
26
+ noexpand: ?boolean; // don't expand the token
27
+ treatAsRelax: ?boolean; // used in \noexpand
26
28
 
27
29
  constructor(
28
30
  text: string, // the text of this token
@@ -8,7 +8,6 @@
8
8
  import {SymbolNode, Anchor, Span, PathNode, SvgNode, createClass} from "./domTree";
9
9
  import {getCharacterMetrics} from "./fontMetrics";
10
10
  import symbols, {ligatures} from "./symbols";
11
- import utils from "./utils";
12
11
  import {wideCharacterFont} from "./wide-character";
13
12
  import {calculateSize} from "./units";
14
13
  import {DocumentFragment} from "./tree";
@@ -21,13 +20,6 @@ import type {documentFragment as HtmlDocumentFragment} from "./domTree";
21
20
  import type {HtmlDomNode, DomSpan, SvgSpan, CssStyle} from "./domTree";
22
21
  import type {Measurement} from "./units";
23
22
 
24
- // The following have to be loaded from Main-Italic font, using class mathit
25
- const mathitLetters = [
26
- "\\imath", "ı", // dotless i
27
- "\\jmath", "ȷ", // dotless j
28
- "\\pounds", "\\mathsterling", "\\textsterling", "£", // pounds symbol
29
- ];
30
-
31
23
  /**
32
24
  * Looks up the given symbol in fontMetrics, after applying any symbol
33
25
  * replacements defined in symbol.js
@@ -80,9 +72,8 @@ const makeSymbol = function(
80
72
  metrics.width, classes);
81
73
  } else {
82
74
  // TODO(emily): Figure out a good way to only print this in development
83
- typeof console !== "undefined" && console.warn(
84
- "No character metrics for '" + value + "' in style '" +
85
- fontName + "'");
75
+ typeof console !== "undefined" && console.warn("No character metrics " +
76
+ `for '${value}' in style '${fontName}' and mode '${mode}'`);
86
77
  symbolNode = new SymbolNode(value, 0, 0, 0, 0, 0, classes);
87
78
  }
88
79
 
@@ -103,13 +94,11 @@ const makeSymbol = function(
103
94
  /**
104
95
  * Makes a symbol in Main-Regular or AMS-Regular.
105
96
  * Used for rel, bin, open, close, inner, and punct.
106
- *
107
- * TODO(#953): Make `options` mandatory and always pass it in.
108
97
  */
109
98
  const mathsym = function(
110
99
  value: string,
111
100
  mode: Mode,
112
- options?: Options,
101
+ options: Options,
113
102
  classes?: string[] = [],
114
103
  ): SymbolNode {
115
104
  // Decide what font to render the symbol in by its entry in the symbols
@@ -119,7 +108,7 @@ const mathsym = function(
119
108
  // text ordinal and is therefore not present as a symbol in the symbols
120
109
  // table for text, as well as a special case for boldsymbol because it
121
110
  // can be used for bold + and -
122
- if ((options && options.font && options.font === "boldsymbol") &&
111
+ if (options.font === "boldsymbol" &&
123
112
  lookupSymbol(value, "Main-Bold", mode).metrics) {
124
113
  return makeSymbol(value, "Main-Bold", mode, options,
125
114
  classes.concat(["mathbf"]));
@@ -131,63 +120,6 @@ const mathsym = function(
131
120
  }
132
121
  };
133
122
 
134
- /**
135
- * Determines which of the two font names (Main-Italic and Math-Italic) and
136
- * corresponding style tags (maindefault or mathit) to use for default math font,
137
- * depending on the symbol.
138
- */
139
- const mathdefault = function(
140
- value: string,
141
- mode: Mode,
142
- options: Options,
143
- classes: string[],
144
- ): {| fontName: string, fontClass: string |} {
145
- if (/[0-9]/.test(value.charAt(0)) ||
146
- // glyphs for \imath and \jmath do not exist in Math-Italic so we
147
- // need to use Main-Italic instead
148
- utils.contains(mathitLetters, value)) {
149
- return {
150
- fontName: "Main-Italic",
151
- fontClass: "mathit",
152
- };
153
- } else {
154
- return {
155
- fontName: "Math-Italic",
156
- fontClass: "mathdefault",
157
- };
158
- }
159
- };
160
-
161
- /**
162
- * Determines which of the font names (Main-Italic, Math-Italic, and Caligraphic)
163
- * and corresponding style tags (mathit, mathdefault, or mathcal) to use for font
164
- * "mathnormal", depending on the symbol. Use this function instead of fontMap for
165
- * font "mathnormal".
166
- */
167
- const mathnormal = function(
168
- value: string,
169
- mode: Mode,
170
- options: Options,
171
- classes: string[],
172
- ): {| fontName: string, fontClass: string |} {
173
- if (utils.contains(mathitLetters, value)) {
174
- return {
175
- fontName: "Main-Italic",
176
- fontClass: "mathit",
177
- };
178
- } else if (/[0-9]/.test(value.charAt(0))) {
179
- return {
180
- fontName: "Caligraphic-Regular",
181
- fontClass: "mathcal",
182
- };
183
- } else {
184
- return {
185
- fontName: "Math-Italic",
186
- fontClass: "mathdefault",
187
- };
188
- }
189
- };
190
-
191
123
  /**
192
124
  * Determines which of the two font names (Main-Bold and Math-BoldItalic) and
193
125
  * corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol",
@@ -199,8 +131,10 @@ const boldsymbol = function(
199
131
  mode: Mode,
200
132
  options: Options,
201
133
  classes: string[],
134
+ type: "mathord" | "textord",
202
135
  ): {| fontName: string, fontClass: string |} {
203
- if (lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
136
+ if (type !== "textord" &&
137
+ lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
204
138
  return {
205
139
  fontName: "Math-BoldItalic",
206
140
  fontClass: "boldsymbol",
@@ -239,15 +173,10 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
239
173
  } else if (fontOrFamily) {
240
174
  let fontName;
241
175
  let fontClasses;
242
- if (fontOrFamily === "boldsymbol" || fontOrFamily === "mathnormal") {
243
- const fontData = fontOrFamily === "boldsymbol"
244
- ? boldsymbol(text, mode, options, classes)
245
- : mathnormal(text, mode, options, classes);
176
+ if (fontOrFamily === "boldsymbol") {
177
+ const fontData = boldsymbol(text, mode, options, classes, type);
246
178
  fontName = fontData.fontName;
247
179
  fontClasses = [fontData.fontClass];
248
- } else if (utils.contains(mathitLetters, text)) {
249
- fontName = "Main-Italic";
250
- fontClasses = ["mathit"];
251
180
  } else if (isFont) {
252
181
  fontName = fontMap[fontOrFamily].fontName;
253
182
  fontClasses = [fontOrFamily];
@@ -274,9 +203,8 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
274
203
 
275
204
  // Makes a symbol in the default font for mathords and textords.
276
205
  if (type === "mathord") {
277
- const fontLookup = mathdefault(text, mode, options, classes);
278
- return makeSymbol(text, fontLookup.fontName, mode, options,
279
- classes.concat([fontLookup.fontClass]));
206
+ return makeSymbol(text, "Math-Italic", mode, options,
207
+ classes.concat(["mathnormal"]));
280
208
  } else if (type === "textord") {
281
209
  const font = symbols[mode][text] && symbols[mode][text].font;
282
210
  if (font === "ams") {
@@ -423,7 +351,10 @@ const makeLineSpan = function(
423
351
  thickness?: number,
424
352
  ) {
425
353
  const line = makeSpan([className], [], options);
426
- line.height = thickness || options.fontMetrics().defaultRuleThickness;
354
+ line.height = Math.max(
355
+ thickness || options.fontMetrics().defaultRuleThickness,
356
+ options.minRuleThickness,
357
+ );
427
358
  line.style.borderBottomWidth = line.height + "em";
428
359
  line.maxFontSize = 1.0;
429
360
  return line;
@@ -752,13 +683,14 @@ const fontMap: {[string]: {| variant: FontVariant, fontName: string |}} = {
752
683
  variant: "italic",
753
684
  fontName: "Main-Italic",
754
685
  },
686
+ "mathnormal": {
687
+ variant: "italic",
688
+ fontName: "Math-Italic",
689
+ },
755
690
 
756
- // Default math font, "mathnormal" and "boldsymbol" are missing because they
757
- // require the use of several fonts: Main-Italic and Math-Italic for default
758
- // math font, Main-Italic, Math-Italic, Caligraphic for "mathnormal", and
759
- // Math-BoldItalic and Main-Bold for "boldsymbol". This is handled by a
760
- // special case in makeOrd which ends up calling mathdefault, mathnormal,
761
- // and boldsymbol.
691
+ // "boldsymbol" is missing because they require the use of multiple fonts:
692
+ // Math-BoldItalic and Main-Bold. This is handled by a special case in
693
+ // makeOrd which ends up calling boldsymbol.
762
694
 
763
695
  // families
764
696
  "mathbb": {
@@ -796,6 +728,8 @@ const svgData: {
796
728
  oiintSize2: ["oiintSize2", 1.472, 0.659],
797
729
  oiiintSize1: ["oiiintSize1", 1.304, 0.499],
798
730
  oiiintSize2: ["oiiintSize2", 1.98, 0.659],
731
+ leftParenInner: ["leftParenInner", 0.875, 0.3],
732
+ rightParenInner: ["rightParenInner", 0.875, 0.3],
799
733
  };
800
734
 
801
735
  const staticSvg = function(value: string, options: Options): SvgSpan {
package/src/buildHTML.js CHANGED
@@ -9,9 +9,8 @@
9
9
  import ParseError from "./ParseError";
10
10
  import Style from "./Style";
11
11
  import buildCommon from "./buildCommon";
12
- import {Anchor} from "./domTree";
12
+ import {Span, Anchor} from "./domTree";
13
13
  import utils from "./utils";
14
- import {checkNodeType} from "./parseNode";
15
14
  import {spacings, tightSpacings} from "./spacingData";
16
15
  import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
17
16
  import {DocumentFragment} from "./tree";
@@ -60,7 +59,7 @@ type DomType = $Keys<typeof DomEnum>;
60
59
  export const buildExpression = function(
61
60
  expression: AnyParseNode[],
62
61
  options: Options,
63
- isRealGroup: boolean,
62
+ isRealGroup: boolean | "root",
64
63
  surrounding: [?DomType, ?DomType] = [null, null],
65
64
  ): HtmlDomNode[] {
66
65
  // Parse expressions into `groups`.
@@ -68,7 +67,7 @@ export const buildExpression = function(
68
67
  for (let i = 0; i < expression.length; i++) {
69
68
  const output = buildGroup(expression[i], options);
70
69
  if (output instanceof DocumentFragment) {
71
- const children: HtmlDomNode[] = output.children;
70
+ const children: $ReadOnlyArray<HtmlDomNode> = output.children;
72
71
  groups.push(...children);
73
72
  } else {
74
73
  groups.push(output);
@@ -83,11 +82,8 @@ export const buildExpression = function(
83
82
 
84
83
  let glueOptions = options;
85
84
  if (expression.length === 1) {
86
- const node = checkNodeType(expression[0], "sizing") ||
87
- checkNodeType(expression[0], "styling");
88
- if (!node) {
89
- // No match.
90
- } else if (node.type === "sizing") {
85
+ const node = expression[0];
86
+ if (node.type === "sizing") {
91
87
  glueOptions = options.havingSize(node.size);
92
88
  } else if (node.type === "styling") {
93
89
  glueOptions = options.havingStyle(styleMap[node.style]);
@@ -106,6 +102,7 @@ export const buildExpression = function(
106
102
 
107
103
  // Before determining what spaces to insert, perform bin cancellation.
108
104
  // Binary operators change to ordinary symbols in some contexts.
105
+ const isRoot = (isRealGroup === "root");
109
106
  traverseNonSpaceNodes(groups, (node, prev) => {
110
107
  const prevType = prev.classes[0];
111
108
  const type = node.classes[0];
@@ -114,7 +111,7 @@ export const buildExpression = function(
114
111
  } else if (type === "mbin" && utils.contains(binLeftCanceller, prevType)) {
115
112
  node.classes[0] = "mord";
116
113
  }
117
- }, {node: dummyPrev}, dummyNext);
114
+ }, {node: dummyPrev}, dummyNext, isRoot);
118
115
 
119
116
  traverseNonSpaceNodes(groups, (node, prev) => {
120
117
  const prevType = getTypeOfDomTree(prev);
@@ -127,7 +124,7 @@ export const buildExpression = function(
127
124
  if (space) { // Insert glue (spacing) after the `prev`.
128
125
  return buildCommon.makeGlue(space, glueOptions);
129
126
  }
130
- }, {node: dummyPrev}, dummyNext);
127
+ }, {node: dummyPrev}, dummyNext, isRoot);
131
128
 
132
129
  return groups;
133
130
  };
@@ -145,6 +142,7 @@ const traverseNonSpaceNodes = function(
145
142
  insertAfter?: HtmlDomNode => void,
146
143
  |},
147
144
  next: ?HtmlDomNode,
145
+ isRoot: boolean,
148
146
  ) {
149
147
  if (next) { // temporarily append the right node, if exists
150
148
  nodes.push(next);
@@ -154,27 +152,32 @@ const traverseNonSpaceNodes = function(
154
152
  const node = nodes[i];
155
153
  const partialGroup = checkPartialGroup(node);
156
154
  if (partialGroup) { // Recursive DFS
157
- traverseNonSpaceNodes(partialGroup.children, callback, prev);
155
+ // $FlowFixMe: make nodes a $ReadOnlyArray by returning a new array
156
+ traverseNonSpaceNodes(partialGroup.children,
157
+ callback, prev, null, isRoot);
158
158
  continue;
159
159
  }
160
160
 
161
161
  // Ignore explicit spaces (e.g., \;, \,) when determining what implicit
162
162
  // spacing should go between atoms of different classes
163
- if (node.classes[0] === "mspace") {
164
- continue;
165
- }
166
-
167
- const result = callback(node, prev.node);
168
- if (result) {
169
- if (prev.insertAfter) {
170
- prev.insertAfter(result);
171
- } else { // insert at front
172
- nodes.unshift(result);
173
- i++;
163
+ const nonspace = !node.hasClass("mspace");
164
+ if (nonspace) {
165
+ const result = callback(node, prev.node);
166
+ if (result) {
167
+ if (prev.insertAfter) {
168
+ prev.insertAfter(result);
169
+ } else { // insert at front
170
+ nodes.unshift(result);
171
+ i++;
172
+ }
174
173
  }
175
174
  }
176
175
 
177
- prev.node = node;
176
+ if (nonspace) {
177
+ prev.node = node;
178
+ } else if (isRoot && node.hasClass("newline")) {
179
+ prev.node = makeSpan(["leftmost"]); // treat like beginning of line
180
+ }
178
181
  prev.insertAfter = (index => n => {
179
182
  nodes.splice(index + 1, 0, n);
180
183
  i++;
@@ -188,8 +191,9 @@ const traverseNonSpaceNodes = function(
188
191
  // Check if given node is a partial group, i.e., does not affect spacing around.
189
192
  const checkPartialGroup = function(
190
193
  node: HtmlDomNode,
191
- ): ?(DocumentFragment<HtmlDomNode> | Anchor) {
192
- if (node instanceof DocumentFragment || node instanceof Anchor) {
194
+ ): ?(DocumentFragment<HtmlDomNode> | Anchor | DomSpan) {
195
+ if (node instanceof DocumentFragment || node instanceof Anchor
196
+ || (node instanceof Span && node.hasClass("enclosing"))) {
193
197
  return node;
194
198
  }
195
199
  return null;
@@ -291,10 +295,6 @@ function buildHTMLUnbreakable(children, options) {
291
295
  // Add strut, which ensures that the top of the HTML element falls at
292
296
  // the height of the expression, and the bottom of the HTML element
293
297
  // falls at the depth of the expression.
294
- // We used to have separate top and bottom struts, where the bottom strut
295
- // would like to use `vertical-align: top`, but in IE 9 this lowers the
296
- // baseline of the box to the bottom of this strut (instead of staying in
297
- // the normal place) so we use an absolute value for vertical-align instead.
298
298
  const strut = makeSpan(["strut"]);
299
299
  strut.style.height = (body.height + body.depth) + "em";
300
300
  strut.style.verticalAlign = -body.depth + "em";
@@ -316,7 +316,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
316
316
  }
317
317
 
318
318
  // Build the expression contained in the tree
319
- const expression = buildExpression(tree, options, true);
319
+ const expression = buildExpression(tree, options, "root");
320
320
 
321
321
  const children = [];
322
322
 
@@ -44,7 +44,7 @@ export const makeText = function(
44
44
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
45
45
  * unless the array has length 1. Always returns a single node.
46
46
  */
47
- export const makeRow = function(body: MathDomNode[]): MathDomNode {
47
+ export const makeRow = function(body: $ReadOnlyArray<MathDomNode>): MathDomNode {
48
48
  if (body.length === 1) {
49
49
  return body[0];
50
50
  } else {
@@ -93,7 +93,20 @@ export const getVariant = function(
93
93
  if (font === "mathit") {
94
94
  return "italic";
95
95
  } else if (font === "boldsymbol") {
96
- return "bold-italic";
96
+ return group.type === "textord" ? "bold" : "bold-italic";
97
+ } else if (font === "mathbf") {
98
+ return "bold";
99
+ } else if (font === "mathbb") {
100
+ return "double-struck";
101
+ } else if (font === "mathfrak") {
102
+ return "fraktur";
103
+ } else if (font === "mathscr" || font === "mathcal") {
104
+ // MathML makes no distinction between script and caligrahpic
105
+ return "script";
106
+ } else if (font === "mathsf") {
107
+ return "sans-serif";
108
+ } else if (font === "mathtt") {
109
+ return "monospace";
97
110
  }
98
111
 
99
112
  let text = group.text;
@@ -121,7 +134,19 @@ export const getVariant = function(
121
134
  export const buildExpression = function(
122
135
  expression: AnyParseNode[],
123
136
  options: Options,
124
- ): MathDomNode[] {
137
+ isOrdgroup?: boolean,
138
+ ): MathNode[] {
139
+ if (expression.length === 1) {
140
+ const group = buildGroup(expression[0], options);
141
+ if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
142
+ // When TeX writers want to suppress spacing on an operator,
143
+ // they often put the operator by itself inside braces.
144
+ group.setAttribute("lspace", "0em");
145
+ group.setAttribute("rspace", "0em");
146
+ }
147
+ return [group];
148
+ }
149
+
125
150
  const groups = [];
126
151
  let lastGroup;
127
152
  for (let i = 0; i < expression.length; i++) {
@@ -145,14 +170,24 @@ export const buildExpression = function(
145
170
  lastGroup.children.push(...group.children);
146
171
  continue;
147
172
  }
173
+ } else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) {
174
+ const lastChild = lastGroup.children[0];
175
+ if (lastChild instanceof TextNode && lastChild.text === '\u0338' &&
176
+ (group.type === 'mo' || group.type === 'mi' ||
177
+ group.type === 'mn')) {
178
+ const child = group.children[0];
179
+ if (child instanceof TextNode && child.text.length > 0) {
180
+ // Overlay with combining character long solidus
181
+ child.text = child.text.slice(0, 1) + "\u0338" +
182
+ child.text.slice(1);
183
+ groups.pop();
184
+ }
185
+ }
148
186
  }
149
187
  }
150
188
  groups.push(group);
151
189
  lastGroup = group;
152
190
  }
153
-
154
- // TODO(kevinb): combine \\not with mrels and mords
155
-
156
191
  return groups;
157
192
  };
158
193
 
@@ -163,8 +198,9 @@ export const buildExpression = function(
163
198
  export const buildExpressionRow = function(
164
199
  expression: AnyParseNode[],
165
200
  options: Options,
201
+ isOrdgroup?: boolean,
166
202
  ): MathDomNode {
167
- return makeRow(buildExpression(expression, options));
203
+ return makeRow(buildExpression(expression, options, isOrdgroup));
168
204
  };
169
205
 
170
206
  /**
@@ -174,7 +210,7 @@ export const buildExpressionRow = function(
174
210
  export const buildGroup = function(
175
211
  group: ?AnyParseNode,
176
212
  options: Options,
177
- ): MathDomNode {
213
+ ): MathNode {
178
214
  if (!group) {
179
215
  return new mathMLTree.MathNode("mrow");
180
216
  }
@@ -202,6 +238,8 @@ export default function buildMathML(
202
238
  tree: AnyParseNode[],
203
239
  texExpression: string,
204
240
  options: Options,
241
+ isDisplayMode: boolean,
242
+ forMathmlOnly: boolean,
205
243
  ): DomSpan {
206
244
  const expression = buildExpression(tree, options);
207
245
 
@@ -225,11 +263,16 @@ export default function buildMathML(
225
263
  "semantics", [wrapper, annotation]);
226
264
 
227
265
  const math = new mathMLTree.MathNode("math", [semantics]);
266
+ math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
267
+ if (isDisplayMode) {
268
+ math.setAttribute("display", "block");
269
+ }
228
270
 
229
271
  // You can't style <math> nodes, so we wrap the node in a span.
230
272
  // NOTE: The span class is not typed to have <math> nodes as children, and
231
273
  // we don't want to make the children type more generic since the children
232
274
  // of span are expected to have more fields in `buildHtml` contexts.
275
+ const wrapperClass = forMathmlOnly ? "katex" : "katex-mathml";
233
276
  // $FlowFixMe
234
- return buildCommon.makeSpan(["katex-mathml"], [math]);
277
+ return buildCommon.makeSpan([wrapperClass], [math]);
235
278
  }
package/src/buildTree.js CHANGED
@@ -13,6 +13,7 @@ const optionsFromSettings = function(settings: Settings) {
13
13
  return new Options({
14
14
  style: (settings.displayMode ? Style.DISPLAY : Style.TEXT),
15
15
  maxSize: settings.maxSize,
16
+ minRuleThickness: settings.minRuleThickness,
16
17
  });
17
18
  };
18
19
 
@@ -36,12 +37,18 @@ export const buildTree = function(
36
37
  settings: Settings,
37
38
  ): DomSpan {
38
39
  const options = optionsFromSettings(settings);
39
- const mathMLNode = buildMathML(tree, expression, options);
40
- const htmlNode = buildHTML(tree, options);
41
-
42
- const katexNode = buildCommon.makeSpan(["katex"], [
43
- mathMLNode, htmlNode,
44
- ]);
40
+ let katexNode;
41
+ if (settings.output === "mathml") {
42
+ return buildMathML(tree, expression, options, settings.displayMode, true);
43
+ } else if (settings.output === "html") {
44
+ const htmlNode = buildHTML(tree, options);
45
+ katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
46
+ } else {
47
+ const mathMLNode = buildMathML(tree, expression, options,
48
+ settings.displayMode, false);
49
+ const htmlNode = buildHTML(tree, options);
50
+ katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, htmlNode]);
51
+ }
45
52
 
46
53
  return displayWrap(katexNode, settings);
47
54
  };