temml 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ });