temml 0.10.0 → 0.10.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/auto-render/test/auto-render-spec.js +1 -1
- package/contrib/mhchem/mhchem.js +2 -2
- package/dist/Temml-Asana.css +17 -1
- package/dist/Temml-Latin-Modern.css +17 -5
- package/dist/Temml-Libertinus.css +17 -5
- package/dist/Temml-Local.css +16 -0
- package/dist/Temml-STIX2.css +16 -0
- package/dist/temml.cjs +67 -58
- package/dist/temml.js +65 -56
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +67 -58
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +2 -1
- package/src/MacroExpander.js +20 -21
- package/src/Parser.js +8 -3
- package/src/Settings.js +1 -1
- package/src/buildMathML.js +4 -13
- package/src/functions/delimsizing.js +5 -0
- package/src/functions/op.js +1 -5
- package/src/functions/operatorname.js +10 -8
- package/src/functions/supsub.js +7 -0
- package/src/linebreaking.js +3 -1
- package/src/mathMLTree.js +1 -1
- package/src/postProcess.js +1 -1
- package/src/symbols.js +5 -2
package/dist/temml.mjs
CHANGED
@@ -188,7 +188,7 @@ class Settings {
|
|
188
188
|
this.leqno = utils.deflt(options.leqno, false); // boolean
|
189
189
|
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
|
190
190
|
this.macros = options.macros || {};
|
191
|
-
this.wrap = utils.deflt(options.wrap, "
|
191
|
+
this.wrap = utils.deflt(options.wrap, "tex"); // "tex" | "="
|
192
192
|
this.xml = utils.deflt(options.xml, false); // boolean
|
193
193
|
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
|
194
194
|
this.strict = utils.deflt(options.strict, false); // boolean
|
@@ -666,7 +666,7 @@ class TextNode {
|
|
666
666
|
|
667
667
|
/**
|
668
668
|
* Converts the text node into a string
|
669
|
-
* (representing the text
|
669
|
+
* (representing the text itself).
|
670
670
|
*/
|
671
671
|
toText() {
|
672
672
|
return this.text;
|
@@ -843,7 +843,6 @@ defineSymbol(math, rel, "\u226a", "\\ll", true);
|
|
843
843
|
defineSymbol(math, rel, "\u226b", "\\gg", true);
|
844
844
|
defineSymbol(math, rel, "\u224d", "\\asymp", true);
|
845
845
|
defineSymbol(math, rel, "\u2225", "\\parallel");
|
846
|
-
defineSymbol(math, rel, "\u22c8", "\\bowtie", true);
|
847
846
|
defineSymbol(math, rel, "\u2323", "\\smile", true);
|
848
847
|
defineSymbol(math, rel, "\u2291", "\\sqsubseteq", true);
|
849
848
|
defineSymbol(math, rel, "\u2292", "\\sqsupseteq", true);
|
@@ -1160,7 +1159,6 @@ defineSymbol(math, rel, "\u22d9", "\\gggtr");
|
|
1160
1159
|
defineSymbol(math, bin, "\u22b2", "\\lhd");
|
1161
1160
|
defineSymbol(math, bin, "\u22b3", "\\rhd");
|
1162
1161
|
defineSymbol(math, rel, "\u2242", "\\eqsim", true);
|
1163
|
-
defineSymbol(math, rel, "\u22c8", "\\Join");
|
1164
1162
|
defineSymbol(math, rel, "\u2251", "\\Doteq", true);
|
1165
1163
|
defineSymbol(math, rel, "\u297d", "\\strictif", true);
|
1166
1164
|
defineSymbol(math, rel, "\u297c", "\\strictfi", true);
|
@@ -1186,6 +1184,11 @@ defineSymbol(math, bin, "\u22ba", "\\intercal", true);
|
|
1186
1184
|
defineSymbol(math, bin, "\u22d2", "\\doublecap");
|
1187
1185
|
defineSymbol(math, bin, "\u22d3", "\\doublecup");
|
1188
1186
|
defineSymbol(math, bin, "\u22a0", "\\boxtimes", true);
|
1187
|
+
defineSymbol(math, bin, "\u22c8", "\\bowtie", true);
|
1188
|
+
defineSymbol(math, bin, "\u22c8", "\\Join");
|
1189
|
+
defineSymbol(math, bin, "\u27d5", "\\leftouterjoin", true);
|
1190
|
+
defineSymbol(math, bin, "\u27d6", "\\rightouterjoin", true);
|
1191
|
+
defineSymbol(math, bin, "\u27d7", "\\fullouterjoin", true);
|
1189
1192
|
|
1190
1193
|
// AMS Arrows
|
1191
1194
|
// Note: unicode-math maps \u21e2 to their own function \rightdasharrow.
|
@@ -1744,10 +1747,12 @@ for (let i = 0; i < 10; i++) {
|
|
1744
1747
|
* Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
|
1745
1748
|
* will break after a <mrow> if the expression extends beyond the container limit.
|
1746
1749
|
*
|
1747
|
-
*
|
1750
|
+
* The default is for soft line breaks after each top-level binary or
|
1748
1751
|
* relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
|
1749
1752
|
* each <mrow> ends in a binary or relational operator.
|
1750
1753
|
*
|
1754
|
+
* An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
|
1755
|
+
*
|
1751
1756
|
* Soft line breaks will not work in Chromium and Safari, only Firefox.
|
1752
1757
|
*
|
1753
1758
|
* Hopefully browsers will someday do their own linebreaking and we will be able to delete
|
@@ -1890,7 +1895,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode, color) {
|
|
1890
1895
|
}
|
1891
1896
|
|
1892
1897
|
/**
|
1893
|
-
* This file converts a parse tree into a
|
1898
|
+
* This file converts a parse tree into a corresponding MathML tree. The main
|
1894
1899
|
* entry point is the `buildMathML` function, which takes a parse tree from the
|
1895
1900
|
* parser.
|
1896
1901
|
*/
|
@@ -2093,18 +2098,6 @@ function buildMathML(tree, texExpression, style, settings) {
|
|
2093
2098
|
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
|
2094
2099
|
}
|
2095
2100
|
|
2096
|
-
if (wrap !== "none" && wrapper.children.length > 1) {
|
2097
|
-
const maths = [];
|
2098
|
-
for (let i = 0; i < wrapper.children.length; i++) {
|
2099
|
-
const math = new mathMLTree.MathNode("math", [wrapper.children[i]]);
|
2100
|
-
if (settings.xml) {
|
2101
|
-
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML");
|
2102
|
-
}
|
2103
|
-
maths.push(math);
|
2104
|
-
}
|
2105
|
-
return mathMLTree.newDocumentFragment(maths)
|
2106
|
-
}
|
2107
|
-
|
2108
2101
|
const math = new mathMLTree.MathNode("math", [wrapper]);
|
2109
2102
|
|
2110
2103
|
if (settings.xml) {
|
@@ -2112,6 +2105,9 @@ function buildMathML(tree, texExpression, style, settings) {
|
|
2112
2105
|
}
|
2113
2106
|
if (settings.displayMode) {
|
2114
2107
|
math.setAttribute("display", "block");
|
2108
|
+
math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
|
2109
|
+
? "inline"
|
2110
|
+
: "inline-block";
|
2115
2111
|
}
|
2116
2112
|
return math;
|
2117
2113
|
}
|
@@ -3521,6 +3517,11 @@ const delimiters = [
|
|
3521
3517
|
"."
|
3522
3518
|
];
|
3523
3519
|
|
3520
|
+
// Export isDelimiter for benefit of parser.
|
3521
|
+
const dels = ["}", "\\left", "\\middle", "\\right"];
|
3522
|
+
const isDelimiter = str => str.length > 0 &&
|
3523
|
+
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
|
3524
|
+
|
3524
3525
|
// Metrics of the different sizes. Found by looking at TeX's output of
|
3525
3526
|
// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
|
3526
3527
|
// Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
|
@@ -6203,10 +6204,6 @@ const noSuccessor = ["\\smallint"];
|
|
6203
6204
|
// Math operators (e.g. \sin) need a space between these types and themselves:
|
6204
6205
|
const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
|
6205
6206
|
|
6206
|
-
const dels$1 = ["}", "\\left", "\\middle", "\\right"];
|
6207
|
-
const isDelimiter$1 = str => str.length > 0 &&
|
6208
|
-
(delimiters.includes(str) || delimiterSizes[str] || dels$1.includes(str));
|
6209
|
-
|
6210
6207
|
// NOTE: Unlike most `builders`s, this one handles not only "op", but also
|
6211
6208
|
// "supsub" since some of them (like \int) can affect super/subscripting.
|
6212
6209
|
|
@@ -6427,7 +6424,7 @@ defineFunction({
|
|
6427
6424
|
parentIsSupSub: false,
|
6428
6425
|
symbol: false,
|
6429
6426
|
stack: false,
|
6430
|
-
isFollowedByDelimiter: isDelimiter
|
6427
|
+
isFollowedByDelimiter: isDelimiter(next),
|
6431
6428
|
needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
|
6432
6429
|
name: funcName
|
6433
6430
|
};
|
@@ -6452,7 +6449,7 @@ defineFunction({
|
|
6452
6449
|
parentIsSupSub: false,
|
6453
6450
|
symbol: false,
|
6454
6451
|
stack: false,
|
6455
|
-
isFollowedByDelimiter: isDelimiter
|
6452
|
+
isFollowedByDelimiter: isDelimiter(next),
|
6456
6453
|
needsLeadingSpace: prevAtomType.length > 0 && ordTypes.includes(prevAtomType),
|
6457
6454
|
name: funcName
|
6458
6455
|
};
|
@@ -6538,11 +6535,7 @@ function defineMacro(name, body) {
|
|
6538
6535
|
_macros[name] = body;
|
6539
6536
|
}
|
6540
6537
|
|
6541
|
-
|
6542
|
-
const isDelimiter = str => str.length > 0 &&
|
6543
|
-
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str));
|
6544
|
-
|
6545
|
-
// NOTE: Unlike most builders, this one handles not only
|
6538
|
+
// NOTE: Unlike most builders, this one handles not only
|
6546
6539
|
// "operatorname", but also "supsub" since \operatorname* can
|
6547
6540
|
// affect super/subscripting.
|
6548
6541
|
|
@@ -6552,8 +6545,12 @@ const mathmlBuilder$1 = (group, style) => {
|
|
6552
6545
|
// Is expression a string or has it something like a fraction?
|
6553
6546
|
let isAllString = true; // default
|
6554
6547
|
for (let i = 0; i < expression.length; i++) {
|
6555
|
-
|
6548
|
+
let node = expression[i];
|
6556
6549
|
if (node instanceof mathMLTree.MathNode) {
|
6550
|
+
if (node.type === "mrow" && node.children.length === 1 &&
|
6551
|
+
node.children[0] instanceof mathMLTree.MathNode) {
|
6552
|
+
node = node.children[0];
|
6553
|
+
}
|
6557
6554
|
switch (node.type) {
|
6558
6555
|
case "mi":
|
6559
6556
|
case "mn":
|
@@ -6611,7 +6608,9 @@ const mathmlBuilder$1 = (group, style) => {
|
|
6611
6608
|
let wrapper;
|
6612
6609
|
if (isAllString) {
|
6613
6610
|
wrapper = new mathMLTree.MathNode("mi", expression);
|
6614
|
-
|
6611
|
+
if (expression[0].text.length === 1) {
|
6612
|
+
wrapper.setAttribute("mathvariant", "normal");
|
6613
|
+
}
|
6615
6614
|
} else {
|
6616
6615
|
wrapper = new mathMLTree.MathNode("mrow", expression);
|
6617
6616
|
}
|
@@ -7149,6 +7148,7 @@ defineFunctionBuilders({
|
|
7149
7148
|
let isOver;
|
7150
7149
|
let isSup;
|
7151
7150
|
let appendApplyFunction = false;
|
7151
|
+
let appendSpace = false;
|
7152
7152
|
let needsLeadingSpace = false;
|
7153
7153
|
|
7154
7154
|
if (group.base && group.base.type === "horizBrace") {
|
@@ -7163,6 +7163,7 @@ defineFunctionBuilders({
|
|
7163
7163
|
(group.base.type === "op" || group.base.type === "operatorname")) {
|
7164
7164
|
group.base.parentIsSupSub = true;
|
7165
7165
|
appendApplyFunction = !group.base.symbol;
|
7166
|
+
appendSpace = appendApplyFunction && !group.isFollowedByDelimiter;
|
7166
7167
|
needsLeadingSpace = group.base.needsLeadingSpace;
|
7167
7168
|
}
|
7168
7169
|
|
@@ -7250,6 +7251,11 @@ defineFunctionBuilders({
|
|
7250
7251
|
} else {
|
7251
7252
|
node = mathMLTree.newDocumentFragment([node, operator]);
|
7252
7253
|
}
|
7254
|
+
if (appendSpace) {
|
7255
|
+
const space = new mathMLTree.MathNode("mspace");
|
7256
|
+
space.setAttribute("width", "0.1667em"); // thin space.
|
7257
|
+
node.children.push(space);
|
7258
|
+
}
|
7253
7259
|
} else if (symbolRegEx.test(nodeType)) {
|
7254
7260
|
// Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
|
7255
7261
|
node = new mathMLTree.MathNode("mrow", [node]);
|
@@ -8882,7 +8888,7 @@ defineMacro("\\incoh", `{\\mkern5mu\\rule{}{0.7em}\\mathrlap{\\smash{\\raise2mu{
|
|
8882
8888
|
defineMacro("\\standardstate", "\\text{\\tiny\\char`⦵}");
|
8883
8889
|
|
8884
8890
|
/* eslint-disable */
|
8885
|
-
/* -*- Mode:
|
8891
|
+
/* -*- Mode: JavaScript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
|
8886
8892
|
/* vim: set ts=2 et sw=2 tw=80: */
|
8887
8893
|
|
8888
8894
|
/*************************************************************
|
@@ -10579,7 +10585,7 @@ defineMacro("\\tripleDashBetweenDoubleLine", `\\kern0.075em\\mathrlap{\\mathrlap
|
|
10579
10585
|
};
|
10580
10586
|
|
10581
10587
|
//
|
10582
|
-
// Helpers for code
|
10588
|
+
// Helpers for code analysis
|
10583
10589
|
// Will show type error at calling position
|
10584
10590
|
//
|
10585
10591
|
/** @param {number} a */
|
@@ -11004,15 +11010,15 @@ class MacroExpander {
|
|
11004
11010
|
* Expand the next token only once if possible.
|
11005
11011
|
*
|
11006
11012
|
* If the token is expanded, the resulting tokens will be pushed onto
|
11007
|
-
* the stack in reverse order and
|
11008
|
-
*
|
11013
|
+
* the stack in reverse order, and the number of such tokens will be
|
11014
|
+
* returned. This number might be zero or positive.
|
11009
11015
|
*
|
11010
|
-
* If not, the
|
11011
|
-
*
|
11012
|
-
* instead of an `Array` return value.
|
11016
|
+
* If not, the return value is `false`, and the next token remains at the
|
11017
|
+
* top of the stack.
|
11013
11018
|
*
|
11014
11019
|
* In either case, the next token will be on the top of the stack,
|
11015
|
-
* or the stack will be empty
|
11020
|
+
* or the stack will be empty (in case of empty expansion
|
11021
|
+
* and no other tokens).
|
11016
11022
|
*
|
11017
11023
|
* Used to implement `expandAfterFuture` and `expandNextToken`.
|
11018
11024
|
*
|
@@ -11028,7 +11034,7 @@ class MacroExpander {
|
|
11028
11034
|
throw new ParseError("Undefined control sequence: " + name);
|
11029
11035
|
}
|
11030
11036
|
this.pushToken(topToken);
|
11031
|
-
return
|
11037
|
+
return false;
|
11032
11038
|
}
|
11033
11039
|
this.expansionCount++;
|
11034
11040
|
if (this.expansionCount > this.settings.maxExpand) {
|
@@ -11062,7 +11068,7 @@ class MacroExpander {
|
|
11062
11068
|
}
|
11063
11069
|
// Concatenate expansion onto top of stack.
|
11064
11070
|
this.pushTokens(tokens);
|
11065
|
-
return tokens;
|
11071
|
+
return tokens.length;
|
11066
11072
|
}
|
11067
11073
|
|
11068
11074
|
/**
|
@@ -11081,14 +11087,13 @@ class MacroExpander {
|
|
11081
11087
|
*/
|
11082
11088
|
expandNextToken() {
|
11083
11089
|
for (;;) {
|
11084
|
-
|
11085
|
-
|
11086
|
-
if (expanded instanceof Token) {
|
11090
|
+
if (this.expandOnce() === false) { // fully expanded
|
11091
|
+
const token = this.stack.pop();
|
11087
11092
|
// The token after \noexpand is interpreted as if its meaning were ‘\relax’
|
11088
|
-
if (
|
11089
|
-
|
11093
|
+
if (token.treatAsRelax) {
|
11094
|
+
token.text = "\\relax";
|
11090
11095
|
}
|
11091
|
-
return
|
11096
|
+
return token
|
11092
11097
|
}
|
11093
11098
|
}
|
11094
11099
|
|
@@ -11114,15 +11119,15 @@ class MacroExpander {
|
|
11114
11119
|
const oldStackLength = this.stack.length;
|
11115
11120
|
this.pushTokens(tokens);
|
11116
11121
|
while (this.stack.length > oldStackLength) {
|
11117
|
-
|
11118
|
-
|
11119
|
-
|
11120
|
-
if (
|
11122
|
+
// Expand only expandable tokens
|
11123
|
+
if (this.expandOnce(true) === false) { // fully expanded
|
11124
|
+
const token = this.stack.pop();
|
11125
|
+
if (token.treatAsRelax) {
|
11121
11126
|
// the expansion of \noexpand is the token itself
|
11122
|
-
|
11123
|
-
|
11127
|
+
token.noexpand = false;
|
11128
|
+
token.treatAsRelax = false;
|
11124
11129
|
}
|
11125
|
-
output.push(
|
11130
|
+
output.push(token);
|
11126
11131
|
}
|
11127
11132
|
}
|
11128
11133
|
return output;
|
@@ -11950,7 +11955,7 @@ class Parser {
|
|
11950
11955
|
* Parses an "expression", which is a list of atoms.
|
11951
11956
|
*
|
11952
11957
|
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
|
11953
|
-
* happens when functions have higher
|
11958
|
+
* happens when functions have higher precedence han infix
|
11954
11959
|
* nodes in implicit parses.
|
11955
11960
|
*
|
11956
11961
|
* `breakOnTokenText`: The text of the token that the expression should end
|
@@ -12201,12 +12206,16 @@ class Parser {
|
|
12201
12206
|
return base
|
12202
12207
|
} else {
|
12203
12208
|
// We got either a superscript or subscript, create a supsub
|
12209
|
+
const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
|
12210
|
+
? undefined
|
12211
|
+
: isDelimiter(this.nextToken.text);
|
12204
12212
|
return {
|
12205
12213
|
type: "supsub",
|
12206
12214
|
mode: this.mode,
|
12207
12215
|
base: base,
|
12208
12216
|
sup: superscript,
|
12209
|
-
sub: subscript
|
12217
|
+
sub: subscript,
|
12218
|
+
isFollowedByDelimiter
|
12210
12219
|
}
|
12211
12220
|
}
|
12212
12221
|
} else {
|
@@ -12367,7 +12376,7 @@ class Parser {
|
|
12367
12376
|
while (true) {
|
12368
12377
|
const ch = this.fetch().text;
|
12369
12378
|
// \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
|
12370
|
-
if (ch === " " || ch === "\ufe0e") {
|
12379
|
+
if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
|
12371
12380
|
this.consume();
|
12372
12381
|
} else {
|
12373
12382
|
break
|
@@ -12959,7 +12968,7 @@ class Style {
|
|
12959
12968
|
* https://mit-license.org/
|
12960
12969
|
*/
|
12961
12970
|
|
12962
|
-
const version = "0.10.
|
12971
|
+
const version = "0.10.3";
|
12963
12972
|
|
12964
12973
|
function postProcess(block) {
|
12965
12974
|
const labelMap = {};
|
package/dist/temmlPostProcess.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "temml",
|
3
|
-
"version": "0.10.
|
3
|
+
"version": "0.10.3",
|
4
4
|
"description": "TeX to MathML conversion in JavaScript.",
|
5
5
|
"main": "dist/temml.js",
|
6
6
|
"homepage": "https://temml.org",
|
@@ -8,6 +8,7 @@
|
|
8
8
|
"type": "git",
|
9
9
|
"url": "git://github.com/ronkok/Temml"
|
10
10
|
},
|
11
|
+
"packageManager": "yarn@3.3.1",
|
11
12
|
"files": [
|
12
13
|
"temml.js",
|
13
14
|
"src/",
|
package/src/MacroExpander.js
CHANGED
@@ -230,15 +230,15 @@ export default class MacroExpander {
|
|
230
230
|
* Expand the next token only once if possible.
|
231
231
|
*
|
232
232
|
* If the token is expanded, the resulting tokens will be pushed onto
|
233
|
-
* the stack in reverse order and
|
234
|
-
*
|
233
|
+
* the stack in reverse order, and the number of such tokens will be
|
234
|
+
* returned. This number might be zero or positive.
|
235
235
|
*
|
236
|
-
* If not, the
|
237
|
-
*
|
238
|
-
* instead of an `Array` return value.
|
236
|
+
* If not, the return value is `false`, and the next token remains at the
|
237
|
+
* top of the stack.
|
239
238
|
*
|
240
239
|
* In either case, the next token will be on the top of the stack,
|
241
|
-
* or the stack will be empty
|
240
|
+
* or the stack will be empty (in case of empty expansion
|
241
|
+
* and no other tokens).
|
242
242
|
*
|
243
243
|
* Used to implement `expandAfterFuture` and `expandNextToken`.
|
244
244
|
*
|
@@ -254,7 +254,7 @@ export default class MacroExpander {
|
|
254
254
|
throw new ParseError("Undefined control sequence: " + name);
|
255
255
|
}
|
256
256
|
this.pushToken(topToken);
|
257
|
-
return
|
257
|
+
return false;
|
258
258
|
}
|
259
259
|
this.expansionCount++;
|
260
260
|
if (this.expansionCount > this.settings.maxExpand) {
|
@@ -288,7 +288,7 @@ export default class MacroExpander {
|
|
288
288
|
}
|
289
289
|
// Concatenate expansion onto top of stack.
|
290
290
|
this.pushTokens(tokens);
|
291
|
-
return tokens;
|
291
|
+
return tokens.length;
|
292
292
|
}
|
293
293
|
|
294
294
|
/**
|
@@ -307,14 +307,13 @@ export default class MacroExpander {
|
|
307
307
|
*/
|
308
308
|
expandNextToken() {
|
309
309
|
for (;;) {
|
310
|
-
|
311
|
-
|
312
|
-
if (expanded instanceof Token) {
|
310
|
+
if (this.expandOnce() === false) { // fully expanded
|
311
|
+
const token = this.stack.pop();
|
313
312
|
// The token after \noexpand is interpreted as if its meaning were ‘\relax’
|
314
|
-
if (
|
315
|
-
|
313
|
+
if (token.treatAsRelax) {
|
314
|
+
token.text = "\\relax"
|
316
315
|
}
|
317
|
-
return
|
316
|
+
return token
|
318
317
|
}
|
319
318
|
}
|
320
319
|
|
@@ -340,15 +339,15 @@ export default class MacroExpander {
|
|
340
339
|
const oldStackLength = this.stack.length;
|
341
340
|
this.pushTokens(tokens);
|
342
341
|
while (this.stack.length > oldStackLength) {
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
if (
|
342
|
+
// Expand only expandable tokens
|
343
|
+
if (this.expandOnce(true) === false) { // fully expanded
|
344
|
+
const token = this.stack.pop();
|
345
|
+
if (token.treatAsRelax) {
|
347
346
|
// the expansion of \noexpand is the token itself
|
348
|
-
|
349
|
-
|
347
|
+
token.noexpand = false;
|
348
|
+
token.treatAsRelax = false;
|
350
349
|
}
|
351
|
-
output.push(
|
350
|
+
output.push(token);
|
352
351
|
}
|
353
352
|
}
|
354
353
|
return output;
|
package/src/Parser.js
CHANGED
@@ -10,6 +10,7 @@ import { uSubsAndSups, unicodeSubRegEx } from "./unicodeSupOrSub"
|
|
10
10
|
import { asciiFromScript } from "./asciiFromScript"
|
11
11
|
import SourceLocation from "./SourceLocation";
|
12
12
|
import { Token } from "./Token";
|
13
|
+
import { isDelimiter } from "./functions/delimsizing"
|
13
14
|
|
14
15
|
// Pre-evaluate both modules as unicodeSymbols require String.normalize()
|
15
16
|
import unicodeAccents from /*preval*/ "./unicodeAccents";
|
@@ -173,7 +174,7 @@ export default class Parser {
|
|
173
174
|
* Parses an "expression", which is a list of atoms.
|
174
175
|
*
|
175
176
|
* `breakOnInfix`: Should the parsing stop when we hit infix nodes? This
|
176
|
-
* happens when functions have higher
|
177
|
+
* happens when functions have higher precedence han infix
|
177
178
|
* nodes in implicit parses.
|
178
179
|
*
|
179
180
|
* `breakOnTokenText`: The text of the token that the expression should end
|
@@ -424,12 +425,16 @@ export default class Parser {
|
|
424
425
|
return base
|
425
426
|
} else {
|
426
427
|
// We got either a superscript or subscript, create a supsub
|
428
|
+
const isFollowedByDelimiter = (!base || base.type !== "op" && base.type !== "operatorname")
|
429
|
+
? undefined
|
430
|
+
: isDelimiter(this.nextToken.text);
|
427
431
|
return {
|
428
432
|
type: "supsub",
|
429
433
|
mode: this.mode,
|
430
434
|
base: base,
|
431
435
|
sup: superscript,
|
432
|
-
sub: subscript
|
436
|
+
sub: subscript,
|
437
|
+
isFollowedByDelimiter
|
433
438
|
}
|
434
439
|
}
|
435
440
|
} else {
|
@@ -590,7 +595,7 @@ export default class Parser {
|
|
590
595
|
while (true) {
|
591
596
|
const ch = this.fetch().text
|
592
597
|
// \ufe0e is the Unicode variation selector to supress emoji. Ignore it.
|
593
|
-
if (ch === " " || ch === "\ufe0e") {
|
598
|
+
if (ch === " " || ch === "\u00a0" || ch === "\ufe0e") {
|
594
599
|
this.consume()
|
595
600
|
} else {
|
596
601
|
break
|
package/src/Settings.js
CHANGED
@@ -17,7 +17,7 @@ export default class Settings {
|
|
17
17
|
this.leqno = utils.deflt(options.leqno, false); // boolean
|
18
18
|
this.errorColor = utils.deflt(options.errorColor, "#b22222"); // string
|
19
19
|
this.macros = options.macros || {};
|
20
|
-
this.wrap = utils.deflt(options.wrap, "
|
20
|
+
this.wrap = utils.deflt(options.wrap, "tex") // "tex" | "="
|
21
21
|
this.xml = utils.deflt(options.xml, false); // boolean
|
22
22
|
this.colorIsTextColor = utils.deflt(options.colorIsTextColor, false); // booelean
|
23
23
|
this.strict = utils.deflt(options.strict, false); // boolean
|
package/src/buildMathML.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* This file converts a parse tree into a
|
2
|
+
* This file converts a parse tree into a corresponding MathML tree. The main
|
3
3
|
* entry point is the `buildMathML` function, which takes a parse tree from the
|
4
4
|
* parser.
|
5
5
|
*/
|
@@ -209,18 +209,6 @@ export default function buildMathML(tree, texExpression, style, settings) {
|
|
209
209
|
wrapper = new mathMLTree.MathNode("semantics", [wrapper, annotation]);
|
210
210
|
}
|
211
211
|
|
212
|
-
if (wrap !== "none" && wrapper.children.length > 1) {
|
213
|
-
const maths = []
|
214
|
-
for (let i = 0; i < wrapper.children.length; i++) {
|
215
|
-
const math = new mathMLTree.MathNode("math", [wrapper.children[i]])
|
216
|
-
if (settings.xml) {
|
217
|
-
math.setAttribute("xmlns", "http://www.w3.org/1998/Math/MathML")
|
218
|
-
}
|
219
|
-
maths.push(math)
|
220
|
-
}
|
221
|
-
return mathMLTree.newDocumentFragment(maths)
|
222
|
-
}
|
223
|
-
|
224
212
|
const math = new mathMLTree.MathNode("math", [wrapper])
|
225
213
|
|
226
214
|
if (settings.xml) {
|
@@ -228,6 +216,9 @@ export default function buildMathML(tree, texExpression, style, settings) {
|
|
228
216
|
}
|
229
217
|
if (settings.displayMode) {
|
230
218
|
math.setAttribute("display", "block");
|
219
|
+
math.style.display = math.children.length === 1 && math.children[0].type === "mtable"
|
220
|
+
? "inline"
|
221
|
+
: "inline-block"
|
231
222
|
}
|
232
223
|
return math;
|
233
224
|
}
|
@@ -89,6 +89,11 @@ export const delimiters = [
|
|
89
89
|
"."
|
90
90
|
];
|
91
91
|
|
92
|
+
// Export isDelimiter for benefit of parser.
|
93
|
+
const dels = ["}", "\\left", "\\middle", "\\right"]
|
94
|
+
export const isDelimiter = str => str.length > 0 &&
|
95
|
+
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
|
96
|
+
|
92
97
|
// Metrics of the different sizes. Found by looking at TeX's output of
|
93
98
|
// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
|
94
99
|
// Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
|
package/src/functions/op.js
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
import defineFunction, { ordargument } from "../defineFunction";
|
3
3
|
import * as mathMLTree from "../mathMLTree";
|
4
4
|
import * as mml from "../buildMathML";
|
5
|
-
import {
|
5
|
+
import { isDelimiter } from "./delimsizing"
|
6
6
|
|
7
7
|
// Some helpers
|
8
8
|
|
@@ -14,10 +14,6 @@ const noSuccessor = ["\\smallint"];
|
|
14
14
|
// Math operators (e.g. \sin) need a space between these types and themselves:
|
15
15
|
export const ordTypes = ["textord", "mathord", "ordgroup", "close", "leftright"];
|
16
16
|
|
17
|
-
const dels = ["}", "\\left", "\\middle", "\\right"]
|
18
|
-
const isDelimiter = str => str.length > 0 &&
|
19
|
-
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
|
20
|
-
|
21
17
|
// NOTE: Unlike most `builders`s, this one handles not only "op", but also
|
22
18
|
// "supsub" since some of them (like \int) can affect super/subscripting.
|
23
19
|
|
@@ -3,15 +3,11 @@ import defineMacro from "../defineMacro";
|
|
3
3
|
import mathMLTree from "../mathMLTree"
|
4
4
|
import { spaceCharacter } from "./kern"
|
5
5
|
import { ordTypes } from "./op"
|
6
|
-
import {
|
6
|
+
import { isDelimiter } from "./delimsizing"
|
7
7
|
|
8
8
|
import * as mml from "../buildMathML"
|
9
9
|
|
10
|
-
|
11
|
-
const isDelimiter = str => str.length > 0 &&
|
12
|
-
(delimiters.includes(str) || delimiterSizes[str] || dels.includes(str))
|
13
|
-
|
14
|
-
// NOTE: Unlike most builders, this one handles not only
|
10
|
+
// NOTE: Unlike most builders, this one handles not only
|
15
11
|
// "operatorname", but also "supsub" since \operatorname* can
|
16
12
|
// affect super/subscripting.
|
17
13
|
|
@@ -21,8 +17,12 @@ const mathmlBuilder = (group, style) => {
|
|
21
17
|
// Is expression a string or has it something like a fraction?
|
22
18
|
let isAllString = true; // default
|
23
19
|
for (let i = 0; i < expression.length; i++) {
|
24
|
-
|
20
|
+
let node = expression[i]
|
25
21
|
if (node instanceof mathMLTree.MathNode) {
|
22
|
+
if (node.type === "mrow" && node.children.length === 1 &&
|
23
|
+
node.children[0] instanceof mathMLTree.MathNode) {
|
24
|
+
node = node.children[0]
|
25
|
+
}
|
26
26
|
switch (node.type) {
|
27
27
|
case "mi":
|
28
28
|
case "mn":
|
@@ -80,7 +80,9 @@ const mathmlBuilder = (group, style) => {
|
|
80
80
|
let wrapper;
|
81
81
|
if (isAllString) {
|
82
82
|
wrapper = new mathMLTree.MathNode("mi", expression)
|
83
|
-
|
83
|
+
if (expression[0].text.length === 1) {
|
84
|
+
wrapper.setAttribute("mathvariant", "normal")
|
85
|
+
}
|
84
86
|
} else {
|
85
87
|
wrapper = new mathMLTree.MathNode("mrow", expression)
|
86
88
|
}
|
package/src/functions/supsub.js
CHANGED
@@ -24,6 +24,7 @@ defineFunctionBuilders({
|
|
24
24
|
let isOver
|
25
25
|
let isSup
|
26
26
|
let appendApplyFunction = false
|
27
|
+
let appendSpace = false
|
27
28
|
let needsLeadingSpace = false
|
28
29
|
|
29
30
|
if (group.base && group.base.type === "horizBrace") {
|
@@ -38,6 +39,7 @@ defineFunctionBuilders({
|
|
38
39
|
(group.base.type === "op" || group.base.type === "operatorname")) {
|
39
40
|
group.base.parentIsSupSub = true
|
40
41
|
appendApplyFunction = !group.base.symbol
|
42
|
+
appendSpace = appendApplyFunction && !group.isFollowedByDelimiter
|
41
43
|
needsLeadingSpace = group.base.needsLeadingSpace
|
42
44
|
}
|
43
45
|
|
@@ -125,6 +127,11 @@ defineFunctionBuilders({
|
|
125
127
|
} else {
|
126
128
|
node = mathMLTree.newDocumentFragment([node, operator])
|
127
129
|
}
|
130
|
+
if (appendSpace) {
|
131
|
+
const space = new mathMLTree.MathNode("mspace")
|
132
|
+
space.setAttribute("width", "0.1667em") // thin space.
|
133
|
+
node.children.push(space)
|
134
|
+
}
|
128
135
|
} else if (symbolRegEx.test(nodeType)) {
|
129
136
|
// Wrap in a <mrow>. Otherwise Firefox stretchy parens will not stretch to include limits.
|
130
137
|
node = new mathMLTree.MathNode("mrow", [node])
|
package/src/linebreaking.js
CHANGED
@@ -13,10 +13,12 @@ import mathMLTree from "./mathMLTree"
|
|
13
13
|
* Then the top level of a <math> element can be occupied by <mrow> elements, and the browser
|
14
14
|
* will break after a <mrow> if the expression extends beyond the container limit.
|
15
15
|
*
|
16
|
-
*
|
16
|
+
* The default is for soft line breaks after each top-level binary or
|
17
17
|
* relational operator, per TeXbook p. 173. So we gather the expression into <mrow>s so that
|
18
18
|
* each <mrow> ends in a binary or relational operator.
|
19
19
|
*
|
20
|
+
* An option is for soft line breaks before an "=" sign. That changes the <mrow>s.
|
21
|
+
*
|
20
22
|
* Soft line breaks will not work in Chromium and Safari, only Firefox.
|
21
23
|
*
|
22
24
|
* Hopefully browsers will someday do their own linebreaking and we will be able to delete
|