katex 0.15.6 → 0.16.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 -3
- package/contrib/auto-render/auto-render.js +20 -1
- package/contrib/auto-render/test/auto-render-spec.js +88 -17
- package/contrib/copy-tex/README.md +9 -19
- package/contrib/copy-tex/copy-tex.js +39 -12
- package/contrib/copy-tex/katex2tex.js +16 -7
- package/contrib/mathtex-script-type/README.md +5 -5
- package/contrib/mhchem/README.md +1 -1
- package/contrib/render-a11y-string/render-a11y-string.js +5 -0
- package/dist/README.md +3 -3
- package/dist/contrib/auto-render.js +23 -1
- package/dist/contrib/auto-render.min.js +1 -1
- package/dist/contrib/auto-render.mjs +23 -1
- package/dist/contrib/copy-tex.js +38 -24
- package/dist/contrib/copy-tex.min.js +1 -1
- package/dist/contrib/copy-tex.mjs +35 -16
- package/dist/contrib/render-a11y-string.js +6 -0
- package/dist/contrib/render-a11y-string.min.js +1 -1
- package/dist/contrib/render-a11y-string.mjs +6 -0
- package/dist/katex.css +1 -1
- package/dist/katex.js +322 -199
- package/dist/katex.min.css +1 -1
- package/dist/katex.min.js +1 -1
- package/dist/katex.mjs +363 -241
- package/package.json +3 -3
- package/src/Parser.js +1 -1
- package/src/buildCommon.js +1 -1
- package/src/buildMathML.js +2 -2
- package/src/delimiter.js +68 -25
- package/src/domTree.js +1 -0
- package/src/environments/array.js +1 -1
- package/src/functions/enclose.js +1 -1
- package/src/functions/mclass.js +1 -1
- package/src/functions/op.js +1 -1
- package/src/functions/pmb.js +44 -0
- package/src/functions.js +1 -0
- package/src/macros.js +1 -9
- package/src/parseNode.js +7 -0
- package/src/stretchy.js +1 -1
- package/src/svgGeometry.js +56 -0
- package/contrib/copy-tex/copy-tex.css +0 -10
- package/contrib/copy-tex/copy-tex.webpack.js +0 -6
- package/dist/contrib/copy-tex.css +0 -13
- package/dist/contrib/copy-tex.min.css +0 -1
package/README.md
CHANGED
|
@@ -31,13 +31,13 @@ Try out KaTeX [on the demo page](https://katex.org/#demo)!
|
|
|
31
31
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
|
32
32
|
<html>
|
|
33
33
|
<head>
|
|
34
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.
|
|
34
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.css" integrity="sha384-bYdxxUwYipFNohQlHt0bjN/LCpueqWz13HufFEV1SUatKs1cm4L6fFgCi1jT643X" crossorigin="anonymous">
|
|
35
35
|
|
|
36
36
|
<!-- The loading of KaTeX is deferred to speed up page rendering -->
|
|
37
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
37
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js" integrity="sha384-Qsn9KnoKISj6dI8g7p1HBlNpVx0I8p1SvlwOldgi3IorMle61nQy4zEahWYtljaz" crossorigin="anonymous"></script>
|
|
38
38
|
|
|
39
39
|
<!-- To automatically render math in text elements, include the auto-render extension: -->
|
|
40
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
40
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
|
|
41
41
|
onload="renderMathInElement(document.body);"></script>
|
|
42
42
|
</head>
|
|
43
43
|
...
|
|
@@ -55,10 +55,29 @@ const renderElem = function(elem, optionsCopy) {
|
|
|
55
55
|
const childNode = elem.childNodes[i];
|
|
56
56
|
if (childNode.nodeType === 3) {
|
|
57
57
|
// Text node
|
|
58
|
-
|
|
58
|
+
// Concatenate all sibling text nodes.
|
|
59
|
+
// Webkit browsers split very large text nodes into smaller ones,
|
|
60
|
+
// so the delimiters may be split across different nodes.
|
|
61
|
+
let textContentConcat = childNode.textContent;
|
|
62
|
+
let sibling = childNode.nextSibling;
|
|
63
|
+
let nSiblings = 0;
|
|
64
|
+
while (sibling && (sibling.nodeType === Node.TEXT_NODE)) {
|
|
65
|
+
textContentConcat += sibling.textContent;
|
|
66
|
+
sibling = sibling.nextSibling;
|
|
67
|
+
nSiblings++;
|
|
68
|
+
}
|
|
69
|
+
const frag = renderMathInText(textContentConcat, optionsCopy);
|
|
59
70
|
if (frag) {
|
|
71
|
+
// Remove extra text nodes
|
|
72
|
+
for (let j = 0; j < nSiblings; j++) {
|
|
73
|
+
childNode.nextSibling.remove();
|
|
74
|
+
}
|
|
60
75
|
i += frag.childNodes.length - 1;
|
|
61
76
|
elem.replaceChild(frag, childNode);
|
|
77
|
+
} else {
|
|
78
|
+
// If the concatenated text does not contain math
|
|
79
|
+
// the siblings will not either
|
|
80
|
+
i += nSiblings;
|
|
62
81
|
}
|
|
63
82
|
} else if (childNode.nodeType === 1) {
|
|
64
83
|
// Element node
|
|
@@ -6,15 +6,14 @@ import renderMathInElement from "../auto-render";
|
|
|
6
6
|
|
|
7
7
|
beforeEach(function() {
|
|
8
8
|
expect.extend({
|
|
9
|
-
toSplitInto: function(actual,
|
|
9
|
+
toSplitInto: function(actual, result, delimiters) {
|
|
10
10
|
const message = {
|
|
11
11
|
pass: true,
|
|
12
12
|
message: () => "'" + actual + "' split correctly",
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const split =
|
|
16
|
-
splitAtDelimiters(actual,
|
|
17
|
-
[{left: left, right: right, display: false}]);
|
|
16
|
+
splitAtDelimiters(actual, delimiters);
|
|
18
17
|
|
|
19
18
|
if (split.length !== result.length) {
|
|
20
19
|
message.pass = false;
|
|
@@ -60,60 +59,76 @@ beforeEach(function() {
|
|
|
60
59
|
|
|
61
60
|
describe("A delimiter splitter", function() {
|
|
62
61
|
it("doesn't split when there are no delimiters", function() {
|
|
63
|
-
expect("hello").toSplitInto(
|
|
62
|
+
expect("hello").toSplitInto(
|
|
63
|
+
[
|
|
64
|
+
{type: "text", data: "hello"},
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
{left: "(", right: ")", display: false},
|
|
68
|
+
]);
|
|
64
69
|
});
|
|
65
70
|
|
|
66
71
|
it("doesn't create a math node with only one left delimiter", function() {
|
|
67
72
|
expect("hello ( world").toSplitInto(
|
|
68
|
-
"(", ")",
|
|
69
73
|
[
|
|
70
74
|
{type: "text", data: "hello "},
|
|
71
75
|
{type: "text", data: "( world"},
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
{left: "(", right: ")", display: false},
|
|
72
79
|
]);
|
|
73
80
|
});
|
|
74
81
|
|
|
75
82
|
it("doesn't split when there's only a right delimiter", function() {
|
|
76
83
|
expect("hello ) world").toSplitInto(
|
|
77
|
-
"(", ")",
|
|
78
84
|
[
|
|
79
85
|
{type: "text", data: "hello ) world"},
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
{left: "(", right: ")", display: false},
|
|
80
89
|
]);
|
|
81
90
|
});
|
|
82
91
|
|
|
83
92
|
it("splits when there are both delimiters", function() {
|
|
84
93
|
expect("hello ( world ) boo").toSplitInto(
|
|
85
|
-
"(", ")",
|
|
86
94
|
[
|
|
87
95
|
{type: "text", data: "hello "},
|
|
88
96
|
{type: "math", data: " world ",
|
|
89
97
|
rawData: "( world )", display: false},
|
|
90
98
|
{type: "text", data: " boo"},
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
{left: "(", right: ")", display: false},
|
|
91
102
|
]);
|
|
92
103
|
});
|
|
93
104
|
|
|
94
105
|
it("splits on multi-character delimiters", function() {
|
|
95
106
|
expect("hello [[ world ]] boo").toSplitInto(
|
|
96
|
-
"[[", "]]",
|
|
97
107
|
[
|
|
98
108
|
{type: "text", data: "hello "},
|
|
99
109
|
{type: "math", data: " world ",
|
|
100
110
|
rawData: "[[ world ]]", display: false},
|
|
101
111
|
{type: "text", data: " boo"},
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
{left: "[[", right: "]]", display: false},
|
|
102
115
|
]);
|
|
103
116
|
expect("hello \\begin{equation} world \\end{equation} boo").toSplitInto(
|
|
104
|
-
"\\begin{equation}", "\\end{equation}",
|
|
105
117
|
[
|
|
106
118
|
{type: "text", data: "hello "},
|
|
107
119
|
{type: "math", data: "\\begin{equation} world \\end{equation}",
|
|
108
120
|
rawData: "\\begin{equation} world \\end{equation}",
|
|
109
121
|
display: false},
|
|
110
122
|
{type: "text", data: " boo"},
|
|
123
|
+
],
|
|
124
|
+
[
|
|
125
|
+
{left: "\\begin{equation}", right: "\\end{equation}",
|
|
126
|
+
display: false},
|
|
111
127
|
]);
|
|
112
128
|
});
|
|
113
129
|
|
|
114
130
|
it("splits mutliple times", function() {
|
|
115
131
|
expect("hello ( world ) boo ( more ) stuff").toSplitInto(
|
|
116
|
-
"(", ")",
|
|
117
132
|
[
|
|
118
133
|
{type: "text", data: "hello "},
|
|
119
134
|
{type: "math", data: " world ",
|
|
@@ -122,44 +137,52 @@ describe("A delimiter splitter", function() {
|
|
|
122
137
|
{type: "math", data: " more ",
|
|
123
138
|
rawData: "( more )", display: false},
|
|
124
139
|
{type: "text", data: " stuff"},
|
|
140
|
+
],
|
|
141
|
+
[
|
|
142
|
+
{left: "(", right: ")", display: false},
|
|
125
143
|
]);
|
|
126
144
|
});
|
|
127
145
|
|
|
128
146
|
it("leaves the ending when there's only a left delimiter", function() {
|
|
129
147
|
expect("hello ( world ) boo ( left").toSplitInto(
|
|
130
|
-
"(", ")",
|
|
131
148
|
[
|
|
132
149
|
{type: "text", data: "hello "},
|
|
133
150
|
{type: "math", data: " world ",
|
|
134
151
|
rawData: "( world )", display: false},
|
|
135
152
|
{type: "text", data: " boo "},
|
|
136
153
|
{type: "text", data: "( left"},
|
|
154
|
+
],
|
|
155
|
+
[
|
|
156
|
+
{left: "(", right: ")", display: false},
|
|
137
157
|
]);
|
|
138
158
|
});
|
|
139
159
|
|
|
140
160
|
it("doesn't split when close delimiters are in {}s", function() {
|
|
141
161
|
expect("hello ( world { ) } ) boo").toSplitInto(
|
|
142
|
-
"(", ")",
|
|
143
162
|
[
|
|
144
163
|
{type: "text", data: "hello "},
|
|
145
164
|
{type: "math", data: " world { ) } ",
|
|
146
165
|
rawData: "( world { ) } )", display: false},
|
|
147
166
|
{type: "text", data: " boo"},
|
|
167
|
+
],
|
|
168
|
+
[
|
|
169
|
+
{left: "(", right: ")", display: false},
|
|
148
170
|
]);
|
|
149
171
|
|
|
150
172
|
expect("hello ( world { { } ) } ) boo").toSplitInto(
|
|
151
|
-
"(", ")",
|
|
152
173
|
[
|
|
153
174
|
{type: "text", data: "hello "},
|
|
154
175
|
{type: "math", data: " world { { } ) } ",
|
|
155
176
|
rawData: "( world { { } ) } )", display: false},
|
|
156
177
|
{type: "text", data: " boo"},
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
{left: "(", right: ")", display: false},
|
|
157
181
|
]);
|
|
158
182
|
});
|
|
159
183
|
|
|
160
184
|
it("correctly processes sequences of $..$", function() {
|
|
161
185
|
expect("$hello$$world$$boo$").toSplitInto(
|
|
162
|
-
"$", "$",
|
|
163
186
|
[
|
|
164
187
|
{type: "math", data: "hello",
|
|
165
188
|
rawData: "$hello$", display: false},
|
|
@@ -167,17 +190,22 @@ describe("A delimiter splitter", function() {
|
|
|
167
190
|
rawData: "$world$", display: false},
|
|
168
191
|
{type: "math", data: "boo",
|
|
169
192
|
rawData: "$boo$", display: false},
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
{left: "$", right: "$", display: false},
|
|
170
196
|
]);
|
|
171
197
|
});
|
|
172
198
|
|
|
173
199
|
it("doesn't split at escaped delimiters", function() {
|
|
174
200
|
expect("hello ( world \\) ) boo").toSplitInto(
|
|
175
|
-
"(", ")",
|
|
176
201
|
[
|
|
177
202
|
{type: "text", data: "hello "},
|
|
178
203
|
{type: "math", data: " world \\) ",
|
|
179
204
|
rawData: "( world \\) )", display: false},
|
|
180
205
|
{type: "text", data: " boo"},
|
|
206
|
+
],
|
|
207
|
+
[
|
|
208
|
+
{left: "(", right: ")", display: false},
|
|
181
209
|
]);
|
|
182
210
|
|
|
183
211
|
/* TODO(emily): make this work maybe?
|
|
@@ -194,21 +222,25 @@ describe("A delimiter splitter", function() {
|
|
|
194
222
|
|
|
195
223
|
it("splits when the right and left delimiters are the same", function() {
|
|
196
224
|
expect("hello $ world $ boo").toSplitInto(
|
|
197
|
-
"$", "$",
|
|
198
225
|
[
|
|
199
226
|
{type: "text", data: "hello "},
|
|
200
227
|
{type: "math", data: " world ",
|
|
201
228
|
rawData: "$ world $", display: false},
|
|
202
229
|
{type: "text", data: " boo"},
|
|
230
|
+
],
|
|
231
|
+
[
|
|
232
|
+
{left: "$", right: "$", display: false},
|
|
203
233
|
]);
|
|
204
234
|
});
|
|
205
235
|
|
|
206
236
|
it("ignores \\$", function() {
|
|
207
237
|
expect("$x = \\$5$").toSplitInto(
|
|
208
|
-
"$", "$",
|
|
209
238
|
[
|
|
210
239
|
{type: "math", data: "x = \\$5",
|
|
211
240
|
rawData: "$x = \\$5$", display: false},
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
{left: "$", right: "$", display: false},
|
|
212
244
|
]);
|
|
213
245
|
});
|
|
214
246
|
|
|
@@ -290,3 +322,42 @@ describe("Pre-process callback", function() {
|
|
|
290
322
|
expect(el1.innerHTML).toEqual(el2.innerHTML);
|
|
291
323
|
});
|
|
292
324
|
});
|
|
325
|
+
|
|
326
|
+
describe("Parse adjacent text nodes", function() {
|
|
327
|
+
it("parse adjacent text nodes with math", function() {
|
|
328
|
+
const textNodes = ['\\[',
|
|
329
|
+
'x^2 + y^2 = r^2',
|
|
330
|
+
'\\]'];
|
|
331
|
+
const el = document.createElement('div');
|
|
332
|
+
for (let i = 0; i < textNodes.length; i++) {
|
|
333
|
+
const txt = document.createTextNode(textNodes[i]);
|
|
334
|
+
el.appendChild(txt);
|
|
335
|
+
}
|
|
336
|
+
const el2 = document.createElement('div');
|
|
337
|
+
const txt = document.createTextNode(textNodes.join(''));
|
|
338
|
+
el2.appendChild(txt);
|
|
339
|
+
const delimiters = [{left: "\\[", right: "\\]", display: true}];
|
|
340
|
+
renderMathInElement(el, {delimiters});
|
|
341
|
+
renderMathInElement(el2, {delimiters});
|
|
342
|
+
expect(el).toStrictEqual(el2);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("parse adjacent text nodes without math", function() {
|
|
346
|
+
const textNodes = ['Lorem ipsum dolor',
|
|
347
|
+
'sit amet',
|
|
348
|
+
'consectetur adipiscing elit'];
|
|
349
|
+
const el = document.createElement('div');
|
|
350
|
+
for (let i = 0; i < textNodes.length; i++) {
|
|
351
|
+
const txt = document.createTextNode(textNodes[i]);
|
|
352
|
+
el.appendChild(txt);
|
|
353
|
+
}
|
|
354
|
+
const el2 = document.createElement('div');
|
|
355
|
+
for (let i = 0; i < textNodes.length; i++) {
|
|
356
|
+
const txt = document.createTextNode(textNodes[i]);
|
|
357
|
+
el2.appendChild(txt);
|
|
358
|
+
}
|
|
359
|
+
const delimiters = [{left: "\\[", right: "\\]", display: true}];
|
|
360
|
+
renderMathInElement(el, {delimiters});
|
|
361
|
+
expect(el).toStrictEqual(el2);
|
|
362
|
+
});
|
|
363
|
+
});
|
|
@@ -2,29 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
This extension modifies the copy/paste behavior in any browser supporting the
|
|
4
4
|
[Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent)
|
|
5
|
-
so that, when selecting and copying
|
|
5
|
+
so that, when selecting and copying KaTeX-rendered elements, the text
|
|
6
6
|
content of the resulting clipboard renders KaTeX elements as their LaTeX source
|
|
7
7
|
surrounded by specified delimiters. (The HTML content of the resulting
|
|
8
8
|
clipboard remains the selected HTML content, as it normally would.)
|
|
9
9
|
The default delimiters are `$...$` for inline math and `$$...$$` for display
|
|
10
10
|
math, but you can easy switch them to e.g. `\(...\)` and `\[...\]` by
|
|
11
11
|
modifying `copyDelimiters` in [the source code](copy-tex.js).
|
|
12
|
+
Note that a selection containing part of a KaTeX formula gets extended to
|
|
13
|
+
include the entire KaTeX formula.
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## Usage
|
|
14
16
|
|
|
15
17
|
This extension isn't part of KaTeX proper, so the script should be separately
|
|
16
|
-
included in the page.
|
|
17
|
-
defines KaTeX equations as
|
|
18
|
-
[`user-select: all`](https://developer.mozilla.org/en-US/docs/Web/CSS/user-select)
|
|
19
|
-
so that they get selected all-or-nothing (and thus trigger the good behavior
|
|
20
|
-
provided by this extension). Without this CSS, partially selected equations
|
|
21
|
-
will just get the usual HTML copy/paste behavior.
|
|
18
|
+
included in the page.
|
|
22
19
|
|
|
23
20
|
```html
|
|
24
|
-
<
|
|
25
|
-
<script src="https://cdn.jsdelivr.net/npm/katex@0.15.6/dist/contrib/copy-tex.min.js" integrity="sha384-Ep9Es0VCjVn9dFeaN2uQxgGcGmG+pfZ4eBaHxUpxXDORrrVACZVOpywyzvFRGbmv" crossorigin="anonymous"></script>
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/copy-tex.min.js" integrity="sha384-ww/583aHhxWkz5DEVn6OKtNiIaLi2iBRNZXfJRiY1Ai7tnJ9UXpEsyvOITVpTl4A" crossorigin="anonymous"></script>
|
|
26
22
|
```
|
|
27
23
|
|
|
24
|
+
(Note that, as of KaTeX 0.16.0, there is no longer a corresponding CSS file.)
|
|
25
|
+
|
|
28
26
|
See [index.html](index.html) for an example.
|
|
29
27
|
(To run this example from a clone of the repository, run `yarn start`
|
|
30
28
|
in the root KaTeX directory, and then visit
|
|
@@ -37,13 +35,5 @@ statement with `require('katex/contrib/copy-tex/katex2tex.js')`.
|
|
|
37
35
|
|
|
38
36
|
ECMAScript module is also available:
|
|
39
37
|
```html
|
|
40
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
38
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/copy-tex.mjs" integrity="sha384-bVEnwt0PtX+1EuJoOEcm4rgTUWvb2ILTdjHfI1gUe/r5fdqrTcQaUuRdHG2DciuQ" crossorigin="anonymous"></script>
|
|
41
39
|
```
|
|
42
|
-
|
|
43
|
-
### Known Issues
|
|
44
|
-
|
|
45
|
-
This extension has been tested on Chrome, Firefox, Edge, and Safari.
|
|
46
|
-
|
|
47
|
-
Safari copies correctly, but the selection rectangle renders strangely
|
|
48
|
-
(too big) when interacting with display math
|
|
49
|
-
(because of the `user-select: all` CSS).
|
|
@@ -1,23 +1,50 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
1
3
|
import katexReplaceWithTex from './katex2tex';
|
|
2
4
|
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
+
// Return <div class="katex"> element containing node, or null if not found.
|
|
6
|
+
function closestKatex(node: Node): ?Element {
|
|
7
|
+
// If node is a Text Node, for example, go up to containing Element,
|
|
8
|
+
// where we can apply the `closest` method.
|
|
9
|
+
const element: ?Element =
|
|
10
|
+
(node instanceof Element ? node : node.parentElement);
|
|
11
|
+
return element && element.closest('.katex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Global copy handler to modify behavior on/within .katex elements.
|
|
15
|
+
document.addEventListener('copy', function(event: ClipboardEvent) {
|
|
5
16
|
const selection = window.getSelection();
|
|
6
|
-
if (selection.isCollapsed) {
|
|
7
|
-
return;
|
|
17
|
+
if (selection.isCollapsed || !event.clipboardData) {
|
|
18
|
+
return; // default action OK if selection is empty or unchangeable
|
|
19
|
+
}
|
|
20
|
+
const clipboardData = event.clipboardData;
|
|
21
|
+
const range = selection.getRangeAt(0);
|
|
22
|
+
|
|
23
|
+
// When start point is within a formula, expand to entire formula.
|
|
24
|
+
const startKatex = closestKatex(range.startContainer);
|
|
25
|
+
if (startKatex) {
|
|
26
|
+
range.setStartBefore(startKatex);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Similarly, when end point is within a formula, expand to entire formula.
|
|
30
|
+
const endKatex = closestKatex(range.endContainer);
|
|
31
|
+
if (endKatex) {
|
|
32
|
+
range.setEndAfter(endKatex);
|
|
8
33
|
}
|
|
9
|
-
|
|
34
|
+
|
|
35
|
+
const fragment = range.cloneContents();
|
|
10
36
|
if (!fragment.querySelector('.katex-mathml')) {
|
|
11
|
-
return;
|
|
37
|
+
return; // default action OK if no .katex-mathml elements
|
|
12
38
|
}
|
|
39
|
+
|
|
40
|
+
const htmlContents = Array.prototype.map.call(fragment.childNodes,
|
|
41
|
+
(el) => (el instanceof Text ? el.textContent : el.outerHTML)
|
|
42
|
+
).join('');
|
|
43
|
+
|
|
13
44
|
// Preserve usual HTML copy/paste behavior.
|
|
14
|
-
|
|
15
|
-
for (let i = 0; i < fragment.childNodes.length; i++) {
|
|
16
|
-
html.push(fragment.childNodes[i].outerHTML);
|
|
17
|
-
}
|
|
18
|
-
event.clipboardData.setData('text/html', html.join(''));
|
|
45
|
+
clipboardData.setData('text/html', htmlContents);
|
|
19
46
|
// Rewrite plain-text version.
|
|
20
|
-
|
|
47
|
+
clipboardData.setData('text/plain',
|
|
21
48
|
katexReplaceWithTex(fragment).textContent);
|
|
22
49
|
// Prevent normal copy handling.
|
|
23
50
|
event.preventDefault();
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
export interface CopyDelimiters {
|
|
4
|
+
inline: [string, string],
|
|
5
|
+
display: [string, string],
|
|
6
|
+
}
|
|
7
|
+
|
|
1
8
|
// Set these to how you want inline and display math to be delimited.
|
|
2
|
-
export const defaultCopyDelimiters = {
|
|
9
|
+
export const defaultCopyDelimiters: CopyDelimiters = {
|
|
3
10
|
inline: ['$', '$'], // alternative: ['\(', '\)']
|
|
4
11
|
display: ['$$', '$$'], // alternative: ['\[', '\]']
|
|
5
12
|
};
|
|
@@ -7,16 +14,18 @@ export const defaultCopyDelimiters = {
|
|
|
7
14
|
// Replace .katex elements with their TeX source (<annotation> element).
|
|
8
15
|
// Modifies fragment in-place. Useful for writing your own 'copy' handler,
|
|
9
16
|
// as in copy-tex.js.
|
|
10
|
-
export
|
|
11
|
-
|
|
17
|
+
export function katexReplaceWithTex(
|
|
18
|
+
fragment: DocumentFragment,
|
|
19
|
+
copyDelimiters: CopyDelimiters = defaultCopyDelimiters
|
|
20
|
+
): DocumentFragment {
|
|
12
21
|
// Remove .katex-html blocks that are preceded by .katex-mathml blocks
|
|
13
22
|
// (which will get replaced below).
|
|
14
23
|
const katexHtml = fragment.querySelectorAll('.katex-mathml + .katex-html');
|
|
15
24
|
for (let i = 0; i < katexHtml.length; i++) {
|
|
16
25
|
const element = katexHtml[i];
|
|
17
26
|
if (element.remove) {
|
|
18
|
-
element.remove(
|
|
19
|
-
} else {
|
|
27
|
+
element.remove();
|
|
28
|
+
} else if (element.parentNode) {
|
|
20
29
|
element.parentNode.removeChild(element);
|
|
21
30
|
}
|
|
22
31
|
}
|
|
@@ -29,7 +38,7 @@ export const katexReplaceWithTex = function(fragment,
|
|
|
29
38
|
if (texSource) {
|
|
30
39
|
if (element.replaceWith) {
|
|
31
40
|
element.replaceWith(texSource);
|
|
32
|
-
} else {
|
|
41
|
+
} else if (element.parentNode) {
|
|
33
42
|
element.parentNode.replaceChild(texSource, element);
|
|
34
43
|
}
|
|
35
44
|
texSource.innerHTML = copyDelimiters.inline[0] +
|
|
@@ -47,6 +56,6 @@ export const katexReplaceWithTex = function(fragment,
|
|
|
47
56
|
+ copyDelimiters.display[1];
|
|
48
57
|
}
|
|
49
58
|
return fragment;
|
|
50
|
-
}
|
|
59
|
+
}
|
|
51
60
|
|
|
52
61
|
export default katexReplaceWithTex;
|
|
@@ -11,7 +11,7 @@ included in the page, in addition to KaTeX.
|
|
|
11
11
|
Load the extension by adding the following line to your HTML file.
|
|
12
12
|
|
|
13
13
|
```html
|
|
14
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
14
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/mathtex-script-type.min.js" integrity="sha384-jiBVvJ8NGGj5n7kJaiWwWp9AjC+Yh8rhZY3GtAX8yU28azcLgoRo4oukO87g7zDT" crossorigin="anonymous"></script>
|
|
15
15
|
```
|
|
16
16
|
You can download the script and use it locally, or from a local KaTeX installation instead.
|
|
17
17
|
|
|
@@ -23,9 +23,9 @@ Then, in the body, we use a `math/tex` script to typeset the equation `x+\sqrt{1
|
|
|
23
23
|
<!DOCTYPE html>
|
|
24
24
|
<html>
|
|
25
25
|
<head>
|
|
26
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.
|
|
27
|
-
<script src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
28
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
26
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.css" integrity="sha384-bYdxxUwYipFNohQlHt0bjN/LCpueqWz13HufFEV1SUatKs1cm4L6fFgCi1jT643X" crossorigin="anonymous">
|
|
27
|
+
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js" integrity="sha384-Qsn9KnoKISj6dI8g7p1HBlNpVx0I8p1SvlwOldgi3IorMle61nQy4zEahWYtljaz" crossorigin="anonymous"></script>
|
|
28
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/mathtex-script-type.min.js" integrity="sha384-jiBVvJ8NGGj5n7kJaiWwWp9AjC+Yh8rhZY3GtAX8yU28azcLgoRo4oukO87g7zDT" crossorigin="anonymous"></script>
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<script type="math/tex">x+\sqrt{1-x^2}</script>
|
|
@@ -35,4 +35,4 @@ Then, in the body, we use a `math/tex` script to typeset the equation `x+\sqrt{1
|
|
|
35
35
|
|
|
36
36
|
ECMAScript module is also available:
|
|
37
37
|
```html
|
|
38
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
38
|
+
<script type="module" src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/mathtex-script-type.mjs" integrity="sha384-4EJvC5tvqq9XJxXvdD4JutBokuFw/dCe2AB4gZ9sRpwFFXECpL3qT43tmE0PkpVg" crossorigin="anonymous"></script>
|
package/contrib/mhchem/README.md
CHANGED
|
@@ -7,7 +7,7 @@ This extension adds to KaTeX the `\ce` and `\pu` functions from the [mhchem](htt
|
|
|
7
7
|
This extension isn't part of core KaTeX, so the script should be separately included. Write the following line into the HTML page's `<head>`. Place it *after* the line that calls `katex.js`, and if you make use of the [auto-render](https://katex.org/docs/autorender.html) extension, place it *before* the line that calls `auto-render.js`.
|
|
8
8
|
|
|
9
9
|
```html
|
|
10
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
10
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/mhchem.min.js" integrity="sha384-RTN08a0AXIioPBcVosEqPUfKK+rPp+h1x/izR7xMkdMyuwkcZCWdxO+RSwIFtJXN" crossorigin="anonymous"></script>
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
If you remove the `defer` attribute from this tag, then you must also remove the `defer` attribute from the `<script src="https://../katex.min.js">` tag.
|
package/dist/README.md
CHANGED
|
@@ -31,13 +31,13 @@ Try out KaTeX [on the demo page](https://katex.org/#demo)!
|
|
|
31
31
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
|
32
32
|
<html>
|
|
33
33
|
<head>
|
|
34
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.
|
|
34
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.css" integrity="sha384-bYdxxUwYipFNohQlHt0bjN/LCpueqWz13HufFEV1SUatKs1cm4L6fFgCi1jT643X" crossorigin="anonymous">
|
|
35
35
|
|
|
36
36
|
<!-- The loading of KaTeX is deferred to speed up page rendering -->
|
|
37
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
37
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/katex.min.js" integrity="sha384-Qsn9KnoKISj6dI8g7p1HBlNpVx0I8p1SvlwOldgi3IorMle61nQy4zEahWYtljaz" crossorigin="anonymous"></script>
|
|
38
38
|
|
|
39
39
|
<!-- To automatically render math in text elements, include the auto-render extension: -->
|
|
40
|
-
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.
|
|
40
|
+
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.2/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
|
|
41
41
|
onload="renderMathInElement(document.body);"></script>
|
|
42
42
|
</head>
|
|
43
43
|
...
|
|
@@ -235,11 +235,33 @@ var renderElem = function renderElem(elem, optionsCopy) {
|
|
|
235
235
|
|
|
236
236
|
if (childNode.nodeType === 3) {
|
|
237
237
|
// Text node
|
|
238
|
-
|
|
238
|
+
// Concatenate all sibling text nodes.
|
|
239
|
+
// Webkit browsers split very large text nodes into smaller ones,
|
|
240
|
+
// so the delimiters may be split across different nodes.
|
|
241
|
+
var textContentConcat = childNode.textContent;
|
|
242
|
+
var sibling = childNode.nextSibling;
|
|
243
|
+
var nSiblings = 0;
|
|
244
|
+
|
|
245
|
+
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
|
|
246
|
+
textContentConcat += sibling.textContent;
|
|
247
|
+
sibling = sibling.nextSibling;
|
|
248
|
+
nSiblings++;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var frag = renderMathInText(textContentConcat, optionsCopy);
|
|
239
252
|
|
|
240
253
|
if (frag) {
|
|
254
|
+
// Remove extra text nodes
|
|
255
|
+
for (var j = 0; j < nSiblings; j++) {
|
|
256
|
+
childNode.nextSibling.remove();
|
|
257
|
+
}
|
|
258
|
+
|
|
241
259
|
i += frag.childNodes.length - 1;
|
|
242
260
|
elem.replaceChild(frag, childNode);
|
|
261
|
+
} else {
|
|
262
|
+
// If the concatenated text does not contain math
|
|
263
|
+
// the siblings will not either
|
|
264
|
+
i += nSiblings;
|
|
243
265
|
}
|
|
244
266
|
} else if (childNode.nodeType === 1) {
|
|
245
267
|
(function () {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={771:function(t){t.exports=e}},r={};function n(e){var i=r[e];if(void 0!==i)return i.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,n),a.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var i={};return function(){n.d(i,{default:function(){return s}});var e=n(771),t=n.n(e),r=function(e,t,r){for(var n=r,i=0,a=e.length;n<t.length;){var o=t[n];if(i<=0&&t.slice(n,n+a)===e)return n;"\\"===o?n++:"{"===o?i++:"}"===o&&i--,n++}return-1},a=/^\\begin{/,o=function(e,t){for(var n,i=[],o=new RegExp("("+t.map((function(e){return e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")})).join("|")+")");-1!==(n=e.search(o));){n>0&&(i.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));var l=t.findIndex((function(t){return e.startsWith(t.left)}));if(-1===(n=r(t[l].right,e,t[l].left.length)))break;var d=e.slice(0,n+t[l].right.length),s=a.test(d)?d:e.slice(t[l].left.length,n);i.push({type:"math",data:s,rawData:d,display:t[l].display}),e=e.slice(n+t[l].right.length)}return""!==e&&i.push({type:"text",data:e}),i},l=function(e,r){var n=o(e,r.delimiters);if(1===n.length&&"text"===n[0].type)return null;for(var i=document.createDocumentFragment(),a=0;a<n.length;a++)if("text"===n[a].type)i.appendChild(document.createTextNode(n[a].data));else{var l=document.createElement("span"),d=n[a].data;r.displayMode=n[a].display;try{r.preProcess&&(d=r.preProcess(d)),t().render(d,l,r)}catch(e){if(!(e instanceof t().ParseError))throw e;r.errorCallback("KaTeX auto-render: Failed to parse `"+n[a].data+"` with ",e),i.appendChild(document.createTextNode(n[a].rawData));continue}i.appendChild(l)}return i},d=function e(t,r){for(var n=0;n<t.childNodes.length;n++){var i=t.childNodes[n];if(3===i.nodeType){for(var a=i.textContent,o=i.nextSibling,d=0;o&&o.nodeType===Node.TEXT_NODE;)a+=o.textContent,o=o.nextSibling,d++;var s=l(a,r);if(s){for(var f=0;f<d;f++)i.nextSibling.remove();n+=s.childNodes.length-1,t.replaceChild(s,i)}else n+=d}else 1===i.nodeType&&function(){var t=" "+i.className+" ";-1===r.ignoredTags.indexOf(i.nodeName.toLowerCase())&&r.ignoredClasses.every((function(e){return-1===t.indexOf(" "+e+" ")}))&&e(i,r)}()}},s=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code","option"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},d(e,r)}}(),i=i.default}()}));
|
|
@@ -138,11 +138,33 @@ var renderElem = function renderElem(elem, optionsCopy) {
|
|
|
138
138
|
|
|
139
139
|
if (childNode.nodeType === 3) {
|
|
140
140
|
// Text node
|
|
141
|
-
|
|
141
|
+
// Concatenate all sibling text nodes.
|
|
142
|
+
// Webkit browsers split very large text nodes into smaller ones,
|
|
143
|
+
// so the delimiters may be split across different nodes.
|
|
144
|
+
var textContentConcat = childNode.textContent;
|
|
145
|
+
var sibling = childNode.nextSibling;
|
|
146
|
+
var nSiblings = 0;
|
|
147
|
+
|
|
148
|
+
while (sibling && sibling.nodeType === Node.TEXT_NODE) {
|
|
149
|
+
textContentConcat += sibling.textContent;
|
|
150
|
+
sibling = sibling.nextSibling;
|
|
151
|
+
nSiblings++;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
var frag = renderMathInText(textContentConcat, optionsCopy);
|
|
142
155
|
|
|
143
156
|
if (frag) {
|
|
157
|
+
// Remove extra text nodes
|
|
158
|
+
for (var j = 0; j < nSiblings; j++) {
|
|
159
|
+
childNode.nextSibling.remove();
|
|
160
|
+
}
|
|
161
|
+
|
|
144
162
|
i += frag.childNodes.length - 1;
|
|
145
163
|
elem.replaceChild(frag, childNode);
|
|
164
|
+
} else {
|
|
165
|
+
// If the concatenated text does not contain math
|
|
166
|
+
// the siblings will not either
|
|
167
|
+
i += nSiblings;
|
|
146
168
|
}
|
|
147
169
|
} else if (childNode.nodeType === 1) {
|
|
148
170
|
(function () {
|