temml 0.9.1

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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -0
  3. package/contrib/auto-render/README.md +89 -0
  4. package/contrib/auto-render/auto-render.js +128 -0
  5. package/contrib/auto-render/dist/auto-render.js +217 -0
  6. package/contrib/auto-render/dist/auto-render.min.js +1 -0
  7. package/contrib/auto-render/splitAtDelimiters.js +84 -0
  8. package/contrib/auto-render/test/auto-render-spec.js +234 -0
  9. package/contrib/auto-render/test/auto-render.js +217 -0
  10. package/contrib/auto-render/test/test_page.html +59 -0
  11. package/contrib/mhchem/README.md +26 -0
  12. package/contrib/mhchem/mhchem.js +1705 -0
  13. package/contrib/mhchem/mhchem.min.js +1 -0
  14. package/contrib/physics/README.md +20 -0
  15. package/contrib/physics/physics.js +131 -0
  16. package/contrib/texvc/README.md +23 -0
  17. package/contrib/texvc/texvc.js +61 -0
  18. package/dist/Temml-Asana.css +201 -0
  19. package/dist/Temml-Latin-Modern.css +216 -0
  20. package/dist/Temml-Libertinus.css +214 -0
  21. package/dist/Temml-Local.css +194 -0
  22. package/dist/Temml-STIX2.css +203 -0
  23. package/dist/Temml.woff2 +0 -0
  24. package/dist/temml.cjs +13122 -0
  25. package/dist/temml.js +11225 -0
  26. package/dist/temml.min.js +1 -0
  27. package/dist/temml.mjs +13120 -0
  28. package/dist/temmlPostProcess.js +70 -0
  29. package/package.json +34 -0
  30. package/src/Lexer.js +121 -0
  31. package/src/MacroExpander.js +437 -0
  32. package/src/Namespace.js +107 -0
  33. package/src/ParseError.js +64 -0
  34. package/src/Parser.js +977 -0
  35. package/src/Settings.js +49 -0
  36. package/src/SourceLocation.js +29 -0
  37. package/src/Style.js +144 -0
  38. package/src/Token.js +40 -0
  39. package/src/buildMathML.js +235 -0
  40. package/src/constants.js +25 -0
  41. package/src/defineEnvironment.js +25 -0
  42. package/src/defineFunction.js +69 -0
  43. package/src/defineMacro.js +11 -0
  44. package/src/domTree.js +185 -0
  45. package/src/environments/array.js +791 -0
  46. package/src/environments/cd.js +252 -0
  47. package/src/environments.js +8 -0
  48. package/src/functions/accent.js +127 -0
  49. package/src/functions/accentunder.js +38 -0
  50. package/src/functions/arrow.js +204 -0
  51. package/src/functions/cancelto.js +36 -0
  52. package/src/functions/char.js +33 -0
  53. package/src/functions/color.js +253 -0
  54. package/src/functions/cr.js +46 -0
  55. package/src/functions/def.js +259 -0
  56. package/src/functions/delimsizing.js +304 -0
  57. package/src/functions/enclose.js +193 -0
  58. package/src/functions/envTag.js +38 -0
  59. package/src/functions/environment.js +59 -0
  60. package/src/functions/font.js +123 -0
  61. package/src/functions/genfrac.js +333 -0
  62. package/src/functions/hbox.js +29 -0
  63. package/src/functions/horizBrace.js +32 -0
  64. package/src/functions/href.js +90 -0
  65. package/src/functions/html.js +95 -0
  66. package/src/functions/includegraphics.js +131 -0
  67. package/src/functions/kern.js +75 -0
  68. package/src/functions/label.js +29 -0
  69. package/src/functions/lap.js +75 -0
  70. package/src/functions/math.js +40 -0
  71. package/src/functions/mathchoice.js +41 -0
  72. package/src/functions/mclass.js +201 -0
  73. package/src/functions/multiscript.js +91 -0
  74. package/src/functions/not.js +46 -0
  75. package/src/functions/op.js +338 -0
  76. package/src/functions/operatorname.js +139 -0
  77. package/src/functions/ordgroup.js +9 -0
  78. package/src/functions/phantom.js +73 -0
  79. package/src/functions/pmb.js +31 -0
  80. package/src/functions/raise.js +68 -0
  81. package/src/functions/ref.js +28 -0
  82. package/src/functions/relax.js +16 -0
  83. package/src/functions/rule.js +52 -0
  84. package/src/functions/sizing.js +64 -0
  85. package/src/functions/smash.js +66 -0
  86. package/src/functions/sqrt.js +31 -0
  87. package/src/functions/styling.js +58 -0
  88. package/src/functions/supsub.js +135 -0
  89. package/src/functions/symbolsOp.js +53 -0
  90. package/src/functions/symbolsOrd.js +102 -0
  91. package/src/functions/symbolsSpacing.js +53 -0
  92. package/src/functions/tag.js +8 -0
  93. package/src/functions/text.js +75 -0
  94. package/src/functions/tip.js +63 -0
  95. package/src/functions/toggle.js +13 -0
  96. package/src/functions/verb.js +33 -0
  97. package/src/functions.js +57 -0
  98. package/src/linebreaking.js +159 -0
  99. package/src/macros.js +708 -0
  100. package/src/mathMLTree.js +175 -0
  101. package/src/parseNode.js +42 -0
  102. package/src/parseTree.js +40 -0
  103. package/src/postProcess.js +57 -0
  104. package/src/replace.js +225 -0
  105. package/src/stretchy.js +66 -0
  106. package/src/svg.js +110 -0
  107. package/src/symbols.js +972 -0
  108. package/src/tree.js +50 -0
  109. package/src/unicodeAccents.js +16 -0
  110. package/src/unicodeScripts.js +119 -0
  111. package/src/unicodeSupOrSub.js +108 -0
  112. package/src/unicodeSymbolBuilder.js +31 -0
  113. package/src/unicodeSymbols.js +320 -0
  114. package/src/units.js +109 -0
  115. package/src/utils.js +109 -0
  116. package/src/variant.js +103 -0
  117. package/temml.js +181 -0
