temml 0.10.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
package/dist/temml.mjs CHANGED
@@ -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.
@@ -1890,7 +1893,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
1890
1893
  }
1891
1894
 
1892
1895
  /**
1893
- * This file converts a parse tree into a cooresponding MathML tree. The main
1896
+ * This file converts a parse tree into a corresponding MathML tree. The main
1894
1897
  * entry point is the `buildMathML` function, which takes a parse tree from the
1895
1898
  * parser.
1896
1899
  */
@@ -3521,6 +3524,11 @@ const delimiters = [
3521
3524
  "."
3522
3525
  ];
3523
3526
 
3527
+ // Export isDelimiter for benefit of parser.
3528
+ const dels = ["}", "\\left", "\\middle", "\\right"];
3529
+ const isDelimiter = str => str.length > 0 &&
3530
+ (delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
3531
+
3524
3532
  // Metrics of the different sizes. Found by looking at TeX's output of
3525
3533
  // $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
3526
3534
  // Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
@@ -6203,10 +6211,6 @@ const noSuccessor = ["\\smallint"];
6203
6211
  // Math operators (e.g. \sin) need a space between these types and themselves:
6204
6212
  const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6205
6213
 
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
6214
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6211
6215
  // "supsub" since some of them (like \int) can affect super/subscripting.
6212
6216
 
@@ -6427,7 +6431,7 @@ defineFunction({
6427
6431
  parentIsSupSub: false,
6428
6432
  symbol: false,
6429
6433
  stack: false,
6430
- isFollowedByDelimiter: isDelimiter$1(next),
6434
+ isFollowedByDelimiter: isDelimiter(next),
6431
6435
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6432
6436
  name: funcName
6433
6437
  };
@@ -6452,7 +6456,7 @@ defineFunction({
6452
6456
  parentIsSupSub: false,
6453
6457
  symbol: false,
6454
6458
  stack: false,
6455
- isFollowedByDelimiter: isDelimiter$1(next),
6459
+ isFollowedByDelimiter: isDelimiter(next),
6456
6460
  needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
6457
6461
  name: funcName
6458
6462
  };
@@ -6538,11 +6542,7 @@ function defineMacro(name, body) {
6538
6542
  _macros[name] = body;
6539
6543
  }
6540
6544
 
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
6545
+ // NOTE: Unlike most builders, this one handles not only
6546
6546
  // "operatorname", but also "supsub" since \operatorname* can
6547
6547
  // affect super/subscripting.
6548
6548
 
@@ -6552,8 +6552,12 @@ const mathmlBuilder$1 = (group, style) => {
6552
6552
  // Is expression a string or has it something like a fraction?
6553
6553
  let isAllString = true; // default
6554
6554
  for (let i = 0; i < expression.length; i++) {
6555
- const node = expression[i];
6555
+ let node = expression[i];
6556
6556
  if (node instanceof mathMLTree.MathNode) {
6557
+ if (node.type === "mrow" && node.children.length === 1 &&
6558
+ node.children[0] instanceof mathMLTree.MathNode) {
6559
+ node = node.children[0];
6560
+ }
6557
6561
  switch (node.type) {
6558
6562
  case "mi":
6559
6563
  case "mn":
@@ -6611,7 +6615,9 @@ const mathmlBuilder$1 = (group, style) => {
6611
6615
  let wrapper;
6612
6616
  if (isAllString) {
6613
6617
  wrapper = new mathMLTree.MathNode("mi", expression);
6614
- wrapper.setAttribute("mathvariant", "normal");
6618
+ if (expression[0].text.length === 1) {
6619
+ wrapper.setAttribute("mathvariant", "normal");
6620
+ }
6615
6621
  } else {
6616
6622
  wrapper = new mathMLTree.MathNode("mrow", expression);
6617
6623
  }
@@ -7149,6 +7155,7 @@ defineFunctionBuilders({
7149
7155
  let isOver;
7150
7156
  let isSup;
7151
7157
  let appendApplyFunction = false;
7158
+ let appendSpace = false;
7152
7159
  let needsLeadingSpace = false;
7153
7160
 
7154
7161
  if (group.base && group.base.type === "horizBrace") {
@@ -7163,6 +7170,7 @@ defineFunctionBuilders({
7163
7170
  (group.base.type === "op" || group.base.type === "operatorname")) {
7164
7171
  group.base.parentIsSupSub = true;
7165
7172
  appendApplyFunction = !group.base.symbol;
7173
+ appendSpace = appendApplyFunction && !group.isFollowedByDelimiter;
7166
7174
  needsLeadingSpace = group.base.needsLeadingSpace;
7167
7175
  }
7168
7176
 
@@ -7250,6 +7258,11 @@ defineFunctionBuilders({
7250
7258
  } else {
7251
7259
  node = mathMLTree.newDocumentFragment([node, operator]);
7252
7260
  }
7261
+ if (appendSpace) {
7262
+ const space = new mathMLTree.MathNode("mspace");
7263
+ space.setAttribute("width", "0.1667em"); // thin space.
7264
+ node.children.push(space);
7265
+ }
7253
7266
  } else if (symbolRegEx.test(nodeType)) {
7254
7267
  // Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
7255
7268
  node = new mathMLTree.MathNode("mrow", [node]);
@@ -8882,7 +8895,7 @@ defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{
8882
8895
  defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}");
8883
8896
 
8884
8897
  /* eslint-disable */
8885
- /* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8898
+ /* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
8886
8899
  /* vim: set ts=2 et sw=2 tw=80: */
8887
8900
 
8888
8901
  /*************************************************************
@@ -10579,7 +10592,7 @@ defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap
10579
10592
  };
10580
10593
 
10581
10594
  //
10582
- // Helpers for code anaylsis
10595
+ // Helpers for code analysis
10583
10596
  // Will show type error at calling position
10584
10597
  //
10585
10598
  /** @param {number} a */
@@ -11004,15 +11017,15 @@ class MacroExpander {
11004
11017
  * Expand the next token only once if possible.
11005
11018
  *
11006
11019
  * 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.
11020
+ * the stack in reverse order, and the number of such tokens will be
11021
+ * returned. This number might be zero or positive.
11009
11022
  *
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.
11023
+ * If not, the return value is `false`, and the next token remains at the
11024
+ * top of the stack.
11013
11025
  *
11014
11026
  * In either case, the next token will be on the top of the stack,
11015
- * or the stack will be empty.
11027
+ * or the stack will be empty (in case of empty expansion
11028
+ * and no other tokens).
11016
11029
  *
11017
11030
  * Used to implement `expandAfterFuture` and `expandNextToken`.
11018
11031
  *
@@ -11028,7 +11041,7 @@ class MacroExpander {
11028
11041
  throw new ParseError("Undefined control sequence: " + name);
11029
11042
  }
11030
11043
  this.pushToken(topToken);
11031
- return topToken;
11044
+ return false;
11032
11045
  }
11033
11046
  this.expansionCount++;
11034
11047
  if (this.expansionCount > this.settings.maxExpand) {
@@ -11062,7 +11075,7 @@ class MacroExpander {
11062
11075
  }
11063
11076
  // Concatenate expansion onto top of stack.
11064
11077
  this.pushTokens(tokens);
11065
- return tokens;
11078
+ return tokens.length;
11066
11079
  }
11067
11080
 
11068
11081
  /**
@@ -11081,14 +11094,13 @@ class MacroExpander {
11081
11094
  */
11082
11095
  expandNextToken() {
11083
11096
  for (;;) {
11084
- const expanded = this.expandOnce();
11085
- // expandOnce returns Token if and only if it's fully expanded.
11086
- if (expanded instanceof Token) {
11097
+ if (this.expandOnce() === false) { // fully expanded
11098
+ const token = this.stack.pop();
11087
11099
  // The token after \noexpand is interpreted as if its meaning were ‘\relax’
11088
- if (expanded.treatAsRelax) {
11089
- expanded.text = "\\relax";
11100
+ if (token.treatAsRelax) {
11101
+ token.text = "\\relax";
11090
11102
  }
11091
- return this.stack.pop(); // === expanded
11103
+ return token
11092
11104
  }
11093
11105
  }
11094
11106
 
@@ -11114,15 +11126,15 @@ class MacroExpander {
11114
11126
  const oldStackLength = this.stack.length;
11115
11127
  this.pushTokens(tokens);
11116
11128
  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) {
11129
+ // Expand only expandable tokens
11130
+ if (this.expandOnce(true) === false) { // fully expanded
11131
+ const token = this.stack.pop();
11132
+ if (token.treatAsRelax) {
11121
11133
  // the expansion of \noexpand is the token itself
11122
- expanded.noexpand = false;
11123
- expanded.treatAsRelax = false;
11134
+ token.noexpand = false;
11135
+ token.treatAsRelax = false;
11124
11136
  }
11125
- output.push(this.stack.pop());
11137
+ output.push(token);
11126
11138
  }
11127
11139
  }
11128
11140
  return output;
@@ -11950,7 +11962,7 @@ class Parser {
11950
11962
  * Parses an "expression", which is a list of atoms.
11951
11963
  *
11952
11964
  * `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
11953
- * happens when functions have higher precendence han infix
11965
+ * happens when functions have higher precedence han infix
11954
11966
  * nodes in implicit parses.
11955
11967
  *
11956
11968
  * `breakOnTokenText`: The text of the token that the expression should end
@@ -12201,12 +12213,16 @@ class Parser {
12201
12213
  return base
12202
12214
  } else {
12203
12215
  // We got either a superscript or subscript, create a supsub
12216
+ const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
12217
+ ? undefined
12218
+ : isDelimiter(this.nextToken.text);
12204
12219
  return {
12205
12220
  type: "supsub",
12206
12221
  mode: this.mode,
12207
12222
  base: base,
12208
12223
  sup: superscript,
12209
- sub: subscript
12224
+ sub: subscript,
12225
+ isFollowedByDelimiter
12210
12226
  }
12211
12227
  }
12212
12228
  } else {
@@ -12367,7 +12383,7 @@ class Parser {
12367
12383
  while (true) {
12368
12384
  const ch = this.fetch().text;
12369
12385
  // \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
12370
- if (ch === " " || ch === "\ufe0e") {
12386
+ if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
12371
12387
  this.consume();
12372
12388
  } else {
12373
12389
  break
@@ -12959,7 +12975,7 @@ class Style {
12959
12975
  * https://mit-license.org/
12960
12976
  */
12961
12977
 
12962
- const version = "0.10.0";
12978
+ const version = "0.10.2";
12963
12979
 
12964
12980
  function postProcess(block) {
12965
12981
  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.2";
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.2",
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
@@ -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
  */
@@ -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])
package/src/mathMLTree.js CHANGED
@@ -148,7 +148,7 @@ export class TextNode {
148
148
 
149
149
  /**
150
150
  * Converts the text node into a string
151
- * (representing the text iteself).
151
+ * (representing the text itself).
152
152
  */
153
153
  toText() {
154
154
  return this.text;
@@ -8,7 +8,7 @@
8
8
  * https://mit-license.org/
9
9
  */
10
10
 
11
- export const version = "0.10.0";
11
+ export const version = "0.10.2";
12
12
 
13
13
  export function postProcess(block) {
14
14
  const labelMap = {}
package/src/symbols.js CHANGED
@@ -85,7 +85,6 @@ defineSymbol(math, rel, "\u226a", "\\ll", true);
85
85
  defineSymbol(math, rel, "\u226b", "\\gg", true);
86
86
  defineSymbol(math, rel, "\u224d", "\\asymp", true);
87
87
  defineSymbol(math, rel, "\u2225", "\\parallel");
88
- defineSymbol(math, rel, "\u22c8", "\\bowtie", true);
89
88
  defineSymbol(math, rel, "\u2323", "\\smile", true);
90
89
  defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
91
90
  defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
@@ -402,7 +401,6 @@ defineSymbol(math, rel, "\u22d9", "\\gggtr");
402
401
  defineSymbol(math, bin, "\u22b2", "\\lhd");
403
402
  defineSymbol(math, bin, "\u22b3", "\\rhd");
404
403
  defineSymbol(math, rel, "\u2242", "\\eqsim", true);
405
- defineSymbol(math, rel, "\u22c8", "\\Join");
406
404
  defineSymbol(math, rel, "\u2251", "\\Doteq", true);
407
405
  defineSymbol(math, rel, "\u297d", "\\strictif", true);
408
406
  defineSymbol(math, rel, "\u297c", "\\strictfi", true);
@@ -428,6 +426,11 @@ defineSymbol(math, bin, "\u22ba", "\\intercal", true);
428
426
  defineSymbol(math, bin, "\u22d2", "\\doublecap");
429
427
  defineSymbol(math, bin, "\u22d3", "\\doublecup");
430
428
  defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
429
+ defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
430
+ defineSymbol(math, bin, "\u22c8", "\\Join");
431
+ defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
432
+ defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
433
+ defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
431
434
 
432
435
  // AMS Arrows
433
436
  // Note: unicode-math maps \u21e2 to their own function \rightdasharrow.