temml 0.13.1 → 0.13.3
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.
- package/README.md +3 -4
- package/contrib/mhchem/mhchem.min.js +1 -1
- package/dist/Temml-Asana.css +16 -13
- package/dist/Temml-Latin-Modern.css +16 -13
- package/dist/Temml-Libertinus.css +16 -13
- package/dist/Temml-Local.css +16 -13
- package/dist/Temml-NotoSans.css +16 -13
- package/dist/Temml-STIX2.css +16 -13
- package/dist/temml.cjs +629 -500
- package/dist/temml.js +502 -373
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +629 -500
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +6 -4
- package/src/MacroExpander.js +2 -1
- package/src/Parser.js +5 -4
- package/src/Settings.js +1 -0
- package/src/environments/array.js +2 -2
- package/src/functions/bordermatrix.js +3 -5
- package/src/functions/cancelto.js +2 -0
- package/src/functions/color.js +1 -1
- package/src/functions/{delimsizing.js → delimiter.js} +139 -52
- package/src/functions/enclose.js +21 -2
- package/src/functions/font.js +14 -4
- package/src/functions/genfrac.js +32 -45
- package/src/functions/mclass.js +10 -2
- package/src/functions/op.js +1 -1
- package/src/functions/operatorname.js +9 -1
- package/src/functions/phantom.js +1 -1
- package/src/functions/symbolsOrd.js +2 -2
- package/src/functions.js +1 -1
- package/src/linebreaking.js +3 -10
- package/src/macros.js +1 -0
- package/src/parseNode.js +1 -1
- package/src/parseTree.js +20 -4
- package/src/postProcess.js +1 -1
- package/src/symbols.js +2 -3
package/dist/temmlPostProcess.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "temml",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.3",
|
|
4
4
|
"description": "TeX to MathML conversion in JavaScript.",
|
|
5
5
|
"main": "dist/temml.js",
|
|
6
6
|
"engines": {
|
|
@@ -33,8 +33,9 @@
|
|
|
33
33
|
"eslint": "^9.11.1",
|
|
34
34
|
"esm": "^3.2.25",
|
|
35
35
|
"globals": "^15.9.0",
|
|
36
|
-
"rollup": "^4.
|
|
37
|
-
"terser": "^5.34.0"
|
|
36
|
+
"rollup": "^4.59.0",
|
|
37
|
+
"terser": "^5.34.0",
|
|
38
|
+
"ws": "^8.20.0"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
40
41
|
"lint": "eslint temml.js src",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"minify": "terser test/temml.js -o site/assets/temml.min.js -c -m && terser contrib/mhchem/mhchem.js -o site/assets/mhchem.min.js -c -m",
|
|
45
46
|
"build": "rollup --config ./utils/rollupConfig.mjs && yarn minify && node utils/insertPlugins.js",
|
|
46
47
|
"docs": "node utils/buildDocs.js",
|
|
47
|
-
"dist": "yarn build && node ./utils/copyfiles.js"
|
|
48
|
+
"dist": "yarn build && node ./utils/copyfiles.js",
|
|
49
|
+
"serve": "node utils/server.cjs site/docs/en/mathml-status.html 8000"
|
|
48
50
|
}
|
|
49
51
|
}
|
package/src/MacroExpander.js
CHANGED
|
@@ -9,6 +9,7 @@ import Lexer from "./Lexer";
|
|
|
9
9
|
import { Token } from "./Token";
|
|
10
10
|
|
|
11
11
|
import ParseError from "./ParseError";
|
|
12
|
+
import SourceLocation from "./SourceLocation";
|
|
12
13
|
import Namespace from "./Namespace";
|
|
13
14
|
import macros from "./macros";
|
|
14
15
|
|
|
@@ -118,7 +119,7 @@ export default class MacroExpander {
|
|
|
118
119
|
this.pushToken(new Token("EOF", end.loc));
|
|
119
120
|
|
|
120
121
|
this.pushTokens(tokens);
|
|
121
|
-
return
|
|
122
|
+
return new Token("", SourceLocation.range(start, end));
|
|
122
123
|
}
|
|
123
124
|
|
|
124
125
|
/**
|
package/src/Parser.js
CHANGED
|
@@ -9,7 +9,7 @@ import { uSubsAndSups, unicodeSubRegEx } from "./unicodeSupOrSub"
|
|
|
9
9
|
import { asciiFromScript } from "./asciiFromScript"
|
|
10
10
|
import SourceLocation from "./SourceLocation";
|
|
11
11
|
import { Token } from "./Token";
|
|
12
|
-
import { isDelimiter } from "./functions/
|
|
12
|
+
import { isDelimiter } from "./functions/delimiter"
|
|
13
13
|
|
|
14
14
|
// Pre-evaluate both modules as unicodeSymbols require String.normalize()
|
|
15
15
|
import unicodeAccents from /*preval*/ "./unicodeAccents";
|
|
@@ -175,7 +175,7 @@ export default class Parser {
|
|
|
175
175
|
* Parses an "expression", which is a list of atoms.
|
|
176
176
|
*
|
|
177
177
|
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
|
|
178
|
-
* happens when functions have higher precedence
|
|
178
|
+
* happens when functions have higher precedence than infix
|
|
179
179
|
* nodes in implicit parses.
|
|
180
180
|
*
|
|
181
181
|
* `breakOnTokenText`: The text of the token that the expression should end
|
|
@@ -783,7 +783,7 @@ export default class Parser {
|
|
|
783
783
|
) {
|
|
784
784
|
const firstToken = this.fetch();
|
|
785
785
|
const text = firstToken.text;
|
|
786
|
-
|
|
786
|
+
if (name === "argument to '\\left'") { return this.parseSymbol() }
|
|
787
787
|
let result;
|
|
788
788
|
// Try to parse an open brace or \begingroup
|
|
789
789
|
if (text === "{" || text === "\\begingroup" || text === "\\toggle") {
|
|
@@ -930,7 +930,8 @@ export default class Parser {
|
|
|
930
930
|
let symbol;
|
|
931
931
|
if (symbols[this.mode][text]) {
|
|
932
932
|
let group = symbols[this.mode][text].group;
|
|
933
|
-
if (group === "bin" &&
|
|
933
|
+
if (group === "bin" &&
|
|
934
|
+
(binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) {
|
|
934
935
|
// Change from a binary operator to a unary (prefix) operator
|
|
935
936
|
group = "open"
|
|
936
937
|
}
|
package/src/Settings.js
CHANGED
|
@@ -358,9 +358,9 @@ const mathmlBuilder = function(group, style) {
|
|
|
358
358
|
}
|
|
359
359
|
if (mustSquashRow) {
|
|
360
360
|
// All the cell contents are \hphantom. Squash the cell.
|
|
361
|
+
// TODO: Remove the next line when Firefox no longer needs it.
|
|
362
|
+
mtr.classes.push("ff-squash") // necessary in Firefox only.
|
|
361
363
|
for (let j = 0; j < mtr.children.length; j++) {
|
|
362
|
-
mtr.children[j].style.display = "block" // necessary in Firefox only
|
|
363
|
-
mtr.children[j].style.height = "0" // necessary in Firefox only
|
|
364
364
|
mtr.children[j].style.paddingTop = "0"
|
|
365
365
|
mtr.children[j].style.paddingBottom = "0"
|
|
366
366
|
}
|
|
@@ -14,13 +14,11 @@ defineFunction({
|
|
|
14
14
|
},
|
|
15
15
|
handler: ({ parser, funcName }, args, optArgs) => {
|
|
16
16
|
// Find out if the author has defined custom delimiters
|
|
17
|
-
let delimiters = ["(", ")"]
|
|
17
|
+
let delimiters = ["(", ")"]; // default
|
|
18
18
|
if (funcName === "\\bordermatrix" && optArgs[0] && optArgs[0].body) {
|
|
19
19
|
const body = optArgs[0].body
|
|
20
|
-
if (body.length ===
|
|
21
|
-
|
|
22
|
-
delimiters = [body[0].text, body[1].text]
|
|
23
|
-
}
|
|
20
|
+
if (body.length === 1 && body[0].type === "delimiter") {
|
|
21
|
+
delimiters = [body[0].left, body[0].right]
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
24
|
// consume the opening brace
|
|
@@ -30,6 +30,7 @@ defineFunction({
|
|
|
30
30
|
// That way, the arrow will be an overlay on the content.
|
|
31
31
|
const phantom = new mathMLTree.MathNode("mphantom", [mml.buildGroup(group.body, style)])
|
|
32
32
|
const arrow = new mathMLTree.MathNode("mrow", [phantom], ["tml-cancelto"])
|
|
33
|
+
arrow.style.color = style.color
|
|
33
34
|
if (group.isCharacterBox && smalls.indexOf(group.body.body[0].text) > -1) {
|
|
34
35
|
arrow.style.left = "0.1em"
|
|
35
36
|
arrow.style.width = "90%"
|
|
@@ -61,6 +62,7 @@ defineFunction({
|
|
|
61
62
|
dummyNode = new mathMLTree.MathNode("mphantom", [zeroWidthNode]) // Hide it.
|
|
62
63
|
}
|
|
63
64
|
const toNode = mml.buildGroup(group.to, style)
|
|
65
|
+
toNode.style.color = style.color
|
|
64
66
|
const zeroWidthToNode = new mathMLTree.MathNode("mpadded", [toNode])
|
|
65
67
|
if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) {
|
|
66
68
|
const w = new mathMLTree.MathNode("mspace", [])
|
package/src/functions/color.js
CHANGED
|
@@ -18,7 +18,7 @@ const toHex = num => {
|
|
|
18
18
|
|
|
19
19
|
// Colors from Tables 4.1 and 4.2 of the xcolor package.
|
|
20
20
|
// Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx.
|
|
21
|
-
// Table 4.2 (
|
|
21
|
+
// Table 4.2 (Capitalized) values were sampled, because Chroma contains a unreliable
|
|
22
22
|
// conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274.
|
|
23
23
|
const xcolors = JSON.parse(`{
|
|
24
24
|
"Apricot": "#ffb484",
|
|
@@ -26,7 +26,41 @@ export const delimiterSizes = {
|
|
|
26
26
|
"\\Bigg": { mclass: "mord", size: 4 }
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export const
|
|
29
|
+
export const leftToRight = {
|
|
30
|
+
"(": ")",
|
|
31
|
+
"\\lparen": "\\rparen",
|
|
32
|
+
"[": "]",
|
|
33
|
+
"\\lbrack": "\\rbrack",
|
|
34
|
+
"\\{": "\\}",
|
|
35
|
+
"\\lbrace": "\\rbrace",
|
|
36
|
+
"⦇": "⦈",
|
|
37
|
+
"\\llparenthesis": "\\rrparenthesis",
|
|
38
|
+
"\\lfloor": "\\rfloor",
|
|
39
|
+
"\u230a": "\u230b",
|
|
40
|
+
"\\lceil": "\\rceil",
|
|
41
|
+
"\u2308": "\u2309",
|
|
42
|
+
"\\langle": "\\rangle",
|
|
43
|
+
"\u27e8": "\u27e9",
|
|
44
|
+
"\\lAngle": "\\rAngle",
|
|
45
|
+
"\u27ea": "\u27eb",
|
|
46
|
+
"\\llangle": "\\rrangle",
|
|
47
|
+
"⦉": "⦊",
|
|
48
|
+
"\\lvert": "\\rvert",
|
|
49
|
+
"\\lVert": "\\rVert",
|
|
50
|
+
"\\lgroup": "\\rgroup",
|
|
51
|
+
"\u27ee": "\u27ef",
|
|
52
|
+
"\\lmoustache": "\\rmoustache",
|
|
53
|
+
"\u23b0": "\u23b1",
|
|
54
|
+
"\\llbracket": "\\rrbracket",
|
|
55
|
+
"\u27e6": "\u27e7",
|
|
56
|
+
"\\lBrace": "\\rBrace",
|
|
57
|
+
"\u2983": "\u2984"
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const leftDelimiterNames = new Set(Object.keys(leftToRight));
|
|
61
|
+
export const rightDelimiterNames = new Set(Object.values(leftToRight));
|
|
62
|
+
|
|
63
|
+
const delimiters = new Set([
|
|
30
64
|
"(",
|
|
31
65
|
"\\lparen",
|
|
32
66
|
")",
|
|
@@ -82,7 +116,7 @@ export const delimiters = [
|
|
|
82
116
|
"\\llbracket",
|
|
83
117
|
"\\rrbracket",
|
|
84
118
|
"\u27e6",
|
|
85
|
-
"\
|
|
119
|
+
"\u27e7",
|
|
86
120
|
"\\lBrace",
|
|
87
121
|
"\\rBrace",
|
|
88
122
|
"\u2983",
|
|
@@ -101,12 +135,12 @@ export const delimiters = [
|
|
|
101
135
|
"\\updownarrow",
|
|
102
136
|
"\\Updownarrow",
|
|
103
137
|
"."
|
|
104
|
-
];
|
|
138
|
+
]);
|
|
105
139
|
|
|
106
140
|
// Export isDelimiter for benefit of parser.
|
|
107
|
-
const dels = ["}", "\\left", "\\middle", "\\right"]
|
|
141
|
+
const dels = new Set(["}", "\\left", "\\middle", "\\right"]);
|
|
108
142
|
export const isDelimiter = str => str.length > 0 &&
|
|
109
|
-
(delimiters.
|
|
143
|
+
(delimiters.has(str) || delimiterSizes[str] || dels.has(str));
|
|
110
144
|
|
|
111
145
|
// Metrics of the different sizes. Found by looking at TeX's output of
|
|
112
146
|
// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
|
|
@@ -119,11 +153,11 @@ function checkDelimiter(delim, context) {
|
|
|
119
153
|
delim = delim.body[0]; // Unwrap the braces
|
|
120
154
|
}
|
|
121
155
|
const symDelim = checkSymbolNodeType(delim)
|
|
122
|
-
if (symDelim && delimiters.
|
|
156
|
+
if (symDelim && delimiters.has(symDelim.text)) {
|
|
123
157
|
// If a character is not in the MathML operator dictionary, it will not stretch.
|
|
124
158
|
// Replace such characters w/characters that will stretch.
|
|
125
|
-
if (
|
|
126
|
-
if (
|
|
159
|
+
if (symDelim.text === "<" || symDelim.text === "\\lt") { symDelim.text = "⟨" }
|
|
160
|
+
if (symDelim.text === ">" || symDelim.text === "\\gt") { symDelim.text = "⟩" }
|
|
127
161
|
return symDelim;
|
|
128
162
|
} else if (symDelim) {
|
|
129
163
|
throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim);
|
|
@@ -133,7 +167,16 @@ function checkDelimiter(delim, context) {
|
|
|
133
167
|
}
|
|
134
168
|
|
|
135
169
|
// / \
|
|
136
|
-
const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"];
|
|
170
|
+
const needExplicitStretch = new Set(["\u002F", "\u005C", "\\backslash", "\u2216", "\\vert", "|"]);
|
|
171
|
+
|
|
172
|
+
const makeFenceMo = (delim, mode, form, isStretchy) => {
|
|
173
|
+
const text = delim === "." ? "" : delim;
|
|
174
|
+
const node = new mathMLTree.MathNode("mo", [mml.makeText(text, mode)]);
|
|
175
|
+
node.setAttribute("fence", "true");
|
|
176
|
+
node.setAttribute("form", form);
|
|
177
|
+
node.setAttribute("stretchy", isStretchy ? "true" : "false");
|
|
178
|
+
return node;
|
|
179
|
+
};
|
|
137
180
|
|
|
138
181
|
defineFunction({
|
|
139
182
|
type: "delimsizing",
|
|
@@ -184,9 +227,9 @@ defineFunction({
|
|
|
184
227
|
},
|
|
185
228
|
mathmlBuilder: (group) => {
|
|
186
229
|
const children = [];
|
|
230
|
+
const delim = group.delim === "." ? "" : group.delim;
|
|
187
231
|
|
|
188
|
-
|
|
189
|
-
children.push(mml.makeText(group.delim, group.mode));
|
|
232
|
+
children.push(mml.makeText(delim, group.mode));
|
|
190
233
|
|
|
191
234
|
const node = new mathMLTree.MathNode("mo", children);
|
|
192
235
|
|
|
@@ -199,7 +242,7 @@ defineFunction({
|
|
|
199
242
|
// defaults.
|
|
200
243
|
node.setAttribute("fence", "false");
|
|
201
244
|
}
|
|
202
|
-
if (needExplicitStretch.
|
|
245
|
+
if (needExplicitStretch.has(delim) || delim.indexOf("arrow") > -1) {
|
|
203
246
|
// We have to explicitly set stretchy to true.
|
|
204
247
|
node.setAttribute("stretchy", "true")
|
|
205
248
|
}
|
|
@@ -212,7 +255,7 @@ defineFunction({
|
|
|
212
255
|
|
|
213
256
|
function assertParsed(group) {
|
|
214
257
|
if (!group.body) {
|
|
215
|
-
throw new Error("Bug: The
|
|
258
|
+
throw new Error("Bug: The delim ParseNode wasn't fully parsed.");
|
|
216
259
|
}
|
|
217
260
|
}
|
|
218
261
|
|
|
@@ -243,17 +286,10 @@ defineFunction({
|
|
|
243
286
|
const delim = checkDelimiter(args[0], context);
|
|
244
287
|
|
|
245
288
|
const parser = context.parser;
|
|
246
|
-
// Parse out the implicit body
|
|
247
289
|
++parser.leftrightDepth;
|
|
248
|
-
|
|
249
|
-
let body = parser.parseExpression(false, null, true)
|
|
290
|
+
let body = parser.parseExpression(false, "\\right", true)
|
|
250
291
|
let nextToken = parser.fetch()
|
|
251
292
|
while (nextToken.text === "\\middle") {
|
|
252
|
-
// `\middle`, from the ε-TeX package, ends one group and starts another group.
|
|
253
|
-
// We had to parse this expression with `breakOnMiddle` enabled in order
|
|
254
|
-
// to get TeX-compliant parsing of \over.
|
|
255
|
-
// But we do not want, at this point, to end on \middle, so continue
|
|
256
|
-
// to parse until we fetch a `\right`.
|
|
257
293
|
parser.consume()
|
|
258
294
|
const middle = parser.fetch().text
|
|
259
295
|
if (!symbols.math[middle]) {
|
|
@@ -262,11 +298,10 @@ defineFunction({
|
|
|
262
298
|
checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" })
|
|
263
299
|
body.push({ type: "middle", mode: "math", delim: middle })
|
|
264
300
|
parser.consume()
|
|
265
|
-
body = body.concat(parser.parseExpression(false,
|
|
301
|
+
body = body.concat(parser.parseExpression(false, "\\right", true))
|
|
266
302
|
nextToken = parser.fetch()
|
|
267
303
|
}
|
|
268
304
|
--parser.leftrightDepth;
|
|
269
|
-
// Check the next token
|
|
270
305
|
parser.expect("\\right", false);
|
|
271
306
|
const right = assertNodeType(parser.parseFunction(), "leftright-right");
|
|
272
307
|
return {
|
|
@@ -274,39 +309,94 @@ defineFunction({
|
|
|
274
309
|
mode: parser.mode,
|
|
275
310
|
body,
|
|
276
311
|
left: delim.text,
|
|
277
|
-
right: right.delim
|
|
312
|
+
right: right.delim,
|
|
313
|
+
isStretchy: true
|
|
278
314
|
};
|
|
279
315
|
},
|
|
280
316
|
mathmlBuilder: (group, style) => {
|
|
281
317
|
assertParsed(group);
|
|
282
318
|
const inner = mml.buildExpression(group.body, style);
|
|
283
319
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (group.
|
|
289
|
-
|
|
320
|
+
const leftNode = makeFenceMo(group.left, group.mode, "prefix", true);
|
|
321
|
+
inner.unshift(leftNode);
|
|
322
|
+
|
|
323
|
+
const rightNode = makeFenceMo(group.right, group.mode, "postfix", true);
|
|
324
|
+
if (group.body.length > 0) {
|
|
325
|
+
const lastElement = group.body[group.body.length - 1];
|
|
326
|
+
if (lastElement.type === "color" && !lastElement.isTextColor) {
|
|
327
|
+
rightNode.setAttribute("mathcolor", lastElement.color);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
inner.push(rightNode);
|
|
331
|
+
|
|
332
|
+
return mml.makeRow(inner);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
defineFunction({
|
|
337
|
+
type: "delimiter",
|
|
338
|
+
names: Array.from(leftDelimiterNames),
|
|
339
|
+
props: {
|
|
340
|
+
numArgs: 0,
|
|
341
|
+
allowedInText: true,
|
|
342
|
+
allowedInMath: true,
|
|
343
|
+
allowedInArgument: true
|
|
344
|
+
},
|
|
345
|
+
handler: ({ parser, funcName, token }) => {
|
|
346
|
+
if (parser.mode === "text") {
|
|
347
|
+
return {
|
|
348
|
+
type: "textord",
|
|
349
|
+
mode: "text",
|
|
350
|
+
text: funcName,
|
|
351
|
+
loc: token.loc
|
|
352
|
+
}
|
|
353
|
+
} else if (!parser.settings.wrapDelimiterPairs) {
|
|
354
|
+
// Treat this token as an ordinary symbol.
|
|
355
|
+
return {
|
|
356
|
+
type: "atom",
|
|
357
|
+
mode: "math",
|
|
358
|
+
family: "open",
|
|
359
|
+
loc: token.loc,
|
|
360
|
+
text: funcName
|
|
361
|
+
};
|
|
290
362
|
}
|
|
291
|
-
|
|
363
|
+
// Otherwise, try to wrap a pair of delimiters with an <mrow>.
|
|
364
|
+
const rightDelim = leftToRight[funcName];
|
|
365
|
+
// Parse the inner expression, looking for the corresponding right delimiter.
|
|
366
|
+
const body = parser.parseExpression(false, rightDelim, false);
|
|
367
|
+
const nextToken = parser.fetch().text;
|
|
292
368
|
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
rightNode.setAttribute("stretchy", "true")
|
|
369
|
+
if (nextToken !== rightDelim) {
|
|
370
|
+
// We were unable to find a matching right delimiter.
|
|
371
|
+
// Throw control back to renderToMathMLTree.
|
|
372
|
+
// It will reparse the entire expression with wrapDelimiterPairs set to false.
|
|
373
|
+
throw new ParseError("Unmatched delimiter");
|
|
299
374
|
}
|
|
375
|
+
parser.consume();
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
type: "delimiter",
|
|
379
|
+
mode: parser.mode,
|
|
380
|
+
body,
|
|
381
|
+
left: funcName,
|
|
382
|
+
right: rightDelim
|
|
383
|
+
};
|
|
384
|
+
},
|
|
385
|
+
mathmlBuilder: (group, style) => {
|
|
386
|
+
assertParsed(group);
|
|
387
|
+
const inner = mml.buildExpression(group.body, style);
|
|
388
|
+
|
|
389
|
+
const leftNode = makeFenceMo(group.left, group.mode, "prefix", false);
|
|
390
|
+
inner.unshift(leftNode);
|
|
391
|
+
|
|
392
|
+
const rightNode = makeFenceMo(group.right, group.mode, "postfix", false);
|
|
300
393
|
if (group.body.length > 0) {
|
|
301
394
|
const lastElement = group.body[group.body.length - 1];
|
|
302
395
|
if (lastElement.type === "color" && !lastElement.isTextColor) {
|
|
303
|
-
// \color is a switch. If the last element is of type "color" then
|
|
304
|
-
// the user set the \color switch and left it on.
|
|
305
|
-
// A \right delimiter turns the switch off, but the delimiter itself gets the color.
|
|
306
396
|
rightNode.setAttribute("mathcolor", lastElement.color);
|
|
307
397
|
}
|
|
308
398
|
}
|
|
309
|
-
inner.push(rightNode)
|
|
399
|
+
inner.push(rightNode);
|
|
310
400
|
|
|
311
401
|
return mml.makeRow(inner);
|
|
312
402
|
}
|
|
@@ -331,20 +421,17 @@ defineFunction({
|
|
|
331
421
|
delim: delim.text
|
|
332
422
|
};
|
|
333
423
|
},
|
|
334
|
-
mathmlBuilder: (group
|
|
424
|
+
mathmlBuilder: (group) => {
|
|
335
425
|
const textNode = mml.makeText(group.delim, group.mode);
|
|
336
426
|
const middleNode = new mathMLTree.MathNode("mo", [textNode]);
|
|
337
|
-
middleNode.setAttribute("
|
|
338
|
-
|
|
339
|
-
|
|
427
|
+
middleNode.setAttribute("stretchy", "true")
|
|
428
|
+
middleNode.setAttribute("form", "infix")
|
|
429
|
+
if (textNode.text !== "/") {
|
|
430
|
+
// MathML gives 5/18em spacing to each <mo> element.
|
|
431
|
+
// \middle should get delimiter spacing instead.
|
|
432
|
+
middleNode.setAttribute("lspace", "0.05em");
|
|
433
|
+
middleNode.setAttribute("rspace", "0.05em");
|
|
340
434
|
}
|
|
341
|
-
// The next line is not semantically correct, but
|
|
342
|
-
// Chromium fails to stretch if it is not there.
|
|
343
|
-
middleNode.setAttribute("form", "prefix")
|
|
344
|
-
// MathML gives 5/18em spacing to each <mo> element.
|
|
345
|
-
// \middle should get delimiter spacing instead.
|
|
346
|
-
middleNode.setAttribute("lspace", "0.05em");
|
|
347
|
-
middleNode.setAttribute("rspace", "0.05em");
|
|
348
435
|
return middleNode;
|
|
349
436
|
}
|
|
350
437
|
});
|
package/src/functions/enclose.js
CHANGED
|
@@ -32,7 +32,7 @@ const mathmlBuilder = (group, style) => {
|
|
|
32
32
|
break
|
|
33
33
|
case "\\xcancel":
|
|
34
34
|
node.setAttribute("notation", "updiagonalstrike downdiagonalstrike")
|
|
35
|
-
node.
|
|
35
|
+
node.children.push(new mathMLTree.MathNode("mrow", [], ["tml-cancel", "tml-xcancel"]))
|
|
36
36
|
break
|
|
37
37
|
// cancelto is handled in cancelto.js
|
|
38
38
|
case "\\longdiv":
|
|
@@ -166,7 +166,7 @@ defineFunction({
|
|
|
166
166
|
|
|
167
167
|
defineFunction({
|
|
168
168
|
type: "enclose",
|
|
169
|
-
names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\
|
|
169
|
+
names: ["\\angl", "\\cancel", "\\bcancel", "\\xcancel", "\\overline",
|
|
170
170
|
"\\boxed", "\\longdiv", "\\phase"],
|
|
171
171
|
props: {
|
|
172
172
|
numArgs: 1
|
|
@@ -183,6 +183,25 @@ defineFunction({
|
|
|
183
183
|
mathmlBuilder
|
|
184
184
|
});
|
|
185
185
|
|
|
186
|
+
defineFunction({
|
|
187
|
+
type: "enclose",
|
|
188
|
+
names: ["\\sout"],
|
|
189
|
+
props: {
|
|
190
|
+
numArgs: 1,
|
|
191
|
+
allowedInText: true
|
|
192
|
+
},
|
|
193
|
+
handler({ parser, funcName }, args) {
|
|
194
|
+
const body = args[0];
|
|
195
|
+
return {
|
|
196
|
+
type: "enclose",
|
|
197
|
+
mode: parser.mode,
|
|
198
|
+
label: funcName,
|
|
199
|
+
body
|
|
200
|
+
};
|
|
201
|
+
},
|
|
202
|
+
mathmlBuilder
|
|
203
|
+
});
|
|
204
|
+
|
|
186
205
|
defineFunction({
|
|
187
206
|
type: "enclose",
|
|
188
207
|
names: ["\\underline"],
|
package/src/functions/font.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import defineFunction, { normalizeArgument } from "../defineFunction"
|
|
2
2
|
import * as mml from "../buildMathML"
|
|
3
3
|
import * as mathMLTree from "../mathMLTree"
|
|
4
|
+
import { variantChar } from "../replace"
|
|
5
|
+
|
|
6
|
+
const varNameFonts = ["mathrm", "mathit"];
|
|
4
7
|
|
|
5
8
|
const isLongVariableName = (group, font) => {
|
|
6
|
-
if (font
|
|
9
|
+
if (!varNameFonts.includes(font) || !group.body || group.body.type !== "ordgroup" ||
|
|
10
|
+
group.body.body.length === 1) {
|
|
7
11
|
return false
|
|
8
12
|
}
|
|
9
13
|
if (group.body.body[0].type !== "mathord") { return false }
|
|
@@ -29,8 +33,7 @@ const mathmlBuilder = (group, style) => {
|
|
|
29
33
|
}
|
|
30
34
|
// Check if it is possible to consolidate elements into a single <mi> element.
|
|
31
35
|
if (isLongVariableName(group, font)) {
|
|
32
|
-
// This is a \mathrm{…} group. It gets special treatment
|
|
33
|
-
// wraps <mi> elements with <mpadded>s to work around a Firefox bug.
|
|
36
|
+
// This is a \mathrm{…} or \mathit{…} group. It gets special treatment.
|
|
34
37
|
const mi = mathGroup.children[0].children[0].children
|
|
35
38
|
? mathGroup.children[0].children[0]
|
|
36
39
|
: mathGroup.children[0];
|
|
@@ -40,7 +43,14 @@ const mathmlBuilder = (group, style) => {
|
|
|
40
43
|
? mathGroup.children[i].children[0].children[0].text
|
|
41
44
|
: mathGroup.children[i].children[0].text
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
if (font === "mathit") {
|
|
47
|
+
// Long <mi> elements are normally rendered in upright font.
|
|
48
|
+
// To get italic, we need to convert each character to the corresponding italic character.
|
|
49
|
+
mi.children[0].text = mi.children[0].text.split("")
|
|
50
|
+
.map(c => variantChar(c, "italic")).join("")
|
|
51
|
+
return mi
|
|
52
|
+
}
|
|
53
|
+
// Otherwise, font is "mathrm". Wrap in a <mpadded> to prevent a Firefox spacing bug.
|
|
44
54
|
const mpadded = new mathMLTree.MathNode("mpadded", [mi])
|
|
45
55
|
mpadded.setAttribute("lspace", "0")
|
|
46
56
|
return mpadded
|
package/src/functions/genfrac.js
CHANGED
|
@@ -9,22 +9,37 @@ import { calculateSize } from "../units";
|
|
|
9
9
|
const stylArray = ["display", "text", "script", "scriptscript"];
|
|
10
10
|
const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 };
|
|
11
11
|
|
|
12
|
+
const adjustStyle = (functionSize, originalStyle) => {
|
|
13
|
+
// Figure out what style this fraction should be in based on the
|
|
14
|
+
// function used
|
|
15
|
+
let style = originalStyle;
|
|
16
|
+
if (functionSize === "display") { //\tfrac or \cfrac
|
|
17
|
+
// Get display style as a default.
|
|
18
|
+
// If incoming style is sub/sup, use style.text() to get correct size.
|
|
19
|
+
const newSize = style.level >= StyleLevel.SCRIPT ? StyleLevel.TEXT : StyleLevel.DISPLAY;
|
|
20
|
+
style = style.withLevel(newSize)
|
|
21
|
+
} else if (functionSize === "text" &&
|
|
22
|
+
style.level === StyleLevel.DISPLAY) {
|
|
23
|
+
// We're in a \tfrac but incoming style is displaystyle, so:
|
|
24
|
+
style = style.withLevel(StyleLevel.TEXT);
|
|
25
|
+
} else if (functionSize === "auto") {
|
|
26
|
+
style = style.incrementLevel()
|
|
27
|
+
} else if (functionSize === "script") {
|
|
28
|
+
style = style.withLevel(StyleLevel.SCRIPT);
|
|
29
|
+
} else if (functionSize === "scriptscript") {
|
|
30
|
+
style = style.withLevel(StyleLevel.SCRIPTSCRIPT)
|
|
31
|
+
}
|
|
32
|
+
return style;
|
|
33
|
+
};
|
|
34
|
+
|
|
12
35
|
const mathmlBuilder = (group, style) => {
|
|
13
|
-
|
|
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);
|
|
36
|
+
style = adjustStyle(group.scriptLevel, style)
|
|
22
37
|
|
|
23
38
|
// Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel.
|
|
24
39
|
// So we check for levels that Chromium shrinks too small.
|
|
25
40
|
// If necessary, set an explicit fraction depth.
|
|
26
|
-
const numer = mml.buildGroup(group.numer,
|
|
27
|
-
const denom = mml.buildGroup(group.denom,
|
|
41
|
+
const numer = mml.buildGroup(group.numer, style)
|
|
42
|
+
const denom = mml.buildGroup(group.denom, style)
|
|
28
43
|
if (style.level === 3) {
|
|
29
44
|
numer.style.mathDepth = "2"
|
|
30
45
|
numer.setAttribute("scriptlevel", "2")
|
|
@@ -77,6 +92,7 @@ const mathmlBuilder = (group, style) => {
|
|
|
77
92
|
defineFunction({
|
|
78
93
|
type: "genfrac",
|
|
79
94
|
names: [
|
|
95
|
+
"\\cfrac",
|
|
80
96
|
"\\dfrac",
|
|
81
97
|
"\\frac",
|
|
82
98
|
"\\tfrac",
|
|
@@ -100,6 +116,7 @@ defineFunction({
|
|
|
100
116
|
let scriptLevel = "auto";
|
|
101
117
|
|
|
102
118
|
switch (funcName) {
|
|
119
|
+
case "\\cfrac":
|
|
103
120
|
case "\\dfrac":
|
|
104
121
|
case "\\frac":
|
|
105
122
|
case "\\tfrac":
|
|
@@ -126,15 +143,10 @@ defineFunction({
|
|
|
126
143
|
throw new Error("Unrecognized genfrac command");
|
|
127
144
|
}
|
|
128
145
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
break;
|
|
134
|
-
case "\\tfrac":
|
|
135
|
-
case "\\tbinom":
|
|
136
|
-
scriptLevel = "text";
|
|
137
|
-
break;
|
|
146
|
+
if (funcName === "\\cfrac" || funcName.startsWith("\\d")) {
|
|
147
|
+
scriptLevel = "display";
|
|
148
|
+
} else if (funcName.startsWith("\\t")) {
|
|
149
|
+
scriptLevel = "text";
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
return {
|
|
@@ -153,31 +165,6 @@ defineFunction({
|
|
|
153
165
|
mathmlBuilder
|
|
154
166
|
});
|
|
155
167
|
|
|
156
|
-
defineFunction({
|
|
157
|
-
type: "genfrac",
|
|
158
|
-
names: ["\\cfrac"],
|
|
159
|
-
props: {
|
|
160
|
-
numArgs: 2
|
|
161
|
-
},
|
|
162
|
-
handler: ({ parser, funcName }, args) => {
|
|
163
|
-
const numer = args[0];
|
|
164
|
-
const denom = args[1];
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
type: "genfrac",
|
|
168
|
-
mode: parser.mode,
|
|
169
|
-
continued: true,
|
|
170
|
-
numer,
|
|
171
|
-
denom,
|
|
172
|
-
hasBarLine: true,
|
|
173
|
-
leftDelim: null,
|
|
174
|
-
rightDelim: null,
|
|
175
|
-
scriptLevel: "display",
|
|
176
|
-
barSize: null
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
|
|
181
168
|
// Infix generalized fractions -- these are not rendered directly, but replaced
|
|
182
169
|
// immediately by one of the variants above.
|
|
183
170
|
defineFunction({
|