temml 0.10.21 → 0.10.22
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/dist/Temml-Asana.css +0 -4
- package/dist/Temml-STIX2.css +0 -4
- package/dist/temml.cjs +123 -79
- package/dist/temml.d.ts +1 -1
- package/dist/temml.js +123 -79
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +123 -79
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +1 -1
- package/src/Parser.js +8 -1
- package/src/functions/color.js +1 -1
- package/src/functions/delimsizing.js +22 -7
- package/src/functions/font.js +1 -1
- package/src/functions/mclass.js +3 -0
- package/src/functions/reflect.js +24 -0
- package/src/functions/sizing.js +1 -1
- package/src/functions/styling.js +1 -1
- package/src/functions/symbolsOrd.js +0 -2
- package/src/functions.js +1 -0
- package/src/linebreaking.js +3 -2
- package/src/macros.js +49 -53
- package/src/postProcess.js +1 -1
- package/src/replace.js +8 -8
- package/src/symbols.js +4 -1
package/dist/temml.mjs
CHANGED
@@ -934,7 +934,8 @@ defineSymbol(math, textord, "\u2135", "\\aleph", true);
|
|
934
934
|
defineSymbol(math, textord, "\u2200", "\\forall", true);
|
935
935
|
defineSymbol(math, textord, "\u210f", "\\hbar", true);
|
936
936
|
defineSymbol(math, textord, "\u2203", "\\exists", true);
|
937
|
-
|
937
|
+
// ∇ is actually a unary operator, not binary. But this works.
|
938
|
+
defineSymbol(math, bin, "\u2207", "\\nabla", true);
|
938
939
|
defineSymbol(math, textord, "\u266d", "\\flat", true);
|
939
940
|
defineSymbol(math, textord, "\u2113", "\\ell", true);
|
940
941
|
defineSymbol(math, textord, "\u266e", "\\natural", true);
|
@@ -988,6 +989,7 @@ defineSymbol(math, bin, "\u2021", "\\ddagger");
|
|
988
989
|
defineSymbol(math, bin, "\u2240", "\\wr", true);
|
989
990
|
defineSymbol(math, bin, "\u2a3f", "\\amalg");
|
990
991
|
defineSymbol(math, bin, "\u0026", "\\And"); // from amsmath
|
992
|
+
defineSymbol(math, bin, "\u2AFD", "\\sslash", true); // from stmaryrd
|
991
993
|
|
992
994
|
// Arrow Symbols
|
993
995
|
defineSymbol(math, rel, "\u27f5", "\\longleftarrow", true);
|
@@ -1406,6 +1408,7 @@ defineSymbol(math, mathord, "\u2aeb", "\\Bot");
|
|
1406
1408
|
defineSymbol(math, bin, "\u2217", "\u2217", true);
|
1407
1409
|
defineSymbol(math, bin, "+", "+");
|
1408
1410
|
defineSymbol(math, bin, "*", "*");
|
1411
|
+
defineSymbol(math, bin, "\u2044", "/", true);
|
1409
1412
|
defineSymbol(math, bin, "\u2044", "\u2044");
|
1410
1413
|
defineSymbol(math, bin, "\u2212", "-", true);
|
1411
1414
|
defineSymbol(math, bin, "\u22c5", "\\cdot", true);
|
@@ -1862,7 +1865,8 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
1862
1865
|
continue
|
1863
1866
|
}
|
1864
1867
|
block.push(node);
|
1865
|
-
if (node.type && node.type === "mo" && node.children.length === 1
|
1868
|
+
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
1869
|
+
!Object.hasOwn(node.attributes, "movablelimits")) {
|
1866
1870
|
const ch = node.children[0].text;
|
1867
1871
|
if (openDelims.indexOf(ch) > -1) {
|
1868
1872
|
level += 1;
|
@@ -1877,7 +1881,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
1877
1881
|
mrows.push(element);
|
1878
1882
|
block = [node];
|
1879
1883
|
}
|
1880
|
-
} else if (level === 0 && wrapMode === "tex") {
|
1884
|
+
} else if (level === 0 && wrapMode === "tex" && ch !== "∇") {
|
1881
1885
|
// Check if the following node is a \nobreak text node, e.g. "~""
|
1882
1886
|
const next = i < expression.length - 1 ? expression[i + 1] : null;
|
1883
1887
|
let glueIsFreeOfNobreak = true;
|
@@ -3256,7 +3260,7 @@ defineFunction({
|
|
3256
3260
|
}
|
3257
3261
|
|
3258
3262
|
// Parse out the implicit body that should be colored.
|
3259
|
-
const body = parser.parseExpression(true, breakOnTokenText);
|
3263
|
+
const body = parser.parseExpression(true, breakOnTokenText, true);
|
3260
3264
|
|
3261
3265
|
return {
|
3262
3266
|
type: "color",
|
@@ -3698,17 +3702,13 @@ const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
|
3698
3702
|
|
3699
3703
|
// Delimiter functions
|
3700
3704
|
function checkDelimiter(delim, context) {
|
3701
|
-
if (delim.type === "ordgroup" && delim.body.length === 1 && delim.body[0].text === "\u2044") {
|
3702
|
-
// Recover "/" from the zero spacing group. (See macros.js)
|
3703
|
-
delim = { type: "textord", text: "/", mode: "math" };
|
3704
|
-
}
|
3705
3705
|
const symDelim = checkSymbolNodeType(delim);
|
3706
3706
|
if (symDelim && delimiters.includes(symDelim.text)) {
|
3707
3707
|
// If a character is not in the MathML operator dictionary, it will not stretch.
|
3708
3708
|
// Replace such characters w/characters that will stretch.
|
3709
|
+
if (["/", "\u2044"].includes(symDelim.text)) { symDelim.text = "\u2215"; }
|
3709
3710
|
if (["<", "\\lt"].includes(symDelim.text)) { symDelim.text = "⟨"; }
|
3710
3711
|
if ([">", "\\gt"].includes(symDelim.text)) { symDelim.text = "⟩"; }
|
3711
|
-
if (symDelim.text === "/") { symDelim.text = "\u2215"; }
|
3712
3712
|
if (symDelim.text === "\\backslash") { symDelim.text = "\u2216"; }
|
3713
3713
|
return symDelim;
|
3714
3714
|
} else if (symDelim) {
|
@@ -3817,8 +3817,26 @@ defineFunction({
|
|
3817
3817
|
const parser = context.parser;
|
3818
3818
|
// Parse out the implicit body
|
3819
3819
|
++parser.leftrightDepth;
|
3820
|
-
// parseExpression stops before '\\right'
|
3821
|
-
|
3820
|
+
// parseExpression stops before '\\right' or `\\middle`
|
3821
|
+
let body = parser.parseExpression(false, null, true);
|
3822
|
+
let nextToken = parser.fetch();
|
3823
|
+
while (nextToken.text === "\\middle") {
|
3824
|
+
// `\middle`, from the ε-TeX package, ends one group and starts another group.
|
3825
|
+
// We had to parse this expression with `breakOnMiddle` enabled in order
|
3826
|
+
// to get TeX-compliant parsing of \over.
|
3827
|
+
// But we do not want, at this point, to end on \middle, so continue
|
3828
|
+
// to parse until we fetch a `\right`.
|
3829
|
+
parser.consume();
|
3830
|
+
const middle = parser.fetch().text;
|
3831
|
+
if (!symbols.math[middle]) {
|
3832
|
+
throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`);
|
3833
|
+
}
|
3834
|
+
checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" });
|
3835
|
+
body.push({ type: "middle", mode: "math", delim: middle });
|
3836
|
+
parser.consume();
|
3837
|
+
body = body.concat(parser.parseExpression(false, null, true));
|
3838
|
+
nextToken = parser.fetch();
|
3839
|
+
}
|
3822
3840
|
--parser.leftrightDepth;
|
3823
3841
|
// Check the next token
|
3824
3842
|
parser.expect("\\right", false);
|
@@ -5193,7 +5211,7 @@ defineFunction({
|
|
5193
5211
|
},
|
5194
5212
|
handler: ({ parser, funcName, breakOnTokenText }, args) => {
|
5195
5213
|
const { mode } = parser;
|
5196
|
-
const body = parser.parseExpression(true, breakOnTokenText);
|
5214
|
+
const body = parser.parseExpression(true, breakOnTokenText, true);
|
5197
5215
|
const fontStyle = `math${funcName.slice(1)}`;
|
5198
5216
|
|
5199
5217
|
return {
|
@@ -6154,6 +6172,9 @@ function mathmlBuilder$3(group, style) {
|
|
6154
6172
|
if (group.isCharacterBox || inner[0].type === "mathord") {
|
6155
6173
|
node = inner[0];
|
6156
6174
|
node.type = "mi";
|
6175
|
+
if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") {
|
6176
|
+
node.setAttribute("mathvariant", "normal");
|
6177
|
+
}
|
6157
6178
|
} else {
|
6158
6179
|
node = new mathMLTree.MathNode("mi", inner);
|
6159
6180
|
}
|
@@ -7141,6 +7162,28 @@ defineFunction({
|
|
7141
7162
|
}
|
7142
7163
|
});
|
7143
7164
|
|
7165
|
+
defineFunction({
|
7166
|
+
type: "reflect",
|
7167
|
+
names: ["\\reflectbox"],
|
7168
|
+
props: {
|
7169
|
+
numArgs: 1,
|
7170
|
+
argTypes: ["hbox"],
|
7171
|
+
allowedInText: true
|
7172
|
+
},
|
7173
|
+
handler({ parser }, args) {
|
7174
|
+
return {
|
7175
|
+
type: "reflect",
|
7176
|
+
mode: parser.mode,
|
7177
|
+
body: args[0]
|
7178
|
+
};
|
7179
|
+
},
|
7180
|
+
mathmlBuilder(group, style) {
|
7181
|
+
const node = buildGroup$1(group.body, style);
|
7182
|
+
node.style.transform = "scaleX(-1)";
|
7183
|
+
return node
|
7184
|
+
}
|
7185
|
+
});
|
7186
|
+
|
7144
7187
|
defineFunction({
|
7145
7188
|
type: "internal",
|
7146
7189
|
names: ["\\relax"],
|
@@ -7246,7 +7289,7 @@ defineFunction({
|
|
7246
7289
|
// eslint-disable-next-line no-console
|
7247
7290
|
console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`);
|
7248
7291
|
}
|
7249
|
-
const body = parser.parseExpression(false, breakOnTokenText);
|
7292
|
+
const body = parser.parseExpression(false, breakOnTokenText, true);
|
7250
7293
|
return {
|
7251
7294
|
type: "sizing",
|
7252
7295
|
mode: parser.mode,
|
@@ -7379,7 +7422,7 @@ defineFunction({
|
|
7379
7422
|
},
|
7380
7423
|
handler({ breakOnTokenText, funcName, parser }, args) {
|
7381
7424
|
// parse out the implicit body
|
7382
|
-
const body = parser.parseExpression(true, breakOnTokenText);
|
7425
|
+
const body = parser.parseExpression(true, breakOnTokenText, true);
|
7383
7426
|
|
7384
7427
|
const scriptLevel = funcName.slice(1, funcName.length - 5);
|
7385
7428
|
return {
|
@@ -7810,22 +7853,22 @@ const offset = Object.freeze({
|
|
7810
7853
|
"sans-serif-bold-italic": ch => { return 0x1D5F5 },
|
7811
7854
|
"monospace": ch => { return 0x1D629 }
|
7812
7855
|
},
|
7813
|
-
upperCaseGreek: { // A-Ω
|
7856
|
+
upperCaseGreek: { // A-Ω
|
7814
7857
|
"normal": ch => { return 0 },
|
7815
|
-
"bold": ch => { return
|
7816
|
-
"italic": ch => { return
|
7858
|
+
"bold": ch => { return 0x1D317 },
|
7859
|
+
"italic": ch => { return 0x1D351 },
|
7817
7860
|
// \boldsymbol actually returns upright bold for upperCaseGreek
|
7818
|
-
"bold-italic": ch => { return
|
7861
|
+
"bold-italic": ch => { return 0x1D317 },
|
7819
7862
|
"script": ch => { return 0 },
|
7820
7863
|
"script-bold": ch => { return 0 },
|
7821
7864
|
"fraktur": ch => { return 0 },
|
7822
7865
|
"fraktur-bold": ch => { return 0 },
|
7823
7866
|
"double-struck": ch => { return 0 },
|
7824
7867
|
// Unicode has no code points for regular-weight san-serif Greek. Use bold.
|
7825
|
-
"sans-serif": ch => { return
|
7826
|
-
"sans-serif-bold": ch => { return
|
7868
|
+
"sans-serif": ch => { return 0x1D3C5 },
|
7869
|
+
"sans-serif-bold": ch => { return 0x1D3C5 },
|
7827
7870
|
"sans-serif-italic": ch => { return 0 },
|
7828
|
-
"sans-serif-bold-italic": ch => { return
|
7871
|
+
"sans-serif-bold-italic": ch => { return 0x1D3FF },
|
7829
7872
|
"monospace": ch => { return 0 }
|
7830
7873
|
},
|
7831
7874
|
lowerCaseGreek: { // α-ω
|
@@ -7885,7 +7928,7 @@ const variantChar = (ch, variant) => {
|
|
7885
7928
|
? "upperCaseLatin"
|
7886
7929
|
: 0x60 < codePoint && codePoint < 0x7b
|
7887
7930
|
? "lowerCaseLatin"
|
7888
|
-
: (0x390 < codePoint && codePoint < 0x3AA)
|
7931
|
+
: (0x390 < codePoint && codePoint < 0x3AA)
|
7889
7932
|
? "upperCaseGreek"
|
7890
7933
|
: 0x3B0 < codePoint && codePoint < 0x3CA || ch === "\u03d5"
|
7891
7934
|
? "lowerCaseGreek"
|
@@ -8014,8 +8057,6 @@ defineFunctionBuilders({
|
|
8014
8057
|
node = new mathMLTree.MathNode("mi", [text]);
|
8015
8058
|
if (text.text === origText && latinRegEx.test(origText)) {
|
8016
8059
|
node.setAttribute("mathvariant", "italic");
|
8017
|
-
} else if (text.text === "∇" && variant === "normal") {
|
8018
|
-
node.setAttribute("mathvariant", "normal");
|
8019
8060
|
}
|
8020
8061
|
}
|
8021
8062
|
return node
|
@@ -8652,6 +8693,24 @@ defineMacro("\\char", function(context) {
|
|
8652
8693
|
return `\\@char{${number}}`;
|
8653
8694
|
});
|
8654
8695
|
|
8696
|
+
function recreateArgStr(context) {
|
8697
|
+
// Recreate the macro's original argument string from the array of parse tokens.
|
8698
|
+
const tokens = context.consumeArgs(1)[0];
|
8699
|
+
let str = "";
|
8700
|
+
let expectedLoc = tokens[tokens.length - 1].loc.start;
|
8701
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
8702
|
+
const actualLoc = tokens[i].loc.start;
|
8703
|
+
if (actualLoc > expectedLoc) {
|
8704
|
+
// context.consumeArgs has eaten a space.
|
8705
|
+
str += " ";
|
8706
|
+
expectedLoc = actualLoc;
|
8707
|
+
}
|
8708
|
+
str += tokens[i].text;
|
8709
|
+
expectedLoc += tokens[i].text.length;
|
8710
|
+
}
|
8711
|
+
return str
|
8712
|
+
}
|
8713
|
+
|
8655
8714
|
// The Latin Modern font renders <mi>√</mi> at the wrong vertical alignment.
|
8656
8715
|
// This macro provides a better rendering.
|
8657
8716
|
defineMacro("\\surd", '\\sqrt{\\vphantom{|}}');
|
@@ -8659,10 +8718,6 @@ defineMacro("\\surd", '\\sqrt{\\vphantom{|}}');
|
|
8659
8718
|
// See comment for \oplus in symbols.js.
|
8660
8719
|
defineMacro("\u2295", "\\oplus");
|
8661
8720
|
|
8662
|
-
// Per TeXbook p.122, "/" gets zero operator spacing.
|
8663
|
-
// And MDN recommends using U+2044 instead of / for inline
|
8664
|
-
defineMacro("/", "{\u2044}");
|
8665
|
-
|
8666
8721
|
// Since Temml has no \par, ignore \long.
|
8667
8722
|
defineMacro("\\long", "");
|
8668
8723
|
|
@@ -9040,6 +9095,11 @@ defineMacro("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}");
|
|
9040
9095
|
defineMacro("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}");
|
9041
9096
|
defineMacro("\\plim", "\\DOTSB\\operatorname*{plim}");
|
9042
9097
|
|
9098
|
+
//////////////////////////////////////////////////////////////////////
|
9099
|
+
// MnSymbol.sty
|
9100
|
+
|
9101
|
+
defineMacro("\\leftmodels", "\\mathop{\\reflectbox{$\\models$}}");
|
9102
|
+
|
9043
9103
|
//////////////////////////////////////////////////////////////////////
|
9044
9104
|
// braket.sty
|
9045
9105
|
// http://ctan.math.washington.edu/tex-archive/macros/latex/contrib/braket/braket.pdf
|
@@ -9049,56 +9109,33 @@ defineMacro("\\ket", "\\mathinner{|{#1}\\rangle}");
|
|
9049
9109
|
defineMacro("\\braket", "\\mathinner{\\langle{#1}\\rangle}");
|
9050
9110
|
defineMacro("\\Bra", "\\left\\langle#1\\right|");
|
9051
9111
|
defineMacro("\\Ket", "\\left|#1\\right\\rangle");
|
9052
|
-
|
9053
|
-
|
9054
|
-
const
|
9055
|
-
const
|
9056
|
-
|
9057
|
-
const oldMiddle = context.macros.get("|");
|
9058
|
-
const oldMiddleDouble = context.macros.get("\\|");
|
9059
|
-
context.macros.beginGroup();
|
9060
|
-
const midMacro = (double) => (context) => {
|
9061
|
-
if (one) {
|
9062
|
-
// Only modify the first instance of | or \|
|
9063
|
-
context.macros.set("|", oldMiddle);
|
9064
|
-
if (middleDouble.length) {
|
9065
|
-
context.macros.set("\\|", oldMiddleDouble);
|
9066
|
-
}
|
9067
|
-
}
|
9068
|
-
let doubled = double;
|
9069
|
-
if (!double && middleDouble.length) {
|
9070
|
-
// Mimic \@ifnextchar
|
9071
|
-
const nextToken = context.future();
|
9072
|
-
if (nextToken.text === "|") {
|
9073
|
-
context.popToken();
|
9074
|
-
doubled = true;
|
9075
|
-
}
|
9076
|
-
}
|
9077
|
-
return {
|
9078
|
-
tokens: doubled ? middleDouble : middle,
|
9079
|
-
numArgs: 0
|
9080
|
-
};
|
9081
|
-
};
|
9082
|
-
context.macros.set("|", midMacro(false));
|
9083
|
-
if (middleDouble.length) {
|
9084
|
-
context.macros.set("\\|", midMacro(true));
|
9085
|
-
}
|
9086
|
-
const arg = context.consumeArg().tokens;
|
9087
|
-
const expanded = context.expandTokens([...right, ...arg, ...left]); // reversed
|
9088
|
-
context.macros.endGroup();
|
9089
|
-
return {
|
9090
|
-
tokens: expanded.reverse(),
|
9091
|
-
numArgs: 0
|
9092
|
-
};
|
9112
|
+
// A helper for \Braket and \Set
|
9113
|
+
const replaceVert = (argStr, match) => {
|
9114
|
+
const ch = match[0] === "|" ? "\\vert" : "\\Vert";
|
9115
|
+
const replaceStr = `}\\,\\middle${ch}\\,{`;
|
9116
|
+
return argStr.slice(0, match.index) + replaceStr + argStr.slice(match.index + match[0].length)
|
9093
9117
|
};
|
9094
|
-
defineMacro("\\
|
9095
|
-
|
9096
|
-
|
9097
|
-
|
9098
|
-
|
9099
|
-
|
9100
|
-
|
9101
|
-
|
9118
|
+
defineMacro("\\Braket", function(context) {
|
9119
|
+
let argStr = recreateArgStr(context);
|
9120
|
+
const regEx = /\|\||\||\\\|/g;
|
9121
|
+
let match;
|
9122
|
+
while ((match = regEx.exec(argStr)) !== null) {
|
9123
|
+
argStr = replaceVert(argStr, match);
|
9124
|
+
}
|
9125
|
+
return "\\left\\langle{" + argStr + "}\\right\\rangle"
|
9126
|
+
});
|
9127
|
+
defineMacro("\\Set", function(context) {
|
9128
|
+
let argStr = recreateArgStr(context);
|
9129
|
+
const match = /\|\||\||\\\|/.exec(argStr);
|
9130
|
+
if (match) {
|
9131
|
+
argStr = replaceVert(argStr, match);
|
9132
|
+
}
|
9133
|
+
return "\\left\\{\\:{" + argStr + "}\\:\\right\\}"
|
9134
|
+
});
|
9135
|
+
defineMacro("\\set", function(context) {
|
9136
|
+
const argStr = recreateArgStr(context);
|
9137
|
+
return "\\{{" + argStr.replace(/\|/, "}\\mid{") + "}\\}"
|
9138
|
+
});
|
9102
9139
|
|
9103
9140
|
//////////////////////////////////////////////////////////////////////
|
9104
9141
|
// actuarialangle.dtx
|
@@ -12147,8 +12184,12 @@ class Parser {
|
|
12147
12184
|
* `breakOnTokenText`: The text of the token that the expression should end
|
12148
12185
|
* with, or `null` if something else should end the
|
12149
12186
|
* expression.
|
12187
|
+
*
|
12188
|
+
* `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group.
|
12189
|
+
* These groups end just before the usual tokens, but they also
|
12190
|
+
* end just before `\middle`.
|
12150
12191
|
*/
|
12151
|
-
parseExpression(breakOnInfix, breakOnTokenText) {
|
12192
|
+
parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) {
|
12152
12193
|
const body = [];
|
12153
12194
|
// Keep adding atoms to the body until we can't parse any more atoms (either
|
12154
12195
|
// we reached the end, a }, or a \right)
|
@@ -12164,6 +12205,9 @@ class Parser {
|
|
12164
12205
|
if (breakOnTokenText && lex.text === breakOnTokenText) {
|
12165
12206
|
break;
|
12166
12207
|
}
|
12208
|
+
if (breakOnMiddle && lex.text === "\\middle") {
|
12209
|
+
break
|
12210
|
+
}
|
12167
12211
|
if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) {
|
12168
12212
|
break;
|
12169
12213
|
}
|
@@ -13142,7 +13186,7 @@ class Style {
|
|
13142
13186
|
* https://mit-license.org/
|
13143
13187
|
*/
|
13144
13188
|
|
13145
|
-
const version = "0.10.
|
13189
|
+
const version = "0.10.22";
|
13146
13190
|
|
13147
13191
|
function postProcess(block) {
|
13148
13192
|
const labelMap = {};
|
package/dist/temmlPostProcess.js
CHANGED
package/package.json
CHANGED
package/src/Parser.js
CHANGED
@@ -179,8 +179,12 @@ export default class Parser {
|
|
179
179
|
* `breakOnTokenText`: The text of the token that the expression should end
|
180
180
|
* with, or `null` if something else should end the
|
181
181
|
* expression.
|
182
|
+
*
|
183
|
+
* `breakOnMiddle`: \color, \over, and old styling functions work on an implicit group.
|
184
|
+
* These groups end just before the usual tokens, but they also
|
185
|
+
* end just before `\middle`.
|
182
186
|
*/
|
183
|
-
parseExpression(breakOnInfix, breakOnTokenText) {
|
187
|
+
parseExpression(breakOnInfix, breakOnTokenText, breakOnMiddle) {
|
184
188
|
const body = [];
|
185
189
|
// Keep adding atoms to the body until we can't parse any more atoms (either
|
186
190
|
// we reached the end, a }, or a \right)
|
@@ -196,6 +200,9 @@ export default class Parser {
|
|
196
200
|
if (breakOnTokenText && lex.text === breakOnTokenText) {
|
197
201
|
break;
|
198
202
|
}
|
203
|
+
if (breakOnMiddle && lex.text === "\\middle") {
|
204
|
+
break
|
205
|
+
}
|
199
206
|
if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) {
|
200
207
|
break;
|
201
208
|
}
|
package/src/functions/color.js
CHANGED
@@ -212,7 +212,7 @@ defineFunction({
|
|
212
212
|
}
|
213
213
|
|
214
214
|
// Parse out the implicit body that should be colored.
|
215
|
-
const body = parser.parseExpression(true, breakOnTokenText)
|
215
|
+
const body = parser.parseExpression(true, breakOnTokenText, true)
|
216
216
|
|
217
217
|
return {
|
218
218
|
type: "color",
|
@@ -4,6 +4,7 @@ import ParseError from "../ParseError";
|
|
4
4
|
import { assertNodeType, checkSymbolNodeType } from "../parseNode";
|
5
5
|
|
6
6
|
import * as mml from "../buildMathML";
|
7
|
+
import symbols from "../symbols";
|
7
8
|
|
8
9
|
// Extra data needed for the delimiter handler down below
|
9
10
|
export const delimiterSizes = {
|
@@ -113,17 +114,13 @@ const sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
|
|
113
114
|
|
114
115
|
// Delimiter functions
|
115
116
|
function checkDelimiter(delim, context) {
|
116
|
-
if (delim.type === "ordgroup" && delim.body.length === 1 && delim.body[0].text === "\u2044") {
|
117
|
-
// Recover "/" from the zero spacing group. (See macros.js)
|
118
|
-
delim = { type: "textord", text: "/", mode: "math" }
|
119
|
-
}
|
120
117
|
const symDelim = checkSymbolNodeType(delim)
|
121
118
|
if (symDelim && delimiters.includes(symDelim.text)) {
|
122
119
|
// If a character is not in the MathML operator dictionary, it will not stretch.
|
123
120
|
// Replace such characters w/characters that will stretch.
|
121
|
+
if (["/", "\u2044"].includes(symDelim.text)) { symDelim.text = "\u2215" }
|
124
122
|
if (["<", "\\lt"].includes(symDelim.text)) { symDelim.text = "⟨" }
|
125
123
|
if ([">", "\\gt"].includes(symDelim.text)) { symDelim.text = "⟩" }
|
126
|
-
if (symDelim.text === "/") { symDelim.text = "\u2215" }
|
127
124
|
if (symDelim.text === "\\backslash") { symDelim.text = "\u2216" }
|
128
125
|
return symDelim;
|
129
126
|
} else if (symDelim) {
|
@@ -232,8 +229,26 @@ defineFunction({
|
|
232
229
|
const parser = context.parser;
|
233
230
|
// Parse out the implicit body
|
234
231
|
++parser.leftrightDepth;
|
235
|
-
// parseExpression stops before '\\right'
|
236
|
-
|
232
|
+
// parseExpression stops before '\\right' or `\\middle`
|
233
|
+
let body = parser.parseExpression(false, null, true)
|
234
|
+
let nextToken = parser.fetch()
|
235
|
+
while (nextToken.text === "\\middle") {
|
236
|
+
// `\middle`, from the ε-TeX package, ends one group and starts another group.
|
237
|
+
// We had to parse this expression with `breakOnMiddle` enabled in order
|
238
|
+
// to get TeX-compliant parsing of \over.
|
239
|
+
// But we do not want, at this point, to end on \middle, so continue
|
240
|
+
// to parse until we fetch a `\right`.
|
241
|
+
parser.consume()
|
242
|
+
const middle = parser.fetch().text
|
243
|
+
if (!symbols.math[middle]) {
|
244
|
+
throw new ParseError(`Invalid delimiter '${middle}' after '\\middle'`);
|
245
|
+
}
|
246
|
+
checkDelimiter({ type: "atom", mode: "math", text: middle }, { funcName: "\\middle" })
|
247
|
+
body.push({ type: "middle", mode: "math", delim: middle })
|
248
|
+
parser.consume()
|
249
|
+
body = body.concat(parser.parseExpression(false, null, true))
|
250
|
+
nextToken = parser.fetch()
|
251
|
+
}
|
237
252
|
--parser.leftrightDepth;
|
238
253
|
// Check the next token
|
239
254
|
parser.expect("\\right", false);
|
package/src/functions/font.js
CHANGED
@@ -103,7 +103,7 @@ defineFunction({
|
|
103
103
|
},
|
104
104
|
handler: ({ parser, funcName, breakOnTokenText }, args) => {
|
105
105
|
const { mode } = parser;
|
106
|
-
const body = parser.parseExpression(true, breakOnTokenText);
|
106
|
+
const body = parser.parseExpression(true, breakOnTokenText, true);
|
107
107
|
const fontStyle = `math${funcName.slice(1)}`;
|
108
108
|
|
109
109
|
return {
|
package/src/functions/mclass.js
CHANGED
@@ -23,6 +23,9 @@ function mathmlBuilder(group, style) {
|
|
23
23
|
if (group.isCharacterBox || inner[0].type === "mathord") {
|
24
24
|
node = inner[0];
|
25
25
|
node.type = "mi";
|
26
|
+
if (node.children.length === 1 && node.children[0].text && node.children[0].text === "∇") {
|
27
|
+
node.setAttribute("mathvariant", "normal")
|
28
|
+
}
|
26
29
|
} else {
|
27
30
|
node = new mathMLTree.MathNode("mi", inner);
|
28
31
|
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import defineFunction from "../defineFunction"
|
2
|
+
import * as mml from "../buildMathML"
|
3
|
+
|
4
|
+
defineFunction({
|
5
|
+
type: "reflect",
|
6
|
+
names: ["\\reflectbox"],
|
7
|
+
props: {
|
8
|
+
numArgs: 1,
|
9
|
+
argTypes: ["hbox"],
|
10
|
+
allowedInText: true
|
11
|
+
},
|
12
|
+
handler({ parser }, args) {
|
13
|
+
return {
|
14
|
+
type: "reflect",
|
15
|
+
mode: parser.mode,
|
16
|
+
body: args[0]
|
17
|
+
};
|
18
|
+
},
|
19
|
+
mathmlBuilder(group, style) {
|
20
|
+
const node = mml.buildGroup(group.body, style)
|
21
|
+
node.style.transform = "scaleX(-1)"
|
22
|
+
return node
|
23
|
+
}
|
24
|
+
})
|
package/src/functions/sizing.js
CHANGED
@@ -44,7 +44,7 @@ defineFunction({
|
|
44
44
|
// eslint-disable-next-line no-console
|
45
45
|
console.log(`Temml strict-mode warning: Command ${funcName} is invalid in math mode.`)
|
46
46
|
}
|
47
|
-
const body = parser.parseExpression(false, breakOnTokenText);
|
47
|
+
const body = parser.parseExpression(false, breakOnTokenText, true);
|
48
48
|
return {
|
49
49
|
type: "sizing",
|
50
50
|
mode: parser.mode,
|
package/src/functions/styling.js
CHANGED
@@ -26,7 +26,7 @@ defineFunction({
|
|
26
26
|
},
|
27
27
|
handler({ breakOnTokenText, funcName, parser }, args) {
|
28
28
|
// parse out the implicit body
|
29
|
-
const body = parser.parseExpression(true, breakOnTokenText);
|
29
|
+
const body = parser.parseExpression(true, breakOnTokenText, true);
|
30
30
|
|
31
31
|
const scriptLevel = funcName.slice(1, funcName.length - 5);
|
32
32
|
return {
|
@@ -90,8 +90,6 @@ defineFunctionBuilders({
|
|
90
90
|
node = new mathMLTree.MathNode("mi", [text])
|
91
91
|
if (text.text === origText && latinRegEx.test(origText)) {
|
92
92
|
node.setAttribute("mathvariant", "italic")
|
93
|
-
} else if (text.text === "∇" && variant === "normal") {
|
94
|
-
node.setAttribute("mathvariant", "normal")
|
95
93
|
}
|
96
94
|
}
|
97
95
|
return node
|
package/src/functions.js
CHANGED
package/src/linebreaking.js
CHANGED
@@ -57,7 +57,8 @@ export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
57
57
|
continue
|
58
58
|
}
|
59
59
|
block.push(node);
|
60
|
-
if (node.type && node.type === "mo" && node.children.length === 1
|
60
|
+
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
61
|
+
!Object.hasOwn(node.attributes, "movablelimits")) {
|
61
62
|
const ch = node.children[0].text
|
62
63
|
if (openDelims.indexOf(ch) > -1) {
|
63
64
|
level += 1
|
@@ -72,7 +73,7 @@ export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
72
73
|
mrows.push(element)
|
73
74
|
block = [node];
|
74
75
|
}
|
75
|
-
} else if (level === 0 && wrapMode === "tex") {
|
76
|
+
} else if (level === 0 && wrapMode === "tex" && ch !== "∇") {
|
76
77
|
// Check if the following node is a \nobreak text node, e.g. "~""
|
77
78
|
const next = i < expression.length - 1 ? expression[i + 1] : null;
|
78
79
|
let glueIsFreeOfNobreak = true;
|