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/dist/temml.mjs
CHANGED
|
@@ -213,6 +213,7 @@ class Settings {
|
|
|
213
213
|
: [Infinity, Infinity]
|
|
214
214
|
);
|
|
215
215
|
this.maxExpand = Math.max(0, deflt(options.maxExpand, 1000)); // number
|
|
216
|
+
this.wrapDelimiterPairs = true; // boolean
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
/**
|
|
@@ -994,8 +995,7 @@ defineSymbol(math, textord, "\u2135", "\\aleph", true);
|
|
|
994
995
|
defineSymbol(math, textord, "\u2200", "\\forall", true);
|
|
995
996
|
defineSymbol(math, textord, "\u210f", "\\hbar", true);
|
|
996
997
|
defineSymbol(math, textord, "\u2203", "\\exists", true);
|
|
997
|
-
|
|
998
|
-
defineSymbol(math, bin, "\u2207", "\\nabla", true);
|
|
998
|
+
defineSymbol(math, open, "\u2207", "\\nabla", true);
|
|
999
999
|
defineSymbol(math, textord, "\u266d", "\\flat", true);
|
|
1000
1000
|
defineSymbol(math, textord, "\u2113", "\\ell", true);
|
|
1001
1001
|
defineSymbol(math, textord, "\u266e", "\\natural", true);
|
|
@@ -1088,7 +1088,7 @@ defineSymbol(math, mathord, "\u2295", "\\Earth");
|
|
|
1088
1088
|
|
|
1089
1089
|
// AMS Negated Binary Relations
|
|
1090
1090
|
defineSymbol(math, rel, "\u226e", "\\nless", true);
|
|
1091
|
-
// Symbol names
|
|
1091
|
+
// Symbol names preceded by "@" each have a corresponding macro.
|
|
1092
1092
|
defineSymbol(math, rel, "\u2a87", "\\lneq", true);
|
|
1093
1093
|
defineSymbol(math, rel, "\u2268", "\\lneqq", true);
|
|
1094
1094
|
defineSymbol(math, rel, "\u2268\ufe00", "\\lvertneqq");
|
|
@@ -1999,16 +1999,12 @@ for (let i = 0; i < 10; i++) {
|
|
|
1999
1999
|
* much of this module.
|
|
2000
2000
|
*/
|
|
2001
2001
|
|
|
2002
|
-
const openDelims = "([{⌊⌈⟨⟮⎰⟦⦃";
|
|
2003
|
-
const closeDelims = ")]}⌋⌉⟩⟯⎱⟦⦄";
|
|
2004
|
-
|
|
2005
2002
|
function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
2006
2003
|
const mtrs = [];
|
|
2007
2004
|
let mrows = [];
|
|
2008
2005
|
let block = [];
|
|
2009
2006
|
let numTopLevelEquals = 0;
|
|
2010
2007
|
let i = 0;
|
|
2011
|
-
let level = 0;
|
|
2012
2008
|
while (i < expression.length) {
|
|
2013
2009
|
while (expression[i] instanceof DocumentFragment) {
|
|
2014
2010
|
expression.splice(i, 1, ...expression[i].children); // Expand the fragment.
|
|
@@ -2031,13 +2027,10 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
|
2031
2027
|
}
|
|
2032
2028
|
block.push(node);
|
|
2033
2029
|
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
|
2030
|
+
!(node.attributes.form && node.attributes.form === "prefix") && // unary operators
|
|
2034
2031
|
!Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) {
|
|
2035
2032
|
const ch = node.children[0].text;
|
|
2036
|
-
if (
|
|
2037
|
-
level += 1;
|
|
2038
|
-
} else if (closeDelims.indexOf(ch) > -1) {
|
|
2039
|
-
level -= 1;
|
|
2040
|
-
} else if (level === 0 && wrapMode === "=" && ch === "=") {
|
|
2033
|
+
if (wrapMode === "=" && ch === "=") {
|
|
2041
2034
|
numTopLevelEquals += 1;
|
|
2042
2035
|
if (numTopLevelEquals > 1) {
|
|
2043
2036
|
block.pop();
|
|
@@ -2046,7 +2039,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
|
2046
2039
|
mrows.push(element);
|
|
2047
2040
|
block = [node];
|
|
2048
2041
|
}
|
|
2049
|
-
} else if (
|
|
2042
|
+
} else if (wrapMode === "tex") {
|
|
2050
2043
|
// Check if the following node is a \nobreak text node, e.g. "~""
|
|
2051
2044
|
const next = i < expression.length - 1 ? expression[i + 1] : null;
|
|
2052
2045
|
let glueIsFreeOfNobreak = true;
|
|
@@ -3068,7 +3061,7 @@ function assertSymbolNodeType(node) {
|
|
|
3068
3061
|
* returns null.
|
|
3069
3062
|
*/
|
|
3070
3063
|
function checkSymbolNodeType(node) {
|
|
3071
|
-
if (node && (node.type === "atom" ||
|
|
3064
|
+
if (node && (node.type === "atom" || node.type === "delimiter" ||
|
|
3072
3065
|
Object.prototype.hasOwnProperty.call(NON_ATOMS, node.type))) {
|
|
3073
3066
|
return node;
|
|
3074
3067
|
}
|
|
@@ -3874,7 +3867,6 @@ const dotsByToken = {
|
|
|
3874
3867
|
"\\iint": "\\dotsi",
|
|
3875
3868
|
"\\iiint": "\\dotsi",
|
|
3876
3869
|
"\\iiiint": "\\dotsi",
|
|
3877
|
-
"\\idotsint": "\\dotsi",
|
|
3878
3870
|
// Symbols whose definition starts with \DOTSX:
|
|
3879
3871
|
"\\DOTSX": "\\dotsx"
|
|
3880
3872
|
};
|
|
@@ -3956,7 +3948,7 @@ defineMacro("\\cdots", function(context) {
|
|
|
3956
3948
|
defineMacro("\\dotsb", "\\cdots");
|
|
3957
3949
|
defineMacro("\\dotsm", "\\cdots");
|
|
3958
3950
|
defineMacro("\\dotsi", "\\!\\cdots");
|
|
3959
|
-
defineMacro("\\idotsint", "\\
|
|
3951
|
+
defineMacro("\\idotsint", "\\int\\!\\cdots\\!\\int");
|
|
3960
3952
|
// amsmath doesn't actually define \dotsx, but \dots followed by a macro
|
|
3961
3953
|
// starting with \DOTSX implies \dotso, and then \extra@ detects this case
|
|
3962
3954
|
// and forces the added `\,`.
|
|
@@ -4251,6 +4243,7 @@ defineMacro("\\upomega", "\\up@greek{\\omega}");
|
|
|
4251
4243
|
// cmll package
|
|
4252
4244
|
defineMacro("\\invamp", '\\mathbin{\\char"214b}');
|
|
4253
4245
|
defineMacro("\\parr", '\\mathbin{\\char"214b}');
|
|
4246
|
+
defineMacro("\\upand", '\\mathbin{\\char"214b}'); // STIX package
|
|
4254
4247
|
defineMacro("\\with", '\\mathbin{\\char"26}');
|
|
4255
4248
|
defineMacro("\\multimapinv", '\\mathrel{\\char"27dc}');
|
|
4256
4249
|
defineMacro("\\multimapboth", '\\mathrel{\\char"29df}');
|
|
@@ -7222,6 +7215,7 @@ defineFunction({
|
|
|
7222
7215
|
// That way, the arrow will be an overlay on the content.
|
|
7223
7216
|
const phantom = new MathNode("mphantom", [buildGroup$1(group.body, style)]);
|
|
7224
7217
|
const arrow = new MathNode("mrow", [phantom], ["tml-cancelto"]);
|
|
7218
|
+
arrow.style.color = style.color;
|
|
7225
7219
|
if (group.isCharacterBox && smalls.indexOf(group.body.body[0].text) > -1) {
|
|
7226
7220
|
arrow.style.left = "0.1em";
|
|
7227
7221
|
arrow.style.width = "90%";
|
|
@@ -7253,6 +7247,7 @@ defineFunction({
|
|
|
7253
7247
|
dummyNode = new MathNode("mphantom", [zeroWidthNode]); // Hide it.
|
|
7254
7248
|
}
|
|
7255
7249
|
const toNode = buildGroup$1(group.to, style);
|
|
7250
|
+
toNode.style.color = style.color;
|
|
7256
7251
|
const zeroWidthToNode = new MathNode("mpadded", [toNode]);
|
|
7257
7252
|
if (!group.isCharacterBox || /[f∫∑]/.test(group.body.body[0].text)) {
|
|
7258
7253
|
const w = new MathNode("mspace", []);
|
|
@@ -7311,7 +7306,7 @@ const toHex = num => {
|
|
|
7311
7306
|
|
|
7312
7307
|
// Colors from Tables 4.1 and 4.2 of the xcolor package.
|
|
7313
7308
|
// Table 4.1 (lower case) RGB values are taken from chroma and xcolor.dtx.
|
|
7314
|
-
// Table 4.2 (
|
|
7309
|
+
// Table 4.2 (Capitalized) values were sampled, because Chroma contains a unreliable
|
|
7315
7310
|
// conversion from cmyk to RGB. See https://tex.stackexchange.com/a/537274.
|
|
7316
7311
|
const xcolors = JSON.parse(`{
|
|
7317
7312
|
"Apricot": "#ffb484",
|
|
@@ -7870,7 +7865,41 @@ const delimiterSizes = {
|
|
|
7870
7865
|
"\\Bigg": { mclass: "mord", size: 4 }
|
|
7871
7866
|
};
|
|
7872
7867
|
|
|
7873
|
-
const
|
|
7868
|
+
const leftToRight = {
|
|
7869
|
+
"(": ")",
|
|
7870
|
+
"\\lparen": "\\rparen",
|
|
7871
|
+
"[": "]",
|
|
7872
|
+
"\\lbrack": "\\rbrack",
|
|
7873
|
+
"\\{": "\\}",
|
|
7874
|
+
"\\lbrace": "\\rbrace",
|
|
7875
|
+
"⦇": "⦈",
|
|
7876
|
+
"\\llparenthesis": "\\rrparenthesis",
|
|
7877
|
+
"\\lfloor": "\\rfloor",
|
|
7878
|
+
"\u230a": "\u230b",
|
|
7879
|
+
"\\lceil": "\\rceil",
|
|
7880
|
+
"\u2308": "\u2309",
|
|
7881
|
+
"\\langle": "\\rangle",
|
|
7882
|
+
"\u27e8": "\u27e9",
|
|
7883
|
+
"\\lAngle": "\\rAngle",
|
|
7884
|
+
"\u27ea": "\u27eb",
|
|
7885
|
+
"\\llangle": "\\rrangle",
|
|
7886
|
+
"⦉": "⦊",
|
|
7887
|
+
"\\lvert": "\\rvert",
|
|
7888
|
+
"\\lVert": "\\rVert",
|
|
7889
|
+
"\\lgroup": "\\rgroup",
|
|
7890
|
+
"\u27ee": "\u27ef",
|
|
7891
|
+
"\\lmoustache": "\\rmoustache",
|
|
7892
|
+
"\u23b0": "\u23b1",
|
|
7893
|
+
"\\llbracket": "\\rrbracket",
|
|
7894
|
+
"\u27e6": "\u27e7",
|
|
7895
|
+
"\\lBrace": "\\rBrace",
|
|
7896
|
+
"\u2983": "\u2984"
|
|
7897
|
+
};
|
|
7898
|
+
|
|
7899
|
+
const leftDelimiterNames = new Set(Object.keys(leftToRight));
|
|
7900
|
+
new Set(Object.values(leftToRight));
|
|
7901
|
+
|
|
7902
|
+
const delimiters = new Set([
|
|
7874
7903
|
"(",
|
|
7875
7904
|
"\\lparen",
|
|
7876
7905
|
")",
|
|
@@ -7926,7 +7955,7 @@ const delimiters = [
|
|
|
7926
7955
|
"\\llbracket",
|
|
7927
7956
|
"\\rrbracket",
|
|
7928
7957
|
"\u27e6",
|
|
7929
|
-
"\
|
|
7958
|
+
"\u27e7",
|
|
7930
7959
|
"\\lBrace",
|
|
7931
7960
|
"\\rBrace",
|
|
7932
7961
|
"\u2983",
|
|
@@ -7945,12 +7974,12 @@ const delimiters = [
|
|
|
7945
7974
|
"\\updownarrow",
|
|
7946
7975
|
"\\Updownarrow",
|
|
7947
7976
|
"."
|
|
7948
|
-
];
|
|
7977
|
+
]);
|
|
7949
7978
|
|
|
7950
7979
|
// Export isDelimiter for benefit of parser.
|
|
7951
|
-
const dels = ["}", "\\left", "\\middle", "\\right"];
|
|
7980
|
+
const dels = new Set(["}", "\\left", "\\middle", "\\right"]);
|
|
7952
7981
|
const isDelimiter = str => str.length > 0 &&
|
|
7953
|
-
(delimiters.
|
|
7982
|
+
(delimiters.has(str) || delimiterSizes[str] || dels.has(str));
|
|
7954
7983
|
|
|
7955
7984
|
// Metrics of the different sizes. Found by looking at TeX's output of
|
|
7956
7985
|
// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
|
|
@@ -7963,11 +7992,11 @@ function checkDelimiter(delim, context) {
|
|
|
7963
7992
|
delim = delim.body[0]; // Unwrap the braces
|
|
7964
7993
|
}
|
|
7965
7994
|
const symDelim = checkSymbolNodeType(delim);
|
|
7966
|
-
if (symDelim && delimiters.
|
|
7995
|
+
if (symDelim && delimiters.has(symDelim.text)) {
|
|
7967
7996
|
// If a character is not in the MathML operator dictionary, it will not stretch.
|
|
7968
7997
|
// Replace such characters w/characters that will stretch.
|
|
7969
|
-
if (
|
|
7970
|
-
if (
|
|
7998
|
+
if (symDelim.text === "<" || symDelim.text === "\\lt") { symDelim.text = "⟨"; }
|
|
7999
|
+
if (symDelim.text === ">" || symDelim.text === "\\gt") { symDelim.text = "⟩"; }
|
|
7971
8000
|
return symDelim;
|
|
7972
8001
|
} else if (symDelim) {
|
|
7973
8002
|
throw new ParseError(`Invalid delimiter '${symDelim.text}' after '${context.funcName}'`, delim);
|
|
@@ -7977,7 +8006,16 @@ function checkDelimiter(delim, context) {
|
|
|
7977
8006
|
}
|
|
7978
8007
|
|
|
7979
8008
|
// / \
|
|
7980
|
-
const needExplicitStretch = ["\u002F", "\u005C", "\\backslash", "\\vert", "|"];
|
|
8009
|
+
const needExplicitStretch = new Set(["\u002F", "\u005C", "\\backslash", "\u2216", "\\vert", "|"]);
|
|
8010
|
+
|
|
8011
|
+
const makeFenceMo = (delim, mode, form, isStretchy) => {
|
|
8012
|
+
const text = delim === "." ? "" : delim;
|
|
8013
|
+
const node = new MathNode("mo", [makeText(text, mode)]);
|
|
8014
|
+
node.setAttribute("fence", "true");
|
|
8015
|
+
node.setAttribute("form", form);
|
|
8016
|
+
node.setAttribute("stretchy", isStretchy ? "true" : "false");
|
|
8017
|
+
return node;
|
|
8018
|
+
};
|
|
7981
8019
|
|
|
7982
8020
|
defineFunction({
|
|
7983
8021
|
type: "delimsizing",
|
|
@@ -8028,9 +8066,9 @@ defineFunction({
|
|
|
8028
8066
|
},
|
|
8029
8067
|
mathmlBuilder: (group) => {
|
|
8030
8068
|
const children = [];
|
|
8069
|
+
const delim = group.delim === "." ? "" : group.delim;
|
|
8031
8070
|
|
|
8032
|
-
|
|
8033
|
-
children.push(makeText(group.delim, group.mode));
|
|
8071
|
+
children.push(makeText(delim, group.mode));
|
|
8034
8072
|
|
|
8035
8073
|
const node = new MathNode("mo", children);
|
|
8036
8074
|
|
|
@@ -8043,7 +8081,7 @@ defineFunction({
|
|
|
8043
8081
|
// defaults.
|
|
8044
8082
|
node.setAttribute("fence", "false");
|
|
8045
8083
|
}
|
|
8046
|
-
if (needExplicitStretch.
|
|
8084
|
+
if (needExplicitStretch.has(delim) || delim.indexOf("arrow") > -1) {
|
|
8047
8085
|
// We have to explicitly set stretchy to true.
|
|
8048
8086
|
node.setAttribute("stretchy", "true");
|
|
8049
8087
|
}
|
|
@@ -8056,7 +8094,7 @@ defineFunction({
|
|
|
8056
8094
|
|
|
8057
8095
|
function assertParsed(group) {
|
|
8058
8096
|
if (!group.body) {
|
|
8059
|
-
throw new Error("Bug: The
|
|
8097
|
+
throw new Error("Bug: The delim ParseNode wasn't fully parsed.");
|
|
8060
8098
|
}
|
|
8061
8099
|
}
|
|
8062
8100
|
|
|
@@ -8087,17 +8125,10 @@ defineFunction({
|
|
|
8087
8125
|
const delim = checkDelimiter(args[0], context);
|
|
8088
8126
|
|
|
8089
8127
|
const parser = context.parser;
|
|
8090
|
-
// Parse out the implicit body
|
|
8091
8128
|
++parser.leftrightDepth;
|
|
8092
|
-
|
|
8093
|
-
let body = parser.parseExpression(false, null, true);
|
|
8129
|
+
let body = parser.parseExpression(false, "\\right", true);
|
|
8094
8130
|
let nextToken = parser.fetch();
|
|
8095
8131
|
while (nextToken.text === "\\middle") {
|
|
8096
|
-
// `\middle`, from the ε-TeX package, ends one group and starts another group.
|
|
8097
|
-
// We had to parse this expression with `breakOnMiddle` enabled in order
|
|
8098
|
-
// to get TeX-compliant parsing of \over.
|
|
8099
|
-
// But we do not want, at this point, to end on \middle, so continue
|
|
8100
|
-
// to parse until we fetch a `\right`.
|
|
8101
8132
|
parser.consume();
|
|
8102
8133
|
const middle = parser.fetch().text;
|
|
8103
8134
|
if (!symbols.math[middle]) {
|
|
@@ -8106,11 +8137,10 @@ defineFunction({
|
|
|
8106
8137
|
checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" });
|
|
8107
8138
|
body.push({ type: "middle", mode: "math", delim: middle });
|
|
8108
8139
|
parser.consume();
|
|
8109
|
-
body = body.concat(parser.parseExpression(false,
|
|
8140
|
+
body = body.concat(parser.parseExpression(false, "\\right", true));
|
|
8110
8141
|
nextToken = parser.fetch();
|
|
8111
8142
|
}
|
|
8112
8143
|
--parser.leftrightDepth;
|
|
8113
|
-
// Check the next token
|
|
8114
8144
|
parser.expect("\\right", false);
|
|
8115
8145
|
const right = assertNodeType(parser.parseFunction(), "leftright-right");
|
|
8116
8146
|
return {
|
|
@@ -8118,35 +8148,90 @@ defineFunction({
|
|
|
8118
8148
|
mode: parser.mode,
|
|
8119
8149
|
body,
|
|
8120
8150
|
left: delim.text,
|
|
8121
|
-
right: right.delim
|
|
8151
|
+
right: right.delim,
|
|
8152
|
+
isStretchy: true
|
|
8122
8153
|
};
|
|
8123
8154
|
},
|
|
8124
8155
|
mathmlBuilder: (group, style) => {
|
|
8125
8156
|
assertParsed(group);
|
|
8126
8157
|
const inner = buildExpression(group.body, style);
|
|
8127
8158
|
|
|
8128
|
-
|
|
8129
|
-
const leftNode = new MathNode("mo", [makeText(group.left, group.mode)]);
|
|
8130
|
-
leftNode.setAttribute("fence", "true");
|
|
8131
|
-
leftNode.setAttribute("form", "prefix");
|
|
8132
|
-
if (group.left === "/" || group.left === "\u005C" || group.left.indexOf("arrow") > -1) {
|
|
8133
|
-
leftNode.setAttribute("stretchy", "true");
|
|
8134
|
-
}
|
|
8159
|
+
const leftNode = makeFenceMo(group.left, group.mode, "prefix", true);
|
|
8135
8160
|
inner.unshift(leftNode);
|
|
8136
8161
|
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
8141
|
-
|
|
8142
|
-
|
|
8162
|
+
const rightNode = makeFenceMo(group.right, group.mode, "postfix", true);
|
|
8163
|
+
if (group.body.length > 0) {
|
|
8164
|
+
const lastElement = group.body[group.body.length - 1];
|
|
8165
|
+
if (lastElement.type === "color" && !lastElement.isTextColor) {
|
|
8166
|
+
rightNode.setAttribute("mathcolor", lastElement.color);
|
|
8167
|
+
}
|
|
8143
8168
|
}
|
|
8169
|
+
inner.push(rightNode);
|
|
8170
|
+
|
|
8171
|
+
return makeRow(inner);
|
|
8172
|
+
}
|
|
8173
|
+
});
|
|
8174
|
+
|
|
8175
|
+
defineFunction({
|
|
8176
|
+
type: "delimiter",
|
|
8177
|
+
names: Array.from(leftDelimiterNames),
|
|
8178
|
+
props: {
|
|
8179
|
+
numArgs: 0,
|
|
8180
|
+
allowedInText: true,
|
|
8181
|
+
allowedInMath: true,
|
|
8182
|
+
allowedInArgument: true
|
|
8183
|
+
},
|
|
8184
|
+
handler: ({ parser, funcName, token }) => {
|
|
8185
|
+
if (parser.mode === "text") {
|
|
8186
|
+
return {
|
|
8187
|
+
type: "textord",
|
|
8188
|
+
mode: "text",
|
|
8189
|
+
text: funcName,
|
|
8190
|
+
loc: token.loc
|
|
8191
|
+
}
|
|
8192
|
+
} else if (!parser.settings.wrapDelimiterPairs) {
|
|
8193
|
+
// Treat this token as an ordinary symbol.
|
|
8194
|
+
return {
|
|
8195
|
+
type: "atom",
|
|
8196
|
+
mode: "math",
|
|
8197
|
+
family: "open",
|
|
8198
|
+
loc: token.loc,
|
|
8199
|
+
text: funcName
|
|
8200
|
+
};
|
|
8201
|
+
}
|
|
8202
|
+
// Otherwise, try to wrap a pair of delimiters with an <mrow>.
|
|
8203
|
+
const rightDelim = leftToRight[funcName];
|
|
8204
|
+
// Parse the inner expression, looking for the corresponding right delimiter.
|
|
8205
|
+
const body = parser.parseExpression(false, rightDelim, false);
|
|
8206
|
+
const nextToken = parser.fetch().text;
|
|
8207
|
+
|
|
8208
|
+
if (nextToken !== rightDelim) {
|
|
8209
|
+
// We were unable to find a matching right delimiter.
|
|
8210
|
+
// Throw control back to renderToMathMLTree.
|
|
8211
|
+
// It will reparse the entire expression with wrapDelimiterPairs set to false.
|
|
8212
|
+
throw new ParseError("Unmatched delimiter");
|
|
8213
|
+
}
|
|
8214
|
+
parser.consume();
|
|
8215
|
+
|
|
8216
|
+
return {
|
|
8217
|
+
type: "delimiter",
|
|
8218
|
+
mode: parser.mode,
|
|
8219
|
+
body,
|
|
8220
|
+
left: funcName,
|
|
8221
|
+
right: rightDelim
|
|
8222
|
+
};
|
|
8223
|
+
},
|
|
8224
|
+
mathmlBuilder: (group, style) => {
|
|
8225
|
+
assertParsed(group);
|
|
8226
|
+
const inner = buildExpression(group.body, style);
|
|
8227
|
+
|
|
8228
|
+
const leftNode = makeFenceMo(group.left, group.mode, "prefix", false);
|
|
8229
|
+
inner.unshift(leftNode);
|
|
8230
|
+
|
|
8231
|
+
const rightNode = makeFenceMo(group.right, group.mode, "postfix", false);
|
|
8144
8232
|
if (group.body.length > 0) {
|
|
8145
8233
|
const lastElement = group.body[group.body.length - 1];
|
|
8146
8234
|
if (lastElement.type === "color" && !lastElement.isTextColor) {
|
|
8147
|
-
// \color is a switch. If the last element is of type "color" then
|
|
8148
|
-
// the user set the \color switch and left it on.
|
|
8149
|
-
// A \right delimiter turns the switch off, but the delimiter itself gets the color.
|
|
8150
8235
|
rightNode.setAttribute("mathcolor", lastElement.color);
|
|
8151
8236
|
}
|
|
8152
8237
|
}
|
|
@@ -8175,7 +8260,7 @@ defineFunction({
|
|
|
8175
8260
|
delim: delim.text
|
|
8176
8261
|
};
|
|
8177
8262
|
},
|
|
8178
|
-
mathmlBuilder: (group
|
|
8263
|
+
mathmlBuilder: (group) => {
|
|
8179
8264
|
const textNode = makeText(group.delim, group.mode);
|
|
8180
8265
|
const middleNode = new MathNode("mo", [textNode]);
|
|
8181
8266
|
middleNode.setAttribute("fence", "true");
|
|
@@ -8221,7 +8306,7 @@ const mathmlBuilder$7 = (group, style) => {
|
|
|
8221
8306
|
break
|
|
8222
8307
|
case "\\xcancel":
|
|
8223
8308
|
node.setAttribute("notation", "updiagonalstrike downdiagonalstrike");
|
|
8224
|
-
node.
|
|
8309
|
+
node.children.push(new MathNode("mrow", [], ["tml-cancel", "tml-xcancel"]));
|
|
8225
8310
|
break
|
|
8226
8311
|
// cancelto is handled in cancelto.js
|
|
8227
8312
|
case "\\longdiv":
|
|
@@ -8658,22 +8743,37 @@ defineFunction({
|
|
|
8658
8743
|
const stylArray = ["display", "text", "script", "scriptscript"];
|
|
8659
8744
|
const scriptLevel = { auto: -1, display: 0, text: 0, script: 1, scriptscript: 2 };
|
|
8660
8745
|
|
|
8746
|
+
const adjustStyle = (functionSize, originalStyle) => {
|
|
8747
|
+
// Figure out what style this fraction should be in based on the
|
|
8748
|
+
// function used
|
|
8749
|
+
let style = originalStyle;
|
|
8750
|
+
if (functionSize === "display") { //\tfrac or \cfrac
|
|
8751
|
+
// Get display style as a default.
|
|
8752
|
+
// If incoming style is sub/sup, use style.text() to get correct size.
|
|
8753
|
+
const newSize = style.level >= StyleLevel.SCRIPT ? StyleLevel.TEXT : StyleLevel.DISPLAY;
|
|
8754
|
+
style = style.withLevel(newSize);
|
|
8755
|
+
} else if (functionSize === "text" &&
|
|
8756
|
+
style.level === StyleLevel.DISPLAY) {
|
|
8757
|
+
// We're in a \tfrac but incoming style is displaystyle, so:
|
|
8758
|
+
style = style.withLevel(StyleLevel.TEXT);
|
|
8759
|
+
} else if (functionSize === "auto") {
|
|
8760
|
+
style = style.incrementLevel();
|
|
8761
|
+
} else if (functionSize === "script") {
|
|
8762
|
+
style = style.withLevel(StyleLevel.SCRIPT);
|
|
8763
|
+
} else if (functionSize === "scriptscript") {
|
|
8764
|
+
style = style.withLevel(StyleLevel.SCRIPTSCRIPT);
|
|
8765
|
+
}
|
|
8766
|
+
return style;
|
|
8767
|
+
};
|
|
8768
|
+
|
|
8661
8769
|
const mathmlBuilder$5 = (group, style) => {
|
|
8662
|
-
|
|
8663
|
-
// We may need that info for \mathchoice or for adjusting em dimensions.
|
|
8664
|
-
const childOptions = group.scriptLevel === "auto"
|
|
8665
|
-
? style.incrementLevel()
|
|
8666
|
-
: group.scriptLevel === "display"
|
|
8667
|
-
? style.withLevel(StyleLevel.TEXT)
|
|
8668
|
-
: group.scriptLevel === "text"
|
|
8669
|
-
? style.withLevel(StyleLevel.SCRIPT)
|
|
8670
|
-
: style.withLevel(StyleLevel.SCRIPTSCRIPT);
|
|
8770
|
+
style = adjustStyle(group.scriptLevel, style);
|
|
8671
8771
|
|
|
8672
8772
|
// Chromium (wrongly) continues to shrink fractions beyond scriptscriptlevel.
|
|
8673
8773
|
// So we check for levels that Chromium shrinks too small.
|
|
8674
8774
|
// If necessary, set an explicit fraction depth.
|
|
8675
|
-
const numer = buildGroup$1(group.numer,
|
|
8676
|
-
const denom = buildGroup$1(group.denom,
|
|
8775
|
+
const numer = buildGroup$1(group.numer, style);
|
|
8776
|
+
const denom = buildGroup$1(group.denom, style);
|
|
8677
8777
|
if (style.level === 3) {
|
|
8678
8778
|
numer.style.mathDepth = "2";
|
|
8679
8779
|
numer.setAttribute("scriptlevel", "2");
|
|
@@ -8726,6 +8826,7 @@ const mathmlBuilder$5 = (group, style) => {
|
|
|
8726
8826
|
defineFunction({
|
|
8727
8827
|
type: "genfrac",
|
|
8728
8828
|
names: [
|
|
8829
|
+
"\\cfrac",
|
|
8729
8830
|
"\\dfrac",
|
|
8730
8831
|
"\\frac",
|
|
8731
8832
|
"\\tfrac",
|
|
@@ -8749,6 +8850,7 @@ defineFunction({
|
|
|
8749
8850
|
let scriptLevel = "auto";
|
|
8750
8851
|
|
|
8751
8852
|
switch (funcName) {
|
|
8853
|
+
case "\\cfrac":
|
|
8752
8854
|
case "\\dfrac":
|
|
8753
8855
|
case "\\frac":
|
|
8754
8856
|
case "\\tfrac":
|
|
@@ -8775,15 +8877,10 @@ defineFunction({
|
|
|
8775
8877
|
throw new Error("Unrecognized genfrac command");
|
|
8776
8878
|
}
|
|
8777
8879
|
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
break;
|
|
8783
|
-
case "\\tfrac":
|
|
8784
|
-
case "\\tbinom":
|
|
8785
|
-
scriptLevel = "text";
|
|
8786
|
-
break;
|
|
8880
|
+
if (funcName === "\\cfrac" || funcName.startsWith("\\d")) {
|
|
8881
|
+
scriptLevel = "display";
|
|
8882
|
+
} else if (funcName.startsWith("\\t")) {
|
|
8883
|
+
scriptLevel = "text";
|
|
8787
8884
|
}
|
|
8788
8885
|
|
|
8789
8886
|
return {
|
|
@@ -8802,31 +8899,6 @@ defineFunction({
|
|
|
8802
8899
|
mathmlBuilder: mathmlBuilder$5
|
|
8803
8900
|
});
|
|
8804
8901
|
|
|
8805
|
-
defineFunction({
|
|
8806
|
-
type: "genfrac",
|
|
8807
|
-
names: ["\\cfrac"],
|
|
8808
|
-
props: {
|
|
8809
|
-
numArgs: 2
|
|
8810
|
-
},
|
|
8811
|
-
handler: ({ parser, funcName }, args) => {
|
|
8812
|
-
const numer = args[0];
|
|
8813
|
-
const denom = args[1];
|
|
8814
|
-
|
|
8815
|
-
return {
|
|
8816
|
-
type: "genfrac",
|
|
8817
|
-
mode: parser.mode,
|
|
8818
|
-
continued: true,
|
|
8819
|
-
numer,
|
|
8820
|
-
denom,
|
|
8821
|
-
hasBarLine: true,
|
|
8822
|
-
leftDelim: null,
|
|
8823
|
-
rightDelim: null,
|
|
8824
|
-
scriptLevel: "display",
|
|
8825
|
-
barSize: null
|
|
8826
|
-
};
|
|
8827
|
-
}
|
|
8828
|
-
});
|
|
8829
|
-
|
|
8830
8902
|
// Infix generalized fractions -- these are not rendered directly, but replaced
|
|
8831
8903
|
// immediately by one of the variants above.
|
|
8832
8904
|
defineFunction({
|
|
@@ -9655,8 +9727,16 @@ const binrelClass = (arg) => {
|
|
|
9655
9727
|
const atom = arg.type === "ordgroup" && arg.body.length && arg.body.length === 1
|
|
9656
9728
|
? arg.body[0]
|
|
9657
9729
|
: arg;
|
|
9658
|
-
if (atom.type === "atom"
|
|
9659
|
-
|
|
9730
|
+
if (atom.type === "atom") {
|
|
9731
|
+
// BIN args are sometimes changed to OPEN, so check the original family.
|
|
9732
|
+
const family = arg.body.length > 0 && arg.body[0].text && symbols.math[arg.body[0].text]
|
|
9733
|
+
? symbols.math[arg.body[0].text].group
|
|
9734
|
+
: atom.family;
|
|
9735
|
+
if (family === "bin" || family === "rel") {
|
|
9736
|
+
return "m" + family;
|
|
9737
|
+
} else {
|
|
9738
|
+
return "mord";
|
|
9739
|
+
}
|
|
9660
9740
|
} else {
|
|
9661
9741
|
return "mord";
|
|
9662
9742
|
}
|
|
@@ -10227,6 +10307,14 @@ const mathmlBuilder$1 = (group, style) => {
|
|
|
10227
10307
|
if ((node.type === "mrow" || node.type === "mpadded") && node.children.length === 1 &&
|
|
10228
10308
|
node.children[0] instanceof MathNode) {
|
|
10229
10309
|
node = node.children[0];
|
|
10310
|
+
} else if (node.type === "mrow" && node.children.length === 2 &&
|
|
10311
|
+
node.children[0] instanceof MathNode &&
|
|
10312
|
+
node.children[1] instanceof MathNode &&
|
|
10313
|
+
node.children[1].type === "mspace" && !node.children[1].attributes.width &&
|
|
10314
|
+
node.children[1].children.length === 0) {
|
|
10315
|
+
// This is a workaround for a Firefox bug that applies spacing to
|
|
10316
|
+
// an <mi> with mathvariant="normal".
|
|
10317
|
+
node = node.children[0];
|
|
10230
10318
|
}
|
|
10231
10319
|
switch (node.type) {
|
|
10232
10320
|
case "mi":
|
|
@@ -11497,8 +11585,8 @@ defineFunctionBuilders({
|
|
|
11497
11585
|
node.setAttribute("mathvariant", "normal");
|
|
11498
11586
|
if (text.text.length === 1) {
|
|
11499
11587
|
// A Firefox bug will apply spacing here, but there should be none. Fix it.
|
|
11500
|
-
|
|
11501
|
-
node
|
|
11588
|
+
const mspace = new MathNode("mspace", []);
|
|
11589
|
+
node = new MathNode("mrow", [node, mspace]);
|
|
11502
11590
|
}
|
|
11503
11591
|
}
|
|
11504
11592
|
return node
|
|
@@ -13035,7 +13123,7 @@ class Parser {
|
|
|
13035
13123
|
* Parses an "expression", which is a list of atoms.
|
|
13036
13124
|
*
|
|
13037
13125
|
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
|
|
13038
|
-
* happens when functions have higher precedence
|
|
13126
|
+
* happens when functions have higher precedence than infix
|
|
13039
13127
|
* nodes in implicit parses.
|
|
13040
13128
|
*
|
|
13041
13129
|
* `breakOnTokenText`: The text of the token that the expression should end
|
|
@@ -13643,7 +13731,7 @@ class Parser {
|
|
|
13643
13731
|
) {
|
|
13644
13732
|
const firstToken = this.fetch();
|
|
13645
13733
|
const text = firstToken.text;
|
|
13646
|
-
|
|
13734
|
+
if (name === "argument to '\\left'") { return this.parseSymbol() }
|
|
13647
13735
|
let result;
|
|
13648
13736
|
// Try to parse an open brace or \begingroup
|
|
13649
13737
|
if (text === "{" || text === "\\begingroup" || text === "\\toggle") {
|
|
@@ -13676,6 +13764,12 @@ class Parser {
|
|
|
13676
13764
|
result = this.parseFunction(breakOnTokenText, name) || this.parseSymbol();
|
|
13677
13765
|
if (result == null && text[0] === "\\" &&
|
|
13678
13766
|
!Object.prototype.hasOwnProperty.call(implicitCommands, text )) {
|
|
13767
|
+
if (this.settings.throwOnError) {
|
|
13768
|
+
throw new ParseError("Unsupported function name: " + text, firstToken);
|
|
13769
|
+
}
|
|
13770
|
+
// For people getting dyanamically rendered math, it's better to
|
|
13771
|
+
// show the unsupported command in red rather than panicking for every
|
|
13772
|
+
// partially written expression.
|
|
13679
13773
|
result = this.formatUnsupportedCmd(text);
|
|
13680
13774
|
this.consume();
|
|
13681
13775
|
}
|
|
@@ -13784,7 +13878,8 @@ class Parser {
|
|
|
13784
13878
|
let symbol;
|
|
13785
13879
|
if (symbols[this.mode][text]) {
|
|
13786
13880
|
let group = symbols[this.mode][text].group;
|
|
13787
|
-
if (group === "bin" &&
|
|
13881
|
+
if (group === "bin" &&
|
|
13882
|
+
(binLeftCancellers.includes(this.prevAtomType) || this.prevAtomType === "")) {
|
|
13788
13883
|
// Change from a binary operator to a unary (prefix) operator
|
|
13789
13884
|
group = "open";
|
|
13790
13885
|
}
|
|
@@ -13880,11 +13975,27 @@ const parseTree = function(toParse, settings) {
|
|
|
13880
13975
|
if (!(typeof toParse === "string" || toParse instanceof String)) {
|
|
13881
13976
|
throw new TypeError("Temml can only parse string typed expression")
|
|
13882
13977
|
}
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
|
|
13978
|
+
let tree;
|
|
13979
|
+
let parser;
|
|
13980
|
+
try {
|
|
13981
|
+
parser = new Parser(toParse, settings);
|
|
13982
|
+
// Blank out any \df@tag to avoid spurious "Duplicate \tag" errors
|
|
13983
|
+
delete parser.gullet.macros.current["\\df@tag"];
|
|
13886
13984
|
|
|
13887
|
-
|
|
13985
|
+
tree = parser.parse();
|
|
13986
|
+
} catch (error) {
|
|
13987
|
+
if (error.toString() === "ParseError: Unmatched delimiter") {
|
|
13988
|
+
// Abandon the attempt to wrap delimiter pairs in an <mrow>.
|
|
13989
|
+
// Try again, and put each delimiter into an <mo> element.
|
|
13990
|
+
settings.wrapDelimiterPairs = false;
|
|
13991
|
+
parser = new Parser(toParse, settings);
|
|
13992
|
+
// Blank out any \df@tag to avoid spurious "Duplicate \tag" errors
|
|
13993
|
+
delete parser.gullet.macros.current["\\df@tag"];
|
|
13994
|
+
tree = parser.parse();
|
|
13995
|
+
} else {
|
|
13996
|
+
throw error;
|
|
13997
|
+
}
|
|
13998
|
+
}
|
|
13888
13999
|
|
|
13889
14000
|
// LaTeX ignores a \tag placed outside an AMS environment.
|
|
13890
14001
|
if (!(tree.length > 0 && tree[0].type && tree[0].type === "array" && tree[0].addEqnNum)) {
|
|
@@ -14061,7 +14172,7 @@ class Style {
|
|
|
14061
14172
|
* https://mit-license.org/
|
|
14062
14173
|
*/
|
|
14063
14174
|
|
|
14064
|
-
const version = "0.
|
|
14175
|
+
const version = "0.13.02";
|
|
14065
14176
|
|
|
14066
14177
|
function postProcess(block) {
|
|
14067
14178
|
const labelMap = {};
|
package/dist/temmlPostProcess.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "temml",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.02",
|
|
4
4
|
"description": "TeX to MathML conversion in JavaScript.",
|
|
5
5
|
"main": "dist/temml.js",
|
|
6
6
|
"engines": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"eslint": "^9.11.1",
|
|
34
34
|
"esm": "^3.2.25",
|
|
35
35
|
"globals": "^15.9.0",
|
|
36
|
-
"rollup": "^4.
|
|
36
|
+
"rollup": "^4.59.0",
|
|
37
37
|
"terser": "^5.34.0"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|