temml 0.10.19 → 0.10.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,6 +47,39 @@ mo.tml-prime {
47
47
  font-feature-settings: 'salt';
48
48
  }
49
49
 
50
+ mrow.tml-cancel {
51
+ background: linear-gradient(to top left,
52
+ rgba(0,0,0,0) 0%,
53
+ rgba(0,0,0,0) calc(50% - 0.06em),
54
+ rgba(0,0,0,1) 50%,
55
+ rgba(0,0,0,0) calc(50% + 0.06em),
56
+ rgba(0,0,0,0) 100%);
57
+ }
58
+
59
+ mrow.tml-bcancel {
60
+ background: linear-gradient(to top right,
61
+ rgba(0,0,0,0) 0%,
62
+ rgba(0,0,0,0) calc(50% - 0.06em),
63
+ rgba(0,0,0,1) 50%,
64
+ rgba(0,0,0,0) calc(50% + 0.06em),
65
+ rgba(0,0,0,0) 100%);
66
+ }
67
+
68
+ mrow.tml-xcancel {
69
+ background: linear-gradient(to top left,
70
+ rgba(0,0,0,0) 0%,
71
+ rgba(0,0,0,0) calc(50% - 0.06em),
72
+ rgba(0,0,0,1) 50%,
73
+ rgba(0,0,0,0) calc(50% + 0.06em),
74
+ rgba(0,0,0,0) 100%),
75
+ linear-gradient(to top right,
76
+ rgba(0,0,0,0) 0%,
77
+ rgba(0,0,0,0) calc(50% - 0.06em),
78
+ rgba(0,0,0,1) 50%,
79
+ rgba(0,0,0,0) calc(50% + 0.06em),
80
+ rgba(0,0,0,0) 100%)
81
+ }
82
+
50
83
  /* Prevent f' from overlapping in Chromium */
51
84
  mo.prime-pad {
52
85
  padding-left: 0.08em;
@@ -57,6 +57,39 @@ mo.tml-prime {
57
57
  font-family: Temml;
58
58
  }
59
59
 
60
+ mrow.tml-cancel {
61
+ background: linear-gradient(to top left,
62
+ rgba(0,0,0,0) 0%,
63
+ rgba(0,0,0,0) calc(50% - 0.06em),
64
+ rgba(0,0,0,1) 50%,
65
+ rgba(0,0,0,0) calc(50% + 0.06em),
66
+ rgba(0,0,0,0) 100%);
67
+ }
68
+
69
+ mrow.tml-bcancel {
70
+ background: linear-gradient(to top right,
71
+ rgba(0,0,0,0) 0%,
72
+ rgba(0,0,0,0) calc(50% - 0.06em),
73
+ rgba(0,0,0,1) 50%,
74
+ rgba(0,0,0,0) calc(50% + 0.06em),
75
+ rgba(0,0,0,0) 100%);
76
+ }
77
+
78
+ mrow.tml-xcancel {
79
+ background: linear-gradient(to top left,
80
+ rgba(0,0,0,0) 0%,
81
+ rgba(0,0,0,0) calc(50% - 0.06em),
82
+ rgba(0,0,0,1) 50%,
83
+ rgba(0,0,0,0) calc(50% + 0.06em),
84
+ rgba(0,0,0,0) 100%),
85
+ linear-gradient(to top right,
86
+ rgba(0,0,0,0) 0%,
87
+ rgba(0,0,0,0) calc(50% - 0.06em),
88
+ rgba(0,0,0,1) 50%,
89
+ rgba(0,0,0,0) calc(50% + 0.06em),
90
+ rgba(0,0,0,0) 100%)
91
+ }
92
+
60
93
  /* Prevent f' from overlapping in Chromium */
61
94
  mo.prime-pad {
62
95
  padding-left: 0.08em;
@@ -54,6 +54,39 @@ mo.tml-prime {
54
54
  font-feature-settings: 'ssty';
55
55
  }
56
56
 
57
+ mrow.tml-cancel {
58
+ background: linear-gradient(to top left,
59
+ rgba(0,0,0,0) 0%,
60
+ rgba(0,0,0,0) calc(50% - 0.06em),
61
+ rgba(0,0,0,1) 50%,
62
+ rgba(0,0,0,0) calc(50% + 0.06em),
63
+ rgba(0,0,0,0) 100%);
64
+ }
65
+
66
+ mrow.tml-bcancel {
67
+ background: linear-gradient(to top right,
68
+ rgba(0,0,0,0) 0%,
69
+ rgba(0,0,0,0) calc(50% - 0.06em),
70
+ rgba(0,0,0,1) 50%,
71
+ rgba(0,0,0,0) calc(50% + 0.06em),
72
+ rgba(0,0,0,0) 100%);
73
+ }
74
+
75
+ mrow.tml-xcancel {
76
+ background: linear-gradient(to top left,
77
+ rgba(0,0,0,0) 0%,
78
+ rgba(0,0,0,0) calc(50% - 0.06em),
79
+ rgba(0,0,0,1) 50%,
80
+ rgba(0,0,0,0) calc(50% + 0.06em),
81
+ rgba(0,0,0,0) 100%),
82
+ linear-gradient(to top right,
83
+ rgba(0,0,0,0) 0%,
84
+ rgba(0,0,0,0) calc(50% - 0.06em),
85
+ rgba(0,0,0,1) 50%,
86
+ rgba(0,0,0,0) calc(50% + 0.06em),
87
+ rgba(0,0,0,0) 100%)
88
+ }
89
+
57
90
  /* Prevent f' from overlapping in Chromium */
58
91
  mo.prime-pad {
59
92
  padding-left: 0.08em;
@@ -40,6 +40,39 @@ mo.tml-prime {
40
40
  font-family: Temml;
41
41
  }
42
42
 
43
+ mrow.tml-cancel {
44
+ background: linear-gradient(to top left,
45
+ rgba(0,0,0,0) 0%,
46
+ rgba(0,0,0,0) calc(50% - 0.06em),
47
+ rgba(0,0,0,1) 50%,
48
+ rgba(0,0,0,0) calc(50% + 0.06em),
49
+ rgba(0,0,0,0) 100%);
50
+ }
51
+
52
+ mrow.tml-bcancel {
53
+ background: linear-gradient(to top right,
54
+ rgba(0,0,0,0) 0%,
55
+ rgba(0,0,0,0) calc(50% - 0.06em),
56
+ rgba(0,0,0,1) 50%,
57
+ rgba(0,0,0,0) calc(50% + 0.06em),
58
+ rgba(0,0,0,0) 100%);
59
+ }
60
+
61
+ mrow.tml-xcancel {
62
+ background: linear-gradient(to top left,
63
+ rgba(0,0,0,0) 0%,
64
+ rgba(0,0,0,0) calc(50% - 0.06em),
65
+ rgba(0,0,0,1) 50%,
66
+ rgba(0,0,0,0) calc(50% + 0.06em),
67
+ rgba(0,0,0,0) 100%),
68
+ linear-gradient(to top right,
69
+ rgba(0,0,0,0) 0%,
70
+ rgba(0,0,0,0) calc(50% - 0.06em),
71
+ rgba(0,0,0,1) 50%,
72
+ rgba(0,0,0,0) calc(50% + 0.06em),
73
+ rgba(0,0,0,0) 100%)
74
+ }
75
+
43
76
  /* Prevent f' from overlapping in Chromium */
44
77
  mo.prime-pad {
45
78
  padding-left: 0.08em;
@@ -48,6 +48,39 @@ mo.tml-prime {
48
48
  font-feature-settings: 'ss04';
49
49
  }
50
50
 
51
+ mrow.tml-cancel {
52
+ background: linear-gradient(to top left,
53
+ rgba(0,0,0,0) 0%,
54
+ rgba(0,0,0,0) calc(50% - 0.06em),
55
+ rgba(0,0,0,1) 50%,
56
+ rgba(0,0,0,0) calc(50% + 0.06em),
57
+ rgba(0,0,0,0) 100%);
58
+ }
59
+
60
+ mrow.tml-bcancel {
61
+ background: linear-gradient(to top right,
62
+ rgba(0,0,0,0) 0%,
63
+ rgba(0,0,0,0) calc(50% - 0.06em),
64
+ rgba(0,0,0,1) 50%,
65
+ rgba(0,0,0,0) calc(50% + 0.06em),
66
+ rgba(0,0,0,0) 100%);
67
+ }
68
+
69
+ mrow.tml-xcancel {
70
+ background: linear-gradient(to top left,
71
+ rgba(0,0,0,0) 0%,
72
+ rgba(0,0,0,0) calc(50% - 0.06em),
73
+ rgba(0,0,0,1) 50%,
74
+ rgba(0,0,0,0) calc(50% + 0.06em),
75
+ rgba(0,0,0,0) 100%),
76
+ linear-gradient(to top right,
77
+ rgba(0,0,0,0) 0%,
78
+ rgba(0,0,0,0) calc(50% - 0.06em),
79
+ rgba(0,0,0,1) 50%,
80
+ rgba(0,0,0,0) calc(50% + 0.06em),
81
+ rgba(0,0,0,0) 100%)
82
+ }
83
+
51
84
  /* Prevent f' from overlapping in Chromium */
52
85
  mo.prime-pad {
53
86
  padding-left: 0.08em;
package/dist/temml.cjs CHANGED
@@ -2072,20 +2072,22 @@ const consolidateNumbers = expression => {
2072
2072
  * Wrap the given array of nodes in an <mrow> node if needed, i.e.,
2073
2073
  * unless the array has length 1. Always returns a single node.
2074
2074
  */
2075
- const makeRow = function(body) {
2075
+ const makeRow = function(body, semisimple = false) {
2076
2076
  if (body.length === 1 && !(body[0] instanceof DocumentFragment)) {
2077
2077
  return body[0];
2078
- } else {
2078
+ } else if (!semisimple) {
2079
2079
  // Suppress spacing on <mo> nodes at both ends of the row.
2080
2080
  if (body[0] instanceof MathNode && body[0].type === "mo" && !body[0].attributes.fence) {
2081
2081
  body[0].attributes.lspace = "0em";
2082
+ body[0].attributes.rspace = "0em";
2082
2083
  }
2083
2084
  const end = body.length - 1;
2084
2085
  if (body[end] instanceof MathNode && body[end].type === "mo" && !body[end].attributes.fence) {
2086
+ body[end].attributes.lspace = "0em";
2085
2087
  body[end].attributes.rspace = "0em";
2086
2088
  }
2087
- return new mathMLTree.MathNode("mrow", body);
2088
2089
  }
2090
+ return new mathMLTree.MathNode("mrow", body);
2089
2091
  };
2090
2092
 
2091
2093
  const isRel = item => {
@@ -2099,10 +2101,10 @@ const isRel = item => {
2099
2101
  * (1) Suppress spacing when an author wraps an operator w/braces, as in {=}.
2100
2102
  * (2) Suppress spacing between two adjacent relations.
2101
2103
  */
2102
- const buildExpression = function(expression, style, isOrdgroup) {
2103
- if (expression.length === 1) {
2104
+ const buildExpression = function(expression, style, semisimple = false) {
2105
+ if (!semisimple && expression.length === 1) {
2104
2106
  const group = buildGroup$1(expression[0], style);
2105
- if (isOrdgroup && group instanceof MathNode && group.type === "mo") {
2107
+ if (group instanceof MathNode && group.type === "mo") {
2106
2108
  // When TeX writers want to suppress spacing on an operator,
2107
2109
  // they often put the operator by itself inside braces.
2108
2110
  group.setAttribute("lspace", "0em");
@@ -2132,8 +2134,8 @@ const buildExpression = function(expression, style, isOrdgroup) {
2132
2134
  * Equivalent to buildExpression, but wraps the elements in an <mrow>
2133
2135
  * if there's more than one. Returns a single node instead of an array.
2134
2136
  */
2135
- const buildExpressionRow = function(expression, style, isOrdgroup) {
2136
- return makeRow(buildExpression(expression, style, isOrdgroup));
2137
+ const buildExpressionRow = function(expression, style, semisimple = false) {
2138
+ return makeRow(buildExpression(expression, style, semisimple), semisimple);
2137
2139
  };
2138
2140
 
2139
2141
  /**
@@ -2818,7 +2820,8 @@ function cdArrow(arrowChar, labels, parser) {
2818
2820
  const arrowGroup = {
2819
2821
  type: "ordgroup",
2820
2822
  mode: "math",
2821
- body: [leftLabel, sizedArrow, rightLabel]
2823
+ body: [leftLabel, sizedArrow, rightLabel],
2824
+ semisimple: true
2822
2825
  };
2823
2826
  return parser.callFunction("\\\\cdparent", [arrowGroup], []);
2824
2827
  }
@@ -3244,7 +3247,7 @@ defineFunction({
3244
3247
  allowedInText: true,
3245
3248
  argTypes: ["raw", "raw"]
3246
3249
  },
3247
- handler({ parser, token }, args, optArgs) {
3250
+ handler({ parser, breakOnTokenText, token }, args, optArgs) {
3248
3251
  const model = optArgs[0] && assertNodeType(optArgs[0], "raw").string;
3249
3252
  let color = "";
3250
3253
  if (model) {
@@ -3254,15 +3257,8 @@ defineFunction({
3254
3257
  color = validateColor(assertNodeType(args[0], "raw").string, parser.gullet.macros, token);
3255
3258
  }
3256
3259
 
3257
- // Set macro \current@color in current namespace to store the current
3258
- // color, mimicking the behavior of color.sty.
3259
- // This is currently used just to correctly color a \right
3260
- // that follows a \color command.
3261
- parser.gullet.macros.set("\\current@color", color);
3262
-
3263
3260
  // Parse out the implicit body that should be colored.
3264
- // Since \color nodes should not be nested, break on \color.
3265
- const body = parser.parseExpression(true, "\\color");
3261
+ const body = parser.parseExpression(true, breakOnTokenText);
3266
3262
 
3267
3263
  return {
3268
3264
  type: "color",
@@ -3802,18 +3798,10 @@ defineFunction({
3802
3798
  argTypes: ["primitive"]
3803
3799
  },
3804
3800
  handler: (context, args) => {
3805
- // \left case below triggers parsing of \right in
3806
- // `const right = parser.parseFunction();`
3807
- // uses this return value.
3808
- const color = context.parser.gullet.macros.get("\\current@color");
3809
- if (color && typeof color !== "string") {
3810
- throw new ParseError("\\current@color set to non-string in \\right");
3811
- }
3812
3801
  return {
3813
3802
  type: "leftright-right",
3814
3803
  mode: context.parser.mode,
3815
- delim: checkDelimiter(args[0], context).text,
3816
- color // undefined if not set via \color
3804
+ delim: checkDelimiter(args[0], context).text
3817
3805
  };
3818
3806
  }
3819
3807
  });
@@ -3842,8 +3830,7 @@ defineFunction({
3842
3830
  mode: parser.mode,
3843
3831
  body,
3844
3832
  left: delim.text,
3845
- right: right.delim,
3846
- rightColor: right.color
3833
+ right: right.delim
3847
3834
  };
3848
3835
  },
3849
3836
  mathmlBuilder: (group, style) => {
@@ -3866,7 +3853,6 @@ defineFunction({
3866
3853
  if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
3867
3854
  rightNode.setAttribute("stretchy", "true");
3868
3855
  }
3869
- if (group.rightColor) { rightNode.style.color = group.rightColor; }
3870
3856
  inner.push(rightNode);
3871
3857
 
3872
3858
  return makeRow(inner);
@@ -3940,20 +3926,12 @@ const mathmlBuilder$8 = (group, style) => {
3940
3926
  node.style.borderBottom = "0.065em solid";
3941
3927
  break
3942
3928
  case "\\cancel":
3943
- node.style.background = `linear-gradient(to top left,
3944
- rgba(0,0,0,0) 0%,
3945
- rgba(0,0,0,0) calc(50% - 0.06em),
3946
- rgba(0,0,0,1) 50%,
3947
- rgba(0,0,0,0) calc(50% + 0.06em),
3948
- rgba(0,0,0,0) 100%);`;
3929
+ // We can't use an inline background-gradient. It does not work client-side.
3930
+ // So set a class and put the rule in the external CSS file.
3931
+ node.classes.push("tml-cancel");
3949
3932
  break
3950
3933
  case "\\bcancel":
3951
- node.style.background = `linear-gradient(to top right,
3952
- rgba(0,0,0,0) 0%,
3953
- rgba(0,0,0,0) calc(50% - 0.06em),
3954
- rgba(0,0,0,1) 50%,
3955
- rgba(0,0,0,0) calc(50% + 0.06em),
3956
- rgba(0,0,0,0) 100%);`;
3934
+ node.classes.push("tml-bcancel");
3957
3935
  break
3958
3936
  /*
3959
3937
  case "\\longdiv":
@@ -4000,18 +3978,7 @@ rgba(0,0,0,0) 100%);`;
4000
3978
  break
4001
3979
  }
4002
3980
  case "\\xcancel":
4003
- node.style.background = `linear-gradient(to top left,
4004
- rgba(0,0,0,0) 0%,
4005
- rgba(0,0,0,0) calc(50% - 0.06em),
4006
- rgba(0,0,0,1) 50%,
4007
- rgba(0,0,0,0) calc(50% + 0.06em),
4008
- rgba(0,0,0,0) 100%),
4009
- linear-gradient(to top right,
4010
- rgba(0,0,0,0) 0%,
4011
- rgba(0,0,0,0) calc(50% - 0.06em),
4012
- rgba(0,0,0,1) 50%,
4013
- rgba(0,0,0,0) calc(50% + 0.06em),
4014
- rgba(0,0,0,0) 100%);`;
3981
+ node.classes.push("tml-xcancel");
4015
3982
  break
4016
3983
  }
4017
3984
  if (group.backgroundColor) {
@@ -4209,7 +4176,7 @@ const getTag = (group, style, rowNum) => {
4209
4176
  if (tagContents) {
4210
4177
  // The author has written a \tag or a \notag in this row.
4211
4178
  if (tagContents.body) {
4212
- tag = buildExpressionRow(tagContents.body, style);
4179
+ tag = buildExpressionRow(tagContents.body, style, true);
4213
4180
  tag.classes = ["tml-tag"];
4214
4181
  } else {
4215
4182
  // \notag. Return an empty span.
@@ -4256,7 +4223,9 @@ function parseArray(
4256
4223
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
4257
4224
  }
4258
4225
  if (addEqnNum) {
4259
- parser.gullet.macros.set("\\tag", "\\env@tag{\\text{#1}}");
4226
+ parser.gullet.macros.set("\\tag", "\\@ifstar\\envtag@literal\\envtag@paren");
4227
+ parser.gullet.macros.set("\\envtag@paren", "\\env@tag{{(\\text{#1})}}");
4228
+ parser.gullet.macros.set("\\envtag@literal", "\\env@tag{\\text{#1}}");
4260
4229
  parser.gullet.macros.set("\\notag", "\\env@notag");
4261
4230
  parser.gullet.macros.set("\\nonumber", "\\env@notag");
4262
4231
  }
@@ -4297,7 +4266,8 @@ function parseArray(
4297
4266
  cell = {
4298
4267
  type: "ordgroup",
4299
4268
  mode: parser.mode,
4300
- body: cell
4269
+ body: cell,
4270
+ semisimple: true
4301
4271
  };
4302
4272
  row.push(cell);
4303
4273
  const next = parser.fetch().text;
@@ -5130,7 +5100,7 @@ const mathmlBuilder$6 = (group, style) => {
5130
5100
  const mathGroup = buildGroup$1(group.body, newStyle);
5131
5101
 
5132
5102
  if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
5133
- if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
5103
+ if (font === "boldsymbol" && ["mo", "mpadded", "mrow"].includes(mathGroup.type)) {
5134
5104
  mathGroup.style.fontWeight = "bold";
5135
5105
  return mathGroup
5136
5106
  }
@@ -6487,10 +6457,10 @@ defineFunction({
6487
6457
  },
6488
6458
  mathmlBuilder(group, style) {
6489
6459
  if (group.isCharacterBox) {
6490
- const inner = buildExpression(group.body, style);
6460
+ const inner = buildExpression(group.body, style, true);
6491
6461
  return inner[0]
6492
6462
  } else {
6493
- return buildExpressionRow(group.body, style, true)
6463
+ return buildExpressionRow(group.body, style)
6494
6464
  }
6495
6465
  }
6496
6466
  });
@@ -6510,6 +6480,13 @@ const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
6510
6480
  // NOTE: Unlike most `builders`s, this one handles not only "op", but also
6511
6481
  // "supsub" since some of them (like \int) can affect super/subscripting.
6512
6482
 
6483
+ const setSpacing = node => {
6484
+ // The user wrote a \mathop{…} function. Change spacing from default to OP spacing.
6485
+ // The most likely spacing for an OP is a thin space per TeXbook p170.
6486
+ node.attributes.lspace = "0.1667em";
6487
+ node.attributes.rspace = "0.1667em";
6488
+ };
6489
+
6513
6490
  const mathmlBuilder$2 = (group, style) => {
6514
6491
  let node;
6515
6492
 
@@ -6521,9 +6498,11 @@ const mathmlBuilder$2 = (group, style) => {
6521
6498
  } else {
6522
6499
  node.setAttribute("movablelimits", "false");
6523
6500
  }
6501
+ if (group.fromMathOp) { setSpacing(node); }
6524
6502
  } else if (group.body) {
6525
6503
  // This is an operator with children. Add them.
6526
6504
  node = new MathNode("mo", buildExpression(group.body, style));
6505
+ if (group.fromMathOp) { setSpacing(node); }
6527
6506
  } else {
6528
6507
  // This is a text operator. Add all of the characters from the operator's name.
6529
6508
  node = new MathNode("mi", [new TextNode(group.name.slice(1))]);
@@ -6643,6 +6622,7 @@ defineFunction({
6643
6622
  limits: true,
6644
6623
  parentIsSupSub: false,
6645
6624
  symbol: isSymbol,
6625
+ fromMathOp: true,
6646
6626
  stack: false,
6647
6627
  name: isSymbol ? arr[0].text : null,
6648
6628
  body: isSymbol ? null : ordargument(body)
@@ -6976,7 +6956,7 @@ defineMacro("\\operatorname",
6976
6956
  defineFunctionBuilders({
6977
6957
  type: "ordgroup",
6978
6958
  mathmlBuilder(group, style) {
6979
- return buildExpressionRow(group.body, style, true);
6959
+ return buildExpressionRow(group.body, style, group.semisimple);
6980
6960
  }
6981
6961
  });
6982
6962
 
@@ -12005,6 +11985,8 @@ var unicodeSymbols = {
12005
11985
 
12006
11986
  /* eslint no-constant-condition:0 */
12007
11987
 
11988
+ const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
11989
+
12008
11990
  /**
12009
11991
  * This file contains the parser used to parse out a TeX expression from the
12010
11992
  * input. Since TeX isn't context-free, standard parsers don't work particularly
@@ -12774,8 +12756,7 @@ class Parser {
12774
12756
  body: expression,
12775
12757
  // A group formed by \begingroup...\endgroup is a semi-simple group
12776
12758
  // which doesn't affect spacing in math mode, i.e., is transparent.
12777
- // https://tex.stackexchange.com/questions/1930/when-should-one-
12778
- // use-begingroup-instead-of-bgroup
12759
+ // https://tex.stackexchange.com/questions/1930/
12779
12760
  semisimple: text === "\\begingroup" || undefined
12780
12761
  };
12781
12762
  } else {
@@ -12889,7 +12870,11 @@ class Parser {
12889
12870
  // Recognize base symbol
12890
12871
  let symbol;
12891
12872
  if (symbols[this.mode][text]) {
12892
- const group = symbols[this.mode][text].group;
12873
+ let group = symbols[this.mode][text].group;
12874
+ if (group === "bin" && binLeftCancellers.includes(this.prevAtomType)) {
12875
+ // Change from a binary operator to a unary (prefix) operator
12876
+ group = "open";
12877
+ }
12893
12878
  const loc = SourceLocation.range(nucleus);
12894
12879
  let s;
12895
12880
  if (Object.prototype.hasOwnProperty.call(ATOMS, group )) {
@@ -13159,7 +13144,7 @@ class Style {
13159
13144
  * https://mit-license.org/
13160
13145
  */
13161
13146
 
13162
- const version = "0.10.19";
13147
+ const version = "0.10.21";
13163
13148
 
13164
13149
  function postProcess(block) {
13165
13150
  const labelMap = {};