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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katex",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "Fast math typesetting for the web.",
5
5
  "main": "dist/katex.js",
6
6
  "homepage": "https://katex.org",
@@ -28,10 +28,11 @@
28
28
  "babel-jest": "^24.1.0",
29
29
  "babel-loader": "^8.0.5",
30
30
  "babel-plugin-istanbul": "^5.1.1",
31
+ "babel-plugin-preval": "^3.0.1",
31
32
  "babel-plugin-version-inline": "^1.0.0",
32
33
  "benchmark": "^2.1.4",
33
- "browserslist": "^4.4.1",
34
- "caniuse-lite": "^1.0.30000938",
34
+ "browserslist": "^4.7.1",
35
+ "caniuse-lite": "^1.0.30001002",
35
36
  "codecov": "^3.2.0",
36
37
  "css-loader": "^3.0.0",
37
38
  "cssnano": "^4.1.10",
@@ -75,7 +76,7 @@
75
76
  },
76
77
  "bin": "cli.js",
77
78
  "scripts": {
78
- "test": "yarn prestart && yarn test:lint && yarn test:flow && yarn test:jest",
79
+ "test": "yarn test:lint && yarn test:flow && yarn test:jest",
79
80
  "test:lint": "yarn test:lint:js && yarn test:lint:css",
80
81
  "test:lint:js": "eslint *.js src static test contrib dockers website",
81
82
  "test:lint:css": "stylelint src/katex.less static/main.css contrib/**/*.css website/static/**/*.css",
@@ -85,14 +86,13 @@
85
86
  "test:jest:update": "jest --updateSnapshot",
86
87
  "test:jest:coverage": "jest --coverage",
87
88
  "test:screenshots": "yarn test:screenshots:update --verify",
88
- "test:screenshots:update": "yarn prestart && dockers/screenshotter/screenshotter.sh",
89
- "test:perf": "yarn prestart && NODE_ENV=test node test/perf-test.js",
89
+ "test:screenshots:update": "dockers/screenshotter/screenshotter.sh",
90
+ "test:perf": "NODE_ENV=test node test/perf-test.js",
90
91
  "clean": "rm -rf dist/ node_modules/",
91
92
  "clean-install": "yarn clean && yarn",
92
- "prestart": "node src/unicodeMake.js",
93
93
  "start": "webpack-dev-server --hot --config webpack.dev.js",
94
94
  "analyze": "webpack --config webpack.analyze.js",
95
- "build": "yarn prestart && rimraf dist/ && mkdirp dist && cp README.md dist && rollup -c && webpack",
95
+ "build": "rimraf dist/ && mkdirp dist && cp README.md dist && rollup -c && webpack",
96
96
  "watch": "yarn build --watch",
97
97
  "dist": "yarn test && yarn build && yarn dist:zip",
98
98
  "dist:zip": "rimraf katex/ katex.tar.gz katex.zip && cp -R dist katex && tar czf katex.tar.gz katex && zip -rq katex.zip katex && rimraf katex/"
@@ -111,7 +111,7 @@
111
111
  "collectCoverageFrom": [
112
112
  "src/**/*.js",
113
113
  "contrib/**/*.js",
114
- "!src/unicodeMake.js",
114
+ "!src/unicodeSymbols.js",
115
115
  "!contrib/mhchem/**"
116
116
  ],
117
117
  "setupFilesAfterEnv": [
@@ -180,13 +180,19 @@ export default class MacroExpander implements MacroContextInterface {
180
180
  * At the moment, macro expansion doesn't handle delimited macros,
181
181
  * i.e. things like those defined by \def\foo#1\end{…}.
182
182
  * See the TeX book page 202ff. for details on how those should behave.
183
+ *
184
+ * If expandableOnly, only expandable tokens are expanded and
185
+ * an undefined control sequence results in an error.
183
186
  */
184
- expandOnce(): Token | Token[] {
187
+ expandOnce(expandableOnly?: boolean): Token | Token[] {
185
188
  const topToken = this.popToken();
186
189
  const name = topToken.text;
187
- const expansion = this._getExpansion(name);
188
- if (expansion == null) { // mainly checking for undefined here
189
- // Fully expanded
190
+ const expansion = !topToken.noexpand ? this._getExpansion(name) : null;
191
+ if (expansion == null || (expandableOnly && expansion.unexpandable)) {
192
+ if (expandableOnly && expansion == null &&
193
+ name[0] === "\\" && !this.isDefined(name)) {
194
+ throw new ParseError("Undefined control sequence: " + name);
195
+ }
190
196
  this.pushToken(topToken);
191
197
  return topToken;
192
198
  }
@@ -248,7 +254,9 @@ export default class MacroExpander implements MacroContextInterface {
248
254
  if (expanded instanceof Token) {
249
255
  // \relax stops the expansion, but shouldn't get returned (a
250
256
  // null return value couldn't get implemented as a function).
251
- if (expanded.text === "\\relax") {
257
+ // the token after \noexpand is interpreted as if its meaning
258
+ // were ‘\relax’
259
+ if (expanded.text === "\\relax" || expanded.treatAsRelax) {
252
260
  this.stack.pop();
253
261
  } else {
254
262
  return this.stack.pop(); // === expanded
@@ -266,16 +274,26 @@ export default class MacroExpander implements MacroContextInterface {
266
274
  * tokens, or return `undefined` if no such macro is defined.
267
275
  */
268
276
  expandMacro(name: string): Token[] | void {
269
- if (!this.macros.get(name)) {
270
- return undefined;
271
- }
277
+ return this.macros.has(name)
278
+ ? this.expandTokens([new Token(name)]) : undefined;
279
+ }
280
+
281
+ /**
282
+ * Fully expand the given token stream and return the resulting list of tokens
283
+ */
284
+ expandTokens(tokens: Token[]): Token[] {
272
285
  const output = [];
273
286
  const oldStackLength = this.stack.length;
274
- this.pushToken(new Token(name));
287
+ this.pushTokens(tokens);
275
288
  while (this.stack.length > oldStackLength) {
276
- const expanded = this.expandOnce();
289
+ const expanded = this.expandOnce(true); // expand only expandable tokens
277
290
  // expandOnce returns Token if and only if it's fully expanded.
278
291
  if (expanded instanceof Token) {
292
+ if (expanded.treatAsRelax) {
293
+ // the expansion of \noexpand is the token itself
294
+ expanded.noexpand = false;
295
+ expanded.treatAsRelax = false;
296
+ }
279
297
  output.push(this.stack.pop());
280
298
  }
281
299
  }
@@ -342,4 +360,15 @@ export default class MacroExpander implements MacroContextInterface {
342
360
  symbols.text.hasOwnProperty(name) ||
343
361
  implicitCommands.hasOwnProperty(name);
344
362
  }
363
+
364
+ /**
365
+ * Determine whether a command is expandable.
366
+ */
367
+ isExpandable(name: string): boolean {
368
+ const macro = this.macros.get(name);
369
+ return macro != null ? typeof macro === "string"
370
+ || typeof macro === "function" || !macro.unexpandable
371
+ // TODO(ylem): #2085
372
+ : functions.hasOwnProperty(name)/* && !functions[name].primitive*/;
373
+ }
345
374
  }
package/src/Parser.js CHANGED
@@ -5,14 +5,16 @@ import MacroExpander, {implicitCommands} from "./MacroExpander";
5
5
  import symbols, {ATOMS, extraLatin} from "./symbols";
6
6
  import {validUnit} from "./units";
7
7
  import {supportedCodepoint} from "./unicodeScripts";
8
- import unicodeAccents from "./unicodeAccents";
9
- import unicodeSymbols from "./unicodeSymbols";
10
- import {checkNodeType} from "./parseNode";
11
8
  import ParseError from "./ParseError";
12
9
  import {combiningDiacriticalMarksEndRegex} from "./Lexer";
13
10
  import Settings from "./Settings";
14
11
  import SourceLocation from "./SourceLocation";
15
12
  import {Token} from "./Token";
13
+
14
+ // Pre-evaluate both modules as unicodeSymbols require String.normalize()
15
+ import unicodeAccents from /*preval*/ "./unicodeAccents";
16
+ import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
+
16
18
  import type {ParseNode, AnyParseNode, SymbolParseNode, UnsupportedCmdParseNode}
17
19
  from "./parseNode";
18
20
  import type {Atom, Group} from "./symbols";
@@ -115,9 +117,11 @@ export default class Parser {
115
117
  * Main parsing function, which parses an entire input.
116
118
  */
117
119
  parse(): AnyParseNode[] {
118
- // Create a group namespace for the math expression.
119
- // (LaTeX creates a new group for every $...$, $$...$$, \[...\].)
120
- this.gullet.beginGroup();
120
+ if (!this.settings.globalGroup) {
121
+ // Create a group namespace for the math expression.
122
+ // (LaTeX creates a new group for every $...$, $$...$$, \[...\].)
123
+ this.gullet.beginGroup();
124
+ }
121
125
 
122
126
  // Use old \color behavior (same as LaTeX's \textcolor) if requested.
123
127
  // We do this within the group for the math expression, so it doesn't
@@ -133,7 +137,9 @@ export default class Parser {
133
137
  this.expect("EOF");
134
138
 
135
139
  // End the group namespace for the expression
136
- this.gullet.endGroup();
140
+ if (!this.settings.globalGroup) {
141
+ this.gullet.endGroup();
142
+ }
137
143
  return parse;
138
144
  }
139
145
 
@@ -181,6 +187,8 @@ export default class Parser {
181
187
  const atom = this.parseAtom(breakOnTokenText);
182
188
  if (!atom) {
183
189
  break;
190
+ } else if (atom.type === "internal") {
191
+ continue;
184
192
  }
185
193
  body.push(atom);
186
194
  }
@@ -202,15 +210,14 @@ export default class Parser {
202
210
  let funcName;
203
211
 
204
212
  for (let i = 0; i < body.length; i++) {
205
- const node = checkNodeType(body[i], "infix");
206
- if (node) {
213
+ if (body[i].type === "infix") {
207
214
  if (overIndex !== -1) {
208
215
  throw new ParseError(
209
216
  "only one infix operator per group",
210
- node.token);
217
+ body[i].token);
211
218
  }
212
219
  overIndex = i;
213
- funcName = node.replaceWith;
220
+ funcName = body[i].replaceWith;
214
221
  }
215
222
  }
216
223
 
@@ -325,21 +332,18 @@ export default class Parser {
325
332
 
326
333
  if (lex.text === "\\limits" || lex.text === "\\nolimits") {
327
334
  // We got a limit control
328
- let opNode = checkNodeType(base, "op");
329
- if (opNode) {
335
+ if (base && base.type === "op") {
330
336
  const limits = lex.text === "\\limits";
331
- opNode.limits = limits;
332
- opNode.alwaysHandleSupSub = true;
337
+ base.limits = limits;
338
+ base.alwaysHandleSupSub = true;
339
+ } else if (base && base.type === "operatorname"
340
+ && base.alwaysHandleSupSub) {
341
+ const limits = lex.text === "\\limits";
342
+ base.limits = limits;
333
343
  } else {
334
- opNode = checkNodeType(base, "operatorname");
335
- if (opNode && opNode.alwaysHandleSupSub) {
336
- const limits = lex.text === "\\limits";
337
- opNode.limits = limits;
338
- } else {
339
- throw new ParseError(
340
- "Limit controls must follow a math operator",
341
- lex);
342
- }
344
+ throw new ParseError(
345
+ "Limit controls must follow a math operator",
346
+ lex);
343
347
  }
344
348
  this.consume();
345
349
  } else if (lex.text === "^") {
package/src/Settings.js CHANGED
@@ -32,6 +32,22 @@ export type TrustContextTypes = {
32
32
  url: string,
33
33
  protocol?: string,
34
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
+ |},
35
51
  };
36
52
  export type AnyTrustContext = $Values<TrustContextTypes>;
37
53
  export type TrustFunction = (context: AnyTrustContext) => ?boolean;
@@ -50,6 +66,7 @@ export type SettingsOptions = {
50
66
  trust?: boolean | TrustFunction;
51
67
  maxSize?: number;
52
68
  maxExpand?: number;
69
+ globalGroup?: boolean;
53
70
  };
54
71
 
55
72
  /**
@@ -76,6 +93,7 @@ export default class Settings {
76
93
  trust: boolean | TrustFunction;
77
94
  maxSize: number;
78
95
  maxExpand: number;
96
+ globalGroup: boolean;
79
97
 
80
98
  constructor(options: SettingsOptions) {
81
99
  // allow null options
@@ -96,6 +114,7 @@ export default class Settings {
96
114
  this.trust = utils.deflt(options.trust, false);
97
115
  this.maxSize = Math.max(0, utils.deflt(options.maxSize, Infinity));
98
116
  this.maxExpand = Math.max(0, utils.deflt(options.maxExpand, 1000));
117
+ this.globalGroup = utils.deflt(options.globalGroup, false);
99
118
  }
100
119
 
101
120
  /**
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
@@ -128,63 +120,6 @@ const mathsym = function(
128
120
  }
129
121
  };
130
122
 
131
- /**
132
- * Determines which of the two font names (Main-Italic and Math-Italic) and
133
- * corresponding style tags (maindefault or mathit) to use for default math font,
134
- * depending on the symbol.
135
- */
136
- const mathdefault = function(
137
- value: string,
138
- mode: Mode,
139
- options: Options,
140
- classes: string[],
141
- ): {| fontName: string, fontClass: string |} {
142
- if (/[0-9]/.test(value.charAt(0)) ||
143
- // glyphs for \imath and \jmath do not exist in Math-Italic so we
144
- // need to use Main-Italic instead
145
- utils.contains(mathitLetters, value)) {
146
- return {
147
- fontName: "Main-Italic",
148
- fontClass: "mathit",
149
- };
150
- } else {
151
- return {
152
- fontName: "Math-Italic",
153
- fontClass: "mathdefault",
154
- };
155
- }
156
- };
157
-
158
- /**
159
- * Determines which of the font names (Main-Italic, Math-Italic, and Caligraphic)
160
- * and corresponding style tags (mathit, mathdefault, or mathcal) to use for font
161
- * "mathnormal", depending on the symbol. Use this function instead of fontMap for
162
- * font "mathnormal".
163
- */
164
- const mathnormal = function(
165
- value: string,
166
- mode: Mode,
167
- options: Options,
168
- classes: string[],
169
- ): {| fontName: string, fontClass: string |} {
170
- if (utils.contains(mathitLetters, value)) {
171
- return {
172
- fontName: "Main-Italic",
173
- fontClass: "mathit",
174
- };
175
- } else if (/[0-9]/.test(value.charAt(0))) {
176
- return {
177
- fontName: "Caligraphic-Regular",
178
- fontClass: "mathcal",
179
- };
180
- } else {
181
- return {
182
- fontName: "Math-Italic",
183
- fontClass: "mathdefault",
184
- };
185
- }
186
- };
187
-
188
123
  /**
189
124
  * Determines which of the two font names (Main-Bold and Math-BoldItalic) and
190
125
  * corresponding style tags (mathbf or boldsymbol) to use for font "boldsymbol",
@@ -196,8 +131,10 @@ const boldsymbol = function(
196
131
  mode: Mode,
197
132
  options: Options,
198
133
  classes: string[],
134
+ type: "mathord" | "textord",
199
135
  ): {| fontName: string, fontClass: string |} {
200
- if (lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
136
+ if (type !== "textord" &&
137
+ lookupSymbol(value, "Math-BoldItalic", mode).metrics) {
201
138
  return {
202
139
  fontName: "Math-BoldItalic",
203
140
  fontClass: "boldsymbol",
@@ -236,15 +173,10 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
236
173
  } else if (fontOrFamily) {
237
174
  let fontName;
238
175
  let fontClasses;
239
- if (fontOrFamily === "boldsymbol" || fontOrFamily === "mathnormal") {
240
- const fontData = fontOrFamily === "boldsymbol"
241
- ? boldsymbol(text, mode, options, classes)
242
- : mathnormal(text, mode, options, classes);
176
+ if (fontOrFamily === "boldsymbol") {
177
+ const fontData = boldsymbol(text, mode, options, classes, type);
243
178
  fontName = fontData.fontName;
244
179
  fontClasses = [fontData.fontClass];
245
- } else if (utils.contains(mathitLetters, text)) {
246
- fontName = "Main-Italic";
247
- fontClasses = ["mathit"];
248
180
  } else if (isFont) {
249
181
  fontName = fontMap[fontOrFamily].fontName;
250
182
  fontClasses = [fontOrFamily];
@@ -271,9 +203,8 @@ const makeOrd = function<NODETYPE: "spacing" | "mathord" | "textord">(
271
203
 
272
204
  // Makes a symbol in the default font for mathords and textords.
273
205
  if (type === "mathord") {
274
- const fontLookup = mathdefault(text, mode, options, classes);
275
- return makeSymbol(text, fontLookup.fontName, mode, options,
276
- classes.concat([fontLookup.fontClass]));
206
+ return makeSymbol(text, "Math-Italic", mode, options,
207
+ classes.concat(["mathnormal"]));
277
208
  } else if (type === "textord") {
278
209
  const font = symbols[mode][text] && symbols[mode][text].font;
279
210
  if (font === "ams") {
@@ -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`.
@@ -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);
@@ -155,27 +153,31 @@ const traverseNonSpaceNodes = function(
155
153
  const partialGroup = checkPartialGroup(node);
156
154
  if (partialGroup) { // Recursive DFS
157
155
  // $FlowFixMe: make nodes a $ReadOnlyArray by returning a new array
158
- traverseNonSpaceNodes(partialGroup.children, callback, prev);
156
+ traverseNonSpaceNodes(partialGroup.children,
157
+ callback, prev, null, isRoot);
159
158
  continue;
160
159
  }
161
160
 
162
161
  // Ignore explicit spaces (e.g., \;, \,) when determining what implicit
163
162
  // spacing should go between atoms of different classes
164
- if (node.classes[0] === "mspace") {
165
- continue;
166
- }
167
-
168
- const result = callback(node, prev.node);
169
- if (result) {
170
- if (prev.insertAfter) {
171
- prev.insertAfter(result);
172
- } else { // insert at front
173
- nodes.unshift(result);
174
- 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
+ }
175
173
  }
176
174
  }
177
175
 
178
- 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
+ }
179
181
  prev.insertAfter = (index => n => {
180
182
  nodes.splice(index + 1, 0, n);
181
183
  i++;
@@ -189,8 +191,9 @@ const traverseNonSpaceNodes = function(
189
191
  // Check if given node is a partial group, i.e., does not affect spacing around.
190
192
  const checkPartialGroup = function(
191
193
  node: HtmlDomNode,
192
- ): ?(DocumentFragment<HtmlDomNode> | Anchor) {
193
- 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"))) {
194
197
  return node;
195
198
  }
196
199
  return null;
@@ -292,10 +295,6 @@ function buildHTMLUnbreakable(children, options) {
292
295
  // Add strut, which ensures that the top of the HTML element falls at
293
296
  // the height of the expression, and the bottom of the HTML element
294
297
  // falls at the depth of the expression.
295
- // We used to have separate top and bottom struts, where the bottom strut
296
- // would like to use `vertical-align: top`, but in IE 9 this lowers the
297
- // baseline of the box to the bottom of this strut (instead of staying in
298
- // the normal place) so we use an absolute value for vertical-align instead.
299
298
  const strut = makeSpan(["strut"]);
300
299
  strut.style.height = (body.height + body.depth) + "em";
301
300
  strut.style.verticalAlign = -body.depth + "em";
@@ -317,7 +316,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
317
316
  }
318
317
 
319
318
  // Build the expression contained in the tree
320
- const expression = buildExpression(tree, options, true);
319
+ const expression = buildExpression(tree, options, "root");
321
320
 
322
321
  const children = [];
323
322
 
@@ -93,7 +93,7 @@ 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
97
  } else if (font === "mathbf") {
98
98
  return "bold";
99
99
  } else if (font === "mathbb") {
@@ -238,6 +238,7 @@ export default function buildMathML(
238
238
  tree: AnyParseNode[],
239
239
  texExpression: string,
240
240
  options: Options,
241
+ isDisplayMode: boolean,
241
242
  forMathmlOnly: boolean,
242
243
  ): DomSpan {
243
244
  const expression = buildExpression(tree, options);
@@ -263,6 +264,9 @@ export default function buildMathML(
263
264
 
264
265
  const math = new mathMLTree.MathNode("math", [semantics]);
265
266
  math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
267
+ if (isDisplayMode) {
268
+ math.setAttribute("display", "block");
269
+ }
266
270
 
267
271
  // You can't style <math> nodes, so we wrap the node in a span.
268
272
  // NOTE: The span class is not typed to have <math> nodes as children, and
package/src/buildTree.js CHANGED
@@ -39,12 +39,13 @@ export const buildTree = function(
39
39
  const options = optionsFromSettings(settings);
40
40
  let katexNode;
41
41
  if (settings.output === "mathml") {
42
- return buildMathML(tree, expression, options, true);
42
+ return buildMathML(tree, expression, options, settings.displayMode, true);
43
43
  } else if (settings.output === "html") {
44
44
  const htmlNode = buildHTML(tree, options);
45
45
  katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
46
46
  } else {
47
- const mathMLNode = buildMathML(tree, expression, options, false);
47
+ const mathMLNode = buildMathML(tree, expression, options,
48
+ settings.displayMode, false);
48
49
  const htmlNode = buildHTML(tree, options);
49
50
  katexNode = buildCommon.makeSpan(["katex"], [mathMLNode, htmlNode]);
50
51
  }
@@ -1,6 +1,4 @@
1
1
  // @flow
2
- import {checkNodeType} from "./parseNode";
3
-
4
2
  import type Parser from "./Parser";
5
3
  import type {ParseNode, AnyParseNode, NodeType, UnsupportedCmdParseNode}
6
4
  from "./parseNode";
@@ -228,6 +226,5 @@ export function defineFunctionBuilders<NODETYPE: NodeType>({
228
226
  // Since the corresponding buildHTML/buildMathML function expects a
229
227
  // list of elements, we normalize for different kinds of arguments
230
228
  export const ordargument = function(arg: AnyParseNode): AnyParseNode[] {
231
- const node = checkNodeType(arg, "ordgroup");
232
- return node ? node.body : [arg];
229
+ return arg.type === "ordgroup" ? arg.body : [arg];
233
230
  };