katex 0.15.0 → 0.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/katex.mjs CHANGED
@@ -4465,7 +4465,7 @@ defineSymbol(math, main, bin, "\u2293", "\\sqcap", true);
4465
4465
  defineSymbol(math, main, bin, "\u2217", "\\ast");
4466
4466
  defineSymbol(math, main, bin, "\u2294", "\\sqcup", true);
4467
4467
  defineSymbol(math, main, bin, "\u25ef", "\\bigcirc", true);
4468
- defineSymbol(math, main, bin, "\u2219", "\\bullet");
4468
+ defineSymbol(math, main, bin, "\u2219", "\\bullet", true);
4469
4469
  defineSymbol(math, main, bin, "\u2021", "\\ddagger");
4470
4470
  defineSymbol(math, main, bin, "\u2240", "\\wr", true);
4471
4471
  defineSymbol(math, main, bin, "\u2a3f", "\\amalg");
@@ -4824,13 +4824,13 @@ defineSymbol(math, main, bin, "\u2217", "*", true);
4824
4824
  defineSymbol(math, main, bin, "+", "+");
4825
4825
  defineSymbol(math, main, bin, "\u2212", "-", true);
4826
4826
  defineSymbol(math, main, bin, "\u22c5", "\\cdot", true);
4827
- defineSymbol(math, main, bin, "\u2218", "\\circ");
4827
+ defineSymbol(math, main, bin, "\u2218", "\\circ", true);
4828
4828
  defineSymbol(math, main, bin, "\u00f7", "\\div", true);
4829
4829
  defineSymbol(math, main, bin, "\u00b1", "\\pm", true);
4830
4830
  defineSymbol(math, main, bin, "\u00d7", "\\times", true);
4831
4831
  defineSymbol(math, main, bin, "\u2229", "\\cap", true);
4832
4832
  defineSymbol(math, main, bin, "\u222a", "\\cup", true);
4833
- defineSymbol(math, main, bin, "\u2216", "\\setminus");
4833
+ defineSymbol(math, main, bin, "\u2216", "\\setminus", true);
4834
4834
  defineSymbol(math, main, bin, "\u2227", "\\land");
4835
4835
  defineSymbol(math, main, bin, "\u2228", "\\lor");
4836
4836
  defineSymbol(math, main, bin, "\u2227", "\\wedge", true);
@@ -9983,6 +9983,17 @@ function defineEnvironment(_ref) {
9983
9983
  }
9984
9984
  }
9985
9985
 
9986
+ /**
9987
+ * All registered global/built-in macros.
9988
+ * `macros.js` exports this same dictionary again and makes it public.
9989
+ * `Parser.js` requires this dictionary via `macros.js`.
9990
+ */
9991
+ var _macros = {}; // This function might one day accept an additional argument and do more things.
9992
+
9993
+ function defineMacro(name, body) {
9994
+ _macros[name] = body;
9995
+ }
9996
+
9986
9997
  // Helper functions
