katex 0.10.0-rc → 0.10.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/CHANGELOG.md +214 -126
- package/README.md +18 -17
- package/cli.js +5 -1
- package/contrib/auto-render/README.md +1 -1
- package/contrib/auto-render/auto-render.js +4 -1
- package/contrib/auto-render/test/auto-render-spec.js +17 -0
- package/contrib/copy-tex/README.md +8 -2
- package/contrib/copy-tex/copy-tex.js +0 -1
- package/contrib/copy-tex/copy-tex.webpack.js +6 -0
- package/contrib/mathtex-script-type/README.md +10 -6
- package/contrib/mhchem/README.md +19 -0
- package/contrib/mhchem/mhchem.js +1695 -0
- package/contrib/mhchem/mhchem.patch +235 -0
- package/dist/README.md +18 -17
- package/dist/contrib/auto-render.js +179 -161
- package/dist/contrib/auto-render.min.js +1 -1
- package/dist/contrib/auto-render.mjs +215 -0
- package/dist/contrib/copy-tex.js +84 -62
- package/dist/contrib/copy-tex.min.css +1 -1
- package/dist/contrib/copy-tex.min.js +1 -1
- package/dist/contrib/copy-tex.mjs +85 -0
- package/dist/contrib/mathtex-script-type.js +17 -14
- package/dist/contrib/mathtex-script-type.mjs +24 -0
- package/dist/contrib/mhchem.js +3241 -0
- package/dist/contrib/mhchem.min.js +1 -0
- package/dist/contrib/mhchem.mjs +3109 -0
- package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/katex.css +24 -9
- package/dist/katex.js +13295 -12413
- package/dist/katex.min.css +1 -1
- package/dist/katex.min.js +1 -1
- package/dist/katex.mjs +13388 -11826
- package/katex.js +1 -2
- package/package.json +60 -48
- package/src/Lexer.js +25 -25
- package/src/MacroExpander.js +0 -1
- package/src/Options.js +11 -75
- package/src/Parser.js +231 -313
- package/src/Settings.js +6 -0
- package/src/buildCommon.js +140 -103
- package/src/buildHTML.js +125 -121
- package/src/buildMathML.js +14 -4
- package/src/buildTree.js +16 -10
- package/src/delimiter.js +4 -3
- package/src/domTree.js +91 -44
- package/src/environments/array.js +120 -7
- package/src/fontMetrics.js +3 -2
- package/src/functions/arrow.js +21 -7
- package/src/functions/color.js +2 -37
- package/src/functions/delimsizing.js +18 -11
- package/src/functions/enclose.js +19 -4
- package/src/functions/environment.js +35 -4
- package/src/functions/font.js +1 -2
- package/src/functions/genfrac.js +35 -20
- package/src/functions/href.js +5 -3
- package/src/functions/includegraphics.js +146 -0
- package/src/functions/mclass.js +1 -0
- package/src/functions/op.js +21 -32
- package/src/functions/operatorname.js +1 -2
- package/src/functions/ordgroup.js +4 -0
- package/src/functions/phantom.js +7 -3
- package/src/functions/rule.js +20 -9
- package/src/functions/sizing.js +2 -4
- package/src/functions/smash.js +5 -2
- package/src/functions/sqrt.js +1 -4
- package/src/functions/styling.js +0 -1
- package/src/functions/supsub.js +6 -2
- package/src/functions/symbolsOp.js +4 -0
- package/src/functions/symbolsSpacing.js +29 -6
- package/src/functions/tag.js +20 -4
- package/src/functions/text.js +6 -4
- package/src/functions/verb.js +16 -4
- package/src/functions.js +2 -0
- package/src/katex.less +35 -12
- package/src/macros.js +161 -36
- package/src/mathMLTree.js +17 -19
- package/src/parseNode.js +27 -1
- package/src/stretchy.js +3 -1
- package/src/svgGeometry.js +1 -1
- package/src/symbols.js +39 -17
- package/src/tree.js +0 -4
- package/src/types.js +4 -3
- package/src/unicodeMake.js +1 -1
- package/src/utils.js +1 -62
- package/src/wide-character.js +2 -2
package/src/buildHTML.js
CHANGED
|
@@ -10,7 +10,7 @@ import ParseError from "./ParseError";
|
|
|
10
10
|
import Style from "./Style";
|
|
11
11
|
import buildCommon from "./buildCommon";
|
|
12
12
|
import {Anchor} from "./domTree";
|
|
13
|
-
import utils
|
|
13
|
+
import utils from "./utils";
|
|
14
14
|
import {checkNodeType} from "./parseNode";
|
|
15
15
|
import {spacings, tightSpacings} from "./spacingData";
|
|
16
16
|
import {_htmlGroupBuilders as groupBuilders} from "./defineFunction";
|
|
@@ -25,32 +25,8 @@ const makeSpan = buildCommon.makeSpan;
|
|
|
25
25
|
// Binary atoms (first class `mbin`) change into ordinary atoms (`mord`)
|
|
26
26
|
// depending on their surroundings. See TeXbook pg. 442-446, Rules 5 and 6,
|
|
27
27
|
// and the text before Rule 19.
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
isRealGroup: boolean,
|
|
31
|
-
): boolean {
|
|
32
|
-
// TODO: This code assumes that a node's math class is the first element
|
|
33
|
-
// of its `classes` array. A later cleanup should ensure this, for
|
|
34
|
-
// instance by changing the signature of `makeSpan`.
|
|
35
|
-
if (node) {
|
|
36
|
-
return utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
|
|
37
|
-
getTypeOfDomTree(node, "right"));
|
|
38
|
-
} else {
|
|
39
|
-
return isRealGroup;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const isBinRightCanceller = function(
|
|
44
|
-
node: ?HtmlDomNode,
|
|
45
|
-
isRealGroup: boolean,
|
|
46
|
-
): boolean {
|
|
47
|
-
if (node) {
|
|
48
|
-
return utils.contains(["mrel", "mclose", "mpunct"],
|
|
49
|
-
getTypeOfDomTree(node, "left"));
|
|
50
|
-
} else {
|
|
51
|
-
return isRealGroup;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
28
|
+
const binLeftCanceller = ["leftmost", "mbin", "mopen", "mrel", "mop", "mpunct"];
|
|
29
|
+
const binRightCanceller = ["rightmost", "mrel", "mclose", "mpunct"];
|
|
54
30
|
|
|
55
31
|
const styleMap = {
|
|
56
32
|
"display": Style.DISPLAY,
|
|
@@ -71,7 +47,7 @@ const DomEnum = {
|
|
|
71
47
|
mpunct: "mpunct",
|
|
72
48
|
minner: "minner",
|
|
73
49
|
};
|
|
74
|
-
|
|
50
|
+
type DomType = $Keys<typeof DomEnum>;
|
|
75
51
|
|
|
76
52
|
/**
|
|
77
53
|
* Take a list of nodes, build them in order, and return a list of the built
|
|
@@ -88,101 +64,135 @@ export const buildExpression = function(
|
|
|
88
64
|
surrounding: [?DomType, ?DomType] = [null, null],
|
|
89
65
|
): HtmlDomNode[] {
|
|
90
66
|
// Parse expressions into `groups`.
|
|
91
|
-
const
|
|
67
|
+
const groups: HtmlDomNode[] = [];
|
|
92
68
|
for (let i = 0; i < expression.length; i++) {
|
|
93
69
|
const output = buildGroup(expression[i], options);
|
|
94
70
|
if (output instanceof DocumentFragment) {
|
|
95
71
|
const children: HtmlDomNode[] = output.children;
|
|
96
|
-
|
|
72
|
+
groups.push(...children);
|
|
97
73
|
} else {
|
|
98
|
-
|
|
74
|
+
groups.push(output);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If `expression` is a partial group, let the parent handle spacings
|
|
79
|
+
// to avoid processing groups multiple times.
|
|
80
|
+
if (!isRealGroup) {
|
|
81
|
+
return groups;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let glueOptions = options;
|
|
85
|
+
if (expression.length === 1) {
|
|
86
|
+
const node = checkNodeType(expression[0], "sizing") ||
|
|
87
|
+
checkNodeType(expression[0], "styling");
|
|
88
|
+
if (!node) {
|
|
89
|
+
// No match.
|
|
90
|
+
} else if (node.type === "sizing") {
|
|
91
|
+
glueOptions = options.havingSize(node.size);
|
|
92
|
+
} else if (node.type === "styling") {
|
|
93
|
+
glueOptions = options.havingStyle(styleMap[node.style]);
|
|
99
94
|
}
|
|
100
95
|
}
|
|
101
|
-
// At this point `rawGroups` consists entirely of `symbolNode`s and `span`s.
|
|
102
96
|
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
// Dummy spans for determining spacings between surrounding atoms.
|
|
98
|
+
// If `expression` has no atoms on the left or right, class "leftmost"
|
|
99
|
+
// or "rightmost", respectively, is used to indicate it.
|
|
100
|
+
const dummyPrev = makeSpan([surrounding[0] || "leftmost"], [], options);
|
|
101
|
+
const dummyNext = makeSpan([surrounding[1] || "rightmost"], [], options);
|
|
102
|
+
|
|
103
|
+
// TODO: These code assumes that a node's math class is the first element
|
|
104
|
+
// of its `classes` array. A later cleanup should ensure this, for
|
|
105
|
+
// instance by changing the signature of `makeSpan`.
|
|
111
106
|
|
|
112
107
|
// Before determining what spaces to insert, perform bin cancellation.
|
|
113
108
|
// Binary operators change to ordinary symbols in some contexts.
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
109
|
+
traverseNonSpaceNodes(groups, (node, prev) => {
|
|
110
|
+
const prevType = prev.classes[0];
|
|
111
|
+
const type = node.classes[0];
|
|
112
|
+
if (prevType === "mbin" && utils.contains(binRightCanceller, type)) {
|
|
113
|
+
prev.classes[0] = "mord";
|
|
114
|
+
} else if (type === "mbin" && utils.contains(binLeftCanceller, prevType)) {
|
|
115
|
+
node.classes[0] = "mord";
|
|
120
116
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
117
|
+
}, {node: dummyPrev}, dummyNext);
|
|
118
|
+
|
|
119
|
+
traverseNonSpaceNodes(groups, (node, prev) => {
|
|
120
|
+
const prevType = getTypeOfDomTree(prev);
|
|
121
|
+
const type = getTypeOfDomTree(node);
|
|
122
|
+
|
|
123
|
+
// 'mtight' indicates that the node is script or scriptscript style.
|
|
124
|
+
const space = prevType && type ? (node.hasClass("mtight")
|
|
125
|
+
? tightSpacings[prevType][type]
|
|
126
|
+
: spacings[prevType][type]) : null;
|
|
127
|
+
if (space) { // Insert glue (spacing) after the `prev`.
|
|
128
|
+
return buildCommon.makeGlue(space, glueOptions);
|
|
126
129
|
}
|
|
130
|
+
}, {node: dummyPrev}, dummyNext);
|
|
131
|
+
|
|
132
|
+
return groups;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Depth-first traverse non-space `nodes`, calling `callback` with the current and
|
|
136
|
+
// previous node as arguments, optionally returning a node to insert after the
|
|
137
|
+
// previous node. `prev` is an object with the previous node and `insertAfter`
|
|
138
|
+
// function to insert after it. `next` is a node that will be added to the right.
|
|
139
|
+
// Used for bin cancellation and inserting spacings.
|
|
140
|
+
const traverseNonSpaceNodes = function(
|
|
141
|
+
nodes: HtmlDomNode[],
|
|
142
|
+
callback: (HtmlDomNode, HtmlDomNode) => ?HtmlDomNode,
|
|
143
|
+
prev: {|
|
|
144
|
+
node: HtmlDomNode,
|
|
145
|
+
insertAfter?: HtmlDomNode => void,
|
|
146
|
+
|},
|
|
147
|
+
next: ?HtmlDomNode,
|
|
148
|
+
) {
|
|
149
|
+
if (next) { // temporarily append the right node, if exists
|
|
150
|
+
nodes.push(next);
|
|
127
151
|
}
|
|
152
|
+
let i = 0;
|
|
153
|
+
for (; i < nodes.length; i++) {
|
|
154
|
+
const node = nodes[i];
|
|
155
|
+
const partialGroup = checkPartialGroup(node);
|
|
156
|
+
if (partialGroup) { // Recursive DFS
|
|
157
|
+
traverseNonSpaceNodes(partialGroup.children, callback, prev);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
128
160
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// For any group that is not a space, get the next non-space. Then
|
|
135
|
-
// lookup what implicit space should be placed between those atoms and
|
|
136
|
-
// add it to groups.
|
|
137
|
-
if (rawGroups[i].classes[0] !== "mspace" && j < nonSpaces.length - 1) {
|
|
138
|
-
// if current non-space node is left dummy span, add a glue before
|
|
139
|
-
// first real non-space node
|
|
140
|
-
if (j === 0) {
|
|
141
|
-
groups.pop();
|
|
142
|
-
i--;
|
|
143
|
-
}
|
|
161
|
+
// Ignore explicit spaces (e.g., \;, \,) when determining what implicit
|
|
162
|
+
// spacing should go between atoms of different classes
|
|
163
|
+
if (node.classes[0] === "mspace") {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
144
166
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// We use buildExpression inside of sizingGroup, but it returns a
|
|
154
|
-
// document fragment of elements. sizingGroup sets `isRealGroup`
|
|
155
|
-
// to false to avoid processing spans multiple times.
|
|
156
|
-
if (left && right && isRealGroup) {
|
|
157
|
-
const nonSpacesJp1: HtmlDomNode = assert(nonSpaces[j + 1]);
|
|
158
|
-
const space = isLeftTight(nonSpacesJp1)
|
|
159
|
-
? tightSpacings[left][right]
|
|
160
|
-
: spacings[left][right];
|
|
161
|
-
|
|
162
|
-
if (space) {
|
|
163
|
-
let glueOptions = options;
|
|
164
|
-
|
|
165
|
-
if (expression.length === 1) {
|
|
166
|
-
const node =
|
|
167
|
-
checkNodeType(expression[0], "sizing") ||
|
|
168
|
-
checkNodeType(expression[0], "styling");
|
|
169
|
-
if (!node) {
|
|
170
|
-
// No match.
|
|
171
|
-
} else if (node.type === "sizing") {
|
|
172
|
-
glueOptions = options.havingSize(node.size);
|
|
173
|
-
} else if (node.type === "styling") {
|
|
174
|
-
glueOptions = options.havingStyle(styleMap[node.style]);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
groups.push(buildCommon.makeGlue(space, glueOptions));
|
|
179
|
-
}
|
|
167
|
+
const result = callback(node, prev.node);
|
|
168
|
+
if (result) {
|
|
169
|
+
if (prev.insertAfter) {
|
|
170
|
+
prev.insertAfter(result);
|
|
171
|
+
} else { // insert at front
|
|
172
|
+
nodes.unshift(result);
|
|
173
|
+
i++;
|
|
180
174
|
}
|
|
181
|
-
j++;
|
|
182
175
|
}
|
|
176
|
+
|
|
177
|
+
prev.node = node;
|
|
178
|
+
prev.insertAfter = (index => n => {
|
|
179
|
+
nodes.splice(index + 1, 0, n);
|
|
180
|
+
i++;
|
|
181
|
+
})(i);
|
|
183
182
|
}
|
|
183
|
+
if (next) {
|
|
184
|
+
nodes.pop();
|
|
185
|
+
}
|
|
186
|
+
};
|
|
184
187
|
|
|
185
|
-
|
|
188
|
+
// Check if given node is a partial group, i.e., does not affect spacing around.
|
|
189
|
+
const checkPartialGroup = function(
|
|
190
|
+
node: HtmlDomNode,
|
|
191
|
+
): ?(DocumentFragment<HtmlDomNode> | Anchor) {
|
|
192
|
+
if (node instanceof DocumentFragment || node instanceof Anchor) {
|
|
193
|
+
return node;
|
|
194
|
+
}
|
|
195
|
+
return null;
|
|
186
196
|
};
|
|
187
197
|
|
|
188
198
|
// Return the outermost node of a domTree.
|
|
@@ -190,14 +200,14 @@ const getOutermostNode = function(
|
|
|
190
200
|
node: HtmlDomNode,
|
|
191
201
|
side: Side,
|
|
192
202
|
): HtmlDomNode {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const children =
|
|
203
|
+
const partialGroup = checkPartialGroup(node);
|
|
204
|
+
if (partialGroup) {
|
|
205
|
+
const children = partialGroup.children;
|
|
196
206
|
if (children.length) {
|
|
197
207
|
if (side === "right") {
|
|
198
208
|
return getOutermostNode(children[children.length - 1], "right");
|
|
199
209
|
} else if (side === "left") {
|
|
200
|
-
return getOutermostNode(children[0], "
|
|
210
|
+
return getOutermostNode(children[0], "left");
|
|
201
211
|
}
|
|
202
212
|
}
|
|
203
213
|
}
|
|
@@ -205,29 +215,22 @@ const getOutermostNode = function(
|
|
|
205
215
|
};
|
|
206
216
|
|
|
207
217
|
// Return math atom class (mclass) of a domTree.
|
|
218
|
+
// If `side` is given, it will get the type of the outermost node at given side.
|
|
208
219
|
export const getTypeOfDomTree = function(
|
|
209
220
|
node: ?HtmlDomNode,
|
|
210
|
-
side: Side,
|
|
221
|
+
side: ?Side,
|
|
211
222
|
): ?DomType {
|
|
212
223
|
if (!node) {
|
|
213
224
|
return null;
|
|
214
225
|
}
|
|
215
|
-
|
|
216
|
-
|
|
226
|
+
if (side) {
|
|
227
|
+
node = getOutermostNode(node, side);
|
|
228
|
+
}
|
|
217
229
|
// This makes a lot of assumptions as to where the type of atom
|
|
218
230
|
// appears. We should do a better job of enforcing this.
|
|
219
231
|
return DomEnum[node.classes[0]] || null;
|
|
220
232
|
};
|
|
221
233
|
|
|
222
|
-
// If `node` is an atom return whether it's been assigned the mtight class.
|
|
223
|
-
// If `node` is a document fragment, return the value of isLeftTight() for the
|
|
224
|
-
// leftmost node in the fragment.
|
|
225
|
-
// 'mtight' indicates that the node is script or scriptscript style.
|
|
226
|
-
export const isLeftTight = function(node: HtmlDomNode): boolean {
|
|
227
|
-
node = getOutermostNode(node, "left");
|
|
228
|
-
return node.hasClass("mtight");
|
|
229
|
-
};
|
|
230
|
-
|
|
231
234
|
export const makeNullDelimiter = function(
|
|
232
235
|
options: Options,
|
|
233
236
|
classes: string[],
|
|
@@ -331,10 +334,11 @@ export default function buildHTML(tree: AnyParseNode[], options: Options): DomSp
|
|
|
331
334
|
expression[i].hasClass("mrel") ||
|
|
332
335
|
expression[i].hasClass("allowbreak")) {
|
|
333
336
|
// Put any post-operator glue on same line as operator.
|
|
334
|
-
// Watch for \nobreak along the way.
|
|
337
|
+
// Watch for \nobreak along the way, and stop at \newline.
|
|
335
338
|
let nobreak = false;
|
|
336
339
|
while (i < expression.length - 1 &&
|
|
337
|
-
expression[i + 1].hasClass("mspace")
|
|
340
|
+
expression[i + 1].hasClass("mspace") &&
|
|
341
|
+
!expression[i + 1].hasClass("newline")) {
|
|
338
342
|
i++;
|
|
339
343
|
parts.push(expression[i]);
|
|
340
344
|
if (expression[i].hasClass("nobreak")) {
|
package/src/buildMathML.js
CHANGED
|
@@ -85,7 +85,7 @@ export const getVariant = function(
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const font = options.font;
|
|
88
|
-
if (!font) {
|
|
88
|
+
if (!font || font === "mathnormal") {
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -145,14 +145,24 @@ export const buildExpression = function(
|
|
|
145
145
|
lastGroup.children.push(...group.children);
|
|
146
146
|
continue;
|
|
147
147
|
}
|
|
148
|
+
} else if (lastGroup.type === 'mi' && lastGroup.children.length === 1) {
|
|
149
|
+
const lastChild = lastGroup.children[0];
|
|
150
|
+
if (lastChild instanceof TextNode && lastChild.text === '\u0338' &&
|
|
151
|
+
(group.type === 'mo' || group.type === 'mi' ||
|
|
152
|
+
group.type === 'mn')) {
|
|
153
|
+
const child = group.children[0];
|
|
154
|
+
if (child instanceof TextNode && child.text.length > 0) {
|
|
155
|
+
// Overlay with combining character long solidus
|
|
156
|
+
child.text = child.text.slice(0, 1) + "\u0338" +
|
|
157
|
+
child.text.slice(1);
|
|
158
|
+
groups.pop();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
163
|
groups.push(group);
|
|
151
164
|
lastGroup = group;
|
|
152
165
|
}
|
|
153
|
-
|
|
154
|
-
// TODO(kevinb): combine \\not with mrels and mords
|
|
155
|
-
|
|
156
166
|
return groups;
|
|
157
167
|
};
|
|
158
168
|
|
package/src/buildTree.js
CHANGED
|
@@ -16,6 +16,20 @@ const optionsFromSettings = function(settings: Settings) {
|
|
|
16
16
|
});
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
const displayWrap = function(node: DomSpan, settings: Settings): DomSpan {
|
|
20
|
+
if (settings.displayMode) {
|
|
21
|
+
const classes = ["katex-display"];
|
|
22
|
+
if (settings.leqno) {
|
|
23
|
+
classes.push("leqno");
|
|
24
|
+
}
|
|
25
|
+
if (settings.fleqn) {
|
|
26
|
+
classes.push("fleqn");
|
|
27
|
+
}
|
|
28
|
+
node = buildCommon.makeSpan(classes, [node]);
|
|
29
|
+
}
|
|
30
|
+
return node;
|
|
31
|
+
};
|
|
32
|
+
|
|
19
33
|
export const buildTree = function(
|
|
20
34
|
tree: AnyParseNode[],
|
|
21
35
|
expression: string,
|
|
@@ -29,11 +43,7 @@ export const buildTree = function(
|
|
|
29
43
|
mathMLNode, htmlNode,
|
|
30
44
|
]);
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
return buildCommon.makeSpan(["katex-display"], [katexNode]);
|
|
34
|
-
} else {
|
|
35
|
-
return katexNode;
|
|
36
|
-
}
|
|
46
|
+
return displayWrap(katexNode, settings);
|
|
37
47
|
};
|
|
38
48
|
|
|
39
49
|
export const buildHTMLTree = function(
|
|
@@ -44,11 +54,7 @@ export const buildHTMLTree = function(
|
|
|
44
54
|
const options = optionsFromSettings(settings);
|
|
45
55
|
const htmlNode = buildHTML(tree, options);
|
|
46
56
|
const katexNode = buildCommon.makeSpan(["katex"], [htmlNode]);
|
|
47
|
-
|
|
48
|
-
return buildCommon.makeSpan(["katex-display"], [katexNode]);
|
|
49
|
-
} else {
|
|
50
|
-
return katexNode;
|
|
51
|
-
}
|
|
57
|
+
return displayWrap(katexNode, settings);
|
|
52
58
|
};
|
|
53
59
|
|
|
54
60
|
export default buildTree;
|
package/src/delimiter.js
CHANGED
|
@@ -245,12 +245,12 @@ const makeStackedDelim = function(
|
|
|
245
245
|
top = "\u23a4";
|
|
246
246
|
repeat = bottom = "\u23a5";
|
|
247
247
|
font = "Size4-Regular";
|
|
248
|
-
} else if (delim === "(") {
|
|
248
|
+
} else if (delim === "(" || delim === "\\lparen") {
|
|
249
249
|
top = "\u239b";
|
|
250
250
|
repeat = "\u239c";
|
|
251
251
|
bottom = "\u239d";
|
|
252
252
|
font = "Size4-Regular";
|
|
253
|
-
} else if (delim === ")") {
|
|
253
|
+
} else if (delim === ")" || delim === "\\rparen") {
|
|
254
254
|
top = "\u239e";
|
|
255
255
|
repeat = "\u239f";
|
|
256
256
|
bottom = "\u23a0";
|
|
@@ -489,7 +489,8 @@ const makeSqrtImage = function(
|
|
|
489
489
|
// There are three kinds of delimiters, delimiters that stack when they become
|
|
490
490
|
// too large
|
|
491
491
|
const stackLargeDelimiters = [
|
|
492
|
-
"(", "
|
|
492
|
+
"(", "\\lparen", ")", "\\rparen",
|
|
493
|
+
"[", "\\lbrack", "]", "\\rbrack",
|
|
493
494
|
"\\{", "\\lbrace", "\\}", "\\rbrace",
|
|
494
495
|
"\\lfloor", "\\rfloor", "\u230a", "\u230b",
|
|
495
496
|
"\\lceil", "\\rceil", "\u2308", "\u2309",
|
package/src/domTree.js
CHANGED
|
@@ -24,7 +24,7 @@ import type {VirtualNode} from "./tree";
|
|
|
24
24
|
* Create an HTML className based on a list of classes. In addition to joining
|
|
25
25
|
* with spaces, we also remove empty classes.
|
|
26
26
|
*/
|
|
27
|
-
const createClass = function(classes: string[]): string {
|
|
27
|
+
export const createClass = function(classes: string[]): string {
|
|
28
28
|
return classes.filter(cls => cls).join(" ");
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -125,7 +125,33 @@ const toMarkup = function(tagName: string): string {
|
|
|
125
125
|
return markup;
|
|
126
126
|
};
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
// Making the type below exact with all optional fields doesn't work due to
|
|
129
|
+
// - https://github.com/facebook/flow/issues/4582
|
|
130
|
+
// - https://github.com/facebook/flow/issues/5688
|
|
131
|
+
// However, since *all* fields are optional, $Shape<> works as suggested in 5688
|
|
132
|
+
// above.
|
|
133
|
+
// This type does not include all CSS properties. Additional properties should
|
|
134
|
+
// be added as needed.
|
|
135
|
+
export type CssStyle = $Shape<{
|
|
136
|
+
backgroundColor: string,
|
|
137
|
+
borderBottomWidth: string,
|
|
138
|
+
borderColor: string,
|
|
139
|
+
borderRightWidth: string,
|
|
140
|
+
borderTopWidth: string,
|
|
141
|
+
bottom: string,
|
|
142
|
+
color: string,
|
|
143
|
+
height: string,
|
|
144
|
+
left: string,
|
|
145
|
+
marginLeft: string,
|
|
146
|
+
marginRight: string,
|
|
147
|
+
marginTop: string,
|
|
148
|
+
minWidth: string,
|
|
149
|
+
paddingLeft: string,
|
|
150
|
+
position: string,
|
|
151
|
+
top: string,
|
|
152
|
+
width: string,
|
|
153
|
+
verticalAlign: string,
|
|
154
|
+
}> & {};
|
|
129
155
|
|
|
130
156
|
export interface HtmlDomNode extends VirtualNode {
|
|
131
157
|
classes: string[];
|
|
@@ -135,7 +161,6 @@ export interface HtmlDomNode extends VirtualNode {
|
|
|
135
161
|
style: CssStyle;
|
|
136
162
|
|
|
137
163
|
hasClass(className: string): boolean;
|
|
138
|
-
tryCombine(sibling: HtmlDomNode): boolean;
|
|
139
164
|
}
|
|
140
165
|
|
|
141
166
|
// Span wrapping other DOM nodes.
|
|
@@ -189,15 +214,6 @@ export class Span<ChildType: VirtualNode> implements HtmlDomNode {
|
|
|
189
214
|
return utils.contains(this.classes, className);
|
|
190
215
|
}
|
|
191
216
|
|
|
192
|
-
/**
|
|
193
|
-
* Try to combine with given sibling. Returns true if the sibling has
|
|
194
|
-
* been successfully merged into this node, and false otherwise.
|
|
195
|
-
* Default behavior fails (returns false).
|
|
196
|
-
*/
|
|
197
|
-
tryCombine(sibling: HtmlDomNode): boolean {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
217
|
toNode(): HTMLElement {
|
|
202
218
|
return toNode.call(this, "span");
|
|
203
219
|
}
|
|
@@ -239,10 +255,6 @@ export class Anchor implements HtmlDomNode {
|
|
|
239
255
|
return utils.contains(this.classes, className);
|
|
240
256
|
}
|
|
241
257
|
|
|
242
|
-
tryCombine(sibling: HtmlDomNode): boolean {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
258
|
toNode(): HTMLElement {
|
|
247
259
|
return toNode.call(this, "a");
|
|
248
260
|
}
|
|
@@ -252,6 +264,69 @@ export class Anchor implements HtmlDomNode {
|
|
|
252
264
|
}
|
|
253
265
|
}
|
|
254
266
|
|
|
267
|
+
/**
|
|
268
|
+
* This node represents an image embed (<img>) element.
|
|
269
|
+
*/
|
|
270
|
+
export class Img implements VirtualNode {
|
|
271
|
+
src: string;
|
|
272
|
+
alt: string;
|
|
273
|
+
classes: string[];
|
|
274
|
+
height: number;
|
|
275
|
+
depth: number;
|
|
276
|
+
maxFontSize: number;
|
|
277
|
+
style: CssStyle;
|
|
278
|
+
|
|
279
|
+
constructor(
|
|
280
|
+
src: string,
|
|
281
|
+
alt: string,
|
|
282
|
+
style: CssStyle,
|
|
283
|
+
) {
|
|
284
|
+
this.alt = alt;
|
|
285
|
+
this.src = src;
|
|
286
|
+
this.classes = ["mord"];
|
|
287
|
+
this.style = style;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
hasClass(className: string): boolean {
|
|
291
|
+
return utils.contains(this.classes, className);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
toNode(): Node {
|
|
295
|
+
const node = document.createElement("img");
|
|
296
|
+
node.src = this.src;
|
|
297
|
+
node.alt = this.alt;
|
|
298
|
+
node.className = "mord";
|
|
299
|
+
|
|
300
|
+
// Apply inline styles
|
|
301
|
+
for (const style in this.style) {
|
|
302
|
+
if (this.style.hasOwnProperty(style)) {
|
|
303
|
+
// $FlowFixMe
|
|
304
|
+
node.style[style] = this.style[style];
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return node;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
toMarkup(): string {
|
|
312
|
+
let markup = `<img src='${this.src} 'alt='${this.alt}' `;
|
|
313
|
+
|
|
314
|
+
// Add the styles, after hyphenation
|
|
315
|
+
let styles = "";
|
|
316
|
+
for (const style in this.style) {
|
|
317
|
+
if (this.style.hasOwnProperty(style)) {
|
|
318
|
+
styles += `${utils.hyphenate(style)}:${this.style[style]};`;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (styles) {
|
|
322
|
+
markup += ` style="${utils.escape(styles)}"`;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
markup += "'/>";
|
|
326
|
+
return markup;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
255
330
|
const iCombinations = {
|
|
256
331
|
'î': '\u0131\u0302',
|
|
257
332
|
'ï': '\u0131\u0308',
|
|
@@ -317,34 +392,6 @@ export class SymbolNode implements HtmlDomNode {
|
|
|
317
392
|
return utils.contains(this.classes, className);
|
|
318
393
|
}
|
|
319
394
|
|
|
320
|
-
tryCombine(sibling: HtmlDomNode): boolean {
|
|
321
|
-
if (!sibling
|
|
322
|
-
|| !(sibling instanceof SymbolNode)
|
|
323
|
-
|| this.italic > 0
|
|
324
|
-
|| createClass(this.classes) !== createClass(sibling.classes)
|
|
325
|
-
|| this.skew !== sibling.skew
|
|
326
|
-
|| this.maxFontSize !== sibling.maxFontSize) {
|
|
327
|
-
return false;
|
|
328
|
-
}
|
|
329
|
-
for (const style in this.style) {
|
|
330
|
-
if (this.style.hasOwnProperty(style)
|
|
331
|
-
&& this.style[style] !== sibling.style[style]) {
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
for (const style in sibling.style) {
|
|
336
|
-
if (sibling.style.hasOwnProperty(style)
|
|
337
|
-
&& this.style[style] !== sibling.style[style]) {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
this.text += sibling.text;
|
|
342
|
-
this.height = Math.max(this.height, sibling.height);
|
|
343
|
-
this.depth = Math.max(this.depth, sibling.depth);
|
|
344
|
-
this.italic = sibling.italic;
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
395
|
/**
|
|
349
396
|
* Creates a text node or span from a symbol node. Note that a span is only
|
|
350
397
|
* created if it is needed.
|