katex 0.17.0 → 0.18.1

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 (49) hide show
  1. package/README.md +3 -3
  2. package/cli.js +6 -9
  3. package/contrib/auto-render/index.html +1 -1
  4. package/contrib/copy-tex/README.md +3 -3
  5. package/contrib/copy-tex/index.html +1 -1
  6. package/contrib/mathtex-script-type/README.md +5 -5
  7. package/contrib/mhchem/README.md +1 -1
  8. package/dist/README.md +3 -3
  9. package/dist/contrib/render-a11y-string.js +15 -40
  10. package/dist/contrib/render-a11y-string.min.js +1 -1
  11. package/dist/contrib/render-a11y-string.mjs +13 -33
  12. package/dist/katex-swap.css +159 -164
  13. package/dist/katex-swap.min.css +1 -1
  14. package/dist/katex.css +159 -164
  15. package/dist/katex.js +277 -421
  16. package/dist/katex.min.css +1 -1
  17. package/dist/katex.min.js +1 -1
  18. package/dist/katex.mjs +279 -423
  19. package/package.json +14 -16
  20. package/src/MacroExpander.ts +10 -7
  21. package/src/Namespace.ts +16 -20
  22. package/src/Options.ts +2 -2
  23. package/src/Parser.ts +2 -2
  24. package/src/atoms.ts +22 -22
  25. package/src/buildCommon.ts +2 -2
  26. package/src/buildHTML.ts +8 -8
  27. package/src/buildMathML.ts +3 -3
  28. package/src/environments/array.ts +3 -3
  29. package/src/fontMetrics.ts +2 -4
  30. package/src/functions/accent.ts +2 -2
  31. package/src/functions/cr.ts +1 -1
  32. package/src/functions/enclose.ts +1 -1
  33. package/src/functions/environment.ts +1 -1
  34. package/src/functions/html.ts +21 -4
  35. package/src/functions/lap.ts +7 -7
  36. package/src/functions/overline.ts +1 -1
  37. package/src/functions/phantom.ts +2 -2
  38. package/src/functions/rule.ts +1 -1
  39. package/src/functions/sizing.ts +1 -1
  40. package/src/functions/smash.ts +1 -1
  41. package/src/functions/sqrt.ts +1 -1
  42. package/src/functions/symbolsSpacing.ts +20 -21
  43. package/src/functions/underline.ts +1 -1
  44. package/src/macros.ts +3 -3
  45. package/src/mathMLTree.ts +6 -10
  46. package/src/parseNode.ts +4 -4
  47. package/src/stretchy.ts +2 -2
  48. package/src/styles/katex.scss +31 -37
  49. package/src/wide-character.ts +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katex",
3
- "version": "0.17.0",
3
+ "version": "0.18.1",
4
4
  "description": "Fast math typesetting for the web.",
5
5
  "main": "dist/katex.js",
6
6
  "types": "types/katex.d.ts",
@@ -55,7 +55,7 @@
55
55
  "types/"
56
56
  ],
57
57
  "license": "MIT",
58
- "packageManager": "yarn@4.1.1",
58
+ "packageManager": "pnpm@11.4.0",
59
59
  "devDependencies": {
60
60
  "@babel/core": "^7.29.0",
61
61
  "@babel/plugin-proposal-class-properties": "^7.18.6",
@@ -92,7 +92,7 @@
92
92
  "fs-extra": "^10.1.0",
93
93
  "globals": "^17.6.0",
94
94
  "got": "^11.8.5",
95
- "husky": "^4.3.8",
95
+ "husky": "^9.1.7",
96
96
  "istanbul-lib-coverage": "^3.2.0",
97
97
  "istanbul-lib-report": "^3.0.0",
98
98
  "istanbul-reports": "^3.1.5",
@@ -109,6 +109,8 @@
109
109
  "mkdirp": "^1.0.4",
110
110
  "p-retry": "^4.6.2",
111
111
  "pako": "^2.0.4",
112
+ "pixelmatch": "^7.2.0",
113
+ "pngjs": "^7.0.0",
112
114
  "postcss": "^8.4.16",
113
115
  "postcss-loader": "^7.0.1",
114
116
  "postcss-preset-env": "^7.8.0",
@@ -135,40 +137,36 @@
135
137
  },