@@ -0,0 +1,123 @@
1
+ import defineFunction, { normalizeArgument } from "../defineFunction"
2
+ import * as mml from "../buildMathML"
3
+ import mathMLTree from "../mathMLTree"
4
+
5
+ const mathmlBuilder = (group, style) => {
6
+ const font = group.font
7
+ const newStyle = style.withFont(font)
8
+ const mathGroup = mml.buildGroup(group.body, newStyle)
9
+
10
+ if (mathGroup.children.length === 0) { return mathGroup } // empty group, e.g., \mathrm{}
11
+ if (font === "boldsymbol" && ["mo", "mpadded"].includes(mathGroup.type)) {
12
+ mathGroup.style.fontWeight = "bold"
13
+ return mathGroup
14
+ }
15
+ // Check if it is possible to consolidate elements into a single <mi> element.
16
+ let canConsolidate = mathGroup.children[0].type === "mo"
17
+ for (let i = 1; i < mathGroup.children.length; i++) {
18
+ if (mathGroup.children[i].type === "mo" && font === "boldsymbol") {
19
+ mathGroup.children[i].style.fontWeight = "bold"
20
+ }
21
+ if (mathGroup.children[i].type !== "mi") { canConsolidate = false }
22
+ const localVariant = mathGroup.children[i].attributes &&
23
+ mathGroup.children[i].attributes.mathvariant || ""
24
+ if (localVariant !== "normal") { canConsolidate = false }
25
+ }
26
+ if (!canConsolidate) { return mathGroup }
27
+ // Consolidate the <mi> elements.
28
+ const mi = mathGroup.children[0]
29
+ for (let i = 1; i < mathGroup.children.length; i++) {
30
+ mi.children.push(mathGroup.children[i].children[0])
31
+ }
32
+ if (mathGroup.attributes.mathcolor) { mi.attributes.mathcolor = mathGroup.attributes.mathcolor }
33
+ if (mi.attributes.mathvariant && mi.attributes.mathvariant === "normal") {
34
+ // Workaround for a Firefox bug that renders spurious space around
35
+ // a <mi mathvariant="normal">
36
+ // Ref: https://bugs.webkit.org/show_bug.cgi?id=129097
37
+ // We insert a text node that contains a zero-width space and wrap in an mrow.
38
+ // TODO: Get rid of this <mi> workaround when the Firefox bug is fixed.
39
+ const bogus = new mathMLTree.MathNode("mtext", new mathMLTree.TextNode("\u200b"))
40
+ return new mathMLTree.MathNode("mrow", [bogus, mi])
41
+ }
42
+ return mi
43
+ };
44
+
45
+ const fontAliases = {
46
+ "\\Bbb": "\\mathbb",
47
+ "\\bold": "\\mathbf",
48
+ "\\frak": "\\mathfrak",
49
+ "\\bm": "\\boldsymbol"
50
+ };
51
+
52
+ defineFunction({
53
+ type: "font",
54
+ names: [
55
+ // styles
56
+ "\\mathrm",
57
+ "\\mathit",
58
+ "\\mathbf",
59
+ "\\mathnormal",
60
+ "\\up@greek",
61
+ "\\boldsymbol",
62
+
63
+ // families
64
+ "\\mathbb",
65
+ "\\mathcal",
66
+ "\\mathfrak",
67
+ "\\mathscr",
68
+ "\\mathsf",
69
+ "\\mathtt",
70
+ "\\oldstylenums",
71
+
72
+ // aliases
73
+ "\\Bbb",
74
+ "\\bm",
75
+ "\\bold",
76
+ "\\frak"
77
+ ],
78
+ props: {
79
+ numArgs: 1,
80
+ allowedInArgument: true
81
+ },
82
+ handler: ({ parser, funcName }, args) => {
83
+ const body = normalizeArgument(args[0]);
84
+ let func = funcName;
85
+ if (func in fontAliases) {
86
+ func = fontAliases[func];
87
+ }
88
+ return {
89
+ type: "font",
90
+ mode: parser.mode,
91
+ font: func.slice(1),
92
+ body
93
+ };
94
+ },
95
+ mathmlBuilder
96
+ });
97
+
98
+ // Old font changing functions
99
+ defineFunction({
100
+ type: "font",
101
+ names: ["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
102
+ props: {
103
+ numArgs: 0,
104
+ allowedInText: true
105
+ },
106
+ handler: ({ parser, funcName, breakOnTokenText }, args) => {
107
+ const { mode } = parser;
108
+ const body = parser.parseExpression(true, breakOnTokenText);
109
+ const fontStyle = `math${funcName.slice(1)}`;
110
+
111
+ return {
112
+ type: "font",
113
+ mode: mode,
114
+ font: fontStyle,
115
+ body: {
116
+ type: "ordgroup",
117
+ mode: parser.mode,
118
+ body
119
+ }
120
+ };
121
+ },
122
+ mathmlBuilder
123
+ });
@@ -0,0 +1,333 @@
1
+ import defineFunction, { normalizeArgument } from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import { StyleLevel } from "../constants"
4
+ import { assertNodeType } from "../parseNode";
5
+ import { assert } from "../utils";
6
+ import * as mml from "../buildMathML";
7
+ import { calculateSize } from "../units";
8
+
9
+ const stylArray = ["display", "text", "script", "scriptscript"];
10
+ const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 };
11
+
12
+ const mathmlBuilder = (group, style) => {
13
+ // Track the scriptLevel of the numerator and denominator.
14
+ // We may need that info for \mathchoice or for adjusting em dimensions.
15
+ const childOptions = group.scriptLevel === "auto"
16
+ ? style.incrementLevel()
17
+ : group.scriptLevel === "display"
18
+ ? style.withLevel(StyleLevel.TEXT)
19
+ : group.scriptLevel === "text"
20
+ ? style.withLevel(StyleLevel.SCRIPT)
21
+ : style.withLevel(StyleLevel.SCRIPTSCRIPT);
22
+
23
+ let node = new mathMLTree.MathNode("mfrac", [
24
+ mml.buildGroup(group.numer, childOptions),
25
+ mml.buildGroup(group.denom, childOptions)
26
+ ]);
27
+
28
+ if (!group.hasBarLine) {
29
+ node.setAttribute("linethickness", "0px");
30
+ } else if (group.barSize) {
31
+ const ruleWidth = calculateSize(group.barSize, style);
32
+ node.setAttribute("linethickness", ruleWidth.number + ruleWidth.unit);
33
+ }
34
+
35
+ if (group.leftDelim != null || group.rightDelim != null) {
36
+ const withDelims = [];
37
+
38
+ if (group.leftDelim != null) {
39
+ const leftOp = new mathMLTree.MathNode("mo", [
40
+ new mathMLTree.TextNode(group.leftDelim.replace("\\", ""))
41
+ ]);
42
+ leftOp.setAttribute("fence", "true");
43
+ withDelims.push(leftOp);
44
+ }
45
+
46
+ withDelims.push(node);
47
+
48
+ if (group.rightDelim != null) {
49
+ const rightOp = new mathMLTree.MathNode("mo", [
50
+ new mathMLTree.TextNode(group.rightDelim.replace("\\", ""))
51
+ ]);
52
+ rightOp.setAttribute("fence", "true");
53
+ withDelims.push(rightOp);
54
+ }
55
+
56
+ node = mml.makeRow(withDelims);
57
+ }
58
+
59
+ if (group.scriptLevel !== "auto") {
60
+ node = new mathMLTree.MathNode("mstyle", [node]);
61
+ node.setAttribute("displaystyle", String(group.scriptLevel === "display"));
62
+ node.setAttribute("scriptlevel", scriptLevel[group.scriptLevel]);
63
+ }
64
+
65
+ return node;
66
+ };
67
+
68
+ defineFunction({
69
+ type: "genfrac",
70
+ names: [
71
+ "\\dfrac",
72
+ "\\frac",
73
+ "\\tfrac",
74
+ "\\dbinom",
75
+ "\\binom",
76
+ "\\tbinom",
77
+ "\\\\atopfrac", // can’t be entered directly
78
+ "\\\\bracefrac",
79
+ "\\\\brackfrac" // ditto
80
+ ],
81
+ props: {
82
+ numArgs: 2,
83
+ allowedInArgument: true
84
+ },
85
+ handler: ({ parser, funcName }, args) => {
86
+ const numer = args[0];
87
+ const denom = args[1];
88
+ let hasBarLine = false;
89
+ let leftDelim = null;
90
+ let rightDelim = null;
91
+ let scriptLevel = "auto";
92
+
93
+ switch (funcName) {
94
+ case "\\dfrac":
95
+ case "\\frac":
96
+ case "\\tfrac":
97
+ hasBarLine = true;
98
+ break;
99
+ case "\\\\atopfrac":
100
+ hasBarLine = false;
101
+ break;
102
+ case "\\dbinom":
103
+ case "\\binom":
104
+ case "\\tbinom":
105
+ leftDelim = "(";
106
+ rightDelim = ")";
107
+ break;
108
+ case "\\\\bracefrac":
109
+ leftDelim = "\\{";
110
+ rightDelim = "\\}";
111
+ break;
112
+ case "\\\\brackfrac":
113
+ leftDelim = "[";
114
+ rightDelim = "]";
115
+ break;
116
+ default:
117
+ throw new Error("Unrecognized genfrac command");
118
+ }
119
+
120
+ switch (funcName) {
121
+ case "\\dfrac":
122
+ case "\\dbinom":
123
+ scriptLevel = "display";
124
+ break;
125
+ case "\\tfrac":
126
+ case "\\tbinom":
127
+ scriptLevel = "text";
128
+ break;
129
+ }
130
+
131
+ return {
132
+ type: "genfrac",
133
+ mode: parser.mode,
134
+ continued: false,
135
+ numer,
136
+ denom,
137
+ hasBarLine,
138
+ leftDelim,
139
+ rightDelim,
140
+ scriptLevel,
141
+ barSize: null
142
+ };
143
+ },
144
+ mathmlBuilder
145
+ });
146
+
147
+ defineFunction({
148
+ type: "genfrac",
149
+ names: ["\\cfrac"],
150
+ props: {
151
+ numArgs: 2
152
+ },
153
+ handler: ({ parser, funcName }, args) => {
154
+ const numer = args[0];
155
+ const denom = args[1];
156
+
157
+ return {
158
+ type: "genfrac",
159
+ mode: parser.mode,
160
+ continued: true,
161
+ numer,
162
+ denom,
163
+ hasBarLine: true,
164
+ leftDelim: null,
165
+ rightDelim: null,
166
+ scriptLevel: "display",
167
+ barSize: null
168
+ };
169
+ }
170
+ });
171
+
172
+ // Infix generalized fractions -- these are not rendered directly, but replaced
173
+ // immediately by one of the variants above.
174
+ defineFunction({
175
+ type: "infix",
176
+ names: ["\\over", "\\choose", "\\atop", "\\brace", "\\brack"],
177
+ props: {
178
+ numArgs: 0,
179
+ infix: true
180
+ },
181
+ handler({ parser, funcName, token }) {
182
+ let replaceWith;
183
+ switch (funcName) {
184
+ case "\\over":
185
+ replaceWith = "\\frac";
186
+ break;
187
+ case "\\choose":
188
+ replaceWith = "\\binom";
189
+ break;
190
+ case "\\atop":
191
+ replaceWith = "\\\\atopfrac";
192
+ break;
193
+ case "\\brace":
194
+ replaceWith = "\\\\bracefrac";
195
+ break;
196
+ case "\\brack":
197
+ replaceWith = "\\\\brackfrac";
198
+ break;
199
+ default:
200
+ throw new Error("Unrecognized infix genfrac command");
201
+ }
202
+ return {
203
+ type: "infix",
204
+ mode: parser.mode,
205
+ replaceWith,
206
+ token
207
+ };
208
+ }
209
+ });
210
+
211
+ const delimFromValue = function(delimString) {
212
+ let delim = null;
213
+ if (delimString.length > 0) {
214
+ delim = delimString;
215
+ delim = delim === "." ? null : delim;
216
+ }
217
+ return delim;
218
+ };
219
+
220
+ defineFunction({
221
+ type: "genfrac",
222
+ names: ["\\genfrac"],
223
+ props: {
224
+ numArgs: 6,
225
+ allowedInArgument: true,
226
+ argTypes: ["math", "math", "size", "text", "math", "math"]
227
+ },
228
+ handler({ parser }, args) {
229
+ const numer = args[4];
230
+ const denom = args[5];
231
+
232
+ // Look into the parse nodes to get the desired delimiters.
233
+ const leftNode = normalizeArgument(args[0]);
234
+ const leftDelim = leftNode.type === "atom" && leftNode.family === "open"
235
+ ? delimFromValue(leftNode.text)
236
+ : null;
237
+ const rightNode = normalizeArgument(args[1]);
238
+ const rightDelim =
239
+ rightNode.type === "atom" && rightNode.family === "close"
240
+ ? delimFromValue(rightNode.text)
241
+ : null;
242
+
243
+ const barNode = assertNodeType(args[2], "size");
244
+ let hasBarLine;
245
+ let barSize = null;
246
+ if (barNode.isBlank) {
247
+ // \genfrac acts differently than \above.
248
+ // \genfrac treats an empty size group as a signal to use a
249
+ // standard bar size. \above would see size = 0 and omit the bar.
250
+ hasBarLine = true;
251
+ } else {
252
+ barSize = barNode.value;
253
+ hasBarLine = barSize.number > 0;
254
+ }
255
+
256
+ // Find out if we want displaystyle, textstyle, etc.
257
+ let scriptLevel = "auto";
258
+ let styl = args[3];
259
+ if (styl.type === "ordgroup") {
260
+ if (styl.body.length > 0) {
261
+ const textOrd = assertNodeType(styl.body[0], "textord");
262
+ scriptLevel = stylArray[Number(textOrd.text)];
263
+ }
264
+ } else {
265
+ styl = assertNodeType(styl, "textord");
266
+ scriptLevel = stylArray[Number(styl.text)];
267
+ }
268
+
269
+ return {
270
+ type: "genfrac",
271
+ mode: parser.mode,
272
+ numer,
273
+ denom,
274
+ continued: false,
275
+ hasBarLine,
276
+ barSize,
277
+ leftDelim,
278
+ rightDelim,
279
+ scriptLevel
280
+ };
281
+ },
282
+ mathmlBuilder
283
+ });
284
+
285
+ // \above is an infix fraction that also defines a fraction bar size.
286
+ defineFunction({
287
+ type: "infix",
288
+ names: ["\\above"],
289
+ props: {
290
+ numArgs: 1,
291
+ argTypes: ["size"],
292
+ infix: true
293
+ },
294
+ handler({ parser, funcName, token }, args) {
295
+ return {
296
+ type: "infix",
297
+ mode: parser.mode,
298
+ replaceWith: "\\\\abovefrac",
299
+ barSize: assertNodeType(args[0], "size").value,
300
+ token
301
+ };
302
+ }
303
+ });
304
+
305
+ defineFunction({
306
+ type: "genfrac",
307
+ names: ["\\\\abovefrac"],
308
+ props: {
309
+ numArgs: 3,
310
+ argTypes: ["math", "size", "math"]
311
+ },
312
+ handler: ({ parser, funcName }, args) => {
313
+ const numer = args[0];
314
+ const barSize = assert(assertNodeType(args[1], "infix").barSize);
315
+ const denom = args[2];
316
+
317
+ const hasBarLine = barSize.number > 0;
318
+ return {
319
+ type: "genfrac",
320
+ mode: parser.mode,
321
+ numer,
322
+ denom,
323
+ continued: false,
324
+ hasBarLine,
325
+ barSize,
326
+ leftDelim: null,
327
+ rightDelim: null,
328
+ scriptLevel: "auto"
329
+ };
330
+ },
331
+
332
+ mathmlBuilder
333
+ });
@@ -0,0 +1,29 @@
1
+ import defineFunction, { ordargument } from "../defineFunction";
2
+ import { StyleLevel } from "../constants"
3
+ import mathMLTree from "../mathMLTree";
4
+ import * as mml from "../buildMathML";
5
+
6
+ // \hbox is provided for compatibility with LaTeX functions that act on a box.
7
+ // This function by itself doesn't do anything but prevent a soft line break.
8
+
9
+ defineFunction({
10
+ type: "hbox",
11
+ names: ["\\hbox"],
12
+ props: {
13
+ numArgs: 1,
14
+ argTypes: ["hbox"],
15
+ allowedInArgument: true,
16
+ allowedInText: false
17
+ },
18
+ handler({ parser }, args) {
19
+ return {
20
+ type: "hbox",
21
+ mode: parser.mode,
22
+ body: ordargument(args[0])
23
+ };
24
+ },
25
+ mathmlBuilder(group, style) {
26
+ const newOptions = style.withLevel(StyleLevel.TEXT)
27
+ return new mathMLTree.MathNode("mrow", mml.buildExpression(group.body, newOptions));
28
+ }
29
+ });
@@ -0,0 +1,32 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import stretchy from "../stretchy";
4
+ import * as mml from "../buildMathML";
5
+
6
+ const mathmlBuilder = (group, style) => {
7
+ const accentNode = stretchy.mathMLnode(group.label);
8
+ accentNode.style["math-depth"] = 0
9
+ return new mathMLTree.MathNode(group.isOver ? "mover" : "munder", [
10
+ mml.buildGroup(group.base, style),
11
+ accentNode
12
+ ]);
13
+ };
14
+
15
+ // Horizontal stretchy braces
16
+ defineFunction({
17
+ type: "horizBrace",
18
+ names: ["\\overbrace", "\\underbrace"],
19
+ props: {
20
+ numArgs: 1
21
+ },
22
+ handler({ parser, funcName }, args) {
23
+ return {
24
+ type: "horizBrace",
25
+ mode: parser.mode,
26
+ label: funcName,
27
+ isOver: /^\\over/.test(funcName),
28
+ base: args[0]
29
+ };
30
+ },
31
+ mathmlBuilder
32
+ });
@@ -0,0 +1,90 @@
1
+ import defineFunction, { ordargument } from "../defineFunction";
2
+ import { assertNodeType } from "../parseNode";
3
+ import { MathNode } from "../mathMLTree";
4
+ import * as mml from "../buildMathML";
5
+ import ParseError from "../ParseError";
6
+
7
+ defineFunction({
8
+ type: "href",
9
+ names: ["\\href"],
10
+ props: {
11
+ numArgs: 2,
12
+ argTypes: ["url", "original"],
13
+ allowedInText: true
14
+ },
15
+ handler: ({ parser, token }, args) => {
16
+ const body = args[1];
17
+ const href = assertNodeType(args[0], "url").url;
18
+
19
+ if (
20
+ !parser.settings.isTrusted({
21
+ command: "\\href",
22
+ url: href
23
+ })
24
+ ) {
25
+ throw new ParseError(`Function "\\href" is not trusted`, token)
26
+ }
27
+
28
+ return {
29
+ type: "href",
30
+ mode: parser.mode,
31
+ href,
32
+ body: ordargument(body)
33
+ };
34
+ },
35
+ mathmlBuilder: (group, style) => {
36
+ let math = mml.buildExpressionRow(group.body, style);
37
+ if (!(math instanceof MathNode)) {
38
+ math = new MathNode("mrow", [math]);
39
+ }
40
+ math.setAttribute("href", group.href);
41
+ return math;
42
+ }
43
+ });
44
+
45
+ defineFunction({
46
+ type: "href",
47
+ names: ["\\url"],
48
+ props: {
49
+ numArgs: 1,
50
+ argTypes: ["url"],
51
+ allowedInText: true
52
+ },
53
+ handler: ({ parser, token }, args) => {
54
+ const href = assertNodeType(args[0], "url").url;
55
+
56
+ if (
57
+ !parser.settings.isTrusted({
58
+ command: "\\url",
59
+ url: href
60
+ })
61
+ ) {
62
+ throw new ParseError(`Function "\\url" is not trusted`, token)
63
+ }
64
+
65
+ const chars = [];
66
+ for (let i = 0; i < href.length; i++) {
67
+ let c = href[i];
68
+ if (c === "~") {
69
+ c = "\\textasciitilde";
70
+ }
71
+ chars.push({
72
+ type: "textord",
73
+ mode: "text",
74
+ text: c
75
+ });
76
+ }
77
+ const body = {
78
+ type: "text",
79
+ mode: parser.mode,
80
+ font: "\\texttt",
81
+ body: chars
82
+ };
83
+ return {
84
+ type: "href",
85
+ mode: parser.mode,
86
+ href,
87
+ body: ordargument(body)
88
+ };
89
+ }
90
+ });
@@ -0,0 +1,95 @@
1
+ import defineFunction, { ordargument } from "../defineFunction";
2
+ import { assertNodeType } from "../parseNode";
3
+ import ParseError from "../ParseError";
4
+
5
+ import * as mml from "../buildMathML";
6
+
7
+ defineFunction({
8
+ type: "html",
9
+ names: ["\\class", "\\id", "\\style", "\\data"],
10
+ props: {
11
+ numArgs: 2,
12
+ argTypes: ["raw", "original"],
13
+ allowedInText: true
14
+ },
15
+ handler: ({ parser, funcName, token }, args) => {
16
+ const value = assertNodeType(args[0], "raw").string;
17
+ const body = args[1];
18
+
19
+ if (parser.settings.strict) {
20
+ throw new ParseError(`Function "${funcName}" is disabled in strict mode`, token)
21
+ }
22
+
23
+ let trustContext;
24
+ const attributes = {};
25
+
26
+ switch (funcName) {
27
+ case "\\class":
28
+ attributes.class = value;
29
+ trustContext = {
30
+ command: "\\class",
31
+ class: value
32
+ };
33
+ break;
34
+ case "\\id":
35
+ attributes.id = value;
36
+ trustContext = {
37
+ command: "\\id",
38
+ id: value
39
+ };
40
+ break;
41
+ case "\\style":
42
+ attributes.style = value;
43
+ trustContext = {
44
+ command: "\\style",
45
+ style: value
46
+ };
47
+ break;
48
+ case "\\data": {
49
+ const data = value.split(",");
50
+ for (let i = 0; i < data.length; i++) {
51
+ const keyVal = data[i].split("=");
52
+ if (keyVal.length !== 2) {
53
+ throw new ParseError("Error parsing key-value for \\data");
54
+ }
55
+ attributes["data-" + keyVal[0].trim()] = keyVal[1].trim();
56
+ }
57
+
58
+ trustContext = {
59
+ command: "\\data",
60
+ attributes
61
+ };
62
+ break;
63
+ }
64
+ default:
65
+ throw new Error("Unrecognized html command");
66
+ }
67
+
68
+ if (!parser.settings.isTrusted(trustContext)) {
69
+ throw new ParseError(`Function "${funcName}" is not trusted`, token)
70
+ }
71
+ return {
72
+ type: "html",
73
+ mode: parser.mode,
74
+ attributes,
75
+ body: ordargument(body)
76
+ };
77
+ },
78
+ mathmlBuilder: (group, style) => {
79
+ const element = mml.buildExpressionRow(group.body, style);
80
+
81
+ const classes = [];
82
+ if (group.attributes.class) {
83
+ classes.push(...group.attributes.class.trim().split(/\s+/));
84
+ }
85
+ element.classes = classes;
86
+
87
+ for (const attr in group.attributes) {
88
+ if (attr !== "class" && Object.prototype.hasOwnProperty.call(group.attributes, attr)) {
89
+ element.setAttribute(attr, group.attributes[attr]);
90
+ }
91
+ }
92
+
93
+ return element;
94
+ }
95
+ });