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,252 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import * as mml from "../buildMathML";
4
+ import { assertSymbolNodeType } from "../parseNode";
5
+ import ParseError from "../ParseError";
6
+
7
+ const cdArrowFunctionName = {
8
+ ">": "\\\\cdrightarrow",
9
+ "<": "\\\\cdleftarrow",
10
+ "=": "\\\\cdlongequal",
11
+ A: "\\uparrow",
12
+ V: "\\downarrow",
13
+ "|": "\\Vert",
14
+ ".": "no arrow"
15
+ };
16
+
17
+ const newCell = () => {
18
+ // Create an empty cell, to be filled below with parse nodes.
19
+ return { type: "styling", body: [], mode: "math", scriptLevel: "display" };
20
+ };
21
+
22
+ const isStartOfArrow = (node) => {
23
+ return node.type === "textord" && node.text === "@";
24
+ };
25
+
26
+ const isLabelEnd = (node, endChar) => {
27
+ return (node.type === "mathord" || node.type === "atom") && node.text === endChar;
28
+ };
29
+
30
+ function cdArrow(arrowChar, labels, parser) {
31
+ // Return a parse tree of an arrow and its labels.
32
+ // This acts in a way similar to a macro expansion.
33
+ const funcName = cdArrowFunctionName[arrowChar];
34
+ switch (funcName) {
35
+ case "\\\\cdrightarrow":
36
+ case "\\\\cdleftarrow":
37
+ return parser.callFunction(funcName, [labels[0]], [labels[1]]);
38
+ case "\\uparrow":
39
+ case "\\downarrow": {
40
+ const leftLabel = parser.callFunction("\\\\cdleft", [labels[0]], []);
41
+ const bareArrow = {
42
+ type: "atom",
43
+ text: funcName,
44
+ mode: "math",
45
+ family: "rel"
46
+ };
47
+ const sizedArrow = parser.callFunction("\\Big", [bareArrow], []);
48
+ const rightLabel = parser.callFunction("\\\\cdright", [labels[1]], []);
49
+ const arrowGroup = {
50
+ type: "ordgroup",
51
+ mode: "math",
52
+ body: [leftLabel, sizedArrow, rightLabel]
53
+ };
54
+ return parser.callFunction("\\\\cdparent", [arrowGroup], []);
55
+ }
56
+ case "\\\\cdlongequal":
57
+ return parser.callFunction("\\\\cdlongequal", [], []);
58
+ case "\\Vert": {
59
+ const arrow = { type: "textord", text: "\\Vert", mode: "math" };
60
+ return parser.callFunction("\\Big", [arrow], []);
61
+ }
62
+ default:
63
+ return { type: "textord", text: " ", mode: "math" };
64
+ }
65
+ }
66
+
67
+ export function parseCD(parser) {
68
+ // Get the array's parse nodes with \\ temporarily mapped to \cr.
69
+ const parsedRows = [];
70
+ parser.gullet.beginGroup();
71
+ parser.gullet.macros.set("\\cr", "\\\\\\relax");
72
+ parser.gullet.beginGroup();
73
+ while (true) { // eslint-disable-line no-constant-condition
74
+ // Get the parse nodes for the next row.
75
+ parsedRows.push(parser.parseExpression(false, "\\\\"));
76
+ parser.gullet.endGroup();
77
+ parser.gullet.beginGroup();
78
+ const next = parser.fetch().text;
79
+ if (next === "&" || next === "\\\\") {
80
+ parser.consume();
81
+ } else if (next === "\\end") {
82
+ if (parsedRows[parsedRows.length - 1].length === 0) {
83
+ parsedRows.pop(); // final row ended in \\
84
+ }
85
+ break;
86
+ } else {
87
+ throw new ParseError("Expected \\\\ or \\cr or \\end", parser.nextToken);
88
+ }
89
+ }
90
+
91
+ let row = [];
92
+ const body = [row];
93
+
94
+ // Loop thru the parse nodes. Collect them into cells and arrows.
95
+ for (let i = 0; i < parsedRows.length; i++) {
96
+ // Start a new row.
97
+ const rowNodes = parsedRows[i];
98
+ // Create the first cell.
99
+ let cell = newCell();
100
+
101
+ for (let j = 0; j < rowNodes.length; j++) {
102
+ if (!isStartOfArrow(rowNodes[j])) {
103
+ // If a parseNode is not an arrow, it goes into a cell.
104
+ cell.body.push(rowNodes[j]);
105
+ } else {
106
+ // Parse node j is an "@", the start of an arrow.
107
+ // Before starting on the arrow, push the cell into `row`.
108
+ row.push(cell);
109
+
110
+ // Now collect parseNodes into an arrow.
111
+ // The character after "@" defines the arrow type.
112
+ j += 1;
113
+ const arrowChar = assertSymbolNodeType(rowNodes[j]).text;
114
+
115
+ // Create two empty label nodes. We may or may not use them.
116
+ const labels = new Array(2);
117
+ labels[0] = { type: "ordgroup", mode: "math", body: [] };
118
+ labels[1] = { type: "ordgroup", mode: "math", body: [] };
119
+
120
+ // Process the arrow.
121
+ if ("=|.".indexOf(arrowChar) > -1) {
122
+ // Three "arrows", ``@=`, `@|`, and `@.`, do not take labels.
123
+ // Do nothing here.
124
+ } else if ("<>AV".indexOf(arrowChar) > -1) {
125
+ // Four arrows, `@>>>`, `@<<<`, `@AAA`, and `@VVV`, each take
126
+ // two optional labels. E.g. the right-point arrow syntax is
127
+ // really: @>{optional label}>{optional label}>
128
+ // Collect parseNodes into labels.
129
+ for (let labelNum = 0; labelNum < 2; labelNum++) {
130
+ let inLabel = true;
131
+ for (let k = j + 1; k < rowNodes.length; k++) {
132
+ if (isLabelEnd(rowNodes[k], arrowChar)) {
133
+ inLabel = false;
134
+ j = k;
135
+ break;
136
+ }
137
+ if (isStartOfArrow(rowNodes[k])) {
138
+ throw new ParseError(
139
+ "Missing a " + arrowChar + " character to complete a CD arrow.",
140
+ rowNodes[k]
141
+ );
142
+ }
143
+
144
+ labels[labelNum].body.push(rowNodes[k]);
145
+ }
146
+ if (inLabel) {
147
+ // isLabelEnd never returned a true.
148
+ throw new ParseError(
149
+ "Missing a " + arrowChar + " character to complete a CD arrow.",
150
+ rowNodes[j]
151
+ );
152
+ }
153
+ }
154
+ } else {
155
+ throw new ParseError(`Expected one of "<>AV=|." after @.`);
156
+ }
157
+
158
+ // Now join the arrow to its labels.
159
+ const arrow = cdArrow(arrowChar, labels, parser);
160
+
161
+ // Wrap the arrow in a styling node
162
+ row.push(arrow);
163
+ // In CD's syntax, cells are implicit. That is, everything that
164
+ // is not an arrow gets collected into a cell. So create an empty
165
+ // cell now. It will collect upcoming parseNodes.
166
+ cell = newCell();
167
+ }
168
+ }
169
+ if (i % 2 === 0) {
170
+ // Even-numbered rows consist of: cell, arrow, cell, arrow, ... cell
171
+ // The last cell is not yet pushed into `row`, so:
172
+ row.push(cell);
173
+ } else {
174
+ // Odd-numbered rows consist of: vert arrow, empty cell, ... vert arrow
175
+ // Remove the empty cell that was placed at the beginning of `row`.
176
+ row.shift();
177
+ }
178
+ row = [];
179
+ body.push(row);
180
+ }
181
+ body.pop()
182
+
183
+ // End row group
184
+ parser.gullet.endGroup();
185
+ // End array group defining \\
186
+ parser.gullet.endGroup();
187
+
188
+ return {
189
+ type: "array",
190
+ mode: "math",
191
+ body,
192
+ envClasses: ["jot", "cd"],
193
+ cols: [],
194
+ hLinesBeforeRow: new Array(body.length + 1).fill([])
195
+ };
196
+ }
197
+
198
+ // The functions below are not available for general use.
199
+ // They are here only for internal use by the {CD} environment in placing labels
200
+ // next to vertical arrows.
201
+
202
+ // We don't need any such functions for horizontal arrows because we can reuse
203
+ // the functionality that already exists for extensible arrows.
204
+
205
+ defineFunction({
206
+ type: "cdlabel",
207
+ names: ["\\\\cdleft", "\\\\cdright"],
208
+ props: {
209
+ numArgs: 1
210
+ },
211
+ handler({ parser, funcName }, args) {
212
+ return {
213
+ type: "cdlabel",
214
+ mode: parser.mode,
215
+ side: funcName.slice(4),
216
+ label: args[0]
217
+ };
218
+ },
219
+ mathmlBuilder(group, style) {
220
+ let label = new mathMLTree.MathNode("mrow", [mml.buildGroup(group.label, style)]);
221
+ label = new mathMLTree.MathNode("mpadded", [label]);
222
+ label.setAttribute("width", "0");
223
+ if (group.side === "left") {
224
+ label.setAttribute("lspace", "-1width");
225
+ }
226
+ // We have to guess at vertical alignment. We know the arrow is 1.8em tall,
227
+ // But we don't know the height or depth of the label.
228
+ label.setAttribute("voffset", "0.7em");
229
+ label = new mathMLTree.MathNode("mstyle", [label]);
230
+ label.setAttribute("displaystyle", "false");
231
+ label.setAttribute("scriptlevel", "1");
232
+ return label;
233
+ }
234
+ });
235
+
236
+ defineFunction({
237
+ type: "cdlabelparent",
238
+ names: ["\\\\cdparent"],
239
+ props: {
240
+ numArgs: 1
241
+ },
242
+ handler({ parser }, args) {
243
+ return {
244
+ type: "cdlabelparent",
245
+ mode: parser.mode,
246
+ fragment: args[0]
247
+ };
248
+ },
249
+ mathmlBuilder(group, style) {
250
+ return new mathMLTree.MathNode("mrow", [mml.buildGroup(group.fragment, style)]);
251
+ }
252
+ });
@@ -0,0 +1,8 @@
1
+ import { _environments } from "./defineEnvironment";
2
+
3
+ const environments = _environments;
4
+
5
+ export default environments;
6
+
7
+ // All environment definitions should be imported below
8
+ import "./environments/array";
@@ -0,0 +1,127 @@
1
+ import defineFunction, { normalizeArgument } 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 = group.isStretchy
8
+ ? stretchy.mathMLnode(group.label)
9
+ : new mathMLTree.MathNode("mo", [mml.makeText(group.label, group.mode)]);
10
+
11
+ if (group.label === "\\vec") {
12
+ accentNode.style.transform = "scale(0.75) translate(10%, 30%)"
13
+ } else {
14
+ accentNode.style.mathStyle = "normal"
15
+ accentNode.style.mathDepth = "0"
16
+ }
17
+ if (!group.isStretchy) {
18
+ accentNode.setAttribute("stretchy", "false")
19
+ }
20
+
21
+ const node = new mathMLTree.MathNode((group.label === "\\c" ? "munder" : "mover"),
22
+ [mml.buildGroup(group.base, style), accentNode]
23
+ );
24
+
25
+ return node;
26
+ };
27
+
28
+ const NON_STRETCHY_ACCENT_REGEX = new RegExp(
29
+ [
30
+ "\\acute",
31
+ "\\grave",
32
+ "\\ddot",
33
+ "\\dddot",
34
+ "\\ddddot",
35
+ "\\tilde",
36
+ "\\bar",
37
+ "\\breve",
38
+ "\\check",
39
+ "\\hat",
40
+ "\\vec",
41
+ "\\dot",
42
+ "\\mathring"
43
+ ]
44
+ .map((accent) => `\\${accent}`)
45
+ .join("|")
46
+ );
47
+
48
+ // Accents
49
+ defineFunction({
50
+ type: "accent",
51
+ names: [
52
+ "\\acute",
53
+ "\\grave",
54
+ "\\ddot",
55
+ "\\dddot",
56
+ "\\ddddot",
57
+ "\\tilde",
58
+ "\\bar",
59
+ "\\breve",
60
+ "\\check",
61
+ "\\hat",
62
+ "\\vec",
63
+ "\\dot",
64
+ "\\mathring",
65
+ "\\overparen",
66
+ "\\widecheck",
67
+ "\\widehat",
68
+ "\\wideparen",
69
+ "\\widetilde",
70
+ "\\overrightarrow",
71
+ "\\overleftarrow",
72
+ "\\Overrightarrow",
73
+ "\\overleftrightarrow",
74
+ "\\overgroup",
75
+ "\\overleftharpoon",
76
+ "\\overrightharpoon"
77
+ ],
78
+ props: {
79
+ numArgs: 1
80
+ },
81
+ handler: (context, args) => {
82
+ const base = normalizeArgument(args[0]);
83
+
84
+ const isStretchy = !NON_STRETCHY_ACCENT_REGEX.test(context.funcName);
85
+
86
+ return {
87
+ type: "accent",
88
+ mode: context.parser.mode,
89
+ label: context.funcName,
90
+ isStretchy: isStretchy,
91
+ base: base
92
+ };
93
+ },
94
+ mathmlBuilder
95
+ });
96
+
97
+ // Text-mode accents
98
+ defineFunction({
99
+ type: "accent",
100
+ names: ["\\'", "\\`", "\\^", "\\~", "\\=", "\\c", "\\u", "\\.", '\\"', "\\r", "\\H", "\\v"],
101
+ props: {
102
+ numArgs: 1,
103
+ allowedInText: true,
104
+ allowedInMath: true,
105
+ argTypes: ["primitive"]
106
+ },
107
+ handler: (context, args) => {
108
+ const base = normalizeArgument(args[0]);
109
+ const mode = context.parser.mode;
110
+
111
+ if (mode === "math" && context.parser.settings.strict) {
112
+ // LaTeX only writes a warning. It doesn't stop. We'll issue the same warning.
113
+ // eslint-disable-next-line no-console
114
+ console.log(`Temml parse error: Command ${context.funcName} is invalid in math mode.`)
115
+ }
116
+
117
+ return {
118
+ type: "accent",
119
+ mode: mode,
120
+ label: context.funcName,
121
+ isStretchy: false,
122
+ isShifty: true,
123
+ base: base
124
+ };
125
+ },
126
+ mathmlBuilder
127
+ });
@@ -0,0 +1,38 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import stretchy from "../stretchy";
4
+
5
+ import * as mml from "../buildMathML";
6
+
7
+ defineFunction({
8
+ type: "accentUnder",
9
+ names: [
10
+ "\\underleftarrow",
11
+ "\\underrightarrow",
12
+ "\\underleftrightarrow",
13
+ "\\undergroup",
14
+ "\\underparen",
15
+ "\\utilde"
16
+ ],
17
+ props: {
18
+ numArgs: 1
19
+ },
20
+ handler: ({ parser, funcName }, args) => {
21
+ const base = args[0];
22
+ return {
23
+ type: "accentUnder",
24
+ mode: parser.mode,
25
+ label: funcName,
26
+ base: base
27
+ };
28
+ },
29
+ mathmlBuilder: (group, style) => {
30
+ const accentNode = stretchy.mathMLnode(group.label);
31
+ accentNode.style["math-depth"] = 0
32
+ const node = new mathMLTree.MathNode("munder", [
33
+ mml.buildGroup(group.base, style),
34
+ accentNode
35
+ ]);
36
+ return node;
37
+ }
38
+ });
@@ -0,0 +1,204 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import stretchy from "../stretchy";
4
+ import { emScale } from "../units";
5
+ import * as mml from "../buildMathML";
6
+
7
+ // Helper functions
8
+
9
+ const padding = width => {
10
+ const node = new mathMLTree.MathNode("mspace")
11
+ node.setAttribute("width", width + "em")
12
+ return node
13
+ }
14
+
15
+ const paddedNode = (group, lspace = 0.3, rspace = 0) => {
16
+ if (group == null && rspace === 0) { return padding(lspace) }
17
+ const row = group ? [group] : [];
18
+ if (lspace !== 0) { row.unshift(padding(lspace)) }
19
+ if (rspace > 0) { row.push(padding(rspace)) }
20
+ return new mathMLTree.MathNode("mrow", row)
21
+ };
22
+
23
+ const labelSize = (size, scriptLevel) => (size / emScale(scriptLevel)).toFixed(4)
24
+
25
+ const munderoverNode = (name, body, below, style) => {
26
+ const arrowNode = stretchy.mathMLnode(name);
27
+ // Is this the short part of a mhchem equilibrium arrow?
28
+ const isEq = name.slice(1, 3) === "eq"
29
+ const minWidth = name.charAt(1) === "x"
30
+ ? "1.75" // mathtools extensible arrows are 1.75em long
31
+ : name.slice(2, 4) === "cd"
32
+ ? "3.0" // cd package arrows
33
+ : isEq
34
+ ? "1.0" // The shorter harpoon of a mhchem equilibrium arrow
35
+ : "2.0"; // other mhchem arrows
36
+ arrowNode.setAttribute("minsize", String(minWidth) + "em");
37
+ arrowNode.setAttribute("lspace", "0")
38
+ arrowNode.setAttribute("rspace", (isEq ? "0.5em" : "0"))
39
+
40
+ // <munderover> upper and lower labels are set to scriptlevel by MathML
41
+ // So we have to adjust our dimensions accordingly.
42
+ const labelStyle = style.withLevel(style.level < 2 ? 2 : 3)
43
+ const emptyLabelWidth = labelSize(minWidth, labelStyle.level)
44
+ const lspace = labelSize((isEq ? 0 : 0.3), labelStyle.level)
45
+ const rspace = labelSize((isEq ? 0 : 0.3), labelStyle.level)
46
+
47
+ const upperNode = (body && body.body &&
48
+ // \hphantom visible content
49
+ (body.body.body || body.body.length > 0))
50
+ ? paddedNode(mml.buildGroup(body, labelStyle), lspace, rspace)
51
+ // Since Firefox does not recognize minsize set on the arrow,
52
+ // create an upper node w/correct width.
53
+ : paddedNode(null, emptyLabelWidth, 0)
54
+ const lowerNode = (below && below.body &&
55
+ (below.body.body || below.body.length > 0))
56
+ ? paddedNode(mml.buildGroup(below, labelStyle), lspace, rspace)
57
+ : paddedNode(null, emptyLabelWidth, 0)
58
+ const node = new mathMLTree.MathNode("munderover", [arrowNode, lowerNode, upperNode]);
59
+ if (minWidth === "3.0") { node.style.height = "1em" }
60
+ return node
61
+ }
62
+
63
+ // Stretchy arrows with an optional argument
64
+ defineFunction({
65
+ type: "xArrow",
66
+ names: [
67
+ "\\xleftarrow",
68
+ "\\xrightarrow",
69
+ "\\xLeftarrow",
70
+ "\\xRightarrow",
71
+ "\\xleftrightarrow",
72
+ "\\xLeftrightarrow",
73
+ "\\xhookleftarrow",
74
+ "\\xhookrightarrow",
75
+ "\\xmapsto",
76
+ "\\xrightharpoondown",
77
+ "\\xrightharpoonup",
78
+ "\\xleftharpoondown",
79
+ "\\xleftharpoonup",
80
+ "\\xlongequal",
81
+ "\\xtwoheadrightarrow",
82
+ "\\xtwoheadleftarrow",
83
+ // The next 5 functions are here only to support mhchem
84
+ "\\yields",
85
+ "\\yieldsLeft",
86
+ "\\mesomerism",
87
+ "\\longrightharpoonup",
88
+ "\\longleftharpoondown",
89
+ // The next 3 functions are here only to support the {CD} environment.
90
+ "\\\\cdrightarrow",
91
+ "\\\\cdleftarrow",
92
+ "\\\\cdlongequal"
93
+ ],
94
+ props: {
95
+ numArgs: 1,
96
+ numOptionalArgs: 1
97
+ },
98
+ handler({ parser, funcName }, args, optArgs) {
99
+ return {
100
+ type: "xArrow",
101
+ mode: parser.mode,
102
+ name: funcName,
103
+ body: args[0],
104
+ below: optArgs[0]
105
+ };
106
+ },
107
+ mathmlBuilder(group, style) {
108
+ // Build the arrow and its labels.
109
+ const node = munderoverNode(group.name, group.body, group.below, style)
110
+ // Create operator spacing for a relation.
111
+ const row = [node]
112
+ row.unshift(padding(0.2778))
113
+ row.push(padding(0.2778))
114
+ return new mathMLTree.MathNode("mrow", row)
115
+ }
116
+ });
117
+
118
+ const arrowComponent = {
119
+ "\\xtofrom": ["\\xrightarrow", "\\xleftarrow"],
120
+ "\\xleftrightharpoons": ["\\xleftharpoonup", "\\xrightharpoondown"],
121
+ "\\xrightleftharpoons": ["\\xrightharpoonup", "\\xleftharpoondown"],
122
+ "\\yieldsLeftRight": ["\\yields", "\\yieldsLeft"],
123
+ // The next three all get the same harpoon glyphs. Only the lengths and paddings differ.
124
+ "\\equilibrium": ["\\longrightharpoonup", "\\longleftharpoondown"],
125
+ "\\equilibriumRight": ["\\longrightharpoonup", "\\eqleftharpoondown"],
126
+ "\\equilibriumLeft": ["\\eqrightharpoonup", "\\longleftharpoondown"]
127
+ }
128
+
129
+ // Browsers are not good at stretching a glyph that contains a pair of stacked arrows such as ⇄.
130
+ // So we stack a pair of single arrows.
131
+ defineFunction({
132
+ type: "stackedArrow",
133
+ names: [
134
+ "\\xtofrom", // expfeil
135
+ "\\xleftrightharpoons", // mathtools
136
+ "\\xrightleftharpoons", // mathtools
137
+ "\\yieldsLeftRight", // mhchem
138
+ "\\equilibrium", // mhchem
139
+ "\\equilibriumRight",
140
+ "\\equilibriumLeft"
141
+ ],
142
+ props: {
143
+ numArgs: 1,
144
+ numOptionalArgs: 1
145
+ },
146
+ handler({ parser, funcName }, args, optArgs) {
147
+ const lowerArrowBody = args[0]
148
+ ? {
149
+ type: "hphantom",
150
+ mode: parser.mode,
151
+ body: args[0]
152
+ }
153
+ : null;
154
+ const upperArrowBelow = optArgs[0]
155
+ ? {
156
+ type: "hphantom",
157
+ mode: parser.mode,
158
+ body: optArgs[0]
159
+ }
160
+ : null;
161
+ return {
162
+ type: "stackedArrow",
163
+ mode: parser.mode,
164
+ name: funcName,
165
+ body: args[0],
166
+ upperArrowBelow,
167
+ lowerArrowBody,
168
+ below: optArgs[0]
169
+ };
170
+ },
171
+ mathmlBuilder(group, style) {
172
+ const topLabel = arrowComponent[group.name][0]
173
+ const botLabel = arrowComponent[group.name][1]
174
+ const topArrow = munderoverNode(topLabel, group.body, group.upperArrowBelow, style)
175
+ const botArrow = munderoverNode(botLabel, group.lowerArrowBody, group.below, style)
176
+ let wrapper
177
+
178
+ const raiseNode = new mathMLTree.MathNode("mpadded", [topArrow])
179
+ raiseNode.setAttribute("voffset", "0.3em")
180
+ raiseNode.setAttribute("height", "+0.3em")
181
+ raiseNode.setAttribute("depth", "-0.3em")
182
+ // One of the arrows is given ~zero width. so the other has the same horzontal alignment.
183
+ if (group.name === "\\equilibriumLeft") {
184
+ const botNode = new mathMLTree.MathNode("mpadded", [botArrow])
185
+ botNode.setAttribute("width", "0.5em")
186
+ wrapper = new mathMLTree.MathNode(
187
+ "mpadded",
188
+ [padding(0.2778), botNode, raiseNode, padding(0.2778)]
189
+ )
190
+ } else {
191
+ raiseNode.setAttribute("width", (group.name === "\\equilibriumRight" ? "0.5em" : "0"))
192
+ wrapper = new mathMLTree.MathNode(
193
+ "mpadded",
194
+ [padding(0.2778), raiseNode, botArrow, padding(0.2778)]
195
+ )
196
+ }
197
+
198
+ wrapper.setAttribute("voffset", "-0.18em")
199
+ wrapper.setAttribute("height", "-0.18em")
200
+ wrapper.setAttribute("depth", "+0.18em")
201
+ return wrapper
202
+ }
203
+ });
204
+
@@ -0,0 +1,36 @@
1
+ import defineFunction from "../defineFunction";
2
+ import mathMLTree from "../mathMLTree";
3
+ import * as mml from "../buildMathML";
4
+
5
+ defineFunction({
6
+ type: "cancelto",
7
+ names: ["\\cancelto"],
8
+ props: {
9
+ numArgs: 2
10
+ },
11
+ handler({ parser }, args) {
12
+ return {
13
+ type: "cancelto",
14
+ mode: parser.mode,
15
+ value: args[0],
16
+ expression: args[1]
17
+ };
18
+ },
19
+ mathmlBuilder(group, style) {
20
+ const value = new mathMLTree.MathNode(
21
+ "mpadded",
22
+ [mml.buildGroup(group.value, style)]
23
+ )
24
+ value.setAttribute("depth", `-0.1em`)
25
+ value.setAttribute("height", `+0.1em`)
26
+ value.setAttribute("voffset", `0.1em`)
27
+
28
+ const expression = new mathMLTree.MathNode(
29
+ "menclose",
30
+ [mml.buildGroup(group.expression, style)]
31
+ )
32
+ expression.setAttribute("notation", `updiagonalarrow`)
33
+
34
+ return new mathMLTree.MathNode("msup", [expression, value])
35
+ }
36
+ })
@@ -0,0 +1,33 @@
1
+ import defineFunction from "../defineFunction"
2
+ import ParseError from "../ParseError"
3
+ import { assertNodeType } from "../parseNode"
4
+
5
+ // \@char is an internal function that takes a grouped decimal argument like
6
+ // {123} and converts into symbol with code 123. It is used by the *macro*
7
+ // \char defined in macros.js.
8
+ defineFunction({
9
+ type: "textord",
10
+ names: ["\\@char"],
11
+ props: {
12
+ numArgs: 1,
13
+ allowedInText: true
14
+ },
15
+ handler({ parser, token }, args) {
16
+ const arg = assertNodeType(args[0], "ordgroup")
17
+ const group = arg.body
18
+ let number = ""
19
+ for (let i = 0; i < group.length; i++) {
20
+ const node = assertNodeType(group[i], "textord")
21
+ number += node.text
22
+ }
23
+ const code = parseInt(number)
24
+ if (isNaN(code)) {
25
+ throw new ParseError(`\\@char has non-numeric argument ${number}`, token)
26
+ }
27
+ return {
28
+ type: "textord",
29
+ mode: parser.mode,
30
+ text: String.fromCodePoint(code)
31
+ }
32
+ }
33
+ })