temml 0.9.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
package/dist/temml.mjs CHANGED
@@ -1232,6 +1232,8 @@ defineSymbol(math, textord, "\u2018", "`");
1232
1232
  defineSymbol(math, textord, "$", "\\$");
1233
1233
  defineSymbol(text, textord, "$", "\\$");
1234
1234
  defineSymbol(text, textord, "$", "\\textdollar");
1235
+ defineSymbol(math, textord, "¢", "\\cent");
1236
+ defineSymbol(text, textord, "¢", "\\cent");
1235
1237
  defineSymbol(math, textord, "%", "\\%");
1236
1238
  defineSymbol(text, textord, "%", "\\%");
1237
1239
  defineSymbol(math, textord, "_", "\\_");
@@ -2077,8 +2079,6 @@ function buildMathML(tree, texExpression, style, settings) {
2077
2079
  let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
2078
2080
  && !(n1.type === "mstyle" && n1.attributes.mathcolor)
2079
2081
  ? expression[0]
2080
- : expression.length > 1 && wrap === "none"
2081
- ? new mathMLTree.MathNode("mrow", expression)
2082
2082
  : setLineBreaks(expression, wrap, settings.displayMode);
2083
2083
 
2084
2084
  if (tag) {
@@ -2093,7 +2093,7 @@ function buildMathML(tree, texExpression, style, settings) {
2093
2093
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
2094
2094
  }
2095
2095
 
2096
- if (wrap !== "none") {
2096
+ if (wrap !== "none" && wrapper.children.length > 1) {
2097
2097
  const maths = [];
2098
2098
  for (let i = 0; i < wrapper.children.length; i++) {
2099
2099
  const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
@@ -3148,13 +3148,12 @@ defineFunction({
3148
3148
  names: ["\\\\"],
3149
3149
  props: {
3150
3150
  numArgs: 0,
3151
- numOptionalArgs: 1,
3152
- argTypes: ["size"],
3151
+ numOptionalArgs: 0,
3153
3152
  allowedInText: true
3154
3153
  },
3155
3154
 
3156
3155
  handler({ parser }, args, optArgs) {
3157
- const size = optArgs[0];
3156
+ const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null;
3158
3157
  const newLine = !parser.settings.displayMode;
3159
3158
  return {
3160
3159
  type: "cr",
@@ -3907,7 +3906,7 @@ defineFunction({
3907
3906
 
3908
3907
  defineFunction({
3909
3908
  type: "enclose",
3910
- names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", "\\underline"],
3909
+ names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline"],
3911
3910
  // , "\\phase", "\\longdiv"
3912
3911
  props: {
3913
3912
  numArgs: 1
@@ -3924,6 +3923,25 @@ defineFunction({
3924
3923
  mathmlBuilder: mathmlBuilder$8
3925
3924
  });
3926
3925
 
3926
+ defineFunction({
3927
+ type: "enclose",
3928
+ names: ["\\underline"],
3929
+ props: {
3930
+ numArgs: 1,
3931
+ allowedInText: true
3932
+ },
3933
+ handler({ parser, funcName }, args) {
3934
+ const body = args[0];
3935
+ return {
3936
+ type: "enclose",
3937
+ mode: parser.mode,
3938
+ label: funcName,
3939
+ body
3940
+ };
3941
+ },
3942
+ mathmlBuilder: mathmlBuilder$8
3943
+ });
3944
+
3927
3945
  /**
3928
3946
  * All registered environments.
3929
3947
  * `environments.js` exports this same dictionary again and makes it public.
@@ -4895,7 +4913,6 @@ defineFunction({
4895
4913
  "\\mathscr",
4896
4914
  "\\mathsf",
4897
4915
  "\\mathtt",
4898
- "\\oldstylenums",
4899
4916
 
4900
4917
  // aliases
4901
4918
  "\\Bbb",
@@ -6729,10 +6746,9 @@ defineFunction({
6729
6746
  }
6730
6747
  });
6731
6748
 
6732
- // \pmb is a simulation of bold font.
6749
+ // In LaTeX, \pmb is a simulation of bold font.
6733
6750
  // The version of \pmb in ambsy.sty works by typesetting three copies of the argument
6734
- // with small offsets. We use CSS text-shadow.
6735
- // It's a hack. Not as good as a real bold font. Better than nothing.
6751
+ // with small offsets. We use CSS font-weight:bold.
6736
6752
 
6737
6753
  defineFunction({
6738
6754
  type: "pmb",
@@ -6752,7 +6768,7 @@ defineFunction({
6752
6768
  const inner = buildExpression(group.body, style);
6753
6769
  // Wrap with an <mstyle> element.
6754
6770
  const node = wrapWithMstyle(inner);
6755
- node.setAttribute("style", "text-shadow: 0.02em 0.01em 0.04px");
6771
+ node.setAttribute("style", "font-weight:bold");
6756
6772
  return node
6757
6773
  }
6758
6774
  });
@@ -7310,8 +7326,7 @@ const fontMap = {
7310
7326
  mathfrak: "fraktur",
7311
7327
  mathscr: "script",
7312
7328
  mathsf: "sans-serif",
7313
- mathtt: "monospace",
7314
- oldstylenums: "oldstylenums"
7329
+ mathtt: "monospace"
7315
7330
  };
7316
7331
 
7317
7332
  /**
@@ -7381,8 +7396,6 @@ const getVariant = function(group, style) {
7381
7396
  return "sans-serif"
7382
7397
  case "mathtt":
7383
7398
  return "monospace"
7384
- case "oldstylenums":
7385
- return "oldstylenums"
7386
7399
  }
7387
7400
 
7388
7401
  let text = group.text;
@@ -7625,8 +7638,8 @@ const smallCaps = Object.freeze({
7625
7638
  const numberRegEx$1 = /^\d(?:[\d,.]*\d)?$/; // Keep in sync with numberRegEx in Parser.js
7626
7639
  const latinRegEx = /[A-Ba-z]/;
7627
7640
 
7628
- const italicNumber = (text, variant) => {
7629
- const mn = new mathMLTree.MathNode("mn", [text]);
7641
+ const italicNumber = (text, variant, tag) => {
7642
+ const mn = new mathMLTree.MathNode(tag, [text]);
7630
7643
  const wrapper = new mathMLTree.MathNode("mstyle", [mn]);
7631
7644
  wrapper.style["font-style"] = "italic";
7632
7645
  wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif";
@@ -7676,28 +7689,21 @@ defineFunctionBuilders({
7676
7689
  const variant = getVariant(group, style) || "normal";
7677
7690
 
7678
7691
  let node;
7679
- if (group.mode === "text") {
7692
+ if (numberRegEx$1.test(group.text)) {
7693
+ const tag = group.mode === "text" ? "mtext" : "mn";
7680
7694
  if (variant === "italic" || variant === "bold-italic") {
7681
- if (numberRegEx$1.test(group.text)) {
7682
- return italicNumber(text, variant)
7695
+ return italicNumber(text, variant, tag)
7696
+ } else {
7697
+ if (variant !== "normal") {
7698
+ text.text = text.text.split("").map(c => variantChar(c, variant)).join("");
7683
7699
  }
7700
+ node = new mathMLTree.MathNode(tag, [text]);
7684
7701
  }
7702
+ } else if (group.mode === "text") {
7685
7703
  if (variant !== "normal") {
7686
7704
  text.text = variantChar(text.text, variant);
7687
7705
  }
7688
7706
  node = new mathMLTree.MathNode("mtext", [text]);
7689
- } else if (numberRegEx$1.test(group.text)) {
7690
- if (variant === "oldstylenums") {
7691
- const ms = new mathMLTree.MathNode("mstyle", [text], ["oldstylenums"]);
7692
- node = new mathMLTree.MathNode("mn", [ms]);
7693
- } else if (variant === "italic" || variant === "bold-italic") {
7694
- return italicNumber(text, variant)
7695
- } else {
7696
- if (variant !== "normal") {
7697
- text.text = text.text.split("").map(c => variantChar(c, variant)).join("");
7698
- }
7699
- node = new mathMLTree.MathNode("mn", [text]);
7700
- }
7701
7707
  } else if (group.text === "\\prime") {
7702
7708
  node = new mathMLTree.MathNode("mo", [text]);
7703
7709
  // TODO: If/when Chromium uses ssty variant for prime, remove the next line.
@@ -8396,7 +8402,7 @@ defineMacro("\\underbar", "\\underline{\\text{#1}}");
8396
8402
  // \kern6\p@\hbox{.}\hbox{.}\hbox{.}}}
8397
8403
  // We'll call \varvdots, which gets a glyph from symbols.js.
8398
8404
  // The zero-width rule gets us an equivalent to the vertical 6pt kern.
8399
- defineMacro("\\vdots", "\\mathord{\\varvdots\\rule{0pt}{15pt}}");
8405
+ defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}");
8400
8406
  defineMacro("\u22ee", "\\vdots");
8401
8407
 
8402
8408
  //////////////////////////////////////////////////////////////////////
@@ -11416,6 +11422,36 @@ const uSubsAndSups = Object.freeze({
11416
11422
  '\u1DBF': 'θ'
11417
11423
  });
11418
11424
 
11425
+ // Used for Unicode input of calligraphic and script letters
11426
+ const asciiFromScript = Object.freeze({
11427
+ "\ud835\udc9c": "A",
11428
+ "\u212c": "B",
11429
+ "\ud835\udc9e": "C",
11430
+ "\ud835\udc9f": "D",
11431
+ "\u2130": "E",
11432
+ "\u2131": "F",
11433
+ "\ud835\udca2": "G",
11434
+ "\u210B": "H",
11435
+ "\u2110": "I",
11436
+ "\ud835\udca5": "J",
11437
+ "\ud835\udca6": "K",
11438
+ "\u2112": "L",
11439
+ "\u2113": "M",
11440
+ "\ud835\udca9": "N",
11441
+ "\ud835\udcaa": "O",
11442
+ "\ud835\udcab": "P",
11443
+ "\ud835\udcac": "Q",
11444
+ "\u211B": "R",
11445
+ "\ud835\udcae": "S",
11446
+ "\ud835\udcaf": "T",
11447
+ "\ud835\udcb0": "U",
11448
+ "\ud835\udcb1": "V",
11449
+ "\ud835\udcb2": "W",
11450
+ "\ud835\udcb3": "X",
11451
+ "\ud835\udcb4": "Y",
11452
+ "\ud835\udcb5": "Z"
11453
+ });
11454
+
11419
11455
  // Mapping of Unicode accent characters to their LaTeX equivalent in text and
11420
11456
  // math mode (when they exist).
11421
11457
  var unicodeAccents = {
@@ -12651,6 +12687,22 @@ class Parser {
12651
12687
  text
12652
12688
  };
12653
12689
  } else {
12690
+ if (asciiFromScript[text]) {
12691
+ // Unicode 14 disambiguates chancery from roundhand.
12692
+ // See https://www.unicode.org/charts/PDF/U1D400.pdf
12693
+ this.consume();
12694
+ const nextCode = this.fetch().text.charCodeAt(0);
12695
+ // mathcal is Temml default. Use mathscript if called for.
12696
+ const font = nextCode === 0xfe01 ? "mathscr" : "mathcal";
12697
+ if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume(); }
12698
+ return {
12699
+ type: "font",
12700
+ mode: "math",
12701
+ font,
12702
+ body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] }
12703
+ }
12704
+ }
12705
+ // Default ord character. No disambiguation necessary.
12654
12706
  s = {
12655
12707
  type: group,
12656
12708
  mode: this.mode,
@@ -12907,7 +12959,7 @@ class Style {
12907
12959
  * https://mit-license.org/
12908
12960
  */
12909
12961
 
12910
- const version = "0.9.1";
12962
+ const version = "0.10.0";
12911
12963
 
12912
12964
  function postProcess(block) {
12913
12965
  const labelMap = {};
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.9.1";
17
+ const version = "0.10.0";
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.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "homepage": "https://temml.org",
package/src/Parser.js CHANGED
@@ -7,6 +7,7 @@ import { supportedCodepoint } from "./unicodeScripts";
7
7
  import ParseError from "./ParseError";
8
8
  import { combiningDiacriticalMarksEndRegex } from "./Lexer";
9
9
  import { uSubsAndSups, unicodeSubRegEx } from "./unicodeSupOrSub"
10
+ import { asciiFromScript } from "./asciiFromScript"
10
11
  import SourceLocation from "./SourceLocation";
11
12
  import { Token } from "./Token";
12
13
 
@@ -909,6 +910,22 @@ export default class Parser {
909
910
  text
910
911
  };
911
912
  } else {
913
+ if (asciiFromScript[text]) {
914
+ // Unicode 14 disambiguates chancery from roundhand.
915
+ // See https://www.unicode.org/charts/PDF/U1D400.pdf
916
+ this.consume()
917
+ const nextCode = this.fetch().text.charCodeAt(0)
918
+ // mathcal is Temml default. Use mathscript if called for.
919
+ const font = nextCode === 0xfe01 ? "mathscr" : "mathcal";
920
+ if (nextCode === 0xfe00 || nextCode === 0xfe01) { this.consume() }
921
+ return {
922
+ type: "font",
923
+ mode: "math",
924
+ font,
925
+ body: { type: "mathord", mode: "math", loc, text: asciiFromScript[text] }
926
+ }
927
+ }
928
+ // Default ord character. No disambiguation necessary.
912
929
  s = {
913
930
  type: group,
914
931
  mode: this.mode,
@@ -0,0 +1,29 @@
1
+ // Used for Unicode input of calligraphic and script letters
2
+ export const asciiFromScript = Object.freeze({
3
+ "\ud835\udc9c": "A",
4
+ "\u212c": "B",
5
+ "\ud835\udc9e": "C",
6
+ "\ud835\udc9f": "D",
7
+ "\u2130": "E",
8
+ "\u2131": "F",
9
+ "\ud835\udca2": "G",
10
+ "\u210B": "H",
11
+ "\u2110": "I",
12
+ "\ud835\udca5": "J",
13
+ "\ud835\udca6": "K",
14
+ "\u2112": "L",
15
+ "\u2113": "M",
16
+ "\ud835\udca9": "N",
17
+ "\ud835\udcaa": "O",
18
+ "\ud835\udcab": "P",
19
+ "\ud835\udcac": "Q",
20
+ "\u211B": "R",
21
+ "\ud835\udcae": "S",
22
+ "\ud835\udcaf": "T",
23
+ "\ud835\udcb0": "U",
24
+ "\ud835\udcb1": "V",
25
+ "\ud835\udcb2": "W",
26
+ "\ud835\udcb3": "X",
27
+ "\ud835\udcb4": "Y",
28
+ "\ud835\udcb5": "Z"
29
+ })
@@ -195,8 +195,6 @@ export default function buildMathML(tree, texExpression, style, settings) {
195
195
  let wrapper = expression.length === 1 && tag === null && (n1 instanceof MathNode)
196
196
  && !(n1.type === "mstyle" && n1.attributes.mathcolor)
197
197
  ? expression[0]
198
- : expression.length > 1 && wrap === "none"
199
- ? new mathMLTree.MathNode("mrow", expression)
200
198
  : setLineBreaks(expression, wrap, settings.displayMode)
201
199
 
202
200
  if (tag) {
@@ -211,7 +209,7 @@ export default function buildMathML(tree, texExpression, style, settings) {
211
209
  wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
212
210
  }
213
211
 
214
- if (wrap !== "none") {
212
+ if (wrap !== "none" && wrapper.children.length > 1) {
215
213
  const maths = []
216
214
  for (let i = 0; i < wrapper.children.length; i++) {
217
215
  const math = new mathMLTree.MathNode("math", [wrapper.children[i]])
@@ -11,13 +11,12 @@ defineFunction({
11
11
  names: ["\\\\"],
12
12
  props: {
13
13
  numArgs: 0,
14
- numOptionalArgs: 1,
15
- argTypes: ["size"],
14
+ numOptionalArgs: 0,
16
15
  allowedInText: true
17
16
  },
18
17
 
19
18
  handler({ parser }, args, optArgs) {
20
- const size = optArgs[0];
19
+ const size = parser.gullet.future().text === "[" ? parser.parseSizeGroup(true) : null;
21
20
  const newLine = !parser.settings.displayMode;
22
21
  return {
23
22
  type: "cr",
@@ -175,7 +175,7 @@ defineFunction({
175
175
 
176
176
  defineFunction({
177
177
  type: "enclose",
178
- names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", "\\underline"],
178
+ names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline"],
179
179
  // , "\\phase", "\\longdiv"
180
180
  props: {
181
181
  numArgs: 1
@@ -191,3 +191,22 @@ defineFunction({
191
191
  },
192
192
  mathmlBuilder
193
193
  });
194
+
195
+ defineFunction({
196
+ type: "enclose",
197
+ names: ["\\underline"],
198
+ props: {
199
+ numArgs: 1,
200
+ allowedInText: true
201
+ },
202
+ handler({ parser, funcName }, args) {
203
+ const body = args[0];
204
+ return {
205
+ type: "enclose",
206
+ mode: parser.mode,
207
+ label: funcName,
208
+ body
209
+ };
210
+ },
211
+ mathmlBuilder
212
+ });
@@ -67,7 +67,6 @@ defineFunction({
67
67
  "\\mathscr",
68
68
  "\\mathsf",
69
69
  "\\mathtt",
70
- "\\oldstylenums",
71
70
 
72
71
  // aliases
73
72
  "\\Bbb",
@@ -2,10 +2,9 @@ import defineFunction, { ordargument } from "../defineFunction"
2
2
  import { wrapWithMstyle } from "../mathMLTree"
3
3
  import * as mml from "../buildMathML"
4
4
 
5
- // \pmb is a simulation of bold font.
5
+ // In LaTeX, \pmb is a simulation of bold font.
6
6
  // The version of \pmb in ambsy.sty works by typesetting three copies of the argument
7
- // with small offsets. We use CSS text-shadow.
8
- // It's a hack. Not as good as a real bold font. Better than nothing.
7
+ // with small offsets. We use CSS font-weight:bold.
9
8
 
10
9
  defineFunction({
11
10
  type: "pmb",
@@ -25,7 +24,7 @@ defineFunction({
25
24
  const inner = mml.buildExpression(group.body, style)
26
25
  // Wrap with an <mstyle> element.
27
26
  const node = wrapWithMstyle(inner)
28
- node.setAttribute("style", "text-shadow: 0.02em 0.01em 0.04px")
27
+ node.setAttribute("style", "font-weight:bold")
29
28
  return node
30
29
  }
31
30
  })
@@ -10,8 +10,8 @@ import * as mml from "../buildMathML"
10
10
  const numberRegEx = /^\d(?:[\d,.]*\d)?$/ // Keep in sync with numberRegEx in Parser.js
11
11
  const latinRegEx = /[A-Ba-z]/
12
12
 
13
- const italicNumber = (text, variant) => {
14
- const mn = new mathMLTree.MathNode("mn", [text])
13
+ const italicNumber = (text, variant, tag) => {
14
+ const mn = new mathMLTree.MathNode(tag, [text])
15
15
  const wrapper = new mathMLTree.MathNode("mstyle", [mn])
16
16
  wrapper.style["font-style"] = "italic"
17
17
  wrapper.style["font-family"] = "Cambria, 'Times New Roman', serif"
@@ -61,28 +61,21 @@ defineFunctionBuilders({
61
61
  const variant = getVariant(group, style) || "normal"
62
62
 
63
63
  let node
64
- if (group.mode === "text") {
64
+ if (numberRegEx.test(group.text)) {
65
+ const tag = group.mode === "text" ? "mtext" : "mn"
65
66
  if (variant === "italic" || variant === "bold-italic") {
66
- if (numberRegEx.test(group.text)) {
67
- return italicNumber(text, variant)
67
+ return italicNumber(text, variant, tag)
68
+ } else {
69
+ if (variant !== "normal") {
70
+ text.text = text.text.split("").map(c => variantChar(c, variant)).join("")
68
71
  }
72
+ node = new mathMLTree.MathNode(tag, [text])
69
73
  }
74
+ } else if (group.mode === "text") {
70
75
  if (variant !== "normal") {
71
76
  text.text = variantChar(text.text, variant)
72
77
  }
73
78
  node = new mathMLTree.MathNode("mtext", [text])
74
- } else if (numberRegEx.test(group.text)) {
75
- if (variant === "oldstylenums") {
76
- const ms = new mathMLTree.MathNode("mstyle", [text], ["oldstylenums"])
77
- node = new mathMLTree.MathNode("mn", [ms])
78
- } else if (variant === "italic" || variant === "bold-italic") {
79
- return italicNumber(text, variant)
80
- } else {
81
- if (variant !== "normal") {
82
- text.text = text.text.split("").map(c => variantChar(c, variant)).join("")
83
- }
84
- node = new mathMLTree.MathNode("mn", [text])
85
- }
86
79
  } else if (group.text === "\\prime") {
87
80
  node = new mathMLTree.MathNode("mo", [text])
88
81
  // TODO: If/when Chromium uses ssty variant for prime, remove the next line.
package/src/macros.js CHANGED
@@ -228,7 +228,7 @@ defineMacro("\\underbar", "\\underline{\\text{#1}}");
228
228
  // \kern6\p@\hbox{.}\hbox{.}\hbox{.}}}
229
229
  // We'll call \varvdots, which gets a glyph from symbols.js.
230
230
  // The zero-width rule gets us an equivalent to the vertical 6pt kern.
231
- defineMacro("\\vdots", "\\mathord{\\varvdots\\rule{0pt}{15pt}}");
231
+ defineMacro("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}");
232
232
  defineMacro("\u22ee", "\\vdots");
233
233
 
234
234
  //////////////////////////////////////////////////////////////////////
@@ -8,7 +8,7 @@
8
8
  * https://mit-license.org/
9
9
  */
10
10
 
11
- export const version = "0.9.1";
11
+ export const version = "0.10.0";
12
12
 
13
13
  export function postProcess(block) {
14
14
  const labelMap = {}
package/src/symbols.js CHANGED
@@ -474,6 +474,8 @@ defineSymbol(math, textord, "\u2018", "`");
474
474
  defineSymbol(math, textord, "$", "\\$");
475
475
  defineSymbol(text, textord, "$", "\\$");
476
476
  defineSymbol(text, textord, "$", "\\textdollar");
477
+ defineSymbol(math, textord, "¢", "\\cent");
478
+ defineSymbol(text, textord, "¢", "\\cent");
477
479
  defineSymbol(math, textord, "%", "\\%");
478
480
  defineSymbol(text, textord, "%", "\\%");
479
481
  defineSymbol(math, textord, "_", "\\_");
package/src/variant.js CHANGED
@@ -17,8 +17,7 @@ const fontMap = {
17
17
  mathfrak: "fraktur",
18
18
  mathscr: "script",
19
19
  mathsf: "sans-serif",
20
- mathtt: "monospace",
21
- oldstylenums: "oldstylenums"
20
+ mathtt: "monospace"
22
21
  }
23
22
 
24
23
  /**
@@ -88,8 +87,6 @@ export const getVariant = function(group, style) {
88
87
  return "sans-serif"
89
88
  case "mathtt":
90
89
  return "monospace"
91
- case "oldstylenums":
92
- return "oldstylenums"
93
90
  default:
94
91
  break
95
92
  }