temml 0.10.24 → 0.10.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,7 +14,7 @@
14
14
  * https://mit-license.org/
15
15
  */
16
16
 
17
- const version = "0.10.24";
17
+ const version = "0.10.30";
18
18
 
19
19
  function postProcess(block) {
20
20
  const labelMap = {};
@@ -65,6 +65,4 @@
65
65
  exports.postProcess = postProcess;
66
66
  exports.version = version;
67
67
 
68
- Object.defineProperty(exports, '__esModule', { value: true });
69
-
70
68
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "temml",
3
- "version": "0.10.24",
3
+ "version": "0.10.30",
4
4
  "description": "TeX to MathML conversion in JavaScript.",
5
5
  "main": "dist/temml.js",
6
6
  "engines": {
@@ -28,10 +28,13 @@
28
28
  ],
29
29
  "license": "MIT",
30
30
  "devDependencies": {
31
- "eslint": "^8.39.0",
31
+ "@eslint/eslintrc": "^3.1.0",
32
+ "@eslint/js": "^9.11.1",
33
+ "eslint": "^9.11.1",
32
34
  "esm": "^3.2.25",
33
- "rollup": "^2.66.1",
34
- "terser": "^5.14.2"
35
+ "globals": "^15.9.0",
36
+ "rollup": "^4.22.4",
37
+ "terser": "^5.34.0"
35
38
  },
36
39
  "scripts": {
37
40
  "lint": "eslint temml.js src",
@@ -39,7 +42,7 @@
39
42
  "visual-test": "node utils/buildTests.js",
40
43
  "test": "yarn lint && node utils/buildTests.js && yarn unit-test",
41
44
  "minify": "terser test/temml.js -o site/assets/temml.min.js -c -m && terser contrib/mhchem/mhchem.js -o site/assets/mhchem.min.js -c -m",
42
- "build": "rollup --config ./utils/rollupConfig.js && yarn minify && node utils/insertPlugins.js",
45
+ "build": "rollup --config ./utils/rollupConfig.mjs && yarn minify && node utils/insertPlugins.js",
43
46
  "docs": "node utils/buildDocs.js",
44
47
  "dist": "yarn build && node ./utils/copyfiles.js && terser contrib/auto-render/test/auto-render.js -o contrib/auto-render/dist/auto-render.min.js -c -m"
45
48
  }
package/src/Parser.js CHANGED
@@ -16,6 +16,7 @@ import unicodeAccents from /*preval*/ "./unicodeAccents";
16
16
  import unicodeSymbols from /*preval*/ "./unicodeSymbols";
17
17
 
18
18
  const binLeftCancellers = ["bin", "op", "open", "punct", "rel"];
19
+ const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/
19
20
 
20
21
  /**
21
22
  * This file contains the parser used to parse out a TeX expression from the
@@ -679,7 +680,7 @@ export default class Parser {
679
680
  res.text = "0pt"; // Enable \above{}
680
681
  isBlank = true; // This is here specifically for \genfrac
681
682
  }
682
- const match = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(res.text);
683
+ const match = sizeRegEx.exec(res.text);
683
684
  if (!match) {
684
685
  throw new ParseError("Invalid size: '" + res.text + "'", res);
685
686
  }
@@ -885,7 +886,7 @@ export default class Parser {
885
886
  // At this point, we should have a symbol, possibly with accents.
886
887
  // First expand any accented base symbol according to unicodeSymbols.
887
888
  if (Object.prototype.hasOwnProperty.call(unicodeSymbols, text[0]) &&
888
- !symbols[this.mode][text[0]]) {
889
+ this.mode === "math" && !symbols[this.mode][text[0]]) {
889
890
  // This behavior is not strict (XeTeX-compatible) in math mode.
890
891
  if (this.settings.strict && this.mode === "math") {
891
892
  throw new ParseError(`Accented Unicode text character "${text[0]}" used in ` + `math mode`,
@@ -895,7 +896,9 @@ export default class Parser {
895
896
  text = unicodeSymbols[text[0]] + text.slice(1);
896
897
  }
897
898
  // Strip off any combining characters
898
- const match = combiningDiacriticalMarksEndRegex.exec(text);
899
+ const match = this.mode === "math"
900
+ ? combiningDiacriticalMarksEndRegex.exec(text)
901
+ : null
899
902
  if (match) {
900
903
  text = text.substring(0, match.index);
901
904
  if (text === "i") {
@@ -948,7 +951,7 @@ export default class Parser {
948
951
  };
949
952
  }
950
953
  symbol = s;
951
- } else if (text.charCodeAt(0) >= 0x80) {
954
+ } else if (text.charCodeAt(0) >= 0x80 || combiningDiacriticalMarksEndRegex.exec(text)) {
952
955
  // no symbol for e.g. ^
953
956
  if (this.settings.strict && this.mode === "math") {
954
957
  throw new ParseError(`Unicode text character "${text[0]}" used in math mode`, nucleus)
@@ -34,49 +34,72 @@ export const makeText = function(text, mode, style) {
34
34
  return new mathMLTree.TextNode(text);
35
35
  };
36
36
 
37
+ const copyChar = (newRow, child) => {
38
+ if (newRow.children.length === 0 ||
39
+ newRow.children[newRow.children.length - 1].type !== "mtext") {
40
+ const mtext = new mathMLTree.MathNode(
41
+ "mtext",
42
+ [new mathMLTree.TextNode(child.children[0].text)]
43
+ )
44
+ newRow.children.push(mtext)
45
+ } else {
46
+ newRow.children[newRow.children.length - 1].children[0].text += child.children[0].text
47
+ }
48
+ }
49
+
37
50
  export const consolidateText = mrow => {
38
51
  // If possible, consolidate adjacent <mtext> elements into a single element.
39
52
  if (mrow.type !== "mrow" && mrow.type !== "mstyle") { return mrow }
40
53
  if (mrow.children.length === 0) { return mrow } // empty group, e.g., \text{}
41
- if (!mrow.children[0].attributes || mrow.children[0].type !== "mtext") { return mrow }
42
- const variant = mrow.children[0].attributes.mathvariant || ""
43
- const mtext = new mathMLTree.MathNode(
44
- "mtext",
45
- [new mathMLTree.TextNode(mrow.children[0].children[0].text)]
46
- )
47
- for (let i = 1; i < mrow.children.length; i++) {
48
- // Check each child and, if possible, copy the character into child[0].
49
- const localVariant = mrow.children[i].attributes.mathvariant || ""
50
- if (mrow.children[i].type === "mrow") {
51
- const childRow = mrow.children[i]
52
- for (let j = 0; j < childRow.children.length; j++) {
53
- // We'll also check the children of a mrow. One level only. No recursion.
54
- const childVariant = childRow.children[j].attributes.mathvariant || ""
55
- if (childVariant !== variant || childRow.children[j].type !== "mtext") {
56
- return mrow // At least one element cannot be consolidated. Get out.
57
- } else {
58
- mtext.children[0].text += childRow.children[j].children[0].text
54
+ const newRow = new mathMLTree.MathNode("mrow")
55
+ for (let i = 0; i < mrow.children.length; i++) {
56
+ const child = mrow.children[i];
57
+ if (child.type === "mtext" && Object.keys(child.attributes).length === 0) {
58
+ copyChar(newRow, child)
59
+ } else if (child.type === "mrow") {
60
+ // We'll also check the children of an mrow. One level only. No recursion.
61
+ let canConsolidate = true
62
+ for (let j = 0; j < child.children.length; j++) {
63
+ const grandChild = child.children[j];
64
+ if (grandChild.type !== "mtext" || Object.keys(child.attributes).length !== 0) {
65
+ canConsolidate = false
66
+ break
67
+ }
68
+ }
69
+ if (canConsolidate) {
70
+ for (let j = 0; j < child.children.length; j++) {
71
+ const grandChild = child.children[j];
72
+ copyChar(newRow, grandChild)
59
73
  }
74
+ } else {
75
+ newRow.children.push(child)
60
76
  }
61
- } else if (localVariant !== variant || mrow.children[i].type !== "mtext") {
62
- return mrow
63
77
  } else {
64
- mtext.children[0].text += mrow.children[i].children[0].text
78
+ newRow.children.push(child)
65
79
  }
66
80
  }
67
- // Firefox does not render a space at either end of an <mtext> string.
68
- // To get proper rendering, we replace leading or trailing spaces with no-break spaces.
69
- if (mtext.children[0].text.charAt(0) === " ") {
70
- mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1)
71
- }
72
- const L = mtext.children[0].text.length
73
- if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") {
74
- mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"
81
+ for (let i = 0; i < newRow.children.length; i++) {
82
+ if (newRow.children[i].type === "mtext") {
83
+ const mtext = newRow.children[i];
84
+ // Firefox does not render a space at either end of an <mtext> string.
85
+ // To get proper rendering, we replace leading or trailing spaces with no-break spaces.
86
+ if (mtext.children[0].text.charAt(0) === " ") {
87
+ mtext.children[0].text = "\u00a0" + mtext.children[0].text.slice(1)
88
+ }
89
+ const L = mtext.children[0].text.length
90
+ if (L > 0 && mtext.children[0].text.charAt(L - 1) === " ") {
91
+ mtext.children[0].text = mtext.children[0].text.slice(0, -1) + "\u00a0"
92
+ }
93
+ for (const [key, value] of Object.entries(mrow.attributes)) {
94
+ mtext.attributes[key] = value
95
+ }
96
+ }
75
97
  }
76
- for (const [key, value] of Object.entries(mrow.attributes)) {
77
- mtext.attributes[key] = value
98
+ if (newRow.children.length === 1 && newRow.children[0].type === "mtext") {
99
+ return newRow.children[0]; // A consolidated <mtext>
100
+ } else {
101
+ return newRow
78
102
  }
79
- return mtext
80
103
  }
81
104
 
82
105
  const numberRegEx = /^[0-9]$/
@@ -7,7 +7,8 @@ import { StyleLevel } from "../constants"
7
7
  import ParseError from "../ParseError";
8
8
  import { assertNodeType, assertSymbolNodeType } from "../parseNode";
9
9
  import { checkSymbolNodeType } from "../parseNode";
10
-
10
+ import { stringFromArg } from "../macros"
11
+ import { calculateSize } from "../units"
11
12
  import * as mml from "../buildMathML";
12
13
 
13
14
  // Helper functions
@@ -38,6 +39,24 @@ const validateAmsEnvironmentContext = context => {
38
39
  }
39
40
  }
40
41
 
42
+ const sizeRegEx = /([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/
43
+ const arrayGaps = macros => {
44
+ let arraystretch = macros.get("\\arraystretch")
45
+ if (typeof arraystretch !== "string") {
46
+ arraystretch = stringFromArg(arraystretch.tokens)
47
+ }
48
+ arraystretch = isNaN(arraystretch) ? null : Number(arraystretch)
49
+ let arraycolsepStr = macros.get("\\arraycolsep")
50
+ if (typeof arraycolsepStr !== "string") {
51
+ arraycolsepStr = stringFromArg(arraycolsepStr.tokens)
52
+ }
53
+ const match = sizeRegEx.exec(arraycolsepStr)
54
+ const arraycolsep = match
55
+ ? { number: +(match[1] + match[2]), unit: match[3] }
56
+ : null
57
+ return [arraystretch, arraycolsep]
58
+ }
59
+
41
60
  const getTag = (group, style, rowNum) => {
42
61
  let tag
43
62
  const tagContents = group.tags.shift()
@@ -76,12 +95,14 @@ function parseArray(
76
95
  {
77
96
  cols, // [{ type: string , align: l|c|r|null }]
78
97
  envClasses, // align(ed|at|edat) | array | cases | cd | small | multline
79
- addEqnNum, // boolean
80
- singleRow, // boolean
98
+ addEqnNum, // boolean
99
+ singleRow, // boolean
81
100
  emptySingleRow, // boolean
82
- maxNumCols, // number
83
- leqno // boolean
84
- },
101
+ maxNumCols, // number
102
+ leqno, // boolean
103
+ arraystretch, // number | null
104
+ arraycolsep // size value | null
105
+ },
85
106
  scriptLevel
86
107
  ) {
87
108
  parser.gullet.beginGroup();
@@ -111,7 +132,6 @@ function parseArray(
111
132
  // Test for \hline at the top of the array.
112
133
  hLinesBeforeRow.push(getHLines(parser));
113
134
 
114
- // eslint-disable-next-line no-constant-condition
115
135
  while (true) {
116
136
  // Parse each cell in its own group (namespace)
117
137
  let cell = parser.parseExpression(false, singleRow ? "\\end" : "\\\\");
@@ -211,7 +231,9 @@ function parseArray(
211
231
  addEqnNum,
212
232
  scriptLevel,
213
233
  tags,
214
- leqno
234
+ leqno,
235
+ arraystretch,
236
+ arraycolsep
215
237
  };
216
238
  }
217
239
 
@@ -301,12 +323,18 @@ const mathmlBuilder = function(group, style) {
301
323
  }
302
324
 
303
325
  if (group.envClasses.length > 0) {
304
- const pad = group.envClasses.includes("jot")
326
+ let pad = group.envClasses.includes("jot")
305
327
  ? "0.7" // 0.5ex + 0.09em top & bot padding
306
328
  : group.envClasses.includes("small")
307
329
  ? "0.35"
308
330
  : "0.5" // 0.5ex default top & bot padding
309
- const sidePadding = group.envClasses.includes("abut")
331
+ if (group.arraystretch && group.arraystretch !== 1) {
332
+ // In LaTeX, \arraystretch is a factor applied to a 12pt strut height.
333
+ // It defines a baseline to baseline distance.
334
+ // Here, we do an approximation of that approach.
335
+ pad = String(1.4 * group.arraystretch - 0.8)
336
+ }
337
+ let sidePadding = group.envClasses.includes("abut")
310
338
  ? "0"
311
339
  : group.envClasses.includes("cases")
312
340
  ? "0"
@@ -315,6 +343,12 @@ const mathmlBuilder = function(group, style) {
315
343
  : group.envClasses.includes("cd")
316
344
  ? "0.25"
317
345
  : "0.4" // default side padding
346
+ let sidePadUnit = "em"
347
+ if (group.arraycolsep) {
348
+ const arraySidePad = calculateSize(group.arraycolsep, style)
349
+ sidePadding = arraySidePad.number
350
+ sidePadUnit = arraySidePad.unit
351
+ }
318
352
 
319
353
  const numCols = tbl.length === 0 ? 0 : tbl[0].children.length
320
354
 
@@ -333,7 +367,8 @@ const mathmlBuilder = function(group, style) {
333
367
  // Padding
334
368
  for (let i = 0; i < tbl.length; i++) {
335
369
  for (let j = 0; j < tbl[i].children.length; j++) {
336
- tbl[i].children[j].style.padding = `${pad}ex ${sidePad(j, 1)}em ${pad}ex ${sidePad(j, 0)}em`
370
+ tbl[i].children[j].style.padding = `${pad}ex ${sidePad(j, 1)}${sidePadUnit}`
371
+ + ` ${pad}ex ${sidePad(j, 0)}${sidePadUnit}`
337
372
  }
338
373
  }
339
374
 
@@ -587,10 +622,13 @@ defineEnvironment({
587
622
  }
588
623
  throw new ParseError("Unknown column alignment: " + ca, nde);
589
624
  });
625
+ const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros)
590
626
  const res = {
591
627
  cols,
592
628
  envClasses: ["array"],
593
- maxNumCols: cols.length
629
+ maxNumCols: cols.length,
630
+ arraystretch,
631
+ arraycolsep
594
632
  };
595
633
  return parseArray(context.parser, res, dCellStyle(context.envName));
596
634
  },
@@ -656,6 +694,7 @@ defineEnvironment({
656
694
  }
657
695
  const res = parseArray(context.parser, payload, "text")
658
696
  res.cols = new Array(res.body[0].length).fill({ type: "align", align: colAlign })
697
+ const [arraystretch, arraycolsep] = arrayGaps(context.parser.gullet.macros)
659
698
  return delimiters
660
699
  ? {
661
700
  type: "leftright",
@@ -663,7 +702,9 @@ defineEnvironment({
663
702
  body: [res],
664
703
  left: delimiters[0],
665
704
  right: delimiters[1],
666
- rightColor: undefined // \right uninfluenced by \color in array
705
+ rightColor: undefined, // \right uninfluenced by \color in array
706
+ arraystretch,
707
+ arraycolsep
667
708
  }
668
709
  : res;
669
710
  },
@@ -71,7 +71,7 @@ export function parseCD(parser) {
71
71
  parser.gullet.beginGroup();
72
72
  parser.gullet.macros.set("\\cr", "\\\\\\relax");
73
73
  parser.gullet.beginGroup();
74
- while (true) { // eslint-disable-line no-constant-condition
74
+ while (true) {
75
75
  // Get the parse nodes for the next row.
76
76
  parsedRows.push(parser.parseExpression(false, "\\\\"));
77
77
  parser.gullet.endGroup();
@@ -71,6 +71,20 @@ const needWebkitShift = new Set([
71
71
  "\\'", "\\^", "\\~", "\\=", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"
72
72
  ])
73
73
 
74
+ const combiningChar = {
75
+ "\\`": "\u0300",
76
+ "\\'": "\u0301",
77
+ "\\^": "\u0302",
78
+ "\\~": "\u0303",
79
+ "\\=": "\u0304",
80
+ "\\u": "\u0306",
81
+ "\\.": "\u0307",
82
+ '\\"': "\u0308",
83
+ "\\r": "\u030A",
84
+ "\\H": "\u030B",
85
+ "\\v": "\u030C"
86
+ }
87
+
74
88
  // Accents
75
89
  defineFunction({
76
90
  type: "accent",
@@ -140,13 +154,24 @@ defineFunction({
140
154
  console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`)
141
155
  }
142
156
 
143
- return {
144
- type: "accent",
145
- mode: mode,
146
- label: context.funcName,
147
- isStretchy: false,
148
- base: base
149
- };
157
+ if (mode === "text" && base.text && base.text.length === 1
158
+ && context.funcName in combiningChar && smalls.indexOf(base.text) > -1) {
159
+ // Return a combining accent character
160
+ return {
161
+ type: "textord",
162
+ mode: "text",
163
+ text: base.text + combiningChar[context.funcName]
164
+ }
165
+ } else {
166
+ // Build up the accent
167
+ return {
168
+ type: "accent",
169
+ mode: mode,
170
+ label: context.funcName,
171
+ isStretchy: false,
172
+ base: base
173
+ }
174
+ }
150
175
  },
151
176
  mathmlBuilder
152
177
  });
@@ -186,6 +186,7 @@ defineFunction({
186
186
  type: "color",
187
187
  mode: parser.mode,
188
188
  color,
189
+ isTextColor: true,
189
190
  body: ordargument(body)
190
191
  }
191
192
  },
@@ -218,6 +219,7 @@ defineFunction({
218
219
  type: "color",
219
220
  mode: parser.mode,
220
221
  color,
222
+ isTextColor: false,
221
223
  body
222
224
  }
223
225
  },
@@ -252,8 +252,7 @@ defineFunction({
252
252
 
253
253
  parser.gullet.macros.set(
254
254
  name,
255
- { tokens, numArgs },
256
- !parser.settings.strict
255
+ { tokens, numArgs }
257
256
  )
258
257
 
259
258
  return { type: "internal", mode: parser.mode };
@@ -118,10 +118,8 @@ function checkDelimiter(delim, context) {
118
118
  if (symDelim && delimiters.includes(symDelim.text)) {
119
119
  // If a character is not in the MathML operator dictionary, it will not stretch.
120
120
  // Replace such characters w/characters that will stretch.
121
- if (["/", "\u2044"].includes(symDelim.text)) { symDelim.text = "\u2215" }
122
121
  if (["<", "\\lt"].includes(symDelim.text)) { symDelim.text = "⟨" }
123
122
  if ([">", "\\gt"].includes(symDelim.text)) { symDelim.text = "⟩" }
124
- if (symDelim.text === "\\backslash") { symDelim.text = "\u2216" }
125
123
  return symDelim;
126
124
  } else if (symDelim) {
127
125
  throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim);
@@ -130,6 +128,9 @@ function checkDelimiter(delim, context) {
130
128
  }
131
129
  }
132
130
 
131
+ // / \
132
+ const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"];
133
+
133
134
  defineFunction({
134
135
  type: "delimsizing",
135
136
  names: [
@@ -182,8 +183,7 @@ defineFunction({
182
183
  // defaults.
183
184
  node.setAttribute("fence", "false");
184
185
  }
185
- if (group.delim === "\u2216" || group.delim === "\\vert" ||
186
- group.delim === "|" || group.delim.indexOf("arrow") > -1) {
186
+ if (needExplicitStretch.includes(group.delim) || group.delim.indexOf("arrow") > -1) {
187
187
  // We have to explicitly set stretchy to true.
188
188
  node.setAttribute("stretchy", "true")
189
189
  }
@@ -269,7 +269,7 @@ defineFunction({
269
269
  const leftNode = new mathMLTree.MathNode("mo", [mml.makeText(group.left, group.mode)]);
270
270
  leftNode.setAttribute("fence", "true")
271
271
  leftNode.setAttribute("form", "prefix")
272
- if (group.left === "\u2216" || group.left.indexOf("arrow") > -1) {
272
+ if (group.left === "/" || group.left === "\u005C" || group.left.indexOf("arrow") > -1) {
273
273
  leftNode.setAttribute("stretchy", "true")
274
274
  }
275
275
  inner.unshift(leftNode)
@@ -281,6 +281,15 @@ defineFunction({
281
281
  if (group.right === "\u2216" || group.right.indexOf("arrow") > -1) {
282
282
  rightNode.setAttribute("stretchy", "true")
283
283
  }
284
+ if (group.body.length > 0) {
285
+ const lastElement = group.body[group.body.length - 1];
286
+ if (lastElement.type === "color" && !lastElement.isTextColor) {
287
+ // \color is a switch. If the last element is of type "color" then
288
+ // the user set the \color switch and left it on.
289
+ // A \right delimiter turns the switch off, but the delimiter itself gets the color.
290
+ rightNode.setAttribute("mathcolor", lastElement.color);
291
+ }
292
+ }
284
293
  inner.push(rightNode)
285
294
 
286
295
  return mml.makeRow(inner);
@@ -22,52 +22,62 @@ const mathmlBuilder = (group, style) => {
22
22
  padding()
23
23
  ])
24
24
  } else {
25
- node = new mathMLTree.MathNode("mrow", [mml.buildGroup(group.body, style)])
25
+ node = new mathMLTree.MathNode("menclose", [mml.buildGroup(group.body, style)])
26
26
  }
27
27
  switch (group.label) {
28
28
  case "\\overline":
29
- node.style.padding = "0.1em 0 0 0"
30
- node.style.borderTop = "0.065em solid"
29
+ node.setAttribute("notation", "top") // for Firefox & WebKit
30
+ node.classes.push("tml-overline") // for Chromium
31
31
  break
32
32
  case "\\underline":
33
- node.style.padding = "0 0 0.1em 0"
34
- node.style.borderBottom = "0.065em solid"
33
+ node.setAttribute("notation", "bottom")
34
+ node.classes.push("tml-underline")
35
35
  break
36
36
  case "\\cancel":
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")
37
+ node.setAttribute("notation", "updiagonalstrike")
38
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "upstrike"]))
40
39
  break
41
40
  case "\\bcancel":
42
- node.classes.push("tml-bcancel")
41
+ node.setAttribute("notation", "downdiagonalstrike")
42
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "downstrike"]))
43
+ break
44
+ case "\\sout":
45
+ node.setAttribute("notation", "horizontalstrike")
46
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "sout"]))
47
+ break
48
+ case "\\xcancel":
49
+ node.setAttribute("notation", "updiagonalstrike downdiagonalstrike")
50
+ node.classes.push("tml-xcancel")
43
51
  break
44
- /*
45
52
  case "\\longdiv":
46
- node.setAttribute("notation", "longdiv");
53
+ node.setAttribute("notation", "longdiv")
54
+ node.classes.push("longdiv-top")
55
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["longdiv-arc"]))
47
56
  break
48
57
  case "\\phase":
49
- node.setAttribute("notation", "phasorangle");
50
- break */
51
- case "\\angl":
52
- node.style.padding = "0.03889em 0.03889em 0 0.03889em"
53
- node.style.borderTop = "0.049em solid"
54
- node.style.borderRight = "0.049em solid"
55
- node.style.marginRight = "0.03889em"
58
+ node.setAttribute("notation", "phasorangle")
59
+ node.classes.push("phasor-bottom")
60
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["phasor-angle"]))
56
61
  break
