temml 0.10.0 → 0.10.2

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
@@ -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.