temml 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/contrib/auto-render/README.md +89 -0
- package/contrib/auto-render/auto-render.js +128 -0
- package/contrib/auto-render/dist/auto-render.js +217 -0
- package/contrib/auto-render/dist/auto-render.min.js +1 -0
- package/contrib/auto-render/splitAtDelimiters.js +84 -0
- package/contrib/auto-render/test/auto-render-spec.js +234 -0
- package/contrib/auto-render/test/auto-render.js +217 -0
- package/contrib/auto-render/test/test_page.html +59 -0
- package/contrib/mhchem/README.md +26 -0
- package/contrib/mhchem/mhchem.js +1705 -0
- package/contrib/mhchem/mhchem.min.js +1 -0
- package/contrib/physics/README.md +20 -0
- package/contrib/physics/physics.js +131 -0
- package/contrib/texvc/README.md +23 -0
- package/contrib/texvc/texvc.js +61 -0
- package/dist/Temml-Asana.css +201 -0
- package/dist/Temml-Latin-Modern.css +216 -0
- package/dist/Temml-Libertinus.css +214 -0
- package/dist/Temml-Local.css +194 -0
- package/dist/Temml-STIX2.css +203 -0
- package/dist/Temml.woff2 +0 -0
- package/dist/temml.cjs +13122 -0
- package/dist/temml.js +11225 -0
- package/dist/temml.min.js +1 -0
- package/dist/temml.mjs +13120 -0
- package/dist/temmlPostProcess.js +70 -0
- package/package.json +34 -0
- package/src/Lexer.js +121 -0
- package/src/MacroExpander.js +437 -0
- package/src/Namespace.js +107 -0
- package/src/ParseError.js +64 -0
- package/src/Parser.js +977 -0
- package/src/Settings.js +49 -0
- package/src/SourceLocation.js +29 -0
- package/src/Style.js +144 -0
- package/src/Token.js +40 -0
- package/src/buildMathML.js +235 -0
- package/src/constants.js +25 -0
- package/src/defineEnvironment.js +25 -0
- package/src/defineFunction.js +69 -0
- package/src/defineMacro.js +11 -0
- package/src/domTree.js +185 -0
- package/src/environments/array.js +791 -0
- package/src/environments/cd.js +252 -0
- package/src/environments.js +8 -0
- package/src/functions/accent.js +127 -0
- package/src/functions/accentunder.js +38 -0
- package/src/functions/arrow.js +204 -0
- package/src/functions/cancelto.js +36 -0
- package/src/functions/char.js +33 -0
- package/src/functions/color.js +253 -0
- package/src/functions/cr.js +46 -0
- package/src/functions/def.js +259 -0
- package/src/functions/delimsizing.js +304 -0
- package/src/functions/enclose.js +193 -0
- package/src/functions/envTag.js +38 -0
- package/src/functions/environment.js +59 -0
- package/src/functions/font.js +123 -0
- package/src/functions/genfrac.js +333 -0
- package/src/functions/hbox.js +29 -0
- package/src/functions/horizBrace.js +32 -0
- package/src/functions/href.js +90 -0
- package/src/functions/html.js +95 -0
- package/src/functions/includegraphics.js +131 -0
- package/src/functions/kern.js +75 -0
- package/src/functions/label.js +29 -0
- package/src/functions/lap.js +75 -0
- package/src/functions/math.js +40 -0
- package/src/functions/mathchoice.js +41 -0
- package/src/functions/mclass.js +201 -0
- package/src/functions/multiscript.js +91 -0
- package/src/functions/not.js +46 -0
- package/src/functions/op.js +338 -0
- package/src/functions/operatorname.js +139 -0
- package/src/functions/ordgroup.js +9 -0
- package/src/functions/phantom.js +73 -0
- package/src/functions/pmb.js +31 -0
- package/src/functions/raise.js +68 -0
- package/src/functions/ref.js +28 -0
- package/src/functions/relax.js +16 -0
- package/src/functions/rule.js +52 -0
- package/src/functions/sizing.js +64 -0
- package/src/functions/smash.js +66 -0
- package/src/functions/sqrt.js +31 -0
- package/src/functions/styling.js +58 -0
- package/src/functions/supsub.js +135 -0
- package/src/functions/symbolsOp.js +53 -0
- package/src/functions/symbolsOrd.js +102 -0
- package/src/functions/symbolsSpacing.js +53 -0
- package/src/functions/tag.js +8 -0
- package/src/functions/text.js +75 -0
- package/src/functions/tip.js +63 -0
- package/src/functions/toggle.js +13 -0
- package/src/functions/verb.js +33 -0
- package/src/functions.js +57 -0
- package/src/linebreaking.js +159 -0
- package/src/macros.js +708 -0
- package/src/mathMLTree.js +175 -0
- package/src/parseNode.js +42 -0
- package/src/parseTree.js +40 -0
- package/src/postProcess.js +57 -0
- package/src/replace.js +225 -0
- package/src/stretchy.js +66 -0
- package/src/svg.js +110 -0
- package/src/symbols.js +972 -0
- package/src/tree.js +50 -0
- package/src/unicodeAccents.js +16 -0
- package/src/unicodeScripts.js +119 -0
- package/src/unicodeSupOrSub.js +108 -0
- package/src/unicodeSymbolBuilder.js +31 -0
- package/src/unicodeSymbols.js +320 -0
- package/src/units.js +109 -0
- package/src/utils.js +109 -0
- package/src/variant.js +103 -0
- 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,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
|
+
})
|