temml 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/contrib/auto-render/README.md +89 -0
- package/contrib/auto-render/auto-render.js +128 -0
- package/contrib/auto-render/dist/auto-render.js +217 -0
- package/contrib/auto-render/dist/auto-render.min.js +1 -0
- package/contrib/auto-render/splitAtDelimiters.js +84 -0
- package/contrib/auto-render/test/auto-render-spec.js +234 -0
- package/contrib/auto-render/test/auto-render.js +217 -0
- package/contrib/auto-render/test/test_page.html +59 -0
- package/contrib/mhchem/README.md +26 -0
- package/contrib/mhchem/mhchem.js +1705 -0
- package/contrib/mhchem/mhchem.min.js +1 -0
- package/contrib/physics/README.md +20 -0
- package/contrib/physics/physics.js +131 -0
- package/contrib/texvc/README.md +23 -0
- package/contrib/texvc/texvc.js +61 -0
- package/dist/Temml-Asana.css +201 -0
- package/dist/Temml-Latin-Modern.css +216 -0
- package/dist/Temml-Libertinus.css +214 -0
- package/dist/Temml-Local.css +194 -0
- package/dist/Temml-STIX2.css +203 -0
- package/dist/Temml.woff2 +0 -0
- package/dist/temml.cjs +13122 -0
- package/dist/temml.js +11225 -0
- package/dist/temml.min.js +1 -0
- package/dist/temml.mjs +13120 -0
- package/dist/temmlPostProcess.js +70 -0
- package/package.json +34 -0
- package/src/Lexer.js +121 -0
- package/src/MacroExpander.js +437 -0
- package/src/Namespace.js +107 -0
- package/src/ParseError.js +64 -0
- package/src/Parser.js +977 -0
- package/src/Settings.js +49 -0
- package/src/SourceLocation.js +29 -0
- package/src/Style.js +144 -0
- package/src/Token.js +40 -0
- package/src/buildMathML.js +235 -0
- package/src/constants.js +25 -0
- package/src/defineEnvironment.js +25 -0
- package/src/defineFunction.js +69 -0
- package/src/defineMacro.js +11 -0
- package/src/domTree.js +185 -0
- package/src/environments/array.js +791 -0
- package/src/environments/cd.js +252 -0
- package/src/environments.js +8 -0
- package/src/functions/accent.js +127 -0
- package/src/functions/accentunder.js +38 -0
- package/src/functions/arrow.js +204 -0
- package/src/functions/cancelto.js +36 -0
- package/src/functions/char.js +33 -0
- package/src/functions/color.js +253 -0
- package/src/functions/cr.js +46 -0
- package/src/functions/def.js +259 -0
- package/src/functions/delimsizing.js +304 -0
- package/src/functions/enclose.js +193 -0
- package/src/functions/envTag.js +38 -0
- package/src/functions/environment.js +59 -0
- package/src/functions/font.js +123 -0
- package/src/functions/genfrac.js +333 -0
- package/src/functions/hbox.js +29 -0
- package/src/functions/horizBrace.js +32 -0
- package/src/functions/href.js +90 -0
- package/src/functions/html.js +95 -0
- package/src/functions/includegraphics.js +131 -0
- package/src/functions/kern.js +75 -0
- package/src/functions/label.js +29 -0
- package/src/functions/lap.js +75 -0
- package/src/functions/math.js +40 -0
- package/src/functions/mathchoice.js +41 -0
- package/src/functions/mclass.js +201 -0
- package/src/functions/multiscript.js +91 -0
- package/src/functions/not.js +46 -0
- package/src/functions/op.js +338 -0
- package/src/functions/operatorname.js +139 -0
- package/src/functions/ordgroup.js +9 -0
- package/src/functions/phantom.js +73 -0
- package/src/functions/pmb.js +31 -0
- package/src/functions/raise.js +68 -0
- package/src/functions/ref.js +28 -0
- package/src/functions/relax.js +16 -0
- package/src/functions/rule.js +52 -0
- package/src/functions/sizing.js +64 -0
- package/src/functions/smash.js +66 -0
- package/src/functions/sqrt.js +31 -0
- package/src/functions/styling.js +58 -0
- package/src/functions/supsub.js +135 -0
- package/src/functions/symbolsOp.js +53 -0
- package/src/functions/symbolsOrd.js +102 -0
- package/src/functions/symbolsSpacing.js +53 -0
- package/src/functions/tag.js +8 -0
- package/src/functions/text.js +75 -0
- package/src/functions/tip.js +63 -0
- package/src/functions/toggle.js +13 -0
- package/src/functions/verb.js +33 -0
- package/src/functions.js +57 -0
- package/src/linebreaking.js +159 -0
- package/src/macros.js +708 -0
- package/src/mathMLTree.js +175 -0
- package/src/parseNode.js +42 -0
- package/src/parseTree.js +40 -0
- package/src/postProcess.js +57 -0
- package/src/replace.js +225 -0
- package/src/stretchy.js +66 -0
- package/src/svg.js +110 -0
- package/src/symbols.js +972 -0
- package/src/tree.js +50 -0
- package/src/unicodeAccents.js +16 -0
- package/src/unicodeScripts.js +119 -0
- package/src/unicodeSupOrSub.js +108 -0
- package/src/unicodeSymbolBuilder.js +31 -0
- package/src/unicodeSymbols.js +320 -0
- package/src/units.js +109 -0
- package/src/utils.js +109 -0
- package/src/variant.js +103 -0
- package/temml.js +181 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Ron Kok
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
*Temml* is a LaTeX-to-MathML JavaScript conversion utility. It is built to be lightweight.
|
2
|
+
|
3
|
+
| Library | Minified JavaScript + CSS |
|
4
|
+
|:--------------|:-------------------------:|
|
5
|
+
| Temml | 147 KB |
|
6
|
+
| MathJax 2.7.5 | 338 KB |
|
7
|
+
| KaTeX | 280 KB |
|
8
|
+
| TeXZilla | 168 KB |
|
9
|
+
|
10
|
+
As a futher advantage, Temml can use local system fonts. The minimum Temml installation serves a font file that is only 12kb.
|
11
|
+
|
12
|
+
Chromium will support MathML in release 109 [early in 2023](https://chromiumdash.appspot.com/schedule), At that point, all the major browsers will support MathML and Temml will become the most lightweight way to render math in a browser.
|
13
|
+
|
14
|
+
Temml’s coverage of LaTeX functions is as good as MathJax, slightly better than KaTeX 0.13.0 and substantially better than TeXZilla. See a [detailed coverage comparison](https://temml.org/docs/en/comparison.html).
|
15
|
+
|
16
|
+
Temml's test suite includes many rendered examples, including the Temml [supported functions page](https://temml.org/docs/en/supported.html) and tests from [Mozilla](https://temml.org/tests/mozilla-tests.html), [Wikipedia](https://temml.org/tests/wiki-tests.html), [mhchem](https://temml.org/tests/mhchem-tests.html), and [LaTeXML](https://temml.org/tests/LaTeXML-tests.html).
|
17
|
+
|
18
|
+
Temml's demonstration page is at https://temml.org/
|
19
|
+
|
20
|
+
Documentation can be found at:
|
21
|
+
|
22
|
+
* [Installation](https://temml.org/docs/en/administration.html)
|
23
|
+
|
24
|
+
* LaTeX function support, [sorted into logical groups](https://temml.org/docs/en/supported.html).
|
25
|
+
|
26
|
+
* LaTeX function support, [sorted alphabetically](https://temml.org/docs/en/support_table.html).
|
27
|
+
|
28
|
+
### Acknowledgements
|
29
|
+
|
30
|
+
I built Temml by:
|
31
|
+
|
32
|
+
1. Forking [KaTeX](https://katex.org/).
|
33
|
+
|
34
|
+
2. Deleting half the code, removing the HTML parts and keeping the parser, the macro expander, and the MathML parts.
|
35
|
+
|
36
|
+
3. Doing some code refactoring and many MathML bug fixes.
|
37
|
+
|
38
|
+
4. Adding new functionality: upright lower-case Greek letters, `\euro`, `\label{…}`, `\ref{…}`, `\prescript`, `\definecolor`, `xcolor` color names, etc.
|
39
|
+
|
40
|
+
I wish to thank Khan Academy and the many volunteer KaTeX contributors. This library would not exist if KaTeX had not existed first.
|
41
|
+
|
42
|
+
---
|
43
|
+
|
44
|
+
Temml is released under terms of the [MIT license](https://mit-license.org/)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Auto-render extension
|
2
|
+
|
3
|
+
This is a client-side extension to automatically render all of the math inside of the
|
4
|
+
text of a running HTML document. It searches all of the text nodes in a given element
|
5
|
+
for the given delimiters, and renders the math in place.
|
6
|
+
|
7
|
+
|
8
|
+
This extension isn't part of Temml proper, so the script needs to be included
|
9
|
+
(via a `<script>` tag) in the page along with Temml itself. For example:
|
10
|
+
|
11
|
+
```html
|
12
|
+
<head>
|
13
|
+
...
|
14
|
+
<link rel="stylesheet" href="./Temml-Local.css">
|
15
|
+
<script src="./temml.min.js"></script>
|
16
|
+
<script src="./auto-render.min.js"></script>
|
17
|
+
...
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
...
|
21
|
+
<script>renderMathInElement(document.body);</script>
|
22
|
+
</body>
|
23
|
+
```
|
24
|
+
|
25
|
+
The auto-render extension exposes a single function, `window.renderMathInElement`, with
|
26
|
+
the following API:
|
27
|
+
|
28
|
+
```js
|
29
|
+
function renderMathInElement(elem, options)
|
30
|
+
```
|
31
|
+
|
32
|
+
`elem` is an HTML DOM element, typically `document.main`. The function will
|
33
|
+
recursively search for text nodes inside this element and render the math in them.
|
34
|
+
|
35
|
+
`options` is an optional object argument that can have the same keys as [the
|
36
|
+
options](https://temml.org/docs/en/administration.html#options) passed to
|
37
|
+
`temml.render`. In addition, there are five auto-render-specific keys:
|
38
|
+
|
39
|
+
- `delimiters`: This is a list of delimiters to look for math, processed in
|
40
|
+
the same order as the list. Each delimiter has three properties:
|
41
|
+
|
42
|
+
- `left`: A string which starts the math expression (i.e. the left delimiter).
|
43
|
+
- `right`: A string which ends the math expression (i.e. the right delimiter).
|
44
|
+
- `display`: A boolean of whether the math in the expression should be
|
45
|
+
rendered in display mode or not.
|
46
|
+
|
47
|
+
The default `delimiters` value is:
|
48
|
+
|
49
|
+
```js
|
50
|
+
[
|
51
|
+
{ left: "$$", right: "$$", display: true },
|
52
|
+
{ left: "\\(", right: "\\)", display: false },
|
53
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
54
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
55
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
56
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
57
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
58
|
+
{ left: "\\begin{multline}", right: "\\end{multline}", display: true },
|
59
|
+
{ left: "\\[", right: "\\]", display: true }
|
60
|
+
]
|
61
|
+
```
|
62
|
+
|
63
|
+
If you want to add support for inline math via `$…$`, be sure to list it
|
64
|
+
**after** `$$…$$`. Because rules are processed in order, putting a `$` rule first would
|
65
|
+
match `$$` and treat as an empty math expression. Here is an example that includes `$…$`:
|
66
|
+
|
67
|
+
```js
|
68
|
+
[
|
69
|
+
{left: "$$", right: "$$", display: true},
|
70
|
+
// Put $ after $$.
|
71
|
+
{left: "$", right: "$", display: false},
|
72
|
+
{left: "\\(", right: "\\)", display: false},
|
73
|
+
// Put \[ last to avoid conflict with possible future \\[1em] row separator.
|
74
|
+
{left: "\\[", right: "\\]", display: true}
|
75
|
+
]
|
76
|
+
```
|
77
|
+
|
78
|
+
- `ignoredTags`: This is a list of DOM node types to ignore when recursing
|
79
|
+
through. The default value is
|
80
|
+
`["script", "noscript", "style", "textarea", "pre", "code", "option"]`.
|
81
|
+
|
82
|
+
- `ignoredClasses`: This is a list of DOM node class names to ignore when
|
83
|
+
recursing through. By default, this value is not set.
|
84
|
+
|
85
|
+
- `errorCallback`: A callback method returning a message and an error stack
|
86
|
+
in case of an critical error during rendering. The default uses `console.error`.
|
87
|
+
|
88
|
+
- `preProcess`: A callback function, `(math: string) => string`, used to process
|
89
|
+
math expressions before rendering.
|
@@ -0,0 +1,128 @@
|
|
1
|
+
/* eslint no-console:0 */
|
2
|
+
|
3
|
+
import temml from "temml";
|
4
|
+
import splitAtDelimiters from "./splitAtDelimiters";
|
5
|
+
|
6
|
+
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
7
|
+
* API, we should copy it before mutating.
|
8
|
+
*/
|
9
|
+
const renderMathInText = function(text, optionsCopy) {
|
10
|
+
const data = splitAtDelimiters(text, optionsCopy.delimiters);
|
11
|
+
if (data.length === 1 && data[0].type === "text") {
|
12
|
+
// There is no formula in the text.
|
13
|
+
// Let's return null which means there is no need to replace
|
14
|
+
// the current text node with a new one.
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
|
18
|
+
const fragment = document.createDocumentFragment();
|
19
|
+
|
20
|
+
for (let i = 0; i < data.length; i++) {
|
21
|
+
if (data[i].type === "text") {
|
22
|
+
fragment.appendChild(document.createTextNode(data[i].data));
|
23
|
+
} else {
|
24
|
+
const span = document.createElement("span");
|
25
|
+
let math = data[i].data;
|
26
|
+
// Override any display mode defined in the settings with that
|
27
|
+
// defined by the text itself
|
28
|
+
optionsCopy.displayMode = data[i].display;
|
29
|
+
try {
|
30
|
+
if (optionsCopy.preProcess) {
|
31
|
+
math = optionsCopy.preProcess(math);
|
32
|
+
}
|
33
|
+
temml.render(math, span, optionsCopy);
|
34
|
+
} catch (e) {
|
35
|
+
if (!(e instanceof temml.ParseError)) {
|
36
|
+
throw e;
|
37
|
+
}
|
38
|
+
optionsCopy.errorCallback(
|
39
|
+
"Temml auto-render: Failed to parse `" + data[i].data + "` with ",
|
40
|
+
e
|
41
|
+
);
|
42
|
+
fragment.appendChild(document.createTextNode(data[i].rawData));
|
43
|
+
continue;
|
44
|
+
}
|
45
|
+
fragment.appendChild(span);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
return fragment;
|
50
|
+
};
|
51
|
+
|
52
|
+
const renderElem = function(elem, optionsCopy) {
|
53
|
+
for (let i = 0; i < elem.childNodes.length; i++) {
|
54
|
+
const childNode = elem.childNodes[i];
|
55
|
+
if (childNode.nodeType === 3) {
|
56
|
+
// Text node
|
57
|
+
const frag = renderMathInText(childNode.textContent, optionsCopy);
|
58
|
+
if (frag) {
|
59
|
+
i += frag.childNodes.length - 1;
|
60
|
+
elem.replaceChild(frag, childNode);
|
61
|
+
}
|
62
|
+
} else if (childNode.nodeType === 1) {
|
63
|
+
// Element node
|
64
|
+
const className = " " + childNode.className + " ";
|
65
|
+
const shouldRender =
|
66
|
+
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
|
67
|
+
optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
|
68
|
+
|
69
|
+
if (shouldRender) {
|
70
|
+
renderElem(childNode, optionsCopy);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
// Otherwise, it's something else, and ignore it.
|
74
|
+
}
|
75
|
+
};
|
76
|
+
|
77
|
+
const renderMathInElement = function(elem, options) {
|
78
|
+
if (!elem) {
|
79
|
+
throw new Error("No element provided to render");
|
80
|
+
}
|
81
|
+
|
82
|
+
const optionsCopy = {};
|
83
|
+
|
84
|
+
// Object.assign(optionsCopy, option)
|
85
|
+
for (const option in options) {
|
86
|
+
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
87
|
+
optionsCopy[option] = options[option];
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
// default options
|
92
|
+
optionsCopy.delimiters = optionsCopy.delimiters || [
|
93
|
+
{ left: "$$", right: "$$", display: true },
|
94
|
+
{ left: "\\(", right: "\\)", display: false },
|
95
|
+
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
96
|
+
// {left: "$", right: "$", display: false},
|
97
|
+
// $ must come after $$
|
98
|
+
|
99
|
+
// Render AMS environments even if outside $$…$$ delimiters.
|
100
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
101
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
102
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
103
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
104
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
105
|
+
|
106
|
+
{ left: "\\[", right: "\\]", display: true }
|
107
|
+
];
|
108
|
+
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
|
109
|
+
"script",
|
110
|
+
"noscript",
|
111
|
+
"style",
|
112
|
+
"textarea",
|
113
|
+
"pre",
|
114
|
+
"code",
|
115
|
+
"option"
|
116
|
+
];
|
117
|
+
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
118
|
+
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
|
119
|
+
|
120
|
+
// Enable sharing of global macros defined via `\gdef` between different
|
121
|
+
// math elements within a single call to `renderMathInElement`.
|
122
|
+
optionsCopy.macros = optionsCopy.macros || {};
|
123
|
+
|
124
|
+
renderElem(elem, optionsCopy);
|
125
|
+
temml.postProcess(elem);
|
126
|
+
};
|
127
|
+
|
128
|
+
export default renderMathInElement;
|
@@ -0,0 +1,217 @@
|
|
1
|
+
var renderMathInElement = (function (temml) {
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
5
|
+
|
6
|
+
var temml__default = /*#__PURE__*/_interopDefaultLegacy(temml);
|
7
|
+
|
8
|
+
/* eslint no-constant-condition:0 */
|
9
|
+
const findEndOfMath = function(delimiter, text, startIndex) {
|
10
|
+
// Adapted from
|
11
|
+
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
12
|
+
let index = startIndex;
|
13
|
+
let braceLevel = 0;
|
14
|
+
|
15
|
+
const delimLength = delimiter.length;
|
16
|
+
|
17
|
+
while (index < text.length) {
|
18
|
+
const character = text[index];
|
19
|
+
|
20
|
+
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
21
|
+
return index;
|
22
|
+
} else if (character === "\\") {
|
23
|
+
index++;
|
24
|
+
} else if (character === "{") {
|
25
|
+
braceLevel++;
|
26
|
+
} else if (character === "}") {
|
27
|
+
braceLevel--;
|
28
|
+
}
|
29
|
+
|
30
|
+
index++;
|
31
|
+
}
|
32
|
+
|
33
|
+
return -1;
|
34
|
+
};
|
35
|
+
|
36
|
+
const escapeRegex = function(string) {
|
37
|
+
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
38
|
+
};
|
39
|
+
|
40
|
+
const amsRegex = /^\\begin{/;
|
41
|
+
|
42
|
+
const splitAtDelimiters = function(text, delimiters) {
|
43
|
+
let index;
|
44
|
+
const data = [];
|
45
|
+
|
46
|
+
const regexLeft = new RegExp(
|
47
|
+
"(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
|
48
|
+
);
|
49
|
+
|
50
|
+
while (true) {
|
51
|
+
index = text.search(regexLeft);
|
52
|
+
if (index === -1) {
|
53
|
+
break;
|
54
|
+
}
|
55
|
+
if (index > 0) {
|
56
|
+
data.push({
|
57
|
+
type: "text",
|
58
|
+
data: text.slice(0, index)
|
59
|
+
});
|
60
|
+
text = text.slice(index); // now text starts with delimiter
|
61
|
+
}
|
62
|
+
// ... so this always succeeds:
|
63
|
+
const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
|
64
|
+
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
65
|
+
if (index === -1) {
|
66
|
+
break;
|
67
|
+
}
|
68
|
+
const rawData = text.slice(0, index + delimiters[i].right.length);
|
69
|
+
const math = amsRegex.test(rawData)
|
70
|
+
? rawData
|
71
|
+
: text.slice(delimiters[i].left.length, index);
|
72
|
+
data.push({
|
73
|
+
type: "math",
|
74
|
+
data: math,
|
75
|
+
rawData,
|
76
|
+
display: delimiters[i].display
|
77
|
+
});
|
78
|
+
text = text.slice(index + delimiters[i].right.length);
|
79
|
+
}
|
80
|
+
|
81
|
+
if (text !== "") {
|
82
|
+
data.push({
|
83
|
+
type: "text",
|
84
|
+
data: text
|
85
|
+
});
|
86
|
+
}
|
87
|
+
|
88
|
+
return data;
|
89
|
+
};
|
90
|
+
|
91
|
+
/* eslint no-console:0 */
|
92
|
+
|
93
|
+
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
94
|
+
* API, we should copy it before mutating.
|
95
|
+
*/
|
96
|
+
const renderMathInText = function(text, optionsCopy) {
|
97
|
+
const data = splitAtDelimiters(text, optionsCopy.delimiters);
|
98
|
+
if (data.length === 1 && data[0].type === "text") {
|
99
|
+
// There is no formula in the text.
|
100
|
+
// Let's return null which means there is no need to replace
|
101
|
+
// the current text node with a new one.
|
102
|
+
return null;
|
103
|
+
}
|
104
|
+
|
105
|
+
const fragment = document.createDocumentFragment();
|
106
|
+
|
107
|
+
for (let i = 0; i < data.length; i++) {
|
108
|
+
if (data[i].type === "text") {
|
109
|
+
fragment.appendChild(document.createTextNode(data[i].data));
|
110
|
+
} else {
|
111
|
+
const span = document.createElement("span");
|
112
|
+
let math = data[i].data;
|
113
|
+
// Override any display mode defined in the settings with that
|
114
|
+
// defined by the text itself
|
115
|
+
optionsCopy.displayMode = data[i].display;
|
116
|
+
try {
|
117
|
+
if (optionsCopy.preProcess) {
|
118
|
+
math = optionsCopy.preProcess(math);
|
119
|
+
}
|
120
|
+
temml__default["default"].render(math, span, optionsCopy);
|
121
|
+
} catch (e) {
|
122
|
+
if (!(e instanceof temml__default["default"].ParseError)) {
|
123
|
+
throw e;
|
124
|
+
}
|
125
|
+
optionsCopy.errorCallback(
|
126
|
+
"Temml auto-render: Failed to parse `" + data[i].data + "` with ",
|
127
|
+
e
|
128
|
+
);
|
129
|
+
fragment.appendChild(document.createTextNode(data[i].rawData));
|
130
|
+
continue;
|
131
|
+
}
|
132
|
+
fragment.appendChild(span);
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
return fragment;
|
137
|
+
};
|
138
|
+
|
139
|
+
const renderElem = function(elem, optionsCopy) {
|
140
|
+
for (let i = 0; i < elem.childNodes.length; i++) {
|
141
|
+
const childNode = elem.childNodes[i];
|
142
|
+
if (childNode.nodeType === 3) {
|
143
|
+
// Text node
|
144
|
+
const frag = renderMathInText(childNode.textContent, optionsCopy);
|
145
|
+
if (frag) {
|
146
|
+
i += frag.childNodes.length - 1;
|
147
|
+
elem.replaceChild(frag, childNode);
|
148
|
+
}
|
149
|
+
} else if (childNode.nodeType === 1) {
|
150
|
+
// Element node
|
151
|
+
const className = " " + childNode.className + " ";
|
152
|
+
const shouldRender =
|
153
|
+
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
|
154
|
+
optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
|
155
|
+
|
156
|
+
if (shouldRender) {
|
157
|
+
renderElem(childNode, optionsCopy);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
// Otherwise, it's something else, and ignore it.
|
161
|
+
}
|
162
|
+
};
|
163
|
+
|
164
|
+
const renderMathInElement = function(elem, options) {
|
165
|
+
if (!elem) {
|
166
|
+
throw new Error("No element provided to render");
|
167
|
+
}
|
168
|
+
|
169
|
+
const optionsCopy = {};
|
170
|
+
|
171
|
+
// Object.assign(optionsCopy, option)
|
172
|
+
for (const option in options) {
|
173
|
+
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
174
|
+
optionsCopy[option] = options[option];
|
175
|
+
}
|
176
|
+
}
|
177
|
+
|
178
|
+
// default options
|
179
|
+
optionsCopy.delimiters = optionsCopy.delimiters || [
|
180
|
+
{ left: "$$", right: "$$", display: true },
|
181
|
+
{ left: "\\(", right: "\\)", display: false },
|
182
|
+
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
183
|
+
// {left: "$", right: "$", display: false},
|
184
|
+
// $ must come after $$
|
185
|
+
|
186
|
+
// Render AMS environments even if outside $$…$$ delimiters.
|
187
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
188
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
189
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
190
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
191
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
192
|
+
|
193
|
+
{ left: "\\[", right: "\\]", display: true }
|
194
|
+
];
|
195
|
+
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
|
196
|
+
"script",
|
197
|
+
"noscript",
|
198
|
+
"style",
|
199
|
+
"textarea",
|
200
|
+
"pre",
|
201
|
+
"code",
|
202
|
+
"option"
|
203
|
+
];
|
204
|
+
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
205
|
+
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
|
206
|
+
|
207
|
+
// Enable sharing of global macros defined via `\gdef` between different
|
208
|
+
// math elements within a single call to `renderMathInElement`.
|
209
|
+
optionsCopy.macros = optionsCopy.macros || {};
|
210
|
+
|
211
|
+
renderElem(elem, optionsCopy);
|
212
|
+
temml__default["default"].postProcess(elem);
|
213
|
+
};
|
214
|
+
|
215
|
+
return renderMathInElement;
|
216
|
+
|
217
|
+
})(temml);
|
@@ -0,0 +1 @@
|
|
1
|
+
var renderMathInElement=function(e){"use strict";function t(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var n=t(e);const r=function(e,t,n){let r=n,a=0;const i=e.length;for(;r<t.length;){const n=t[r];if(a<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?a++:"}"===n&&a--,r++}return-1},a=/^\\begin{/,i=function(e,t){const i=function(e,t){let n;const i=[],l=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(l),-1!==n;){n>0&&(i.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const l=t.findIndex((t=>e.startsWith(t.left)));if(n=r(t[l].right,e,t[l].left.length),-1===n)break;const o=e.slice(0,n+t[l].right.length),s=a.test(o)?o:e.slice(t[l].left.length,n);i.push({type:"math",data:s,rawData:o,display:t[l].display}),e=e.slice(n+t[l].right.length)}return""!==e&&i.push({type:"text",data:e}),i}(e,t.delimiters);if(1===i.length&&"text"===i[0].type)return null;const l=document.createDocumentFragment();for(let e=0;e<i.length;e++)if("text"===i[e].type)l.appendChild(document.createTextNode(i[e].data));else{const r=document.createElement("span");let a=i[e].data;t.displayMode=i[e].display;try{t.preProcess&&(a=t.preProcess(a)),n.default.render(a,r,t)}catch(r){if(!(r instanceof n.default.ParseError))throw r;t.errorCallback("Temml auto-render: Failed to parse `"+i[e].data+"` with ",r),l.appendChild(document.createTextNode(i[e].rawData));continue}l.appendChild(r)}return l},l=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){const a=i(r.textContent,t);a&&(n+=a.childNodes.length-1,e.replaceChild(a,r))}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&l(r,t)}}};return function(e,t){if(!e)throw new Error("No element provided to render");const r={};for(const e in t)Object.prototype.hasOwnProperty.call(t,e)&&(r[e]=t[e]);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||{},l(e,r),n.default.postProcess(e)}}(temml);
|
@@ -0,0 +1,84 @@
|
|
1
|
+
/* eslint no-constant-condition:0 */
|
2
|
+
const findEndOfMath = function(delimiter, text, startIndex) {
|
3
|
+
// Adapted from
|
4
|
+
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
5
|
+
let index = startIndex;
|
6
|
+
let braceLevel = 0;
|
7
|
+
|
8
|
+
const delimLength = delimiter.length;
|
9
|
+
|
10
|
+
while (index < text.length) {
|
11
|
+
const character = text[index];
|
12
|
+
|
13
|
+
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
14
|
+
return index;
|
15
|
+
} else if (character === "\\") {
|
16
|
+
index++;
|
17
|
+
} else if (character === "{") {
|
18
|
+
braceLevel++;
|
19
|
+
} else if (character === "}") {
|
20
|
+
braceLevel--;
|
21
|
+
}
|
22
|
+
|
23
|
+
index++;
|
24
|
+
}
|
25
|
+
|
26
|
+
return -1;
|
27
|
+
};
|
28
|
+
|
29
|
+
const escapeRegex = function(string) {
|
30
|
+
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
31
|
+
};
|
32
|
+
|
33
|
+
const amsRegex = /^\\begin{/;
|
34
|
+
|
35
|
+
const splitAtDelimiters = function(text, delimiters) {
|
36
|
+
let index;
|
37
|
+
const data = [];
|
38
|
+
|
39
|
+
const regexLeft = new RegExp(
|
40
|
+
"(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
|
41
|
+
)
|
42
|
+
|
43
|
+
while (true) {
|
44
|
+
index = text.search(regexLeft);
|
45
|
+
if (index === -1) {
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
if (index > 0) {
|
49
|
+
data.push({
|
50
|
+
type: "text",
|
51
|
+
data: text.slice(0, index)
|
52
|
+
});
|
53
|
+
text = text.slice(index); // now text starts with delimiter
|
54
|
+
}
|
55
|
+
// ... so this always succeeds:
|
56
|
+
const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
|
57
|
+
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
58
|
+
if (index === -1) {
|
59
|
+
break;
|
60
|
+
}
|
61
|
+
const rawData = text.slice(0, index + delimiters[i].right.length);
|
62
|
+
const math = amsRegex.test(rawData)
|
63
|
+
? rawData
|
64
|
+
: text.slice(delimiters[i].left.length, index);
|
65
|
+
data.push({
|
66
|
+
type: "math",
|
67
|
+
data: math,
|
68
|
+
rawData,
|
69
|
+
display: delimiters[i].display
|
70
|
+
});
|
71
|
+
text = text.slice(index + delimiters[i].right.length);
|
72
|
+
}
|
73
|
+
|
74
|
+
if (text !== "") {
|
75
|
+
data.push({
|
76
|
+
type: "text",
|
77
|
+
data: text
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
return data;
|
82
|
+
};
|
83
|
+
|
84
|
+
export default splitAtDelimiters;
|