136
138
  "bin": "cli.js",
137
139
  "scripts": {
138
- "test": "yarn test:lint && yarn test:ts && yarn test:jest",
139
- "test:lint": "yarn test:lint:js && yarn test:lint:css",
140
+ "test": "pnpm test:lint && pnpm test:ts && pnpm test:jest",
141
+ "test:lint": "pnpm test:lint:js && pnpm test:lint:css",
140
142
  "test:lint:js": "eslint .",
141
143
  "test:lint:css": "stylelint src/styles/katex.scss static/main.css website/static/**/*.css",
142
144
  "test:jest": "jest",
143
145
  "test:jest:watch": "jest --watch",
144
146
  "test:jest:update": "jest --updateSnapshot",
145
147
  "test:jest:coverage": "jest --coverage",
146
- "test:screenshots": "yarn test:screenshots:update --verify",
148
+ "test:screenshots": "pnpm test:screenshots:update --verify",
147
149
  "test:screenshots:update": "dockers/screenshotter/screenshotter.sh",
148
150
  "test:perf": "NODE_ENV=test node -e \"require('@babel/register')({extensions:['.js','.ts']}); require('./test/perf-test.ts')\"",
149
151
  "test:ts": "tsc --noEmit",
150
152
  "test:build": "node test/verify-build.js",
151
153
  "clean": "rm -rf dist/ node_modules/",
152
- "clean-install": "yarn clean && yarn",
154
+ "clean-install": "pnpm clean && pnpm install",
153
155
  "start": "webpack serve --config webpack.dev.js",
154
156
  "analyze": "webpack --config webpack.analyze.js",
155
157
  "build": "rimraf dist/ && mkdirp dist && cp README.md dist && rollup -c --failAfterWarnings && webpack && node update-sri.js package dist/README.md",
156
158
  "build:fonts": "dockers/fonts/buildFonts.sh",
157
159
  "build:metrics": "dockers/fonts/buildMetrics.sh",
158
- "watch": "yarn build --watch",
159
- "postversion": "yarn dist && node update-sri.js package README.md contrib/*/README.md docs/*.md website/pages/index.html",
160
+ "watch": "pnpm build --watch",
161
+ "postversion": "pnpm dist && node update-sri.js package README.md contrib/*/README.md docs/*.md website/pages/index.html",
160
162
  "semantic-release": "semantic-release",
161
- "dist": "yarn build && yarn dist:zip",
162
- "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/"
163
+ "dist": "pnpm build && pnpm dist:zip",
164
+ "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/",
165
+ "prepare": "husky"
163
166
  },
164
167
  "dependencies": {
165
168
  "commander": "^8.3.0"
166
169
  },
