temml 0.12.2 → 0.13.2
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 +11 -13
- package/dist/Temml-Latin-Modern.css +11 -13
- package/dist/Temml-Libertinus.css +11 -13
- package/dist/Temml-Local.css +11 -13
- package/dist/Temml-NotoSans.css +11 -13
- package/dist/Temml-STIX2.css +11 -13
- package/dist/temml.cjs +227 -116
- package/dist/temml.js +227 -116
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +227 -116
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +2 -2
- package/src/Parser.js +11 -4
- package/src/Settings.js +1 -0
- package/src/functions/cancelto.js +2 -0
- package/src/functions/color.js +1 -1
- package/src/functions/{delimsizing.js → delimiter.js} +132 -42
- package/src/functions/enclose.js +1 -1
- 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/symbolsOrd.js +2 -2
- package/src/functions.js +1 -1
- package/src/linebreaking.js +3 -10
- package/src/macros.js +2 -2
- 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/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") {
|
|
@@ -816,6 +816,12 @@ export default class Parser {
|
|
|
816
816
|
result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol();
|
|
817
817
|
if (result == null && text[0] === "\\" &&
|
|
818
818
|
!Object.prototype.hasOwnProperty.call(implicitCommands, text )) {
|
|
819
|
+
if (this.settings.throwOnError) {
|
|
820
|
+
throw new ParseError("Unsupported function name: " + text, firstToken);
|
|
821
|
+
}
|
|
822
|
+
// For people getting dyanamically rendered math, it's better to
|
|
823
|
+
// show the unsupported command in red rather than panicking for every
|
|
824
|
+
// partially written expression.
|
|
819
825
|
result = this.formatUnsupportedCmd(text);
|
|
820
826
|
this.consume();
|
|
821
827
|
}
|
|
@@ -924,7 +930,8 @@ export default class Parser {
|
|
|
924
930
|
let symbol;
|
|
925
931
|
if (symbols[this.mode][text]) {
|
|
926
932
|
let group = symbols[this.mode][text].group;
|
|
927
|
-
if (group === "bin" &&
|
|
933
|
+
if (group === "bin" &&
|
|
934
|
+
(binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) {
|
|
928
935
|
// Change from a binary operator to a unary (prefix) operator
|
|
929
936
|
group = "open"
|
|
930
937
|
}
|
package/src/Settings.js
CHANGED
|
@@ -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,7 +421,7 @@ 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
427
|
middleNode.setAttribute("fence", "true");
|
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":
|
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({
|
package/src/functions/mclass.js
CHANGED
|
@@ -148,8 +148,16 @@ export const binrelClass = (arg) => {
|
|
|
148
148
|
const atom = arg.type === "ordgroup" && arg.body.length && arg.body.length === 1
|
|
149
149
|
? arg.body[0]
|
|
150
150
|
: arg;
|
|
151
|
-
if (atom.type === "atom"
|
|
152
|
-
|
|
151
|
+
if (atom.type === "atom") {
|
|
152
|
+
// BIN args are sometimes changed to OPEN, so check the original family.
|
|
153
|
+
const family = arg.body.length > 0 && arg.body[0].text && symbols.math[arg.body[0].text]
|
|
154
|
+
? symbols.math[arg.body[0].text].group
|
|
155
|
+
: atom.family
|
|
156
|
+
if (family === "bin" || family === "rel") {
|
|
157
|
+
return "m" + family;
|
|
158
|
+
} else {
|
|
159
|
+
return "mord";
|
|
160
|
+
}
|
|
153
161
|
} else {
|
|
154
162
|
return "mord";
|
|
155
163
|
}
|
package/src/functions/op.js
CHANGED
|
@@ -3,7 +3,7 @@ import defineMacro from "../defineMacro";
|
|
|
3
3
|
import * as mathMLTree from "../mathMLTree"
|
|
4
4
|
import { spaceCharacter } from "./kern"
|
|
5
5
|
import { ordTypes } from "./op"
|
|
6
|
-
import { isDelimiter } from "./
|
|
6
|
+
import { isDelimiter } from "./delimiter"
|
|
7
7
|
|
|
8
8
|
import * as mml from "../buildMathML"
|
|
9
9
|
|
|
@@ -22,6 +22,14 @@ const mathmlBuilder = (group, style) => {
|
|
|
22
22
|
if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 &&
|
|
23
23
|
node.children[0] instanceof mathMLTree.MathNode) {
|
|
24
24
|
node = node.children[0]
|
|
25
|
+
} else if (node.type === "mrow" && node.children.length === 2 &&
|
|
26
|
+
node.children[0] instanceof mathMLTree.MathNode &&
|
|
27
|
+
node.children[1] instanceof mathMLTree.MathNode &&
|
|
28
|
+
node.children[1].type === "mspace" && !node.children[1].attributes.width &&
|
|
29
|
+
node.children[1].children.length === 0) {
|
|
30
|
+
// This is a workaround for a Firefox bug that applies spacing to
|
|
31
|
+
// an <mi> with mathvariant="normal".
|
|
32
|
+
node = node.children[0];
|
|
25
33
|
}
|
|
26
34
|
switch (node.type) {
|
|
27
35
|
case "mi":
|
|
@@ -41,8 +41,8 @@ defineFunctionBuilders({
|
|
|
41
41
|
node.setAttribute("mathvariant", "normal")
|
|
42
42
|
if (text.text.length === 1) {
|
|
43
43
|
// A Firefox bug will apply spacing here, but there should be none. Fix it.
|
|
44
|
-
|
|
45
|
-
node.
|
|
44
|
+
const mspace = new mathMLTree.MathNode("mspace", [])
|
|
45
|
+
node = new mathMLTree.MathNode("mrow", [node, mspace])
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
return node
|
package/src/functions.js
CHANGED
|
@@ -16,7 +16,7 @@ import "./functions/char";
|
|
|
16
16
|
import "./functions/color";
|
|
17
17
|
import "./functions/cr";
|
|
18
18
|
import "./functions/def";
|
|
19
|
-
import "./functions/
|
|
19
|
+
import "./functions/delimiter";
|
|
20
20
|
import "./functions/enclose";
|
|
21
21
|
import "./functions/environment";
|
|
22
22
|
import "./functions/envTag";
|
package/src/linebreaking.js
CHANGED
|
@@ -26,16 +26,12 @@ import { DocumentFragment } from "./tree"
|
|
|
26
26
|
* much of this module.
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
|
-
const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃"
|
|
30
|
-
const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄"
|
|
31
|
-
|
|
32
29
|
export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
33
30
|
const mtrs = [];
|
|
34
31
|
let mrows = [];
|
|
35
32
|
let block = [];
|
|
36
33
|
let numTopLevelEquals = 0
|
|
37
34
|
let i = 0
|
|
38
|
-
let level = 0
|
|
39
35
|
while (i < expression.length) {
|
|
40
36
|
while (expression[i] instanceof DocumentFragment) {
|
|
41
37
|
expression.splice(i, 1, ...expression[i].children) // Expand the fragment.
|
|
@@ -58,13 +54,10 @@ export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
|
58
54
|
}
|
|
59
55
|
block.push(node);
|
|
60
56
|
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
|
57
|
+
!(node.attributes.form && node.attributes.form === "prefix") && // unary operators
|
|
61
58
|
!Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) {
|
|
62
59
|
const ch = node.children[0].text
|
|
63
|
-
if (
|
|
64
|
-
level += 1
|
|
65
|
-
} else if (closeDelims.indexOf(ch) > -1) {
|
|
66
|
-
level -= 1
|
|
67
|
-
} else if (level === 0 && wrapMode === "=" && ch === "=") {
|
|
60
|
+
if (wrapMode === "=" && ch === "=") {
|
|
68
61
|
numTopLevelEquals += 1
|
|
69
62
|
if (numTopLevelEquals > 1) {
|
|
70
63
|
block.pop()
|
|
@@ -73,7 +66,7 @@ export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
|
73
66
|
mrows.push(element)
|
|
74
67
|
block = [node];
|
|
75
68
|
}
|
|
76
|
-
} else if (
|
|
69
|
+
} else if (wrapMode === "tex") {
|
|
77
70
|
// Check if the following node is a \nobreak text node, e.g. "~""
|
|
78
71
|
const next = i < expression.length - 1 ? expression[i + 1] : null;
|
|
79
72
|
let glueIsFreeOfNobreak = true;
|
package/src/macros.js
CHANGED
|
@@ -318,7 +318,6 @@ const dotsByToken = {
|
|
|
318
318
|
"\\iint": "\\dotsi",
|
|
319
319
|
"\\iiint": "\\dotsi",
|
|
320
320
|
"\\iiiint": "\\dotsi",
|
|
321
|
-
"\\idotsint": "\\dotsi",
|
|
322
321
|
// Symbols whose definition starts with \DOTSX:
|
|
323
322
|
"\\DOTSX": "\\dotsx"
|
|
324
323
|
};
|
|
@@ -400,7 +399,7 @@ defineMacro("\\cdots", function(context) {
|
|
|
400
399
|
defineMacro("\\dotsb", "\\cdots");
|
|
401
400
|
defineMacro("\\dotsm", "\\cdots");
|
|
402
401
|
defineMacro("\\dotsi", "\\!\\cdots");
|
|
403
|
-
defineMacro("\\idotsint", "\\
|
|
402
|
+
defineMacro("\\idotsint", "\\int\\!\\cdots\\!\\int");
|
|
404
403
|
// amsmath doesn't actually define \dotsx, but \dots followed by a macro
|
|
405
404
|
// starting with \DOTSX implies \dotso, and then \extra@ detects this case
|
|
406
405
|
// and forces the added `\,`.
|
|
@@ -695,6 +694,7 @@ defineMacro("\\upomega", "\\up@greek{\\omega}");
|
|
|
695
694
|
// cmll package
|
|
696
695
|
defineMacro("\\invamp", '\\mathbin{\\char"214b}')
|
|
697
696
|
defineMacro("\\parr", '\\mathbin{\\char"214b}')
|
|
697
|
+
defineMacro("\\upand", '\\mathbin{\\char"214b}') // STIX package
|
|
698
698
|
defineMacro("\\with", '\\mathbin{\\char"26}')
|
|
699
699
|
defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}')
|
|
700
700
|
defineMacro("\\multimapboth", '\\mathrel{\\char"29df}')
|
package/src/parseNode.js
CHANGED
|
@@ -34,7 +34,7 @@ export function assertSymbolNodeType(node) {
|
|
|
34
34
|
* returns null.
|
|
35
35
|
*/
|
|
36
36
|
export function checkSymbolNodeType(node) {
|
|
37
|
-
if (node && (node.type === "atom" ||
|
|
37
|
+
if (node && (node.type === "atom" || node.type === "delimiter" ||
|
|
38
38
|
Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) {
|
|
39
39
|
return node;
|
|
40
40
|
}
|