temml 0.10.33 → 0.11.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.
package/dist/temml.cjs CHANGED
@@ -609,6 +609,7 @@ class MathNode {
609
609
  this.children = children || [];
610
610
  this.classes = classes || [];
611
611
  this.style = style || {}; // Used for <mstyle> elements
612
+ this.label = "";
612
613
  }
613
614
 
614
615
  /**
@@ -626,6 +627,10 @@ class MathNode {
626
627
  return this.attributes[name];
627
628
  }
628
629
 
630
+ setLabel(value) {
631
+ this.label = value;
632
+ }
633
+
629
634
  /**
630
635
  * Converts the math node into a MathML-namespaced DOM element.
631
636
  */
@@ -1542,7 +1547,7 @@ defineSymbol(math, mathord, "\u03db", "\\stigma", true);
1542
1547
  defineSymbol(math, mathord, "\u2aeb", "\\Bot");
1543
1548
  defineSymbol(math, bin, "\u2217", "\u2217", true);
1544
1549
  defineSymbol(math, bin, "+", "+");
1545
- defineSymbol(math, bin, "*", "*");
1550
+ defineSymbol(math, bin, "\u2217", "*");
1546
1551
  defineSymbol(math, bin, "\u2044", "/", true);
1547
1552
  defineSymbol(math, bin, "\u2044", "\u2044");
1548
1553
  defineSymbol(math, bin, "\u2212", "-", true);
@@ -2005,7 +2010,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
2005
2010
  }
2006
2011
  block.push(node);