167
- "husky": {
168
- "hooks": {
169
- "pre-commit": "yarn test:lint"
170
- }
171
- },
172
170
  "jest": {
173
171
  "collectCoverageFrom": [
174
172
  "src/**/*.{js,ts}",
@@ -449,10 +449,10 @@ export default class MacroExpander implements MacroContextInterface {
449
449
  */
450
450
  isDefined(name: string): boolean {
451
451
  return this.macros.has(name) ||
452
- functions.hasOwnProperty(name) ||
453
- symbols.math.hasOwnProperty(name) ||
454
- symbols.text.hasOwnProperty(name) ||
455
- implicitCommands.hasOwnProperty(name);
452
+ Object.prototype.hasOwnProperty.call(functions, name) ||
453
+ Object.prototype.hasOwnProperty.call(symbols.math, name) ||
454
+ Object.prototype.hasOwnProperty.call(symbols.text, name) ||
455
+ Object.prototype.hasOwnProperty.call(implicitCommands, name);
456
456
  }
457
457
 
458
458
  /**
@@ -460,8 +460,11 @@ export default class MacroExpander implements MacroContextInterface {
460
460
  */
461
461
  isExpandable(name: string): boolean {
462
462
  const macro = this.macros.get(name);
463
- return macro != null ? typeof macro === "string"
464
- || typeof macro === "function" || !macro.unexpandable
465
- : functions.hasOwnProperty(name) && !functions[name].primitive;
463
+ if (macro != null) {
464
+ return typeof macro === "string"
465
+ || typeof macro === "function"
466
+ || !macro.unexpandable;
467
+ }
468
+ return Object.prototype.hasOwnProperty.call(functions, name) && !functions[name].primitive;
466
469
  }
467
470
  }
package/src/Namespace.ts CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  /**
3
2
  * A `Namespace` refers to a space of nameable things like macros or lengths,
4
3
  * which can be `set` either globally or local to a nested group, using an
@@ -8,12 +7,11 @@
8
7
  */
9
8
 
10
9
  import ParseError from "./ParseError";
11
- export type Mapping<Value> = Record<string, Value>;
12
10
 
13
11
  export default class Namespace<Value> {
14
- current: Mapping<Value>;
15
- builtins: Mapping<Value>;
16
- undefStack: Mapping<Value | null | undefined>[];
12
+ current: Record<string, Value>;
13
+ builtins: Record<string, Value>;
14
+ undefStack: Record<string, Value | undefined>[];
17
15
 
18
16
  /**
19
17
  * Both arguments are optional. The first argument is an object of
@@ -21,8 +19,8 @@ export default class Namespace<Value> {
21
19
  * of initial (global-level) mappings, which will constantly change
22
20
  * according to any global/top-level `set`s done.
23
21
  */
24
- constructor(builtins: Mapping<Value> = {},
25
- globalMacros: Mapping<Value> = {}) {
22
+ constructor(builtins: Record<string, Value> = {},
23
+ globalMacros: Record<string, Value> = {}) {
26
24
  this.current = globalMacros;
27
25
  this.builtins = builtins;
28
26
  this.undefStack = [];
@@ -43,14 +41,12 @@ export default class Namespace<Value> {
43
41
  throw new ParseError("Unbalanced namespace destruction: attempt " +
44
42
  "to pop global namespace; please report this as a bug");
45
43
  }
46
- const undefs = this.undefStack.pop();
47
- for (const undef in undefs) {
48
- if (undefs.hasOwnProperty(undef)) {
49
- if (undefs[undef] == null) {
50
- delete this.current[undef];
51
- } else {
52
- this.current[undef] = undefs[undef];
53
- }
44
+ const undefs = this.undefStack.pop()!;
45
+ for (const key of Object.keys(undefs)) {
46
+ if (undefs[key] === undefined) {
47
+ delete this.current[key];
48
+ } else {
49
+ this.current[key] = undefs[key];
54
50
  }
55
51
  }
56
52
  }
@@ -70,8 +66,8 @@ export default class Namespace<Value> {
70
66
  * `get(name) != null`.
71
67
  */
72
68
  has(name: string): boolean {
73
- return this.current.hasOwnProperty(name) ||
74
- this.builtins.hasOwnProperty(name);
69
+ return Object.prototype.hasOwnProperty.call(this.current, name) ||
70
+ Object.prototype.hasOwnProperty.call(this.builtins, name);
75
71
  }
76
72
 
77
73
  /**
@@ -83,7 +79,7 @@ export default class Namespace<Value> {
83
79
  * `if (namespace.has(...))`.
84
80
  */
85
81
  get(name: string): Value | undefined {
86
- if (this.current.hasOwnProperty(name)) {
82
+ if (Object.prototype.hasOwnProperty.call(this.current, name)) {
87
83
  return this.current[name];
88
84
  } else {
89
85
  return this.builtins[name];
@@ -97,7 +93,7 @@ export default class Namespace<Value> {
97
93
  * operation at every level, so takes time linear in their number.
98
94
  * A value of undefined means to delete existing definitions.
99
95
  */
100
- set(name: string, value: Value | null | undefined, global: boolean = false) {
96
+ set(name: string, value: Value | undefined, global: boolean = false) {
101
97
  if (global) {
102
98
  // Global set is equivalent to setting in all groups. Simulate this
103
99
  // by destroying any undos currently scheduled for this name,
@@ -114,7 +110,7 @@ export default class Namespace<Value> {
114
110
  // unless an undo is already in place, in which case that older
115
111
  // value is the correct one.
116
112
  const top = this.undefStack[this.undefStack.length - 1];
117
- if (top && !top.hasOwnProperty(name)) {
113
+ if (top && !Object.prototype.hasOwnProperty.call(top, name)) {
118
114
  top[name] = this.current[name];
119
115
  }
120
116
  }
package/src/Options.ts CHANGED
@@ -265,7 +265,7 @@ class Options {
265
265
  sizingClasses(oldOptions: Options): Array<string> {
266
266
  if (oldOptions.size !== this.size) {
267
267
  return [
268
- "sizing",
268
+ "katex-sizing",
269
269
  "reset-size" + oldOptions.size,
270
270
  "size" + this.size,
271
271
  ];
@@ -281,7 +281,7 @@ class Options {
281
281
  baseSizingClasses(): Array<string> {
282
282
  if (this.size !== Options.BASESIZE) {
283
283
  return [
284
- "sizing",
284
+ "katex-sizing",
285
285
  "reset-size" + this.size,
286
286
  "size" + Options.BASESIZE,
287
287
  ];
package/src/Parser.ts CHANGED
@@ -843,7 +843,7 @@ export default class Parser {
843
843
  result = this.parseFunction(breakOnTokenText, name) ||
844
844
  this.parseSymbol();
845
845
  if (result == null && text[0] === "\\" &&
846
- !implicitCommands.hasOwnProperty(text)) {
846
+ !Object.prototype.hasOwnProperty.call(implicitCommands, text)) {
847
847
  if (this.settings.throwOnError) {
848
848
  throw new ParseError(
849
849
  "Undefined control sequence: " + text, firstToken);
@@ -939,7 +939,7 @@ export default class Parser {
939
939
  }
940
940
  // At this point, we should have a symbol, possibly with accents.
941
941
  // First expand any accented base symbol according to unicodeSymbols.
942
- if (unicodeSymbols.hasOwnProperty(text[0]) &&
942
+ if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) &&
943
943
  !symbols[this.mode][text[0]]) {
944
944
  // This behavior is not strict (XeTeX-compatible) in math mode.
945
945
  if (this.settings.strict && this.mode === "math") {
package/src/atoms.ts CHANGED
@@ -4,30 +4,30 @@
4
4
  * pull in `isAtom` without dragging in the ~870-line symbol tables.
5
5
  */
6
6
 
7
- // Some of these have a "-token" suffix since these are also used as `ParseNode`
8
- // types for raw text tokens, and we want to avoid conflicts with higher-level
9
- // `ParseNode` types. These `ParseNode`s are constructed within `Parser` by
10
- // looking up the `symbols` map.
11
- export const ATOMS = {
12
- "bin": 1,
13
- "close": 1,
14
- "inner": 1,
15
- "open": 1,
16
- "punct": 1,
17
- "rel": 1,
18
- };
19
- export const NON_ATOMS = {
20
- "accent-token": 1,
21
- "mathord": 1,
22
- "op-token": 1,
23
- "spacing": 1,
24
- "textord": 1,
25
- };
7
+ const atomList = [
8
+ "bin",
9
+ "close",
10
+ "inner",
11
+ "open",
12
+ "punct",
13
+ "rel",
14
+ ] as const;
26
15
 
27
- export type Atom = keyof typeof ATOMS;
28
- export type NonAtom = keyof typeof NON_ATOMS;
16
+ const nonAtomList = [
17
+ "accent-token",
18
+ "mathord",
19
+ "op-token",
20
+ "spacing",
21
+ "textord",
22
+ ] as const;
23
+
24
+ const Atoms: ReadonlySet<string> = new Set(atomList);
25
+ export const NonAtoms: ReadonlySet<string> = new Set(nonAtomList);
26
+
27
+ export type Atom = typeof atomList[number];
28
+ type NonAtom = typeof nonAtomList[number];
29
29
  export type Group = Atom | NonAtom;
30
30
 
31
31
  export function isAtom(value: string): value is Atom {
32
- return value in ATOMS;
32
+ return Atoms.has(value);
33
33
  }
@@ -212,7 +212,7 @@ export const makeOrd = function(
212
212
  if (lookupSymbol(text, fontName, mode).metrics) {
213
213
  return makeSymbol(text, fontName, mode, options,
214
214
  classes.concat(fontClasses));
215
- } else if (ligatures.hasOwnProperty(text) &&
215
+ } else if (Object.prototype.hasOwnProperty.call(ligatures, text) &&
216
216
  fontName.slice(0, 10) === "Typewriter") {
217
217
  // Deconstruct ligatures in monospace fonts (\texttt, \tt).
218
218
  const parts = [];
@@ -775,7 +775,7 @@ export const staticSvg = function(value: string, options: Options): SvgSpan {
775
775
  "viewBox": "0 0 " + 1000 * width + " " + 1000 * height,
776
776
  "preserveAspectRatio": "xMinYMin",
777
777
  });
778
- const span = makeSvgSpan(["overlay"], [svgNode], options);
778
+ const span = makeSvgSpan(["katex-overlay"], [svgNode], options);
779
779
  span.height = height;
780
780
  span.style.height = makeEm(height);
781
781
  span.style.width = makeEm(width);
package/src/buildHTML.ts CHANGED
@@ -182,7 +182,7 @@ const traverseNonSpaceNodes = function(
182
182
 
183
183
  if (nonspace) {
184
184
  prev.node = node;
185
- } else if (isRoot && node.hasClass("newline")) {
185
+ } else if (isRoot && node.hasClass("katex-newline")) {
186
186
  prev.node = makeSpan(["leftmost"]); // treat like beginning of line
187
187
  }
188
188
  prev.insertAfter = (index => n => {
@@ -292,18 +292,18 @@ export const buildGroup = function(
292
292
 
293
293
  /**
294
294
  * Combine an array of HTML DOM nodes (e.g., the output of `buildExpression`)
295
- * into an unbreakable HTML node of class .base, with proper struts to
295
+ * into an unbreakable HTML node of class .katex-base, with proper struts to
296
296
  * guarantee correct vertical extent. `buildHTML` calls this repeatedly to
297
297
  * make up the entire expression as a sequence of unbreakable units.
298
298
  */
299
299
  function buildHTMLUnbreakable(children: HtmlDomNode[], options: Options) {
300
300
  // Compute height and depth of this chunk.
301
- const body = makeSpan(["base"], children, options);
301
+ const body = makeSpan(["katex-base"], children, options);
302
302
 
303
303
  // Add strut, which ensures that the top of the HTML element falls at
304
304
  // the height of the expression, and the bottom of the HTML element
305
305
  // falls at the depth of the expression.
306
- const strut = makeSpan(["strut"]);
306
+ const strut = makeSpan(["katex-strut"]);
307
307
  strut.style.height = makeEm(body.height + body.depth);
308
308
  if (body.depth) {
309
309
  strut.style.verticalAlign = makeEm(-body.depth);
@@ -329,7 +329,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
329
329
  const expression = buildExpression(tree, options, "root");
330
330
 
331
331
  let eqnNum;
332
- if (expression.length === 2 && expression[1].hasClass("tag")) {
332
+ if (expression.length === 2 && expression[1].hasClass("katex-tag")) {
333
333
  // An environment with automatic equation numbers, e.g. {gather}.
334
334
  eqnNum = expression.pop();
335
335
  }
@@ -354,7 +354,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
354
354
  let nobreak = false;
355
355
  while (i < expression.length - 1 &&
356
356
  expression[i + 1].hasClass("mspace") &&
357
- !expression[i + 1].hasClass("newline")) {
357
+ !expression[i + 1].hasClass("katex-newline")) {
358
358
  i++;
359
359
  parts.push(expression[i]);
360
360
  if (expression[i].hasClass("nobreak")) {
@@ -366,7 +366,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
366
366
  children.push(buildHTMLUnbreakable(parts, options));
367
367
  parts = [];
368
368
  }
369
- } else if (expression[i].hasClass("newline")) {
369
+ } else if (expression[i].hasClass("katex-newline")) {
370
370
  // Write the line except the newline
371
371
  parts.pop();
372
372
  if (parts.length > 0) {
@@ -388,7 +388,7 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
388
388
  buildExpression(tag, options, true),
389
389
  options,
390
390
  );
391
- tagChild.classes = ["tag"];
391
+ tagChild.classes = ["katex-tag"];
392
392
  children.push(tagChild);
393
393
  } else if (eqnNum) {
394
394
  children.push(eqnNum);
@@ -32,9 +32,9 @@ export const makeText = function(
32
32
  ): TextNode {
33
33
  if (symbols[mode][text] && symbols[mode][text].replace &&
34
34
  text.charCodeAt(0) !== 0xD835 &&
35
- !(ligatures.hasOwnProperty(text) && options &&
36
- ((options.fontFamily && options.fontFamily.slice(4, 6) === "tt") ||
37
- (options.font && options.font.slice(4, 6) === "tt")))) {
35
+ !(Object.prototype.hasOwnProperty.call(ligatures, text) &&
36
+ (options?.fontFamily?.slice(4, 6) === "tt" ||
37
+ options?.font?.slice(4, 6) === "tt"))) {
38
38
  text = symbols[mode][text].replace!;
39
39
  }
40
40
 
@@ -520,8 +520,8 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
520
520
 
521
521
  // Add \hline(s), if any.
522
522
  if (hlines.length > 0) {
523
- const line = makeLineSpan("hline", options, ruleThickness);
524
- const dashes = makeLineSpan("hdashline", options, ruleThickness);
523
+ const line = makeLineSpan("katex-hline", options, ruleThickness);
524
+ const dashes = makeLineSpan("katex-hdashline", options, ruleThickness);
525
525
  const vListElems = [{type: "elem" as const, elem: tableBody, shift: 0}];
526
526
  while (hlines.length > 0) {
527
527
  const hline = hlines.pop()!;
@@ -546,7 +546,7 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
546
546
  positionType: "individualShift",
547
547
  children: tagSpans,
548
548
  }, options);
549
- const tagCol = makeSpan(["tag"], [eqnNumCol], options);
549
+ const tagCol = makeSpan(["katex-tag"], [eqnNumCol], options);
550
550
  return makeFragment([tableBody, tagCol]);
551
551
  }
552
552
  };
@@ -257,10 +257,8 @@ export function getGlobalMetrics(size: number): FontMetrics {
257
257
  const metrics: FontMetrics = fontMetricsBySizeIndex[sizeIndex] = {
258
258
  cssEmPerMu: sigmasAndXis.quad[sizeIndex] / 18,
259
259
  };
260
- for (const key in sigmasAndXis) {
261
- if (sigmasAndXis.hasOwnProperty(key)) {
262
- metrics[key] = sigmasAndXis[key][sizeIndex];
263
- }
260
+ for (const key of Object.keys(sigmasAndXis)) {
261
+ metrics[key] = sigmasAndXis[key][sizeIndex];
264
262
  }
265
263
  }
266
264
  return fontMetricsBySizeIndex[sizeIndex]!;
@@ -125,7 +125,7 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
125
125
  // Shift the accent over by the skew.
126
126
  let left = skew;
127
127
 
128
- // CSS defines `.katex .accent .accent-body:not(.accent-full) { width: 0 }`
128
+ // CSS defines `.katex .katex-accent .accent-body:not(.accent-full) { width: 0 }`
129
129
  // so that the accent doesn't contribute to the bounding box.
130
130
  // We need to shift the character by its width (effectively half
131
131
  // its width) to compensate.
@@ -173,7 +173,7 @@ export const htmlBuilder: HtmlBuilderSupSub<"accent"> = (grp, options) => {
173
173
  }
174
174
 
175
175
  const accentWrap =
176
- makeSpan(["mord", "accent"], [accentBody], options);
176
+ makeSpan(["mord", "katex-accent"], [accentBody], options);
177
177
 
178
178
  if (supSubGroup) {
179
179
  // Here, we replace the "base" child of the supsub with our newly
@@ -35,7 +35,7 @@ defineFunction({
35
35
  htmlBuilder(group, options) {
36
36
  const span = makeSpan(["mspace"], [], options);
37
37
  if (group.newLine) {
38
- span.classes.push("newline");
38
+ span.classes.push("katex-newline");
39
39
  if (group.size) {
40
40
  span.style.marginTop =
41
41
  makeEm(calculateSize(group.size, options));
@@ -32,7 +32,7 @@ const htmlBuilder: HtmlBuilder<"enclose"> = (group, options) => {
32
32
  const isSingleChar = isCharacterBox(group.body);
33
33
 
34
34
  if (label === "sout") {
35
- img = makeSpan(["stretchy", "sout"]);
35
+ img = makeSpan(["katex-stretchy", "katex-sout"]);
36
36
  img.height = options.fontMetrics().defaultRuleThickness / scale;
37
37
  imgShift = -0.5 * options.fontMetrics().xHeight;
38
38
 
@@ -25,7 +25,7 @@ defineFunction({
25
25
 
26
26
  if (funcName === "\\begin") {
27
27
  // begin...end is similar to left...right
28
- if (!environments.hasOwnProperty(envName)) {
28
+ if (!Object.prototype.hasOwnProperty.call(environments, envName)) {
29
29
  throw new ParseError(
30
30
  "No such environment: " + envName, nameGroup);
31
31
  }
@@ -49,7 +49,24 @@ defineFunction({
49
49
  };
50
50
  break;
51
51
  case "\\htmlData": {
52
- const data = value.split(",");
52
+ // `{,}` escapes a literal comma. Braces are used rather than a
53
+ // backslash because `\,` is a macro (a thin space) that expands
54
+ // away before this raw argument is ever read.
55
+ const ESCAPED_COMMA = "{,}";
56
+ const data: string[] = [];
57
+ let current = "";
58
+ for (let i = 0; i < value.length; i++) {
59
+ if (value.startsWith(ESCAPED_COMMA, i)) {
60
+ current += ",";
61
+ i += ESCAPED_COMMA.length - 1;
62
+ } else if (value[i] === ",") {
63
+ data.push(current);
64
+ current = "";
65
+ } else {
66
+ current += value[i];
67
+ }
68
+ }
69
+ data.push(current);
53
70
  for (let i = 0; i < data.length; i++) {
54
71
  const item = data[i];
55
72
  const firstEquals = item.indexOf("=");
@@ -92,9 +109,9 @@ defineFunction({
92
109
  }
93
110
 
94
111
  const span = makeSpan(classes, elements, options);
95
- for (const attr in group.attributes) {
96
- if (attr !== "class" && group.attributes.hasOwnProperty(attr)) {
97
- span.setAttribute(attr, group.attributes[attr]);
112
+ for (const [attr, value] of Object.entries(group.attributes)) {
113
+ if (attr !== "class") {
114
+ span.setAttribute(attr, value);
98
115
  }
99
116
  }
100
117
  return span;
@@ -30,13 +30,13 @@ defineFunction({
30
30
  // ref: https://www.math.lsu.edu/~aperlis/publications/mathclap/
31
31
  inner = makeSpan(
32
32
  [], [html.buildGroup(group.body, options)]);
33
- // wrap, since CSS will center a .clap > .inner > span
34
- inner = makeSpan(["inner"], [inner], options);
33
+ // wrap, since CSS will center a .clap > .katex-inner > span
34
+ inner = makeSpan(["katex-inner"], [inner], options);
35
35
  } else {
36
36
  inner = makeSpan(
37
- ["inner"], [html.buildGroup(group.body, options)]);
37
+ ["katex-inner"], [html.buildGroup(group.body, options)]);
38
38
  }
39
- const fix = makeSpan(["fix"], []);
39
+ const fix = makeSpan(["katex-fix"], []);
40
40
  let node = makeSpan(
41
41
  [group.alignment], [inner, fix], options);
42
42
 
@@ -45,7 +45,7 @@ defineFunction({
45
45
  // Next, use a strut to set the height of the HTML bounding box.
46
46
  // Otherwise, a tall argument may be misplaced.
47
47
  // This code resolved issue #1153
48
- const strut = makeSpan(["strut"]);
48
+ const strut = makeSpan(["katex-strut"]);
49
49
  strut.style.height = makeEm(node.height + node.depth);
50
50
  if (node.depth) {
51
51
  strut.style.verticalAlign = makeEm(-node.depth);
@@ -54,8 +54,8 @@ defineFunction({
54
54
 
55
55
  // Next, prevent vertical misplacement when next to something tall.
56
56
  // This code resolves issue #1234
57
- node = makeSpan(["thinbox"], [node], options);
58
- return makeSpan(["mord", "vbox"], [node], options);
57
+ node = makeSpan(["katex-thinbox"], [node], options);
58
+ return makeSpan(["mord", "katex-vbox"], [node], options);
59
59
  },
60
60
  mathmlBuilder: (group, options) => {
61
61
  // mathllap, mathrlap, mathclap
@@ -41,7 +41,7 @@ defineFunction({
41
41
  ],
42
42
  }, options);
43
43
 
44
- return makeSpan(["mord", "overline"], [vlist], options);
44
+ return makeSpan(["mord", "katex-overline"], [vlist], options);
45
45
  },
46
46
  mathmlBuilder(group, options) {
47
47
  const operator = new MathNode(
@@ -57,9 +57,9 @@ defineFunction({
57
57
 
58
58
  htmlBuilder: (group, options) => {
59
59
  const inner = makeSpan(
60
- ["inner"],
60
+ ["katex-inner"],
61
61
  [html.buildGroup(group.body, options.withPhantom())]);
62
- const fix = makeSpan(["fix"], []);
62
+ const fix = makeSpan(["katex-fix"], []);
63
63
  return makeSpan(
64
64
  ["mord", "rlap"], [inner, fix], options);
65
65
  },
@@ -28,7 +28,7 @@ defineFunction({
28
28
 
29
29
  htmlBuilder(group, options) {
30
30
  // Make an empty span for the rule
31
- const rule = makeSpan(["mord", "rule"], [], options);
31
+ const rule = makeSpan(["mord", "katex-rule"], [], options);
32
32
 
33
33
  // Calculate the shift, width, and height of the rule, and account for units
34
34
  const width = calculateSize(group.width, options);
@@ -22,7 +22,7 @@ export function sizingGroup(
22
22
  // Add size-resetting classes to the inner list and set maxFontSize
23
23
  // manually. Handle nested size changes.
24
24
  for (let i = 0; i < inner.length; i++) {
25
- const pos = inner[i].classes.indexOf("sizing");
25
+ const pos = inner[i].classes.indexOf("katex-sizing");
26
26
  if (pos < 0) {
27
27
  Array.prototype.push.apply(inner[i].classes,
28
28
  options.sizingClasses(baseOptions));
@@ -69,7 +69,7 @@ defineFunction({
69
69
 
70
70
  if (group.smashHeight && group.smashDepth) {
71
71
  // Symmetric \smash can stay in inline layout.
72
- return makeSpan(["mord", "smash"], [node], options);
72
+ return makeSpan(["mord", "katex-smash"], [node], options);
73
73
  }
74
74
 
75
75
  // In order to influence makeVList for asymmetric smashing, we have to
@@ -104,7 +104,7 @@ defineFunction({
104
104
  }, options);
105
105
  // Add a class surrounding it so we can add on the appropriate
106
106
  // kerning
107
- const rootVListWrap = makeSpan(["root"], [rootVList]);
107
+ const rootVListWrap = makeSpan(["katex-root"], [rootVList]);
108
108
 
109
109
  return makeSpan(["mord", "sqrt"],
110
110
  [rootVListWrap, body], options);