temml 0.10.19 → 0.10.21

Sign up to get free protection for your applications and to get access to all the features.
package/dist/temml.mjs CHANGED
@@ -2070,20 +2070,22 @@ const consolidateNumbers = expression => {
2070
2070
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
2071
2071
  * unless the array has length 1. Always returns a single node.
2072
2072
  */
2073
- const makeRow = function(body) {
2073
+ const makeRow = function(body, semisimple = false) {
2074
2074
  if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
2075
2075
  return body[0];
2076
- } else {
2076
+ } else if (!semisimple) {
2077
2077
  // Suppress spacing on <mo> nodes at both ends of the row.
2078
2078
  if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
2079
2079
  body[0].attributes.lspace = "0em";
2080
+ body[0].attributes.rspace = "0em";
2080
2081
  }
2081
2082
  const end = body.length - 1;
2082
2083
  if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
2084
+ body[end].attributes.lspace = "0em";
2083
2085
  body[end].attributes.rspace = "0em";
2084
2086
  }
2085
- return new mathMLTree.MathNode("mrow", body);
2086
2087
  }
2088
+ return new mathMLTree.MathNode("mrow", body);
2087
2089
  };
2088
2090
 
2089
2091
  const isRel = item => {
@@ -2097,10 +2099,10 @@ const isRel = item => {
2097
2099
  * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
2098
2100
  * (2) Suppress spacing between two adjacent relations.
2099
2101
  */
2100
- const buildExpression = function(expression, style, isOrdgroup) {
2101
- if (expression.length === 1) {
2102
+ const buildExpression = function(expression, style, semisimple = false) {
2103
+ if (!semisimple && expression.length === 1) {
2102
2104
  const group = buildGroup$1(expression[0], style);
2103
- if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
2105
+ if (group instanceof MathNode && group.type === "mo") {
2104
2106
  // When TeX writers want to suppress spacing on an operator,
2105
2107
  // they often put the operator by itself inside braces.
2106
2108
  group.setAttribute("lspace", "0em");
@@ -2130,8 +2132,8 @@ const buildExpression = function(expression, style, isOrdgroup) {
2130
2132
  * Equivalent to buildExpression, but wraps the elements in an <mrow>
2131
2133
  * if there's more than one. Returns a single node instead of an array.
2132
2134
  */
2133
- const buildExpressionRow = function(expression, style, isOrdgroup) {
2134
- return makeRow(buildExpression(expression, style, isOrdgroup));
2135
+ const buildExpressionRow = function(expression, style, semisimple = false) {
2136
+ return makeRow(buildExpression(expression, style, semisimple), semisimple);
2135
2137
  };
2136
2138
 
2137
2139
  /**
@@ -2816,7 +2818,8 @@ function cdArrow(arrowChar, labels, parser) {
2816
2818
  const arrowGroup = {
2817
2819
  type: "ordgroup",
2818
2820
  mode: "math",
2819
- body: [leftLabel, sizedArrow, rightLabel]
2821
+ body: [leftLabel, sizedArrow, rightLabel],
2822
+ semisimple: true
2820
2823
  };
2821
2824
  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
2822
2825
  }
@@ -3242,7 +3245,7 @@ defineFunction({
3242
3245
  allowedInText: true,
3243
3246
  argTypes: ["raw", "raw"]
3244
3247
  },
3245
- handler({ parser, token }, args, optArgs) {
3248
+ handler({ parser, breakOnTokenText, token }, args, optArgs) {
3246
3249
  const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
3247
3250
  let color = "";
3248
3251
  if (model) {
@@ -3252,15 +3255,8 @@ defineFunction({
3252
3255
  color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token);
3253
3256
  }
3254
3257
 
3255
- // Set macro \current@color in current namespace to store the current
3256
- // color, mimicking the behavior of color.sty.
3257
- // This is currently used just to correctly color a \right
3258
- // that follows a \color command.
3259
- parser.gullet.macros.set("\\current@color", color);
3260
-
3261
3258
  // Parse out the implicit body that should be colored.
3262
- // Since \color nodes should not be nested, break on \color.
3263
- const body = parser.parseExpression(true, "\\color");
3259
+ const body = parser.parseExpression(true, breakOnTokenText);
3264
3260
 
3265
3261
  return {
3266
3262
  type: "color",
@@ -3800,18 +3796,10 @@ defineFunction({
3800
3796
  argTypes: ["primitive"]
3801
3797
  },
3802
3798
  handler: (context, args) => {
3803
- // \left case below triggers parsing of \right in
3804
- // `const right = parser.parseFunction();`
3805
- // uses this return value.
3806
- const color = context.parser.gullet.macros.get("\\current@color");
3807
- if (color && typeof color !== "string") {
3808
- throw new ParseError("\\current@color set to non-string in \\right");
3809
- }
3810
3799
  return {
3811
3800
  type: "leftright-right",
3812
3801
  mode: context.parser.mode,
3813
- delim: checkDelimiter(args[0], context).text,
3814
- color // undefined if not set via \color
3802
+ delim: checkDelimiter(args[0], context).text
3815
3803
  };
3816
3804
  }
3817
3805
  });
@@ -3840,8 +3828,7 @@ defineFunction({
3840
3828
  mode: parser.mode,
3841
3829
  body,
3842
3830
  left: delim.text,
3843
- right: right.delim,
3844
- rightColor: right.color
3831
+ right: right.delim
3845
3832
  };
3846
3833
  },
3847
3834
  mathmlBuilder: (group, style) => {
@@ -3864,7 +3851,6 @@ defineFunction({
3864
3851
  if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
3865
3852
  rightNode.setAttribute("stretchy", "true");
3866
3853
  }
3867
- if (group.rightColor) { rightNode.style.color = group.rightColor; }
3868
3854
  inner.push(rightNode);
3869
3855
 
3870
3856
  return makeRow(inner);
@@ -3938,20 +3924,12 @@ const mathmlBuilder$8 = (group, style) => {
3938
3924
  node.style.borderBottom = "0.065em solid";
3939
3925
  break
3940
3926
  case "\\cancel":
3941
- node.style.background = `linear-gradient(to top left,
3942
- rgba(0,0,0,0) 0%,
3943
- rgba(0,0,0,0) calc(50% - 0.06em),
3944
- rgba(0,0,0,1) 50%,
3945
- rgba(0,0,0,0) calc(50% + 0.06em),
3946
- rgba(0,0,0,0) 100%);`;
3927
+ // We can't use an inline background-gradient. It does not work client-side.
3928
+ // So set a class and put the rule in the external CSS file.
3929
+ node.classes.push("tml-cancel");
3947
3930
  break
3948
3931
  case "\\bcancel":
3949
- node.style.background = `linear-gradient(to top right,
3950
- rgba(0,0,0,0) 0%,
3951
- rgba(0,0,0,0) calc(50% - 0.06em),
3952
- rgba(0,0,0,1) 50%,
3953
- rgba(0,0,0,0) calc(50% + 0.06em),
3954
- rgba(0,0,0,0) 100%);`;
3932
+ node.classes.push("tml-bcancel");
3955
3933
  break
3956
3934
  /*
3957
3935
  case "\\longdiv":
@@ -3998,18 +3976,7 @@ rgba(0,0,0,0) 100%);`;
3998
3976
  break
3999
3977
  }
4000
3978
  case "\\xcancel":
4001
- node.style.background = `linear-gradient(to top left,
4002
- rgba(0,0,0,0) 0%,
4003
- rgba(0,0,0,0) calc(50% - 0.06em),
4004
- rgba(0,0,0,1) 50%,
4005
- rgba(0,0,0,0) calc(50% + 0.06em),
4006
- rgba(0,0,0,0) 100%),
4007
- linear-gradient(to top right,
4008
- rgba(0,0,0,0) 0%,
4009
- rgba(0,0,0,0) calc(50% - 0.06em),
4010
- rgba(0,0,0,1) 50%,
4011
- rgba(0,0,0,0) calc(50% + 0.06em),
4012
- rgba(0,0,0,0) 100%);`;
3979
+ node.classes.push("tml-xcancel");
4013
3980
  break
4014
3981
  }
4015
3982
  if (group.backgroundColor) {
@@ -4207,7 +4174,7 @@ const getTag = (group, style, rowNum) => {
4207
4174
  if (tagContents) {
4208
4175
  // The author has written a \tag or a \notag in this row.
4209
4176
  if (tagContents.body) {
4210
- tag = buildExpressionRow(tagContents.body, style);
4177
+ tag = buildExpressionRow(tagContents.body, style, true);
4211
4178
  tag.classes = ["tml-tag"];
4212
4179
  } else {
4213
4180
  // \notag. Return an empty span.
@@ -4254,7 +4221,9 @@ function parseArray(
4254
4221
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
4255
4222
  }
4256
4223
  if (addEqnNum) {
4257
- parser.gullet.macros.set("\\tag", "\\env@tag{\\text{#1}}");
4224
+ parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren");
4225
+ parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
4226
+ parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}");
4258
4227
  parser.gullet.macros.set("\\notag", "\\env@notag");
4259
4228
  parser.gullet.macros.set("\\nonumber", "\\env@notag");
4260
4229
  }
@@ -4295,7 +4264,8 @@ function parseArray(
4295
4264
  cell = {
4296
4265
  type: "ordgroup",
4297
4266
  mode: parser.mode,
4298
- body: cell
4267
+ body: cell,
4268
+ semisimple: true
4299
4269
  };
4300
4270
  row.push(cell);
4301
4271
  const next = parser.fetch().text;
@@ -5128,7 +5098,7 @@ const mathmlBuilder$6 = (group, style) => {
5128
5098
  const mathGroup = buildGroup$1(group.body, newStyle);
5129
5099
 
5130
5100
  if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
5131
- if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
5101
+ if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
5132
5102
  mathGroup.style.fontWeight = "bold";
5133
5103
  return mathGroup
5134
5104
  }
@@ -6485,10 +6455,10 @@ defineFunction({
6485
6455
  },
6486
6456
  mathmlBuilder(group, style) {
6487
6457
  if (group.isCharacterBox) {
6488
- const inner = buildExpression(group.body, style);
6458
+ const inner = buildExpression(group.body, style, true);
6489
6459
  return inner[0]
6490
6460
  } else {
6491
- return buildExpressionRow(group.body, style, true)
6461
+ return buildExpressionRow(group.body, style)
6492
6462
  }
6493
6463
  }
6494
6464
  });
@@ -6508,6 +6478,13 @@ const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6508
6478
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6509
6479
  // "supsub" since some of them (like \int) can affect super/subscripting.
6510
6480
 
6481
+ const setSpacing = node => {
6482
+ // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
6483
+ // The most likely spacing for an OP is a thin space per TeXbook p170.
6484
+ node.attributes.lspace = "0.1667em";
6485
+ node.attributes.rspace = "0.1667em";
6486
+ };
6487
+
6511
6488
  const mathmlBuilder$2 = (group, style) => {
6512
6489
  let node;
6513
6490
 
@@ -6519,9 +6496,11 @@ const mathmlBuilder$2 = (group, style) => {
6519
6496
  } else {
6520
6497
  node.setAttribute("movablelimits", "false");
6521
6498
  }
6499
+ if (group.fromMathOp) { setSpacing(node); }
6522
6500
  } else if (group.body) {
6523
6501
  // This is an operator with children. Add them.
6524
6502
  node = new MathNode("mo", buildExpression(group.body, style));
6503
+ if (group.fromMathOp) { setSpacing(node); }
6525
6504
  } else {
6526
6505
  // This is a text operator. Add all of the characters from the operator's name.
6527
6506
  node = new MathNode("mi", [new TextNode(group.name.slice(1))]);
@@ -6641,6 +6620,7 @@ defineFunction({
6641
6620
  limits: true,
6642
6621
  parentIsSupSub: false,
6643
6622
  symbol: isSymbol,
6623
+ fromMathOp: true,
6644
6624
  stack: false,
6645
6625
  name: isSymbol ? arr[0].text : null,
6646
6626
  body: isSymbol ? null : ordargument(body)
@@ -6974,7 +6954,7 @@ defineMacro("\\operatorname",
6974
6954
  defineFunctionBuilders({
6975
6955
  type: "ordgroup",
6976
6956
  mathmlBuilder(group, style) {
6977
- return buildExpressionRow(group.body, style, true);
6957
+ return buildExpressionRow(group.body, style, group.semisimple);
6978
6958
  }
6979
6959
  });
6980
6960
 
@@ -12003,6 +11983,8 @@ var unicodeSymbols = {
12003
11983
 
12004
11984
  /* eslint no-constant-condition:0 */
12005
11985
 
11986
+ const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
11987
+
12006
11988
  /**
12007
11989
  * This file contains the parser used to parse out a TeX expression from the
12008
11990
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -12772,8 +12754,7 @@ class Parser {
12772
12754
  body: expression,
12773
12755
  // A group formed by \begingroup...\endgroup is a semi-simple group
12774
12756
  // which doesn't affect spacing in math mode, i.e., is transparent.
12775
- // https://tex.stackexchange.com/questions/1930/when-should-one-
12776
- // use-begingroup-instead-of-bgroup
12757
+ // https://tex.stackexchange.com/questions/1930/
12777
12758
  semisimple: text === "\\begingroup" || undefined
12778
12759
  };
12779
12760
  } else {
@@ -12887,7 +12868,11 @@ class Parser {
12887
12868
  // Recognize base symbol
12888
12869
  let symbol;
12889
12870
  if (symbols[this.mode][text]) {
12890
- const group = symbols[this.mode][text].group;
12871
+ let group = symbols[this.mode][text].group;
12872
+ if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
12873
+ // Change from a binary operator to a unary (prefix) operator
12874
+ group = "open";
12875
+ }
12891
12876
  const loc = SourceLocation.range(nucleus);
12892
12877
  let s;
12893
12878
  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
@@ -13157,7 +13142,7 @@ class Style {
13157
13142
  * https://mit-license.org/
13158
13143
  */
13159
13144
 
13160
- const version = "0.10.19";
13145
+ const version = "0.10.21";
13161
13146
 
13162
13147
  function postProcess(block) {
13163
13148
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.19";
17
+ const version = "0.10.21";
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.19",
3
+ "version": "0.10.21",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "engines": {
package/src/Parser.js CHANGED
@@ -15,6 +15,8 @@ import { isDelimiter } from "./functions/delimsizing"
15
15
  import unicodeAccents from /*preval*/ "./unicodeAccents";
16
16
  import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
17
 
18
+ const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
19
+
18
20
  /**
19
21
  * This file contains the parser used to parse out a TeX expression from the
20
22
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -784,8 +786,7 @@ export default class Parser {
784
786
  body: expression,
785
787
  // A group formed by \begingroup...\endgroup is a semi-simple group
786
788
  // which doesn't affect spacing in math mode, i.e., is transparent.
787
- // https://tex.stackexchange.com/questions/1930/when-should-one-
788
- // use-begingroup-instead-of-bgroup
789
+ // https://tex.stackexchange.com/questions/1930/
789
790
  semisimple: text === "\\begingroup" || undefined
790
791
  };
791
792
  } else {
@@ -899,7 +900,11 @@ export default class Parser {
899
900
  // Recognize base symbol
900
901
  let symbol;
901
902
  if (symbols[this.mode][text]) {
902
- const group = symbols[this.mode][text].group;
903
+ let group = symbols[this.mode][text].group;
904
+ if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
905
+ // Change from a binary operator to a unary (prefix) operator
906
+ group = "open"
907
+ }
903
908
  const loc = SourceLocation.range(nucleus);
904
909
  let s;
905
910
  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
@@ -135,20 +135,22 @@ const consolidateNumbers = expression => {
135
135
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
136
136
  * unless the array has length 1. Always returns a single node.
137
137
  */
138
- export const makeRow = function(body) {
138
+ export const makeRow = function(body, semisimple = false) {
139
139
  if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
140
140
  return body[0];
141
- } else {
141
+ } else if (!semisimple) {
142
142
  // Suppress spacing on <mo> nodes at both ends of the row.
143
143
  if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
144
144
  body[0].attributes.lspace = "0em"
145
+ body[0].attributes.rspace = "0em"
145
146
  }
146
147
  const end = body.length - 1
147
148
  if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
149
+ body[end].attributes.lspace = "0em"
148
150
  body[end].attributes.rspace = "0em"
149
151
  }
150
- return new mathMLTree.MathNode("mrow", body);
151
152
  }
153
+ return new mathMLTree.MathNode("mrow", body);
152
154
  };
153
155
 
154
156
  const isRel = item => {
@@ -162,10 +164,10 @@ const isRel = item => {
162
164
  * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
163
165
  * (2) Suppress spacing between two adjacent relations.
164
166
  */
165
- export const buildExpression = function(expression, style, isOrdgroup) {
166
- if (expression.length === 1) {
167
+ export const buildExpression = function(expression, style, semisimple = false) {
168
+ if (!semisimple && expression.length === 1) {
167
169
  const group = buildGroup(expression[0], style);
168
- if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
170
+ if (group instanceof MathNode && group.type === "mo") {
169
171
  // When TeX writers want to suppress spacing on an operator,
170
172
  // they often put the operator by itself inside braces.
171
173
  group.setAttribute("lspace", "0em");
@@ -195,8 +197,8 @@ export const buildExpression = function(expression, style, isOrdgroup) {
195
197
  * Equivalent to buildExpression, but wraps the elements in an <mrow>
196
198
  * if there's more than one. Returns a single node instead of an array.
197
199
  */
198
- export const buildExpressionRow = function(expression, style, isOrdgroup) {
199
- return makeRow(buildExpression(expression, style, isOrdgroup));
200
+ export const buildExpressionRow = function(expression, style, semisimple = false) {
201
+ return makeRow(buildExpression(expression, style, semisimple), semisimple);
200
202
  };
201
203
 
202
204
  /**
@@ -44,7 +44,7 @@ const getTag = (group, style, rowNum) => {
44
44
  if (tagContents) {
45
45
  // The author has written a \tag or a \notag in this row.
46
46
  if (tagContents.body) {
47
- tag = mml.buildExpressionRow(tagContents.body, style)
47
+ tag = mml.buildExpressionRow(tagContents.body, style, true)
48
48
  tag.classes = ["tml-tag"]
49
49
  } else {
50
50
  // \notag. Return an empty span.
@@ -91,7 +91,9 @@ function parseArray(
91
91
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
92
92
  }
93
93
  if (addEqnNum) {
94
- parser.gullet.macros.set("\\tag", "\\env@tag{\\text{#1}}");
94
+ parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren")
95
+ parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
96
+ parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}")
95
97
  parser.gullet.macros.set("\\notag", "\\env@notag");
96
98
  parser.gullet.macros.set("\\nonumber", "\\env@notag")
97
99
  }
@@ -132,7 +134,8 @@ function parseArray(
132
134
  cell = {
133
135
  type: "ordgroup",
134
136
  mode: parser.mode,
135
- body: cell
137
+ body: cell,
138
+ semisimple: true
136
139
  };
137
140
  row.push(cell);
138
141
  const next = parser.fetch().text;
@@ -49,7 +49,8 @@ function cdArrow(arrowChar, labels, parser) {
49
49
  const arrowGroup = {
50
50
  type: "ordgroup",
51
51
  mode: "math",
52
- body: [leftLabel, sizedArrow, rightLabel]
52
+ body: [leftLabel, sizedArrow, rightLabel],
53
+ semisimple: true
53
54
  };
54
55
  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
55
56
  }
@@ -201,7 +201,7 @@ defineFunction({
201
201
  allowedInText: true,
202
202
  argTypes: ["raw", "raw"]
203
203
  },
204
- handler({ parser, token }, args, optArgs) {
204
+ handler({ parser, breakOnTokenText, token }, args, optArgs) {
205
205
  const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string
206
206
  let color = ""
207
207
  if (model) {
@@ -211,15 +211,8 @@ defineFunction({
211
211
  color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token)
212
212
  }
213
213
 
214
- // Set macro \current@color in current namespace to store the current
215
- // color, mimicking the behavior of color.sty.
216
- // This is currently used just to correctly color a \right
217
- // that follows a \color command.
218
- parser.gullet.macros.set("\\current@color", color)
219
-
220
214
  // Parse out the implicit body that should be colored.
221
- // Since \color nodes should not be nested, break on \color.
222
- const body = parser.parseExpression(true, "\\color")
215
+ const body = parser.parseExpression(true, breakOnTokenText)
223
216
 
224
217
  return {
225
218
  type: "color",
@@ -211,18 +211,10 @@ defineFunction({
211
211
  argTypes: ["primitive"]
212
212
  },
213
213
  handler: (context, args) => {
214
- // \left case below triggers parsing of \right in
215
- // `const right = parser.parseFunction();`
216
- // uses this return value.
217
- const color = context.parser.gullet.macros.get("\\current@color");
218
- if (color && typeof color !== "string") {
219
- throw new ParseError("\\current@color set to non-string in \\right");
220
- }
221
214
  return {
222
215
  type: "leftright-right",
223
216
  mode: context.parser.mode,
224
- delim: checkDelimiter(args[0], context).text,
225
- color // undefined if not set via \color
217
+ delim: checkDelimiter(args[0], context).text
226
218
  };
227
219
  }
228
220
  });
@@ -251,8 +243,7 @@ defineFunction({
251
243
  mode: parser.mode,
252
244
  body,
253
245
  left: delim.text,
254
- right: right.delim,
255
- rightColor: right.color
246
+ right: right.delim
256
247
  };
257
248
  },
258
249
  mathmlBuilder: (group, style) => {
@@ -275,7 +266,6 @@ defineFunction({
275
266
  if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
276
267
  rightNode.setAttribute("stretchy", "true")
277
268
  }
278
- if (group.rightColor) { rightNode.style.color = group.rightColor }
279
269
  inner.push(rightNode)
280
270
 
281
271
  return mml.makeRow(inner);
@@ -34,20 +34,12 @@ const mathmlBuilder = (group, style) => {
34
34
  node.style.borderBottom = "0.065em solid"
35
35
  break
36
36
  case "\\cancel":
37
- node.style.background = `linear-gradient(to top left,
38
- rgba(0,0,0,0) 0%,
39
- rgba(0,0,0,0) calc(50% - 0.06em),
40
- rgba(0,0,0,1) 50%,
41
- rgba(0,0,0,0) calc(50% + 0.06em),
42
- rgba(0,0,0,0) 100%);`
37
+ // We can't use an inline background-gradient. It does not work client-side.
38
+ // So set a class and put the rule in the external CSS file.
39
+ node.classes.push("tml-cancel")
43
40
  break
44
41
  case "\\bcancel":
45
- node.style.background = `linear-gradient(to top right,
46
- rgba(0,0,0,0) 0%,
47
- rgba(0,0,0,0) calc(50% - 0.06em),
48
- rgba(0,0,0,1) 50%,
49
- rgba(0,0,0,0) calc(50% + 0.06em),
50
- rgba(0,0,0,0) 100%);`
42
+ node.classes.push("tml-bcancel")
51
43
  break
52
44
  /*
53
45
  case "\\longdiv":
@@ -94,18 +86,7 @@ rgba(0,0,0,0) 100%);`
94
86
  break
95
87
  }
96
88
  case "\\xcancel":
97
- node.style.background = `linear-gradient(to top left,
98
- rgba(0,0,0,0) 0%,
99
- rgba(0,0,0,0) calc(50% - 0.06em),
100
- rgba(0,0,0,1) 50%,
101
- rgba(0,0,0,0) calc(50% + 0.06em),
102
- rgba(0,0,0,0) 100%),
103
- linear-gradient(to top right,
104
- rgba(0,0,0,0) 0%,
105
- rgba(0,0,0,0) calc(50% - 0.06em),
106
- rgba(0,0,0,1) 50%,
107
- rgba(0,0,0,0) calc(50% + 0.06em),
108
- rgba(0,0,0,0) 100%);`
89
+ node.classes.push("tml-xcancel")
109
90
  break
110
91
  }
111
92
  if (group.backgroundColor) {
@@ -8,7 +8,7 @@ const mathmlBuilder = (group, style) => {
8
8
  const mathGroup = mml.buildGroup(group.body, newStyle)
9
9
 
10
10
  if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
11
- if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
11
+ if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
12
12
  mathGroup.style.fontWeight = "bold"
13
13
  return mathGroup
14
14
  }
@@ -37,10 +37,10 @@ defineFunction({
37
37
  },
38
38
  mathmlBuilder(group, style) {
39
39
  if (group.isCharacterBox) {
40
- const inner = mml.buildExpression(group.body, style);
40
+ const inner = mml.buildExpression(group.body, style, true);
41
41
  return inner[0]
42
42
  } else {
43
- return mml.buildExpressionRow(group.body, style, true)
43
+ return mml.buildExpressionRow(group.body, style)
44
44
  }
45
45
  }
46
46
  });
@@ -17,6 +17,13 @@ export const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"]
17
17
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
18
18
  // "supsub" since some of them (like \int) can affect super/subscripting.
19
19
 
20
+ const setSpacing = node => {
21
+ // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
22
+ // The most likely spacing for an OP is a thin space per TeXbook p170.
23
+ node.attributes.lspace = "0.1667em"
24
+ node.attributes.rspace = "0.1667em"
25
+ }
26
+
20
27
  const mathmlBuilder = (group, style) => {
21
28
  let node;
22
29
 
@@ -28,9 +35,11 @@ const mathmlBuilder = (group, style) => {
28
35
  } else {
29
36
  node.setAttribute("movablelimits", "false")
30
37
  }
38
+ if (group.fromMathOp) { setSpacing(node) }
31
39
  } else if (group.body) {
32
40
  // This is an operator with children. Add them.
33
41
  node = new mathMLTree.MathNode("mo", mml.buildExpression(group.body, style));
42
+ if (group.fromMathOp) { setSpacing(node) }
34
43
  } else {
35
44
  // This is a text operator. Add all of the characters from the operator's name.
36
45
  node = new mathMLTree.MathNode("mi", [new mathMLTree.TextNode(group.name.slice(1))]);
@@ -150,6 +159,7 @@ defineFunction({
150
159
  limits: true,
151
160
  parentIsSupSub: false,
152
161
  symbol: isSymbol,
162
+ fromMathOp: true,
153
163
  stack: false,
154
164
  name: isSymbol ? arr[0].text : null,
155
165
  body: isSymbol ? null : ordargument(body)
@@ -4,6 +4,6 @@ import * as mml from "../buildMathML";
4
4
  defineFunctionBuilders({
5
5
  type: "ordgroup",
6
6
  mathmlBuilder(group, style) {
7
- return mml.buildExpressionRow(group.body, style, true);
7
+ return mml.buildExpressionRow(group.body, style, group.semisimple);
8
8
  }
9
9
  });
@@ -8,7 +8,7 @@
8
8
  * https://mit-license.org/
9
9
  */
10
10
 
11
- export const version = "0.10.19";
11
+ export const version = "0.10.21";
12
12
 
13
13
  export function postProcess(block) {
14
14
  const labelMap = {}