2007
2012
  if (node.type && node.type === "mo" && node.children.length === 1 &&
2008
- !Object.hasOwn(node.attributes, "movablelimits")) {
2013
+ !Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) {
2009
2014
  const ch = node.children[0].text;
2010
2015
  if (openDelims.indexOf(ch) > -1) {
2011
2016
  level += 1;
@@ -2341,16 +2346,36 @@ const glue$1 = _ => {
2341
2346
  return new mathMLTree.MathNode("mtd", [], [], { padding: "0", width: "50%" })
2342
2347
  };
2343
2348
 
2349
+ const labelContainers = ["mrow", "mtd", "mtable", "mtr"];
2350
+ const getLabel = parent => {
2351
+ for (const node of parent.children) {
2352
+ if (node.type && labelContainers.includes(node.type)) {
2353
+ if (node.classes && node.classes[0] === "tml-label") {
2354
+ const label = node.label;
2355
+ return label
2356
+ } else {
2357
+ const label = getLabel(node);
2358
+ if (label) { return label }
2359
+ }
2360
+ } else if (!node.type) {
2361
+ const label = getLabel(node);
2362
+ if (label) { return label }
2363
+ }
2364
+ }
2365
+ };
2366
+
2344
2367
  const taggedExpression = (expression, tag, style, leqno) => {
2345
2368
  tag = buildExpressionRow(tag[0].body, style);
2346
2369
  tag = consolidateText(tag);
2347
2370
  tag.classes.push("tml-tag");
2348
2371
 
2372
+ const label = getLabel(expression); // from a \label{} function.
2349
2373
  expression = new mathMLTree.MathNode("mtd", [expression]);
2350
2374
  const rowArray = [glue$1(), expression, glue$1()];
2351
2375
  rowArray[leqno ? 0 : 2].classes.push(leqno ? "tml-left" : "tml-right");
2352
2376
  rowArray[leqno ? 0 : 2].children.push(tag);
2353
2377
  const mtr = new mathMLTree.MathNode("mtr", rowArray, ["tml-tageqn"]);
2378
+ if (label) { mtr.setAttribute("id", label); }
2354
2379
  const table = new mathMLTree.MathNode("mtable", [mtr]);
2355
2380
  table.style.width = "100%";
2356
2381
  table.setAttribute("displaystyle", "true");
@@ -3181,6 +3206,8 @@ function parseCD(parser) {
3181
3206
  type: "array",
3182
3207
  mode: "math",
3183
3208
  body,
3209
+ tags: null,
3210
+ labels: new Array(body.length + 1).fill(""),
3184
3211
  envClasses: ["jot", "cd"],
3185
3212
  cols: [],
3186
3213
  hLinesBeforeRow: new Array(body.length + 1).fill([])
@@ -4421,6 +4448,75 @@ function defineEnvironment({ type, names, props, handler, mathmlBuilder }) {
4421
4448
  }
4422
4449
  }
4423
4450
 
4451
+ /**
4452
+ * Lexing or parsing positional information for error reporting.
4453
+ * This object is immutable.
4454
+ */
4455
+ class SourceLocation {
4456
+ constructor(lexer, start, end) {
4457
+ this.lexer = lexer; // Lexer holding the input string.
4458
+ this.start = start; // Start offset, zero-based inclusive.
4459
+ this.end = end; // End offset, zero-based exclusive.
4460
+ }
4461
+
4462
+ /**
4463
+ * Merges two `SourceLocation`s from location providers, given they are
4464
+ * provided in order of appearance.
4465
+ * - Returns the first one's location if only the first is provided.
4466
+ * - Returns a merged range of the first and the last if both are provided
4467
+ * and their lexers match.
4468
+ * - Otherwise, returns null.
4469
+ */
4470
+ static range(first, second) {
4471
+ if (!second) {
4472
+ return first && first.loc;
4473
+ } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) {
4474
+ return null;
4475
+ } else {
4476
+ return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end);
4477
+ }
4478
+ }
4479
+ }
4480
+
4481
+ /**
4482
+ * Interface required to break circular dependency between Token, Lexer, and
4483
+ * ParseError.
4484
+ */
4485
+
4486
+ /**
4487
+ * The resulting token returned from `lex`.
4488
+ *
4489
+ * It consists of the token text plus some position information.
4490
+ * The position information is essentially a range in an input string,
4491
+ * but instead of referencing the bare input string, we refer to the lexer.
4492
+ * That way it is possible to attach extra metadata to the input string,
4493
+ * like for example a file name or similar.
4494
+ *
4495
+ * The position information is optional, so it is OK to construct synthetic
4496
+ * tokens if appropriate. Not providing available position information may
4497
+ * lead to degraded error reporting, though.
4498
+ */
4499
+ class Token {
4500
+ constructor(
4501
+ text, // the text of this token
4502
+ loc
4503
+ ) {
4504
+ this.text = text;
4505
+ this.loc = loc;
4506
+ }
4507
+
4508
+ /**
4509
+ * Given a pair of tokens (this and endToken), compute a `Token` encompassing
4510
+ * the whole input range enclosed by these two.
4511
+ */
4512
+ range(
4513
+ endToken, // last token of the range, inclusive
4514
+ text // the text of the newly constructed token
4515
+ ) {
4516
+ return new Token(text, SourceLocation.range(this, endToken));
4517
+ }
4518
+ }
4519
+
4424
4520
  // In TeX, there are actually three sets of dimensions, one for each of
4425
4521
  // textstyle, scriptstyle, and scriptscriptstyle. These are
4426
4522
  // provided in the the arrays below, in that order.
@@ -4905,8 +5001,10 @@ defineMacro("\\tag@literal", (context) => {
4905
5001
  if (context.macros.get("\\df@tag")) {
4906
5002
  throw new ParseError("Multiple \\tag");
4907
5003
  }
4908
- return "\\def\\df@tag{\\text{#1}}";
5004
+ return "\\gdef\\df@tag{\\text{#1}}";
4909
5005
  });
5006
+ defineMacro("\\notag", "\\nonumber");
5007
+ defineMacro("\\nonumber", "\\gdef\\@eqnsw{0}");
4910
5008
 
4911
5009
  // \renewcommand{\bmod}{\nonscript\mskip-\medmuskip\mkern5mu\mathbin
4912
5010
  // {\operator@font mod}\penalty900
@@ -7110,33 +7208,30 @@ const arrayGaps = macros => {
7110
7208
  return [arraystretch, arraycolsep]
7111
7209
  };
7112
7210
 
7113
- const getTag = (group, style, rowNum) => {
7114
- let tag;
7115
- const tagContents = group.tags.shift();
7116
- if (tagContents) {
7117
- // The author has written a \tag or a \notag in this row.
7118
- if (tagContents.body) {
7119
- tag = buildExpressionRow(tagContents.body, style, true);
7120
- tag.classes = ["tml-tag"];
7121
- } else {
7122
- // \notag. Return an empty span.
7123
- tag = new mathMLTree.MathNode("mtext", [], []);
7124
- return tag
7125
- }
7126
- } else if (group.envClasses.includes("multline") &&
7127
- ((group.leqno && rowNum !== 0) || (!group.leqno && rowNum !== group.body.length - 1))) {
7128
- // A multiline that does not receive a tag. Return an empty cell.
7129
- tag = new mathMLTree.MathNode("mtext", [], []);
7130
- return tag
7131
- } else {
7132
- // AMS automatcally numbered equaton.
7133
- // Insert a class so the element can be populated by a CSS counter.
7134
- // WebKit will display the CSS counter only inside a span.
7135
- tag = new mathMLTree.MathNode("mtext", [new Span(["tml-eqn"])]);
7211
+ const checkCellForLabels = cell => {
7212
+ // Check if the author wrote a \tag{} inside this cell.
7213
+ let rowLabel = "";
7214
+ for (let i = 0; i < cell.length; i++) {
7215
+ if (cell[i].type === "label") {
7216
+ if (rowLabel) { throw new ParseError(("Multiple \\labels in one row")) }
7217
+ rowLabel = cell[i].string;
7218
+ }
7136
7219
  }
7137
- return tag
7220
+ return rowLabel
7138
7221
  };
7139
7222
 
7223
+ // autoTag (an argument to parseArray) can be one of three values:
7224
+ // * undefined: Regular (not-top-level) array; no tags on each row
7225
+ // * true: Automatic equation numbering, overridable by \tag
7226
+ // * false: Tags allowed on each row, but no automatic numbering
7227
+ // This function *doesn't* work with the "split" environment name.
7228
+ function getAutoTag(name) {
7229
+ if (name.indexOf("ed") === -1) {
7230
+ return name.indexOf("*") === -1;
7231
+ }
7232
+ // return undefined;
7233
+ }
7234
+
7140
7235
  /**
7141
7236
  * Parse the body of the environment, with rows delimited by \\ and
7142
7237
  * columns delimited by &, and create a nested list in row-major order
@@ -7148,7 +7243,7 @@ function parseArray(
7148
7243
  {
7149
7244
  cols, // [{ type: string , align: l|c|r|null }]
7150
7245
  envClasses, // align(ed|at|edat) | array | cases | cd | small | multline
7151
- addEqnNum, // boolean
7246
+ autoTag, // boolean
7152
7247
  singleRow, // boolean
7153
7248
  emptySingleRow, // boolean
7154
7249
  maxNumCols, // number
@@ -7164,13 +7259,6 @@ function parseArray(
7164
7259
  // TODO: provide helpful error when \cr is used outside array environment
7165
7260
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
7166
7261
  }
7167
- if (addEqnNum) {
7168
- parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren");
7169
- parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
7170
- parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}");
7171
- parser.gullet.macros.set("\\notag", "\\env@notag");
7172
- parser.gullet.macros.set("\\nonumber", "\\env@notag");
7173
- }
7174
7262
 
7175
7263
  // Start group for first cell
7176
7264
  parser.gullet.beginGroup();
@@ -7178,29 +7266,39 @@ function parseArray(
7178
7266
  let row = [];
7179
7267
  const body = [row];
7180
7268
  const rowGaps = [];
7181
- const tags = [];
7182
- let rowTag;
7269
+ const labels = [];
7270
+
7183
7271
  const hLinesBeforeRow = [];
7184
7272
 
7273
+ const tags = (autoTag != null ? [] : undefined);
7274
+
7275
+ // amsmath uses \global\@eqnswtrue and \global\@eqnswfalse to represent
7276
+ // whether this row should have an equation number. Simulate this with
7277
+ // a \@eqnsw macro set to 1 or 0.
7278
+ function beginRow() {
7279
+ if (autoTag) {
7280
+ parser.gullet.macros.set("\\@eqnsw", "1", true);
7281
+ }
7282
+ }
7283
+ function endRow() {
7284
+ if (tags) {
7285
+ if (parser.gullet.macros.get("\\df@tag")) {
7286
+ tags.push(parser.subparse([new Token("\\df@tag")]));
7287
+ parser.gullet.macros.set("\\df@tag", undefined, true);
7288
+ } else {
7289
+ tags.push(Boolean(autoTag) &&
7290
+ parser.gullet.macros.get("\\@eqnsw") === "1");
7291
+ }
7292
+ }
7293
+ }
7294
+ beginRow();
7295
+
7185
7296
  // Test for \hline at the top of the array.
7186
7297
  hLinesBeforeRow.push(getHLines(parser));
7187
7298
 
7188
7299
  while (true) {
7189
7300
  // Parse each cell in its own group (namespace)
7190
7301
  let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\");
7191
-
7192
- if (addEqnNum && !rowTag) {
7193
- // Check if the author wrote a \tag{} inside this cell.
7194
- for (let i = 0; i < cell.length; i++) {
7195
- if (cell[i].type === "envTag" || cell[i].type === "noTag") {
7196
- // Get the contents of the \text{} nested inside the \env@Tag{}
7197
- rowTag = cell[i].type === "envTag"
7198
- ? cell.splice(i, 1)[0].body.body[0]
7199
- : { body: null };
7200
- break
7201
- }
7202
- }
7203
- }
7204
7302
  parser.gullet.endGroup();
7205
7303
  parser.gullet.beginGroup();
7206
7304
 
@@ -7229,6 +7327,7 @@ function parseArray(
7229
7327
  }
7230
7328
  parser.consume();
7231
7329
  } else if (next === "\\end") {
7330
+ endRow();
7232
7331
  // Arrays terminate newlines with `\crcr` which consumes a `\cr` if
7233
7332
  // the last line is empty. However, AMS environments keep the
7234
7333
  // empty row if it's the only one.
@@ -7236,6 +7335,7 @@ function parseArray(
7236
7335
  if (row.length === 1 && cell.body.length === 0 && (body.length > 1 || !emptySingleRow)) {
7237
7336
  body.pop();
7238
7337
  }
7338
+ labels.push(checkCellForLabels(cell.body));
7239
7339
  if (hLinesBeforeRow.length < body.length + 1) {
7240
7340
  hLinesBeforeRow.push([]);
7241
7341
  }
@@ -7252,15 +7352,16 @@ function parseArray(
7252
7352
  size = parser.parseSizeGroup(true);
7253
7353
  }
7254
7354
  rowGaps.push(size ? size.value : null);
7355
+ endRow();
7255
7356
 
7256
- tags.push(rowTag);
7357
+ labels.push(checkCellForLabels(cell.body));
7257
7358
 
7258
7359
  // check for \hline(s) following the row separator
7259
7360
  hLinesBeforeRow.push(getHLines(parser));
7260
7361
 
7261
7362
  row = [];
7262
- rowTag = null;
7263
7363
  body.push(row);
7364
+ beginRow();
7264
7365
  } else {
7265
7366
  throw new ParseError("Expected & or \\\\ or \\cr or \\end", parser.nextToken);
7266
7367
  }
@@ -7271,8 +7372,6 @@ function parseArray(
7271
7372
  // End array group defining \cr
7272
7373
  parser.gullet.endGroup();
7273
7374
 
7274
- tags.push(rowTag);
7275
-
7276
7375
  return {
7277
7376
  type: "array",
7278
7377
  mode: parser.mode,
@@ -7281,9 +7380,10 @@ function parseArray(
7281
7380
  rowGaps,
7282
7381
  hLinesBeforeRow,
7283
7382
  envClasses,
7284
- addEqnNum,
7383
+ autoTag,
7285
7384
  scriptLevel,
7286
7385
  tags,
7386
+ labels,
7287
7387
  leqno,
7288
7388
  arraystretch,
7289
7389
  arraycolsep
@@ -7340,19 +7440,43 @@ const mathmlBuilder$7 = function(group, style) {
7340
7440
  }
7341
7441
  row.push(mtd);
7342
7442
  }
7343
- if (group.addEqnNum) {
7344
- row.unshift(glue(group));
7345
- row.push(glue(group));
7346
- const tag = getTag(group, style.withLevel(cellLevel), i);
7347
- if (group.leqno) {
7348
- row[0].children.push(tag);
7349
- row[0].classes.push("tml-left");
7350
- } else {
7351
- row[row.length - 1].children.push(tag);
7352
- row[row.length - 1].classes.push("tml-right");
7443
+ const numColumns = group.body[0].length;
7444
+ // Fill out a short row with empty <mtd> elements.
7445
+ for (let k = 0; k < numColumns - rw.length; k++) {
7446
+ row.push(new mathMLTree.MathNode("mtd", [], style));
7447
+ }
7448
+ if (group.autoTag) {
7449
+ const tag = group.tags[i];
7450
+ let tagElement;
7451
+ if (tag === true) { // automatic numbering
7452
+ tagElement = new mathMLTree.MathNode("mtext", [new Span(["tml-eqn"])]);
7453
+ } else if (tag === false) {
7454
+ // \nonumber/\notag or starred environment
7455
+ tagElement = new mathMLTree.MathNode("mtext", [], []);
7456
+ } else { // manual \tag
7457
+ tagElement = buildExpressionRow(tag[0].body, style.withLevel(cellLevel), true);
7458
+ tagElement = consolidateText(tagElement);
7459
+ tagElement.classes = ["tml-tag"];
7460
+ }
7461
+ if (tagElement) {
7462
+ row.unshift(glue(group));
7463
+ row.push(glue(group));
7464
+ if (group.leqno) {
7465
+ row[0].children.push(tagElement);
7466
+ row[0].classes.push("tml-left");
7467
+ } else {
7468
+ row[row.length - 1].children.push(tagElement);
7469
+ row[row.length - 1].classes.push("tml-right");
7470
+ }
7353
7471
  }
7354
7472
  }
7355
7473
  const mtr = new mathMLTree.MathNode("mtr", row, []);
7474
+ const label = group.labels.shift();
7475
+ if (label && group.tags && group.tags[i]) {
7476
+ mtr.setAttribute("id", label);
7477
+ if (Array.isArray(group.tags[i])) { mtr.classes.push("tml-tageqn"); }
7478
+ }
7479
+
7356
7480
  // Write horizontal rules
7357
7481
  if (i === 0 && hlines[0].length > 0) {
7358
7482
  if (hlines[0].length === 2) {
@@ -7376,16 +7500,17 @@ const mathmlBuilder$7 = function(group, style) {
7376
7500
  }
7377
7501
 
7378
7502
  if (group.envClasses.length > 0) {
7379
- let pad = group.envClasses.includes("jot")
7380
- ? "0.7" // 0.5ex + 0.09em top & bot padding
7381
- : group.envClasses.includes("small")
7382
- ? "0.35"
7383
- : "0.5"; // 0.5ex default top & bot padding
7384
7503
  if (group.arraystretch && group.arraystretch !== 1) {
7385
7504
  // In LaTeX, \arraystretch is a factor applied to a 12pt strut height.
7386
7505
  // It defines a baseline to baseline distance.
7387
7506
  // Here, we do an approximation of that approach.
7388
- pad = String(1.4 * group.arraystretch - 0.8);
7507
+ const pad = String(1.4 * group.arraystretch - 0.8) + "ex";
7508
+ for (let i = 0; i < tbl.length; i++) {
7509
+ for (let j = 0; j < tbl[i].children.length; j++) {
7510
+ tbl[i].children[j].style.paddingTop = pad;
7511
+ tbl[i].children[j].style.paddingBottom = pad;
7512
+ }
7513
+ }
7389
7514
  }
7390
7515
  let sidePadding = group.envClasses.includes("abut")
7391
7516
  ? "0"
@@ -7399,7 +7524,7 @@ const mathmlBuilder$7 = function(group, style) {
7399
7524
  let sidePadUnit = "em";
7400
7525
  if (group.arraycolsep) {
7401
7526
  const arraySidePad = calculateSize(group.arraycolsep, style);
7402
- sidePadding = arraySidePad.number;
7527
+ sidePadding = arraySidePad.number.toFixed(4);
7403
7528
  sidePadUnit = arraySidePad.unit;
7404
7529
  }
7405
7530
 
@@ -7410,18 +7535,18 @@ const mathmlBuilder$7 = function(group, style) {
7410
7535
  if (j === numCols - 1 && hand === 1) { return "0" }
7411
7536
  if (group.envClasses[0] !== "align") { return sidePadding }
7412
7537
  if (hand === 1) { return "0" }
7413
- if (group.addEqnNum) {
7538
+ if (group.autoTag) {
7414
7539
  return (j % 2) ? "1" : "0"
7415
7540
  } else {
7416
7541
  return (j % 2) ? "0" : "1"
7417
7542
  }
7418
7543
  };
7419
7544
 
7420
- // Padding
7545
+ // Side padding
7421
7546
  for (let i = 0; i < tbl.length; i++) {
7422
7547
  for (let j = 0; j < tbl[i].children.length; j++) {
7423
- tbl[i].children[j].style.padding = `${pad}ex ${sidePad(j, 1)}${sidePadUnit}`
7424
- + ` ${pad}ex ${sidePad(j, 0)}${sidePadUnit}`;
7548
+ tbl[i].children[j].style.paddingLeft = `${sidePad(j, 0)}${sidePadUnit}`;
7549
+ tbl[i].children[j].style.paddingRight = `${sidePad(j, 1)}${sidePadUnit}`;
7425
7550
  }
7426
7551
  }
7427
7552
 
@@ -7435,13 +7560,13 @@ const mathmlBuilder$7 = function(group, style) {
7435
7560
  // TODO: Remove -webkit- when Chromium no longer needs it.
7436
7561
  row.children[j].classes = ["tml-" + (j % 2 ? "left" : "right")];
7437
7562
  }
7438
- if (group.addEqnNum) {
7563
+ if (group.autoTag) {
7439
7564
  const k = group.leqno ? 0 : row.children.length - 1;
7440
7565
  row.children[k].classes = ["tml-" + (group.leqno ? "left" : "right")];
7441
7566
  }
7442
7567
  }
7443
7568
  if (row.children.length > 1 && group.envClasses.includes("cases")) {
7444
- row.children[1].style.padding = row.children[1].style.padding.replace(/0em$/, "1em");
7569
+ row.children[1].style.paddingLeft = "1em";
7445
7570
  }
7446
7571
 
7447
7572
  if (group.envClasses.includes("cases") || group.envClasses.includes("subarray")) {
@@ -7461,9 +7586,17 @@ const mathmlBuilder$7 = function(group, style) {
7461
7586
  }
7462
7587
 
7463
7588
  let table = new mathMLTree.MathNode("mtable", tbl);
7589
+ if (group.envClasses.length > 0) {
7590
+ // Top & bottom padding
7591
+ if (group.envClasses.includes("jot")) {
7592
+ table.classes.push("tml-jot");
7593
+ } else if (group.envClasses.includes("small")) {
7594
+ table.classes.push("tml-small");
7595
+ }
7596
+ }
7464
7597
  if (group.scriptLevel === "display") { table.setAttribute("displaystyle", "true"); }
7465
7598
 
7466
- if (group.addEqnNum || group.envClasses.includes("multline")) {
7599
+ if (group.autoTag || group.envClasses.includes("multline")) {
7467
7600
  table.style.width = "100%";
7468
7601
  }
7469
7602
 
@@ -7493,7 +7626,7 @@ const mathmlBuilder$7 = function(group, style) {
7493
7626
  row.children[0].style.borderLeft = sep;
7494
7627
  }
7495
7628
  }
7496
- let iCol = group.addEqnNum ? 0 : -1;
7629
+ let iCol = group.autoTag ? 0 : -1;
7497
7630
  for (let i = iStart; i < iEnd; i++) {
7498
7631
  if (cols[i].type === "align") {
7499
7632
  const colAlign = alignMap[cols[i].align];
@@ -7535,7 +7668,7 @@ const mathmlBuilder$7 = function(group, style) {
7535
7668
  }
7536
7669
  }
7537
7670
  }
7538
- if (group.addEqnNum) {
7671
+ if (group.autoTag) {
7539
7672
  // allow for glue cells on each side
7540
7673
  align = "left " + (align.length > 0 ? align : "center ") + "right ";
7541
7674
  }
@@ -7559,13 +7692,14 @@ const alignedHandler = function(context, args) {
7559
7692
  if (context.envName.indexOf("ed") === -1) {
7560
7693
  validateAmsEnvironmentContext(context);
7561
7694
  }
7695
+ const isSplit = context.envName === "split";
7562
7696
  const cols = [];
7563
7697
  const res = parseArray(
7564
7698
  context.parser,
7565
7699
  {
7566
7700
  cols,
7567
- addEqnNum: context.envName === "align" || context.envName === "alignat",
7568
7701
  emptySingleRow: true,
7702
+ autoTag: isSplit ? undefined : getAutoTag(context.envName),
7569
7703
  envClasses: ["abut", "jot"], // set row spacing & provisional column spacing
7570
7704
  maxNumCols: context.envName === "split" ? 2 : undefined,
7571
7705
  leqno: context.parser.settings.leqno
@@ -7887,7 +8021,7 @@ defineEnvironment({
7887
8021
  const res = {
7888
8022
  cols: [],
7889
8023
  envClasses: ["abut", "jot"],
7890
- addEqnNum: context.envName === "gather",
8024
+ autoTag: getAutoTag(context.envName),
7891
8025
  emptySingleRow: true,
7892
8026
  leqno: context.parser.settings.leqno
7893
8027
  };
@@ -7905,7 +8039,7 @@ defineEnvironment({
7905
8039
  handler(context) {
7906
8040
  validateAmsEnvironmentContext(context);
7907
8041
  const res = {
7908
- addEqnNum: context.envName === "equation",
8042
+ autoTag: getAutoTag(context.envName),
7909
8043
  emptySingleRow: true,
7910
8044
  singleRow: true,
7911
8045
  maxNumCols: 1,
@@ -7926,7 +8060,7 @@ defineEnvironment({
7926
8060
  handler(context) {
7927
8061
  validateAmsEnvironmentContext(context);
7928
8062
  const res = {
7929
- addEqnNum: context.envName === "multline",
8063
+ autoTag: context.envName === "multline",
7930
8064
  maxNumCols: 1,
7931
8065
  envClasses: ["jot", "multline"],
7932
8066
  leqno: context.parser.settings.leqno
@@ -8983,7 +9117,7 @@ defineFunction({
8983
9117
  // Return a no-width, no-ink element with an HTML id.
8984
9118
  const node = new mathMLTree.MathNode("mrow", [], ["tml-label"]);
8985
9119
  if (group.string.length > 0) {
8986
- node.setAttribute("id", group.string);
9120
+ node.setLabel(group.string);
8987
9121
  }
8988
9122
  return node
8989
9123
  }
@@ -11246,75 +11380,6 @@ const makeVerb = (group) => group.body.replace(/ /g, group.star ? "\u2423" : "\x
11246
11380
 
11247
11381
  const functions = _functions;
11248
11382
 
11249
- /**
11250
- * Lexing or parsing positional information for error reporting.
11251
- * This object is immutable.
11252
- */
11253
- class SourceLocation {
11254
- constructor(lexer, start, end) {
11255
- this.lexer = lexer; // Lexer holding the input string.
11256
- this.start = start; // Start offset, zero-based inclusive.
11257
- this.end = end; // End offset, zero-based exclusive.
11258
- }
11259
-
11260
- /**
11261
- * Merges two `SourceLocation`s from location providers, given they are
11262
- * provided in order of appearance.
11263
- * - Returns the first one's location if only the first is provided.
11264
- * - Returns a merged range of the first and the last if both are provided
11265
- * and their lexers match.
11266
- * - Otherwise, returns null.
11267
- */
11268
- static range(first, second) {
11269
- if (!second) {
11270
- return first && first.loc;
11271
- } else if (!first || !first.loc || !second.loc || first.loc.lexer !== second.loc.lexer) {
11272
- return null;
11273
- } else {
11274
- return new SourceLocation(first.loc.lexer, first.loc.start, second.loc.end);
11275
- }
11276
- }
11277
- }
11278
-
11279
- /**
11280
- * Interface required to break circular dependency between Token, Lexer, and
11281
- * ParseError.
11282
- */
11283
-
11284
- /**
11285
- * The resulting token returned from `lex`.
11286
- *
11287
- * It consists of the token text plus some position information.
11288
- * The position information is essentially a range in an input string,
11289
- * but instead of referencing the bare input string, we refer to the lexer.
11290
- * That way it is possible to attach extra metadata to the input string,
11291
- * like for example a file name or similar.
11292
- *
11293
- * The position information is optional, so it is OK to construct synthetic
11294
- * tokens if appropriate. Not providing available position information may
11295
- * lead to degraded error reporting, though.
11296
- */
11297
- class Token {
11298
- constructor(
11299
- text, // the text of this token
11300
- loc
11301
- ) {
11302
- this.text = text;
11303
- this.loc = loc;
11304
- }
11305
-
11306
- /**
11307
- * Given a pair of tokens (this and endToken), compute a `Token` encompassing
11308
- * the whole input range enclosed by these two.
11309
- */
11310
- range(
11311
- endToken, // last token of the range, inclusive
11312
- text // the text of the newly constructed token
11313
- ) {
11314
- return new Token(text, SourceLocation.range(this, endToken));
11315
- }
11316
- }
11317
-
11318
11383
  /**
11319
11384
  * The Lexer class handles tokenizing the input in various ways. Since our
11320
11385
  * parser expects us to be able to backtrack, the lexer allows lexing from any
@@ -13612,7 +13677,7 @@ class Style {
13612
13677
  * https://mit-license.org/
13613
13678
  */
13614
13679
 
13615
- const version = "0.10.33";
13680
+ const version = "0.11.00";
13616
13681
 
13617
13682
  function postProcess(block) {
13618
13683
  const labelMap = {};
@@ -13628,15 +13693,15 @@ function postProcess(block) {
13628
13693
  // No need to write a number into the text content of the element.
13629
13694
  // A CSS counter has done that even if this postProcess() function is not used.
13630
13695
 
13631
- // Find any \label that refers to an AMS eqn number.
13696
+ // Find any \label that refers to an AMS automatic eqn number.
13632
13697
  while (true) {
13698
+ if (parent.tagName === "mtable") { break }
13633
13699
  const labels = parent.getElementsByClassName("tml-label");
13634
13700
  if (labels.length > 0) {
13635
- parent.setAttribute("id", labels[0].id);
13636
- labelMap[labels[0].id] = String(i);
13701
+ const id = parent.attributes.id.value;
13702
+ labelMap[id] = String(i);
13637
13703
  break
13638
13704
  } else {
13639
- if (parent.tagName === "mtable") { break }
13640
13705
  parent = parent.parentElement;
13641
13706
  }
13642
13707
  }
@@ -13649,7 +13714,8 @@ function postProcess(block) {
13649
13714
  if (labels.length > 0) {
13650
13715
  const tags = parent.getElementsByClassName("tml-tag");
13651
13716
  if (tags.length > 0) {
13652
- labelMap[labels[0].id] = tags[0].textContent;
13717
+ const id = parent.attributes.id.value;
13718
+ labelMap[id] = tags[0].textContent;
13653
13719
  }
13654
13720
  }
13655
13721
  }
@@ -13676,6 +13742,267 @@ function postProcess(block) {
13676
13742
  });
13677
13743
  }
13678
13744
 
13745
+ const findEndOfMath = function(delimiter, text, startIndex) {
13746
+ // Adapted from
13747
+ // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
13748
+ let index = startIndex;
13749
+ let braceLevel = 0;
13750
+
13751
+ const delimLength = delimiter.length;
13752
+
13753
+ while (index < text.length) {
13754
+ const character = text[index];
13755
+
13756
+ if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
13757
+ return index;
13758
+ } else if (character === "\\") {
13759
+ index++;
13760
+ } else if (character === "{") {
13761
+ braceLevel++;
13762
+ } else if (character === "}") {
13763
+ braceLevel--;
13764
+ }
13765
+
13766
+ index++;
13767
+ }
13768
+
13769
+ return -1;
13770
+ };
13771
+
13772
+ const escapeRegex = function(string) {
13773
+ return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
13774
+ };
13775
+
13776
+ const amsRegex = /^\\(?:begin|(?:eq)?ref){/;
13777
+
13778
+ const splitAtDelimiters = function(text, delimiters) {
13779
+ let index;
13780
+ const data = [];
13781
+
13782
+ const regexLeft = new RegExp(
13783
+ "(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
13784
+ );
13785
+
13786
+ while (true) {
13787
+ index = text.search(regexLeft);
13788
+ if (index === -1) {
13789
+ break;
13790
+ }
13791
+ if (index > 0) {
13792
+ data.push({
13793
+ type: "text",
13794
+ data: text.slice(0, index)
13795
+ });
13796
+ text = text.slice(index); // now text starts with delimiter
13797
+ }
13798
+ // ... so this always succeeds:
13799
+ const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
13800
+ index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
13801
+ if (index === -1) {
13802
+ break;
13803
+ }
13804
+ const rawData = text.slice(0, index + delimiters[i].right.length);
13805
+ const math = amsRegex.test(rawData)
13806
+ ? rawData
13807
+ : text.slice(delimiters[i].left.length, index);
13808
+ data.push({
13809
+ type: "math",
13810
+ data: math,
13811
+ rawData,
13812
+ display: delimiters[i].display
13813
+ });
13814
+ text = text.slice(index + delimiters[i].right.length);
13815
+ }
13816
+
13817
+ if (text !== "") {
13818
+ data.push({
13819
+ type: "text",
13820
+ data: text
13821
+ });
13822
+ }
13823
+
13824
+ return data;
13825
+ };
13826
+
13827
+ const defaultDelimiters = [
13828
+ { left: "$$", right: "$$", display: true },
13829
+ { left: "\\(", right: "\\)", display: false },
13830
+ // LaTeX uses $…$, but it ruins the display of normal `$` in text:
13831
+ // {left: "$", right: "$", display: false},
13832
+ // $ must come after $$
13833
+
13834
+ // Render AMS environments even if outside $$…$$ delimiters.
13835
+ { left: "\\begin{equation}", right: "\\end{equation}", display: true },
13836
+ { left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
13837
+ { left: "\\begin{align}", right: "\\end{align}", display: true },
13838
+ { left: "\\begin{align*}", right: "\\end{align*}", display: true },
13839
+ { left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
13840
+ { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
13841
+ { left: "\\begin{gather}", right: "\\end{gather}", display: true },
13842
+ { left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
13843
+ { left: "\\begin{CD}", right: "\\end{CD}", display: true },
13844
+ // Ditto \ref & \eqref
13845
+ { left: "\\ref{", right: "}", display: false },
13846
+ { left: "\\eqref{", right: "}", display: false },
13847
+
13848
+ { left: "\\[", right: "\\]", display: true }
13849
+ ];
13850
+
13851
+ const firstDraftDelimiters = {
13852
+ "$": [
13853
+ { left: "$$", right: "$$", display: true },
13854
+ { left: "$`", right: "`$", display: false },
13855
+ { left: "$", right: "$", display: false }
13856
+ ],
13857
+ "(": [
13858
+ { left: "\\[", right: "\\]", display: true },
13859
+ { left: "\\(", right: "\\)", display: false }
13860
+ ]
13861
+ };
13862
+
13863
+ const amsDelimiters = [
13864
+ { left: "\\begin{equation}", right: "\\end{equation}", display: true },
13865
+ { left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
13866
+ { left: "\\begin{align}", right: "\\end{align}", display: true },
13867
+ { left: "\\begin{align*}", right: "\\end{align*}", display: true },
13868
+ { left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
13869
+ { left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
13870
+ { left: "\\begin{gather}", right: "\\end{gather}", display: true },
13871
+ { left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
13872
+ { left: "\\begin{CD}", right: "\\end{CD}", display: true },
13873
+ { left: "\\ref{", right: "}", display: false },
13874
+ { left: "\\eqref{", right: "}", display: false }
13875
+ ];
13876
+
13877
+ const delimitersFromKey = key => {
13878
+ if (key === "$" || key === "(") {
13879
+ return firstDraftDelimiters[key];
13880
+ } else if (key === "$+" || key === "(+") {
13881
+ const firstDraft = firstDraftDelimiters[key.slice(0, 1)];
13882
+ return firstDraft.concat(amsDelimiters)
13883
+ } else if (key === "ams") {
13884
+ return amsDelimiters
13885
+ } else if (key === "all") {
13886
+ return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters)
13887
+ } else {
13888
+ return defaultDelimiters
13889
+ }
13890
+ };
13891
+
13892
+ /* Note: optionsCopy is mutated by this method. If it is ever exposed in the
13893
+ * API, we should copy it before mutating.
13894
+ */
13895
+ const renderMathInText = function(text, optionsCopy) {
13896
+ const data = splitAtDelimiters(text, optionsCopy.delimiters);
13897
+ if (data.length === 1 && data[0].type === "text") {
13898
+ // There is no formula in the text.
13899
+ // Let's return null which means there is no need to replace
13900
+ // the current text node with a new one.
13901
+ return null;
13902
+ }
13903
+
13904
+ const fragment = document.createDocumentFragment();
13905
+
13906
+ for (let i = 0; i < data.length; i++) {
13907
+ if (data[i].type === "text") {
13908
+ fragment.appendChild(document.createTextNode(data[i].data));
13909
+ } else {
13910
+ const span = document.createElement("span");
13911
+ let math = data[i].data;
13912
+ // Override any display mode defined in the settings with that
13913
+ // defined by the text itself
13914
+ optionsCopy.displayMode = data[i].display;
13915
+ try {
13916
+ if (optionsCopy.preProcess) {
13917
+ math = optionsCopy.preProcess(math);
13918
+ }
13919
+ // Importing render() from temml.js would be a circular dependency.
13920
+ // So call the global version.
13921
+ // eslint-disable-next-line no-undef
13922
+ temml.render(math, span, optionsCopy);
13923
+ } catch (e) {
13924
+ if (!(e instanceof ParseError)) {
13925
+ throw e;
13926
+ }
13927
+ optionsCopy.errorCallback(
13928
+ "Temml auto-render: Failed to parse `" + data[i].data + "` with ",
13929
+ e
13930
+ );
13931
+ fragment.appendChild(document.createTextNode(data[i].rawData));
13932
+ continue;
13933
+ }
13934
+ fragment.appendChild(span);
13935
+ }
13936
+ }
13937
+
13938
+ return fragment;
13939
+ };
13940
+
13941
+ const renderElem = function(elem, optionsCopy) {
13942
+ for (let i = 0; i < elem.childNodes.length; i++) {
13943
+ const childNode = elem.childNodes[i];
13944
+ if (childNode.nodeType === 3) {
13945
+ // Text node
13946
+ const frag = renderMathInText(childNode.textContent, optionsCopy);
13947
+ if (frag) {
13948
+ i += frag.childNodes.length - 1;
13949
+ elem.replaceChild(frag, childNode);
13950
+ }
13951
+ } else if (childNode.nodeType === 1) {
13952
+ // Element node
13953
+ const className = " " + childNode.className + " ";
13954
+ const shouldRender =
13955
+ optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
13956
+ optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
13957
+
13958
+ if (shouldRender) {
13959
+ renderElem(childNode, optionsCopy);
13960
+ }
13961
+ }
13962
+ // Otherwise, it's something else, and ignore it.
13963
+ }
13964
+ };
13965
+
13966
+ const renderMathInElement = function(elem, options) {
13967
+ if (!elem) {
13968
+ throw new Error("No element provided to render");
13969
+ }
13970
+
13971
+ const optionsCopy = {};
13972
+
13973
+ // Object.assign(optionsCopy, option)
13974
+ for (const option in options) {
13975
+ if (Object.prototype.hasOwnProperty.call(options, option)) {
13976
+ optionsCopy[option] = options[option];
13977
+ }
13978
+ }
13979
+
13980
+ if (optionsCopy.fences) {
13981
+ optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences);
13982
+ } else {
13983
+ optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters;
13984
+ }
13985
+ optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
13986
+ "script",
13987
+ "noscript",
13988
+ "style",
13989
+ "textarea",
13990
+ "pre",
13991
+ "code",
13992
+ "option"
13993
+ ];
13994
+ optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
13995
+ // eslint-disable-next-line no-console
13996
+ optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
13997
+
13998
+ // Enable sharing of global macros defined via `\gdef` between different
13999
+ // math elements within a single call to `renderMathInElement`.
14000
+ optionsCopy.macros = optionsCopy.macros || {};
14001
+
14002
+ renderElem(elem, optionsCopy);
14003
+ postProcess(elem);
14004
+ };
14005
+
13679
14006
  /* eslint no-console:0 */
13680
14007
  /**
13681
14008
  * This is the main entry point for Temml. Here, we expose functions for
@@ -13795,7 +14122,7 @@ const renderToMathMLTree = function(expression, options) {
13795
14122
  };
13796
14123
 
13797
14124
  /** @type {import('./temml').default} */
13798
- var temml = {
14125
+ var temml$1 = {
13799
14126
  /**
13800
14127
  * Current Temml version
13801
14128
  */
@@ -13810,6 +14137,11 @@ var temml = {
13810
14137
  * for sending to the client.
13811
14138
  */
13812
14139
  renderToString,
14140
+ /**
14141
+ * Finds all the math delimiters in a given element of a running HTML document
14142
+ * and converts the contents of each instance into a <math> element.
14143
+ */
14144
+ renderMathInElement,
13813
14145
  /**
13814
14146
  * Post-process an entire HTML block.
13815
14147
  * Writes AMS auto-numbers and implements \ref{}.
@@ -13852,4 +14184,4 @@ var temml = {
13852
14184
  __defineMacro: defineMacro
13853
14185
  };
13854
14186
 
13855
- module.exports = temml;
14187
+ module.exports = temml$1;