temml 0.10.0 → 0.10.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/temml.mjs CHANGED
@@ -188,7 +188,7 @@ class Settings {
188
188
  this.leqno = utils.deflt(options.leqno, false); // boolean
189
189
  this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
190
190
  this.macros = options.macros || {};
191
- this.wrap = utils.deflt(options.wrap, "none"); // "none" | "tex" | "="
191
+ this.wrap = utils.deflt(options.wrap, "tex"); // "tex" | "="
192
192
  this.xml = utils.deflt(options.xml, false); // boolean
193
193
  this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
194
194
  this.strict = utils.deflt(options.strict, false); // boolean
@@ -666,7 +666,7 @@ class TextNode {
666
666
 
667
667
  /**
668
668
  * Converts the text node into a string
669
- * (representing the text iteself).
669
+ * (representing the text itself).
670
670
  */
671
671
  toText() {
672
672
  return this.text;
@@ -843,7 +843,6 @@ defineSymbol(math, rel, "\u226a", "\\ll", true);
843
843
  defineSymbol(math, rel, "\u226b", "\\gg", true);
844
844
  defineSymbol(math, rel, "\u224d", "\\asymp", true);
845
845
  defineSymbol(math, rel, "\u2225", "\\parallel");
846
- defineSymbol(math, rel, "\u22c8", "\\bowtie", true);
847
846
  defineSymbol(math, rel, "\u2323", "\\smile", true);
848
847
  defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
849
848
  defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
@@ -1160,7 +1159,6 @@ defineSymbol(math, rel, "\u22d9", "\\gggtr");
1160
1159
  defineSymbol(math, bin, "\u22b2", "\\lhd");
1161
1160
  defineSymbol(math, bin, "\u22b3", "\\rhd");
1162
1161
  defineSymbol(math, rel, "\u2242", "\\eqsim", true);
1163
- defineSymbol(math, rel, "\u22c8", "\\Join");
1164
1162
  defineSymbol(math, rel, "\u2251", "\\Doteq", true);
1165
1163
  defineSymbol(math, rel, "\u297d", "\\strictif", true);
1166
1164
  defineSymbol(math, rel, "\u297c", "\\strictfi", true);
@@ -1186,6 +1184,11 @@ defineSymbol(math, bin, "\u22ba", "\\intercal", true);
1186
1184
  defineSymbol(math, bin, "\u22d2", "\\doublecap");
1187
1185
  defineSymbol(math, bin, "\u22d3", "\\doublecup");
1188
1186
  defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
1187
+ defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
1188
+ defineSymbol(math, bin, "\u22c8", "\\Join");
1189
+ defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
1190
+ defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
1191
+ defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
1189
1192
 
1190
1193
  // AMS Arrows
1191
1194
  // Note: unicode-math maps \u21e2 to their own function \rightdasharrow.
@@ -1744,10 +1747,12 @@ for (let i = 0; i < 10; i++) {
1744
1747
  * Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
1745
1748
  * will break after a <mrow> if the expression extends beyond the container limit.
1746
1749
  *
1747
- * We want the expression to render with soft line breaks after each top-level binary or
1750
+ * The default is for soft line breaks after each top-level binary or
1748
1751
  * relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
1749
1752
  * each <mrow> ends in a binary or relational operator.
1750
1753
  *
1754
+ * An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
1755
+ *
1751
1756
  * Soft line breaks will not work in Chromium and Safari, only Firefox.
1752
1757
  *
1753
1758
  * Hopefully browsers will someday do their own linebreaking and we will be able to delete
@@ -1890,7 +1895,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
1890
1895
  }
1891
1896
 
1892
1897
  /**
1893
- * This file converts a parse tree into a cooresponding MathML tree. The main
1898
+ * This file converts a parse tree into a corresponding MathML tree. The main
1894
1899
  * entry point is the `buildMathML` function, which takes a parse tree from the
1895
1900
  * parser.
1896
1901
  */
@@ -2093,18 +2098,6 @@ function buildMathML(tree, texExpression, style, settings) {
2093
2098
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
2094
2099
  }
2095
2100
 
2096
- if (wrap !== "none" && wrapper.children.length > 1) {
2097
- const maths = [];
2098
- for (let i = 0; i < wrapper.children.length; i++) {
2099
- const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
2100
- if (settings.xml) {
2101
- math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
2102
- }
2103
- maths.push(math);
2104
- }
2105
- return mathMLTree.newDocumentFragment(maths)
2106
- }
2107
-
2108
2101
  const math = new mathMLTree.MathNode("math", [wrapper]);
2109
2102
 
2110
2103
  if (settings.xml) {
@@ -2112,6 +2105,9 @@ function buildMathML(tree, texExpression, style, settings) {
2112
2105
  }
2113
2106
  if (settings.displayMode) {
2114
2107
  math.setAttribute("display", "block");
2108
+ math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
2109
+ ? "inline"
2110
+ : "inline-block";
2115
2111
  }
2116
2112
  return math;
2117
2113
  }
@@ -3521,6 +3517,11 @@ const delimiters = [
3521
3517
  "."
3522
3518
  ];
3523
3519
 
3520
+ // Export isDelimiter for benefit of parser.
3521
+ const dels = ["}", "\\left", "\\middle", "\\right"];
3522
+ const isDelimiter = str => str.length > 0 &&
3523
+ (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
3524
+
3524
3525
  // Metrics of the different sizes. Found by looking at TeX's output of
3525
3526
  // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
3526
3527
  // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
@@ -6203,10 +6204,6 @@ const noSuccessor = ["\\smallint"];
6203
6204
  // Math operators (e.g. \sin) need a space between these types and themselves:
6204
6205
  const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6205
6206
 
6206
- const dels$1 = ["}", "\\left", "\\middle", "\\right"];
6207
- const isDelimiter$1 = str => str.length > 0 &&
6208
- (delimiters.includes(str) || delimiterSizes[str] || dels$1.includes(str));
6209
-
6210
6207
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6211
6208
  // "supsub" since some of them (like \int) can affect super/subscripting.
6212
6209
 
@@ -6427,7 +6424,7 @@ defineFunction({
6427
6424
  parentIsSupSub: false,
6428
6425
  symbol: false,
6429
6426
  stack: false,
6430
- isFollowedByDelimiter: isDelimiter$1(next),
6427
+ isFollowedByDelimiter: isDelimiter(next),
6431
6428
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6432
6429
  name: funcName
6433
6430
  };
@@ -6452,7 +6449,7 @@ defineFunction({
6452
6449
  parentIsSupSub: false,
6453
6450
  symbol: false,
6454
6451
  stack: false,
6455
- isFollowedByDelimiter: isDelimiter$1(next),
6452
+ isFollowedByDelimiter: isDelimiter(next),
6456
6453
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6457
6454
  name: funcName
6458
6455
  };
@@ -6538,11 +6535,7 @@ function defineMacro(name, body) {
6538
6535
  _macros[name] = body;
6539
6536
  }
6540
6537
 
6541
- const dels = ["}", "\\left", "\\middle", "\\right"];
6542
- const isDelimiter = str => str.length > 0 &&
6543
- (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
6544
-
6545
- // NOTE: Unlike most builders, this one handles not only
6538
+ // NOTE: Unlike most builders, this one handles not only
6546
6539
  // "operatorname", but also "supsub" since \operatorname* can
6547
6540
  // affect super/subscripting.
6548
6541
 
@@ -6552,8 +6545,12 @@ const mathmlBuilder$1 = (group, style) => {
6552
6545
  // Is expression a string or has it something like a fraction?
6553
6546
  let isAllString = true; // default
6554
6547
  for (let i = 0; i < expression.length; i++) {
6555
- const node = expression[i];
6548
+ let node = expression[i];
6556
6549
  if (node instanceof mathMLTree.MathNode) {
6550
+ if (node.type === "mrow" && node.children.length === 1 &&
6551
+ node.children[0] instanceof mathMLTree.MathNode) {
6552
+ node = node.children[0];
6553
+ }
6557
6554
  switch (node.type) {
6558
6555
  case "mi":
6559
6556
  case "mn":
@@ -6611,7 +6608,9 @@ const mathmlBuilder$1 = (group, style) => {
6611
6608
  let wrapper;
6612
6609
  if (isAllString) {
6613
6610
  wrapper = new mathMLTree.MathNode("mi", expression);
6614
- wrapper.setAttribute("mathvariant", "normal");
6611
+ if (expression[0].text.length === 1) {
6612
+ wrapper.setAttribute("mathvariant", "normal");
6613
+ }
6615
6614
  } else {
6616
6615
  wrapper = new mathMLTree.MathNode("mrow", expression);
6617
6616
  }
@@ -7149,6 +7148,7 @@ defineFunctionBuilders({
7149
7148
  let isOver;
7150
7149
  let isSup;
7151
7150
  let appendApplyFunction = false;
7151
+ let appendSpace = false;
7152
7152
  let needsLeadingSpace = false;
7153
7153
 
7154
7154
  if (group.base && group.base.type === "horizBrace") {
@@ -7163,6 +7163,7 @@ defineFunctionBuilders({
7163
7163
  (group.base.type === "op" || group.base.type === "operatorname")) {
7164
7164
  group.base.parentIsSupSub = true;
7165
7165
  appendApplyFunction = !group.base.symbol;
7166
+ appendSpace = appendApplyFunction && !group.isFollowedByDelimiter;
7166
7167
  needsLeadingSpace = group.base.needsLeadingSpace;
7167
7168
  }
7168
7169
 
@@ -7250,6 +7251,11 @@ defineFunctionBuilders({
7250
7251
  } else {
7251
7252
  node = mathMLTree.newDocumentFragment([node, operator]);
7252
7253
  }
7254
+ if (appendSpace) {
7255
+ const space = new mathMLTree.MathNode("mspace");
7256
+ space.setAttribute("width", "0.1667em"); // thin space.
7257
+ node.children.push(space);
7258
+ }
7253
7259
  } else if (symbolRegEx.test(nodeType)) {
7254
7260
  // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
7255
7261
  node = new mathMLTree.MathNode("mrow", [node]);
@@ -8882,7 +8888,7 @@ defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{
8882
8888
  defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}");
8883
8889
 
8884
8890
  /* eslint-disable */
8885
- /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8891
+ /* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8886
8892
  /* vim: set ts=2 et sw=2 tw=80: */
8887
8893
 
8888
8894
  /*************************************************************
@@ -10579,7 +10585,7 @@ defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap
10579
10585
  };
10580
10586
 
10581
10587
  //
10582
- // Helpers for code anaylsis
10588
+ // Helpers for code analysis
10583
10589
  // Will show type error at calling position
10584
10590
  //
10585
10591
  /** @param {number} a */
@@ -11004,15 +11010,15 @@ class MacroExpander {
11004
11010
  * Expand the next token only once if possible.
11005
11011
  *
11006
11012
  * If the token is expanded, the resulting tokens will be pushed onto
11007
- * the stack in reverse order and will be returned as an array,
11008
- * also in reverse order.
11013
+ * the stack in reverse order, and the number of such tokens will be
11014
+ * returned. This number might be zero or positive.
11009
11015
  *
11010
- * If not, the next token will be returned without removing it
11011
- * from the stack. This case can be detected by a `Token` return value
11012
- * instead of an `Array` return value.
11016
+ * If not, the return value is `false`, and the next token remains at the
11017
+ * top of the stack.
11013
11018
  *
11014
11019
  * In either case, the next token will be on the top of the stack,
11015
- * or the stack will be empty.
11020
+ * or the stack will be empty (in case of empty expansion
11021
+ * and no other tokens).
11016
11022
  *
11017
11023
  * Used to implement `expandAfterFuture` and `expandNextToken`.
11018
11024
  *
@@ -11028,7 +11034,7 @@ class MacroExpander {
11028
11034
  throw new ParseError("Undefined control sequence: " + name);
11029
11035
  }
11030
11036
  this.pushToken(topToken);
11031
- return topToken;
11037
+ return false;
11032
11038
  }
11033
11039
  this.expansionCount++;
11034
11040
  if (this.expansionCount > this.settings.maxExpand) {
@@ -11062,7 +11068,7 @@ class MacroExpander {
11062
11068
  }
11063
11069
  // Concatenate expansion onto top of stack.
11064
11070
  this.pushTokens(tokens);
11065
- return tokens;
11071
+ return tokens.length;
11066
11072
  }
11067
11073
 
11068
11074
  /**
@@ -11081,14 +11087,13 @@ class MacroExpander {
11081
11087
  */
11082
11088
  expandNextToken() {
11083
11089
  for (;;) {
11084
- const expanded = this.expandOnce();
11085
- // expandOnce returns Token if and only if it's fully expanded.
11086
- if (expanded instanceof Token) {
11090
+ if (this.expandOnce() === false) { // fully expanded
11091
+ const token = this.stack.pop();
11087
11092
  // The token after \noexpand is interpreted as if its meaning were ‘\relax’
11088
- if (expanded.treatAsRelax) {
11089
- expanded.text = "\\relax";
11093
+ if (token.treatAsRelax) {
11094
+ token.text = "\\relax";
11090
11095
  }
11091
- return this.stack.pop(); // === expanded
11096
+ return token
11092
11097
  }
11093
11098
  }
11094
11099
 
@@ -11114,15 +11119,15 @@ class MacroExpander {
11114
11119
  const oldStackLength = this.stack.length;
11115
11120
  this.pushTokens(tokens);
11116
11121
  while (this.stack.length > oldStackLength) {
11117
- const expanded = this.expandOnce(true); // expand only expandable tokens
11118
- // expandOnce returns Token if and only if it's fully expanded.
11119
- if (expanded instanceof Token) {
11120
- if (expanded.treatAsRelax) {
11122
+ // Expand only expandable tokens
11123
+ if (this.expandOnce(true) === false) { // fully expanded
11124
+ const token = this.stack.pop();
11125
+ if (token.treatAsRelax) {
11121
11126
  // the expansion of \noexpand is the token itself
11122
- expanded.noexpand = false;
11123
- expanded.treatAsRelax = false;
11127
+ token.noexpand = false;
11128
+ token.treatAsRelax = false;
11124
11129
  }
11125
- output.push(this.stack.pop());
11130
+ output.push(token);
11126
11131
  }
11127
11132
  }
11128
11133
  return output;
@@ -11950,7 +11955,7 @@ class Parser {
11950
11955
  * Parses an "expression", which is a list of atoms.
11951
11956
  *
11952
11957
  * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
11953
- * happens when functions have higher precendence han infix
11958
+ * happens when functions have higher precedence han infix
11954
11959
  * nodes in implicit parses.
11955
11960
  *
11956
11961
  * `breakOnTokenText`: The text of the token that the expression should end
@@ -12201,12 +12206,16 @@ class Parser {
12201
12206
  return base
12202
12207
  } else {
12203
12208
  // We got either a superscript or subscript, create a supsub
12209
+ const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
12210
+ ? undefined
12211
+ : isDelimiter(this.nextToken.text);
12204
12212
  return {
12205
12213
  type: "supsub",
12206
12214
  mode: this.mode,
12207
12215
  base: base,
12208
12216
  sup: superscript,
12209
- sub: subscript
12217
+ sub: subscript,
12218
+ isFollowedByDelimiter
12210
12219
  }
12211
12220
  }
12212
12221
  } else {
@@ -12367,7 +12376,7 @@ class Parser {
12367
12376
  while (true) {
12368
12377
  const ch = this.fetch().text;
12369
12378
  // \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
12370
- if (ch === " " || ch === "\ufe0e") {
12379
+ if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
12371
12380
  this.consume();
12372
12381
  } else {
12373
12382
  break
@@ -12959,7 +12968,7 @@ class Style {
12959
12968
  * https://mit-license.org/
12960
12969
  */
12961
12970
 
12962
- const version = "0.10.0";
12971
+ const version = "0.10.3";
12963
12972
 
12964
12973
  function postProcess(block) {
12965
12974
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.0";
17
+ const version = "0.10.3";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.10.0",
3
+ "version": "0.10.3",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "homepage": "https://temml.org",
@@ -8,6 +8,7 @@
8
8
  "type": "git",
9
9
  "url": "git://github.com/ronkok/Temml"
10
10
  },
11
+ "packageManager": "yarn@3.3.1",
11
12
  "files": [
12
13
  "temml.js",
13
14
  "src/",
@@ -230,15 +230,15 @@ export default class MacroExpander {
230
230
  * Expand the next token only once if possible.
231
231
  *
232
232
  * If the token is expanded, the resulting tokens will be pushed onto
233
- * the stack in reverse order and will be returned as an array,
234
- * also in reverse order.
233
+ * the stack in reverse order, and the number of such tokens will be
234
+ * returned. This number might be zero or positive.
235
235
  *
236
- * If not, the next token will be returned without removing it
237
- * from the stack. This case can be detected by a `Token` return value
238
- * instead of an `Array` return value.
236
+ * If not, the return value is `false`, and the next token remains at the
237
+ * top of the stack.
239
238
  *
240
239
  * In either case, the next token will be on the top of the stack,
241
- * or the stack will be empty.
240
+ * or the stack will be empty (in case of empty expansion
241
+ * and no other tokens).
242
242
  *
243
243
  * Used to implement `expandAfterFuture` and `expandNextToken`.
244
244
  *
@@ -254,7 +254,7 @@ export default class MacroExpander {
254
254
  throw new ParseError("Undefined control sequence: " + name);
255
255
  }
256
256
  this.pushToken(topToken);
257
- return topToken;
257
+ return false;
258
258
  }
259
259
  this.expansionCount++;
260
260
  if (this.expansionCount > this.settings.maxExpand) {
@@ -288,7 +288,7 @@ export default class MacroExpander {
288
288
  }
289
289
  // Concatenate expansion onto top of stack.
290
290
  this.pushTokens(tokens);
291
- return tokens;
291
+ return tokens.length;
292
292
  }
293
293
 
294
294
  /**
@@ -307,14 +307,13 @@ export default class MacroExpander {
307
307
  */
308
308
  expandNextToken() {
309
309
  for (;;) {
310
- const expanded = this.expandOnce();
311
- // expandOnce returns Token if and only if it's fully expanded.
312
- if (expanded instanceof Token) {
310
+ if (this.expandOnce() === false) { // fully expanded
311
+ const token = this.stack.pop();
313
312
  // The token after \noexpand is interpreted as if its meaning were ‘\relax’
314
- if (expanded.treatAsRelax) {
315
- expanded.text = "\\relax"
313
+ if (token.treatAsRelax) {
314
+ token.text = "\\relax"
316
315
  }
317
- return this.stack.pop(); // === expanded
316
+ return token
318
317
  }
319
318
  }
320
319
 
@@ -340,15 +339,15 @@ export default class MacroExpander {
340
339
  const oldStackLength = this.stack.length;
341
340
  this.pushTokens(tokens);
342
341
  while (this.stack.length > oldStackLength) {
343
- const expanded = this.expandOnce(true); // expand only expandable tokens
344
- // expandOnce returns Token if and only if it's fully expanded.
345
- if (expanded instanceof Token) {
346
- if (expanded.treatAsRelax) {
342
+ // Expand only expandable tokens
343
+ if (this.expandOnce(true) === false) { // fully expanded
344
+ const token = this.stack.pop();
345
+ if (token.treatAsRelax) {
347
346
  // the expansion of \noexpand is the token itself
348
- expanded.noexpand = false;
349
- expanded.treatAsRelax = false;
347
+ token.noexpand = false;
348
+ token.treatAsRelax = false;
350
349
  }
351
- output.push(this.stack.pop());
350
+ output.push(token);
352
351
  }
353
352
  }
354
353
  return output;
package/src/Parser.js CHANGED
@@ -10,6 +10,7 @@ import { uSubsAndSups, unicodeSubRegEx } from "./unicodeSupOrSub"
10
10
  import { asciiFromScript } from "./asciiFromScript"
11
11
  import SourceLocation from "./SourceLocation";
12
12
  import { Token } from "./Token";
13
+ import { isDelimiter } from "./functions/delimsizing"
13
14
 
14
15
  // Pre-evaluate both modules as unicodeSymbols require String.normalize()
15
16
  import unicodeAccents from /*preval*/ "./unicodeAccents";
@@ -173,7 +174,7 @@ export default class Parser {
173
174
  * Parses an "expression", which is a list of atoms.
174
175
  *
175
176
  * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
176
- * happens when functions have higher precendence han infix
177
+ * happens when functions have higher precedence han infix
177
178
  * nodes in implicit parses.
178
179
  *
179
180
  * `breakOnTokenText`: The text of the token that the expression should end
@@ -424,12 +425,16 @@ export default class Parser {
424
425
  return base
425
426
  } else {
426
427
  // We got either a superscript or subscript, create a supsub
428
+ const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
429
+ ? undefined
430
+ : isDelimiter(this.nextToken.text);
427
431
  return {
428
432
  type: "supsub",
429
433
  mode: this.mode,
430
434
  base: base,
431
435
  sup: superscript,
432
- sub: subscript
436
+ sub: subscript,
437
+ isFollowedByDelimiter
433
438
  }
434
439
  }
435
440
  } else {
@@ -590,7 +595,7 @@ export default class Parser {
590
595
  while (true) {
591
596
  const ch = this.fetch().text
592
597
  // \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
593
- if (ch === " " || ch === "\ufe0e") {
598
+ if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
594
599
  this.consume()
595
600
  } else {
596
601
  break
package/src/Settings.js CHANGED
@@ -17,7 +17,7 @@ export default class Settings {
17
17
  this.leqno = utils.deflt(options.leqno, false); // boolean
18
18
  this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
19
19
  this.macros = options.macros || {};
20
- this.wrap = utils.deflt(options.wrap, "none") // "none" | "tex" | "="
20
+ this.wrap = utils.deflt(options.wrap, "tex") // "tex" | "="
21
21
  this.xml = utils.deflt(options.xml, false); // boolean
22
22
  this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
23
23
  this.strict = utils.deflt(options.strict, false); // boolean
@@ -1,5 +1,5 @@
1
1
  /**
2
- * This file converts a parse tree into a cooresponding MathML tree. The main
2
+ * This file converts a parse tree into a corresponding MathML tree. The main
3
3
  * entry point is the `buildMathML` function, which takes a parse tree from the
4
4
  * parser.
5
5
  */
@@ -209,18 +209,6 @@ export default function buildMathML(tree, texExpression, style, settings) {
209
209
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
210
210
  }
211
211
 
212
- if (wrap !== "none" && wrapper.children.length > 1) {
213
- const maths = []
214
- for (let i = 0; i < wrapper.children.length; i++) {
215
- const math = new mathMLTree.MathNode("math", [wrapper.children[i]])
216
- if (settings.xml) {
217
- math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML")
218
- }
219
- maths.push(math)
220
- }
221
- return mathMLTree.newDocumentFragment(maths)
222
- }
223
-
224
212
  const math = new mathMLTree.MathNode("math", [wrapper])
225
213
 
226
214
  if (settings.xml) {
@@ -228,6 +216,9 @@ export default function buildMathML(tree, texExpression, style, settings) {
228
216
  }
229
217
  if (settings.displayMode) {
230
218
  math.setAttribute("display", "block");
219
+ math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
220
+ ? "inline"
221
+ : "inline-block"
231
222
  }
232
223
  return math;
233
224
  }
@@ -89,6 +89,11 @@ export const delimiters = [
89
89
  "."
90
90
  ];
91
91
 
92
+ // Export isDelimiter for benefit of parser.
93
+ const dels = ["}", "\\left", "\\middle", "\\right"]
94
+ export const isDelimiter = str => str.length > 0 &&
95
+ (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
96
+
92
97
  // Metrics of the different sizes. Found by looking at TeX's output of
93
98
  // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
94
99
  // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
@@ -2,7 +2,7 @@
2
2
  import defineFunction, { ordargument } from "../defineFunction";
3
3
  import * as mathMLTree from "../mathMLTree";
4
4
  import * as mml from "../buildMathML";
5
- import { delimiters, delimiterSizes } from "./delimsizing"
5
+ import { isDelimiter } from "./delimsizing"
6
6
 
7
7
  // Some helpers
8
8
 
@@ -14,10 +14,6 @@ const noSuccessor = ["\\smallint"];
14
14
  // Math operators (e.g. \sin) need a space between these types and themselves:
15
15
  export const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
16
16
 
17
- const dels = ["}", "\\left", "\\middle", "\\right"]
18
- const isDelimiter = str => str.length > 0 &&
19
- (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
20
-
21
17
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
22
18
  // "supsub" since some of them (like \int) can affect super/subscripting.
23
19
 
@@ -3,15 +3,11 @@ import defineMacro from "../defineMacro";
3
3
  import mathMLTree from "../mathMLTree"
4
4
  import { spaceCharacter } from "./kern"
5
5
  import { ordTypes } from "./op"
6
- import { delimiters, delimiterSizes } from "./delimsizing"
6
+ import { isDelimiter } from "./delimsizing"
7
7
 
8
8
  import * as mml from "../buildMathML"
9
9
 
10
- const dels = ["}", "\\left", "\\middle", "\\right"]
11
- const isDelimiter = str => str.length > 0 &&
12
- (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
13
-
14
- // NOTE: Unlike most builders, this one handles not only
10
+ // NOTE: Unlike most builders, this one handles not only
15
11
  // "operatorname", but also "supsub" since \operatorname* can
16
12
  // affect super/subscripting.
17
13
 
@@ -21,8 +17,12 @@ const mathmlBuilder = (group, style) => {
21
17
  // Is expression a string or has it something like a fraction?
22
18
  let isAllString = true; // default
23
19
  for (let i = 0; i < expression.length; i++) {
24
- const node = expression[i]
20
+ let node = expression[i]
25
21
  if (node instanceof mathMLTree.MathNode) {
22
+ if (node.type === "mrow" && node.children.length === 1 &&
23
+ node.children[0] instanceof mathMLTree.MathNode) {
24
+ node = node.children[0]
25
+ }
26
26
  switch (node.type) {
27
27
  case "mi":
28
28
  case "mn":
@@ -80,7 +80,9 @@ const mathmlBuilder = (group, style) => {
80
80
  let wrapper;
81
81
  if (isAllString) {
82
82
  wrapper = new mathMLTree.MathNode("mi", expression)
83
- wrapper.setAttribute("mathvariant", "normal")
83
+ if (expression[0].text.length === 1) {
84
+ wrapper.setAttribute("mathvariant", "normal")
85
+ }
84
86
  } else {
85
87
  wrapper = new mathMLTree.MathNode("mrow", expression)
86
88
  }
@@ -24,6 +24,7 @@ defineFunctionBuilders({
24
24
  let isOver
25
25
  let isSup
26
26
  let appendApplyFunction = false
27
+ let appendSpace = false
27
28
  let needsLeadingSpace = false
28
29
 
29
30
  if (group.base && group.base.type === "horizBrace") {
@@ -38,6 +39,7 @@ defineFunctionBuilders({
38
39
  (group.base.type === "op" || group.base.type === "operatorname")) {
39
40
  group.base.parentIsSupSub = true
40
41
  appendApplyFunction = !group.base.symbol
42
+ appendSpace = appendApplyFunction && !group.isFollowedByDelimiter
41
43
  needsLeadingSpace = group.base.needsLeadingSpace
42
44
  }
43
45
 
@@ -125,6 +127,11 @@ defineFunctionBuilders({
125
127
  } else {
126
128
  node = mathMLTree.newDocumentFragment([node, operator])
127
129
  }
130
+ if (appendSpace) {
131
+ const space = new mathMLTree.MathNode("mspace")
132
+ space.setAttribute("width", "0.1667em") // thin space.
133
+ node.children.push(space)
134
+ }
128
135
  } else if (symbolRegEx.test(nodeType)) {
129
136
  // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
130
137
  node = new mathMLTree.MathNode("mrow", [node])
@@ -13,10 +13,12 @@ import mathMLTree from "./mathMLTree"
13
13
  * Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
14
14
  * will break after a <mrow> if the expression extends beyond the container limit.
15
15
  *
16
- * We want the expression to render with soft line breaks after each top-level binary or
16
+ * The default is for soft line breaks after each top-level binary or
17
17
  * relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
18
18
  * each <mrow> ends in a binary or relational operator.
19
19
  *
20
+ * An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
21
+ *
20
22
  * Soft line breaks will not work in Chromium and Safari, only Firefox.
21
23
  *
22
24
  * Hopefully browsers will someday do their own linebreaking and we will be able to delete