57
- case "\\sout":
58
- node.style.backgroundImage = 'linear-gradient(black, black)'
59
- node.style.backgroundRepeat = 'no-repeat'
60
- node.style.backgroundSize = '100% 1.5px'
61
- node.style.backgroundPosition = '0 center'
62
+ case "\\textcircled":
63
+ node.setAttribute("notation", "circle")
64
+ node.classes.push("circle-pad")
65
+ node.children.push(new mathMLTree.MathNode("mrow", [], ["textcircle"]))
66
+ break
67
+ case "\\angl":
68
+ node.setAttribute("notation", "actuarial")
69
+ node.classes.push("actuarial")
62
70
  break
63
71
  case "\\boxed":
64
72
  // \newcommand{\boxed}[1]{\fbox{\m@th$\displaystyle#1$}} from amsmath.sty
65
- node.style = { padding: "3pt 0 3pt 0", border: "1px solid" }
73
+ node.setAttribute("notation", "box")
74
+ node.classes.push("tml-box")
66
75
  node.setAttribute("scriptlevel", "0")
67
76
  node.setAttribute("displaystyle", "true")
68
77
  break
69
78
  case "\\fbox":
70
- node.style = { padding: "3pt", border: "1px solid" }
79
+ node.setAttribute("notation", "box")
80
+ node.classes.push("tml-fbox")
71
81
  break