9987
9998
  function getHLines(parser) {
9988
9999
  // Return an array. The array length = number of hlines.
@@ -10007,7 +10018,19 @@ var validateAmsEnvironmentContext = context => {
10007
10018
  if (!settings.displayMode) {
10008
10019
  throw new ParseError("{" + context.envName + "} can be used only in" + " display mode.");
10009
10020
  }
10010
- };
10021
+ }; // autoTag (an argument to parseArray) can be one of three values:
10022
+ // * undefined: Regular (not-top-level) array; no tags on each row
10023
+ // * true: Automatic equation numbering, overridable by \tag
10024
+ // * false: Tags allowed on each row, but no automatic numbering
10025
+ // This function *doesn't* work with the "split" environment name.
10026
+
10027
+
10028
+ function getAutoTag(name) {
10029
+ if (name.indexOf("ed") === -1) {
10030
+ return name.indexOf("*") === -1;
10031
+ } // return undefined;
10032
+
10033
+ }
10011
10034
  /**
10012
10035
  * Parse the body of the environment, with rows delimited by \\ and
10013
10036
  * columns delimited by &, and create a nested list in row-major order
@@ -10023,7 +10046,7 @@ function parseArray(parser, _ref, style) {
10023
10046
  cols,
10024
10047
  arraystretch,
10025
10048
  colSeparationType,
10026
- addEqnNum,
10049
+ autoTag,
10027
10050
  singleRow,
10028
10051
  emptySingleRow,
10029
10052
  maxNumCols,
@@ -10058,7 +10081,29 @@ function parseArray(parser, _ref, style) {
10058
10081
  var row = [];
10059
10082
  var body = [row];
10060
10083
  var rowGaps = [];
10061
- var hLinesBeforeRow = []; // Test for \hline at the top of the array.
10084
+ var hLinesBeforeRow = [];
10085
+ var tags = autoTag != null ? [] : undefined; // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent
10086
+ // whether this row should have an equation number. Simulate this with
10087
+ // a \@eqnsw macro set to 1 or 0.
10088
+
10089
+ function beginRow() {
10090
+ if (autoTag) {
10091
+ parser.gullet.macros.set("\\@eqnsw", "1", true);
10092
+ }
10093
+ }
10094
+
10095
+ function endRow() {
10096
+ if (tags) {
10097
+ if (parser.gullet.macros.get("\\df@tag")) {
10098
+ tags.push(parser.subparse([new Token("\\df@tag")]));
10099
+ parser.gullet.macros.set("\\df@tag", undefined, true);
10100
+ } else {
10101
+ tags.push(Boolean(autoTag) && parser.gullet.macros.get("\\@eqnsw") === "1");
10102
+ }
10103
+ }
10104
+ }
10105
+
10106
+ beginRow(); // Test for \hline at the top of the array.
10062
10107
 
10063
10108
  hLinesBeforeRow.push(getHLines(parser));
10064
10109
 
@@ -10099,10 +10144,11 @@ function parseArray(parser, _ref, style) {
10099
10144
 
10100
10145
  parser.consume();
10101
10146
  } else if (next === "\\end") {
10102
- // Arrays terminate newlines with `\crcr` which consumes a `\cr` if
10147
+ endRow(); // Arrays terminate newlines with `\crcr` which consumes a `\cr` if
10103
10148
  // the last line is empty. However, AMS environments keep the
10104
10149
  // empty row if it's the only one.
10105
10150
  // NOTE: Currently, `cell` is the last item added into `row`.
10151
+
10106
10152
  if (row.length === 1 && cell.type === "styling" && cell.body[0].body.length === 0 && (body.length > 1 || !emptySingleRow)) {
10107
10153
  body.pop();
10108
10154
  }
@@ -10124,11 +10170,13 @@ function parseArray(parser, _ref, style) {
10124
10170
  size = parser.parseSizeGroup(true);
10125
10171
  }
10126
10172
 
10127
- rowGaps.push(size ? size.value : null); // check for \hline(s) following the row separator
10173
+ rowGaps.push(size ? size.value : null);
10174
+ endRow(); // check for \hline(s) following the row separator
10128
10175
 
10129
10176
  hLinesBeforeRow.push(getHLines(parser));
10130
10177
  row = [];
10131
10178
  body.push(row);
10179
+ beginRow();
10132
10180
  } else {
10133
10181
  throw new ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken);
10134
10182
  }
@@ -10149,7 +10197,7 @@ function parseArray(parser, _ref, style) {
10149
10197
  hskipBeforeAndAfter,
10150
10198
  hLinesBeforeRow,
10151
10199
  colSeparationType,
10152
- addEqnNum,
10200
+ tags,
10153
10201
  leqno
10154
10202
  };
10155
10203
  } // Decides on a style for cells in an array according to whether the given
@@ -10287,20 +10335,33 @@ var htmlBuilder$7 = function htmlBuilder(group, options) {
10287
10335
  var cols = [];
10288
10336
  var colSep;
10289
10337
  var colDescrNum;
10290
- var eqnNumSpans = [];
10338
+ var tagSpans = [];
10291
10339
 
10292
- if (group.addEqnNum) {
10293
- // An environment with automatic equation numbers.
10294
- // Create node(s) that will trigger CSS counter increment.
10340
+ if (group.tags && group.tags.some(tag => tag)) {
10341
+ // An environment with manual tags and/or automatic equation numbers.
10342
+ // Create node(s), the latter of which trigger CSS counter increment.
10295
10343
  for (r = 0; r < nr; ++r) {
10296
10344
  var rw = body[r];
10297
10345
  var shift = rw.pos - offset;
10298
- var eqnTag = buildCommon.makeSpan(["eqn-num"], [], options);
10299
- eqnTag.depth = rw.depth;
10300
- eqnTag.height = rw.height;
10301
- eqnNumSpans.push({
10346
+ var tag = group.tags[r];
10347
+ var tagSpan = void 0;
10348
+
10349
+ if (tag === true) {
10350
+ // automatic numbering
10351
+ tagSpan = buildCommon.makeSpan(["eqn-num"], [], options);
10352
+ } else if (tag === false) {
10353
+ // \nonumber/\notag or starred environment
10354
+ tagSpan = buildCommon.makeSpan([], [], options);
10355
+ } else {
10356
+ // manual \tag
10357
+ tagSpan = buildCommon.makeSpan([], buildExpression$1(tag, options, true), options);
10358
+ }
10359
+
10360
+ tagSpan.depth = rw.depth;
10361
+ tagSpan.height = rw.height;
10362
+ tagSpans.push({
10302
10363
  type: "elem",
10303
- elem: eqnTag,
10364
+ elem: tagSpan,
10304
10365
  shift
10305
10366
  });
10306
10367
  }
@@ -10436,12 +10497,12 @@ var htmlBuilder$7 = function htmlBuilder(group, options) {
10436
10497
  }, options);
10437
10498
  }
10438
10499
 
10439
- if (!group.addEqnNum) {
10500
+ if (tagSpans.length === 0) {
10440
10501
  return buildCommon.makeSpan(["mord"], [body], options);
10441
10502
  } else {
10442
10503
  var eqnNumCol = buildCommon.makeVList({
10443
10504
  positionType: "individualShift",
10444
- children: eqnNumSpans
10505
+ children: tagSpans
10445
10506
  }, options);
10446
10507
  eqnNumCol = buildCommon.makeSpan(["tag"], [eqnNumCol], options);
10447
10508
  return buildCommon.makeFragment([body, eqnNumCol]);
@@ -10467,7 +10528,7 @@ var mathmlBuilder$6 = function mathmlBuilder(group, options) {
10467
10528
  row.push(new mathMLTree.MathNode("mtd", [buildGroup(rw[j], options)]));
10468
10529
  }
10469
10530
 
10470
- if (group.addEqnNum) {
10531
+ if (group.tags && group.tags[i]) {
10471
10532
  row.unshift(glue);
10472
10533
  row.push(glue);
10473
10534
 
@@ -10602,13 +10663,14 @@ var alignedHandler = function alignedHandler(context, args) {
10602
10663
 
10603
10664
  var cols = [];
10604
10665
  var separationType = context.envName.indexOf("at") > -1 ? "alignat" : "align";
10666
+ var isSplit = context.envName === "split";
10605
10667
  var res = parseArray(context.parser, {
10606
10668
  cols,
10607
10669
  addJot: true,
10608
- addEqnNum: context.envName === "align" || context.envName === "alignat",
10670
+ autoTag: isSplit ? undefined : getAutoTag(context.envName),
10609
10671
  emptySingleRow: true,
10610
10672
  colSeparationType: separationType,
10611
- maxNumCols: context.envName === "split" ? 2 : undefined,
10673
+ maxNumCols: isSplit ? 2 : undefined,
10612
10674
  leqno: context.parser.settings.leqno
10613
10675
  }, "display"); // Determining number of columns.
10614
10676
  // 1. If the first argument is given, we use it as a number of columns,
@@ -10968,7 +11030,7 @@ defineEnvironment({
10968
11030
  }],
10969
11031
  addJot: true,
10970
11032
  colSeparationType: "gather",
10971
- addEqnNum: context.envName === "gather",
11033
+ autoTag: getAutoTag(context.envName),
10972
11034
  emptySingleRow: true,
10973
11035
  leqno: context.parser.settings.leqno
10974
11036
  };
@@ -11001,7 +11063,7 @@ defineEnvironment({
11001
11063
  handler(context) {
11002
11064
  validateAmsEnvironmentContext(context);
11003
11065
  var res = {
11004
- addEqnNum: context.envName === "equation",
11066
+ autoTag: getAutoTag(context.envName),
11005
11067
  emptySingleRow: true,
11006
11068
  singleRow: true,
11007
11069
  maxNumCols: 1,
@@ -11027,7 +11089,9 @@ defineEnvironment({
11027
11089
 
11028
11090
  htmlBuilder: htmlBuilder$7,
11029
11091
  mathmlBuilder: mathmlBuilder$6
11030
- }); // Catch \hline outside array environment
11092
+ });
11093
+ defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}");
11094
+ defineMacro("\\notag", "\\nonumber"); // Catch \hline outside array environment
11031
11095
 
11032
11096
  defineFunction({
11033
11097
  type: "text",
@@ -11127,7 +11191,7 @@ function mathmlBuilder$5(group, options) {
11127
11191
  var inner = buildExpression(group.body, options);
11128
11192
 
11129
11193
  if (group.mclass === "minner") {
11130
- return mathMLTree.newDocumentFragment(inner);
11194
+ node = new mathMLTree.MathNode("mpadded", inner);
11131
11195
  } else if (group.mclass === "mord") {
11132
11196
  if (group.isCharacterBox) {
11133
11197
  node = inner[0];
@@ -11155,6 +11219,10 @@ function mathmlBuilder$5(group, options) {
11155
11219
  } else if (group.mclass === "mopen" || group.mclass === "mclose") {
11156
11220
  node.attributes.lspace = "0em";
11157
11221
  node.attributes.rspace = "0em";
11222
+ } else if (group.mclass === "minner") {
11223
+ node.attributes.lspace = "0.0556em"; // 1 mu is the most likely option
11224
+
11225
+ node.attributes.width = "+0.1111em";
11158
11226
  } // MathML <mo> default space is 5/18 em, so <mrel> needs no action.
11159
11227
  // Ref: https://developer.mozilla.org/en-US/docs/Web/MathML/Element/mo
11160
11228
 
@@ -13153,17 +13221,6 @@ defineFunction({
13153
13221
  mathmlBuilder: mathmlBuilder$1
13154
13222
  });
13155
13223
 
13156
- /**
13157
- * All registered global/built-in macros.
13158
- * `macros.js` exports this same dictionary again and makes it public.
13159
- * `Parser.js` requires this dictionary via `macros.js`.
13160
- */
13161
- var _macros = {}; // This function might one day accept an additional argument and do more things.
13162
-
13163
- function defineMacro(name, body) {
13164
- _macros[name] = body;
13165
- }
13166
-
13167
13224
  // NOTE: Unlike most `htmlBuilder`s, this one handles not only