72
82
  case "\\fcolorbox":
73
83
  case "\\colorbox": {
@@ -80,14 +90,11 @@ const mathmlBuilder = (group, style) => {
80
90
  const style = { padding: "3pt 0 3pt 0" }
81
91
 
82
92
  if (group.label === "\\fcolorbox") {
83
- style.border = "0.06em solid " + String(group.borderColor)
93
+ style.border = "0.0667em solid " + String(group.borderColor)
84
94
  }
85
95
  node.style = style
86
96
  break
87
97
  }
88
- case "\\xcancel":
89
- node.classes.push("tml-xcancel")
90
- break
91
98
  }
92
99
  if (group.backgroundColor) {
93
100
  node.setAttribute("mathbackground", group.backgroundColor);
@@ -180,8 +187,8 @@ defineFunction({
180
187
 
181
188
  defineFunction({
182
189
  type: "enclose",
183
- names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline", "\\boxed"],
184
- // , "\\phase", "\\longdiv"
190
+ names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\sout", "\\overline",
191
+ "\\boxed", "\\longdiv", "\\phase"],
185
192
  props: {
186
193
  numArgs: 1
187
194
  },
@@ -215,3 +222,25 @@ defineFunction({
215
222
  },
216
223
  mathmlBuilder
217
224
  });
225
+
226
+
227
+ defineFunction({
228
+ type: "enclose",
229
+ names: ["\\textcircled"],
230
+ props: {
231
+ numArgs: 1,
232
+ argTypes: ["text"],
233
+ allowedInArgument: true,
234
+ allowedInText: true
235
+ },
236
+ handler({ parser, funcName }, args) {
237
+ const body = args[0];
238
+ return {
239
+ type: "enclose",
240
+ mode: parser.mode,
241
+ label: funcName,
242
+ body
243
+ };
244
+ },
245
+ mathmlBuilder
246
+ });