13168
13225
  // "operatorname", but also "supsub" since \operatorname* can
13169
13226
  // affect super/subscripting.
@@ -14812,7 +14869,7 @@ class Namespace {
14812
14869
 
14813
14870
  for (var undef in undefs) {
14814
14871
  if (undefs.hasOwnProperty(undef)) {
14815
- if (undefs[undef] === undefined) {
14872
+ if (undefs[undef] == null) {
14816
14873
  delete this.current[undef];
14817
14874
  } else {
14818
14875
  this.current[undef] = undefs[undef];
@@ -14862,6 +14919,7 @@ class Namespace {
14862
14919
  * Local set() sets the current value and (when appropriate) adds an undo
14863
14920
  * operation to the undo stack. Global set() may change the undo
14864
14921
  * operation at every level, so takes time linear in their number.
14922
+ * A value of undefined means to delete existing definitions.
14865
14923
  */
14866
14924
 
14867
14925
 
@@ -14893,7 +14951,11 @@ class Namespace {
14893
14951
  }
14894
14952
  }
14895
14953
 
14896
- this.current[name] = value;
14954
+ if (value == null) {
14955
+ delete this.current[name];
14956
+ } else {
14957
+ this.current[name] = value;
14958
+ }
14897
14959
  }
14898
14960
 
14899
14961
  }
@@ -16831,6 +16893,25 @@ class Parser {
16831
16893
  this.gullet.endGroups();
16832
16894
  }
16833
16895
  }
16896
+ /**
16897
+ * Fully parse a separate sequence of tokens as a separate job.
16898
+ * Tokens should be specified in reverse order, as in a MacroDefinition.
16899
+ */
16900
+
16901
+
16902
+ subparse(tokens) {
16903
+ // Save the next token from the current job.
16904
+ var oldToken = this.nextToken;
16905
+ this.consume(); // Run the new job, terminating it with an excess '}'
16906
+
16907
+ this.gullet.pushToken(new Token("}"));
16908
+ this.gullet.pushTokens(tokens);
16909
+ var parse = this.parseExpression(false);
16910
+ this.expect("}"); // Restore the next token from the current job.
16911
+
16912
+ this.nextToken = oldToken;
16913
+ return parse;
16914
+ }
16834
16915
 
16835
16916
  /**
16836
16917
  * Parses an "expression", which is a list of atoms.
@@ -17772,12 +17853,11 @@ var parseTree = function parseTree(toParse, settings) {
17772
17853
  throw new ParseError("\\tag works only in display equations");
17773
17854
  }
17774
17855
 
17775
- parser.gullet.feed("\\df@tag");
17776
17856
  tree = [{
17777
17857
  type: "tag",
17778
17858
  mode: "text",
17779
17859
  body: tree,
17780
- tag: parser.parse()
17860
+ tag: parser.subparse([new Token("\\df@tag")])
17781
17861
  }];
17782
17862
  }
17783
17863
 
@@ -17879,7 +17959,7 @@ var katex = {
17879
17959
  /**
17880
17960
  * Current KaTeX version
17881
17961
  */
17882
- version: "0.15.0",
17962
+ version: "0.15.3",
17883
17963
 
17884
17964
  /**
17885
17965
  * Renders the given LaTeX into an HTML+MathML combination, and adds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "katex",
3
- "version": "0.15.0",
3
+ "version": "0.15.3",
4
4
  "description": "Fast math typesetting for the web.",
5
5
  "main": "dist/katex.js",
6
6
  "exports": {
@@ -47,7 +47,7 @@
47
47
  "dist/"
48
48
  ],
49
49
  "license": "MIT",
50
- "packageManager": "yarn@3.0.1",
50
+ "packageManager": "yarn@3.2.0",
51
51
  "devDependencies": {
52
52
  "@babel/core": "^7.10.4",
53
53
  "@babel/eslint-parser": "^7.15.0",
@@ -103,9 +103,9 @@
103
103
  "p-retry": "^4.6.1",
104
104
  "pako": "^2.0.0",
105
105
  "postcss": "^8.0.0",
106
- "postcss-less": "^5.0.0",
106
+ "postcss-less": "^6.0.0",
107
107
  "postcss-loader": "^6.0.0",
108
- "postcss-preset-env": "^6.7.0",
108
+ "postcss-preset-env": "^7.0.0",
109
109
  "prettier": "^2.0.5",
110
110
  "query-string": "^7.0.0",
111
111
  "rimraf": "^3.0.2",
@@ -115,7 +115,7 @@
115
115
  "sri-toolbox": "^0.2.0",
116
116
  "style-loader": "^3.0.0",
117
117
  "stylelint": "^14.0.0",
118
- "stylelint-config-standard": "^23.0.0",
118
+ "stylelint-config-standard": "^24.0.0",
119
119
  "terser-webpack-plugin": "^5.0.3",
120
120
  "webpack": "^5.51.1",
121
121
  "webpack-bundle-analyzer": "^4.0.0",
package/src/Namespace.js CHANGED
@@ -15,7 +15,7 @@ export type Mapping<Value> = {[string]: Value};
15
15
  export default class Namespace<Value> {
16
16
  current: Mapping<Value>;
17
17
  builtins: Mapping<Value>;
18
- undefStack: Mapping<Value>[];
18
+ undefStack: Mapping<?Value>[];
19
19
 
20
20
  /**
21
21
  * Both arguments are optional. The first argument is an object of
@@ -48,7 +48,7 @@ export default class Namespace<Value> {
48
48
  const undefs = this.undefStack.pop();
49
49
  for (const undef in undefs) {
50
50
  if (undefs.hasOwnProperty(undef)) {
51
- if (undefs[undef] === undefined) {
51
+ if (undefs[undef] == null) {
52
52
  delete this.current[undef];
53
53
  } else {
54
54
  this.current[undef] = undefs[undef];
@@ -97,8 +97,9 @@ export default class Namespace<Value> {
97
97
  * Local set() sets the current value and (when appropriate) adds an undo
98
98
  * operation to the undo stack. Global set() may change the undo
99
99
  * operation at every level, so takes time linear in their number.
100
+ * A value of undefined means to delete existing definitions.
100
101
  */
101
- set(name: string, value: Value, global: boolean = false) {
102
+ set(name: string, value: ?Value, global: boolean = false) {
102
103
  if (global) {
103
104
  // Global set is equivalent to setting in all groups. Simulate this
104
105
  // by destroying any undos currently scheduled for this name,
@@ -119,6 +120,10 @@ export default class Namespace<Value> {
119
120
  top[name] = this.current[name];
120
121
  }
121
122
  }
122
- this.current[name] = value;
123
+ if (value == null) {
124
+ delete this.current[name];
125
+ } else {
126
+ this.current[name] = value;
127
+ }
123
128
  }
124
129
  }
package/src/Parser.js CHANGED
@@ -150,6 +150,27 @@ export default class Parser {
150
150
  }
151
151
  }
152
152
 
153
+ /**
154
+ * Fully parse a separate sequence of tokens as a separate job.
155
+ * Tokens should be specified in reverse order, as in a MacroDefinition.
156
+ */
157
+ subparse(tokens: Token[]): AnyParseNode[] {
158
+ // Save the next token from the current job.
159
+ const oldToken = this.nextToken;
160
+ this.consume();
161
+
162
+ // Run the new job, terminating it with an excess '}'
163
+ this.gullet.pushToken(new Token("}"));
164
+ this.gullet.pushTokens(tokens);
165
+ const parse = this.parseExpression(false);
166
+ this.expect("}");
167
+
168
+ // Restore the next token from the current job.
169
+ this.nextToken = oldToken;
170
+
171
+ return parse;
172
+ }
173
+
153
174
  static endOfExpression: string[] = ["}", "\\endgroup", "\\end", "\\right", "&"];
154
175
 
155
176
  /**
@@ -4,10 +4,12 @@ import Style from "../Style";
4
4
  import defineEnvironment from "../defineEnvironment";
5
5
  import {parseCD} from "./cd";
6
6
  import defineFunction from "../defineFunction";
7
+ import defineMacro from "../defineMacro";
7
8
  import mathMLTree from "../mathMLTree";
8
9
  import ParseError from "../ParseError";
9
10
  import {assertNodeType, assertSymbolNodeType} from "../parseNode";
10
11
  import {checkSymbolNodeType} from "../parseNode";
12
+ import {Token} from "../Token";
11
13
  import {calculateSize, makeEm} from "../units";
12
14
  import utils from "../utils";
13
15
 
@@ -54,6 +56,18 @@ const validateAmsEnvironmentContext = context => {
54
56
  }
55
57
  };
56
58
 
59
+ // autoTag (an argument to parseArray) can be one of three values:
60
+ // * undefined: Regular (not-top-level) array; no tags on each row
61
+ // * true: Automatic equation numbering, overridable by \tag
62
+ // * false: Tags allowed on each row, but no automatic numbering
63
+ // This function *doesn't* work with the "split" environment name.
64
+ function getAutoTag(name): ?boolean {
65
+ if (name.indexOf("ed") === -1) {
66
+ return name.indexOf("*") === -1;
67
+ }
68
+ // return undefined;
69
+ }
70
+
57
71
  /**
58
72
  * Parse the body of the environment, with rows delimited by \\ and
59
73
  * columns delimited by &, and create a nested list in row-major order
@@ -68,7 +82,7 @@ function parseArray(
68
82
  cols,
69
83
  arraystretch,
70
84
  colSeparationType,
71
- addEqnNum,
85
+ autoTag,
72
86
  singleRow,
73
87
  emptySingleRow,
74
88
  maxNumCols,
@@ -79,7 +93,7 @@ function parseArray(
79
93
  cols?: AlignSpec[],
80
94
  arraystretch?: number,
81
95
  colSeparationType?: ColSeparationType,
82
- addEqnNum?: boolean,
96
+ autoTag?: ?boolean,
83
97
  singleRow?: boolean,
84
98
  emptySingleRow?: boolean,
85
99
  maxNumCols?: number,
@@ -116,6 +130,29 @@ function parseArray(
116
130
  const rowGaps = [];
117
131
  const hLinesBeforeRow = [];
118
132
 
133
+ const tags = (autoTag != null ? [] : undefined);
134
+
135
+ // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent
136
+ // whether this row should have an equation number. Simulate this with
137
+ // a \@eqnsw macro set to 1 or 0.
138
+ function beginRow() {
139
+ if (autoTag) {
140
+ parser.gullet.macros.set("\\@eqnsw", "1", true);
141
+ }
142
+ }
143
+ function endRow() {
144
+ if (tags) {
145
+ if (parser.gullet.macros.get("\\df@tag")) {
146
+ tags.push(parser.subparse([new Token("\\df@tag")]));
147
+ parser.gullet.macros.set("\\df@tag", undefined, true);
148
+ } else {
149
+ tags.push(Boolean(autoTag) &&
150
+ parser.gullet.macros.get("\\@eqnsw") === "1");
151
+ }
152
+ }
153
+ }
154
+ beginRow();
155
+
119
156
  // Test for \hline at the top of the array.
120
157
  hLinesBeforeRow.push(getHLines(parser));
121
158
 
@@ -154,6 +191,7 @@ function parseArray(
154
191
  }
155
192
  parser.consume();
156
193
  } else if (next === "\\end") {
194
+ endRow();
157
195
  // Arrays terminate newlines with `\crcr` which consumes a `\cr` if
158
196
  // the last line is empty. However, AMS environments keep the
159
197
  // empty row if it's the only one.
@@ -179,12 +217,14 @@ function parseArray(
179
217
  size = parser.parseSizeGroup(true);
180
218
  }
181
219
  rowGaps.push(size ? size.value : null);
220
+ endRow();
182
221
 
183
222
  // check for \hline(s) following the row separator
184
223
  hLinesBeforeRow.push(getHLines(parser));
185
224
 
186
225
  row = [];
187
226
  body.push(row);
227
+ beginRow();
188
228
  } else {
189
229
  throw new ParseError("Expected & or \\\\ or \\cr or \\end",
190
230
  parser.nextToken);
@@ -207,7 +247,7 @@ function parseArray(
207
247
  hskipBeforeAndAfter,
208
248
  hLinesBeforeRow,
209
249
  colSeparationType,
210
- addEqnNum,
250
+ tags,
211
251
  leqno,
212
252
  };
213
253
  }
@@ -339,17 +379,27 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
339
379
  let colSep;
340
380
  let colDescrNum;
341
381
 
342
- const eqnNumSpans = [];
343
- if (group.addEqnNum) {
344
- // An environment with automatic equation numbers.
345
- // Create node(s) that will trigger CSS counter increment.
382
+ const tagSpans = [];
383
+ if (group.tags && group.tags.some((tag) => tag)) {
384
+ // An environment with manual tags and/or automatic equation numbers.
385
+ // Create node(s), the latter of which trigger CSS counter increment.
346
386
  for (r = 0; r < nr; ++r) {
347
387
  const rw = body[r];
348
388
  const shift = rw.pos - offset;
349
- const eqnTag = buildCommon.makeSpan(["eqn-num"], [], options);
350
- eqnTag.depth = rw.depth;
351
- eqnTag.height = rw.height;
352
- eqnNumSpans.push({type: "elem", elem: eqnTag, shift});
389
+ const tag = group.tags[r];
390
+ let tagSpan;
391
+ if (tag === true) { // automatic numbering
392
+ tagSpan = buildCommon.makeSpan(["eqn-num"], [], options);
393
+ } else if (tag === false) {
394
+ // \nonumber/\notag or starred environment
395
+ tagSpan = buildCommon.makeSpan([], [], options);
396
+ } else { // manual \tag
397
+ tagSpan = buildCommon.makeSpan([],
398
+ html.buildExpression(tag, options, true), options);
399
+ }
400
+ tagSpan.depth = rw.depth;
401
+ tagSpan.height = rw.height;
402
+ tagSpans.push({type: "elem", elem: tagSpan, shift});
353
403
  }
354
404
  }
355
405
 
@@ -465,12 +515,12 @@ const htmlBuilder: HtmlBuilder<"array"> = function(group, options) {
465
515
  }, options);
466
516
  }
467
517
 
468
- if (!group.addEqnNum) {
518
+ if (tagSpans.length === 0) {
469
519
  return buildCommon.makeSpan(["mord"], [body], options);
470
520
  } else {
471
521
  let eqnNumCol = buildCommon.makeVList({
472
522
  positionType: "individualShift",
473
- children: eqnNumSpans,
523
+ children: tagSpans,
474
524
  }, options);
475
525
  eqnNumCol = buildCommon.makeSpan(["tag"], [eqnNumCol], options);
476
526
  return buildCommon.makeFragment([body, eqnNumCol]);
@@ -494,7 +544,7 @@ const mathmlBuilder: MathMLBuilder<"array"> = function(group, options) {
494
544
  row.push(new mathMLTree.MathNode("mtd",
495
545
  [mml.buildGroup(rw[j], options)]));
496
546
  }
497
- if (group.addEqnNum) {
547
+ if (group.tags && group.tags[i]) {
498
548
  row.unshift(glue);
499
549
  row.push(glue);
500
550
  if (group.leqno) {
@@ -631,14 +681,15 @@ const alignedHandler = function(context, args) {
631
681
  }
632
682
  const cols = [];
633
683
  const separationType = context.envName.indexOf("at") > -1 ? "alignat" : "align";
684
+ const isSplit = context.envName === "split";
634
685
  const res = parseArray(context.parser,
635
686
  {
636
687
  cols,
637
688
  addJot: true,
638
- addEqnNum: context.envName === "align" || context.envName === "alignat",
689
+ autoTag: isSplit ? undefined : getAutoTag(context.envName),
639
690
  emptySingleRow: true,
640
691
  colSeparationType: separationType,
641
- maxNumCols: context.envName === "split" ? 2 : undefined,
692
+ maxNumCols: isSplit ? 2 : undefined,
642
693
  leqno: context.parser.settings.leqno,
643
694
  },
644
695
  "display"
@@ -984,7 +1035,7 @@ defineEnvironment({
984
1035
  }],
985
1036
  addJot: true,
986
1037
  colSeparationType: "gather",
987
- addEqnNum: context.envName === "gather",
1038
+ autoTag: getAutoTag(context.envName),
988
1039
  emptySingleRow: true,
989
1040
  leqno: context.parser.settings.leqno,
990
1041
  };
@@ -1017,7 +1068,7 @@ defineEnvironment({
1017
1068
  handler(context) {
1018
1069
  validateAmsEnvironmentContext(context);
1019
1070
  const res = {
1020
- addEqnNum: context.envName === "equation",
1071
+ autoTag: getAutoTag(context.envName),
1021
1072
  emptySingleRow: true,
1022
1073
  singleRow: true,
1023
1074
  maxNumCols: 1,
@@ -1043,6 +1094,9 @@ defineEnvironment({
1043
1094
  mathmlBuilder,
1044
1095
  });
1045
1096
 
1097
+ defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}");
1098
+ defineMacro("\\notag", "\\nonumber");
1099
+
1046
1100
  // Catch \hline outside array environment
1047
1101
  defineFunction({
1048
1102
  type: "text", // Doesn't matter what this is.