temml 0.10.34 → 0.11.0
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 +1 -1
- package/dist/temml.cjs +270 -4
- package/dist/temml.d.ts +3 -0
- package/dist/temml.js +270 -4
- package/dist/temml.min.js +1 -1
- package/dist/temml.mjs +270 -4
- package/dist/temmlPostProcess.js +1 -1
- package/package.json +2 -2
- package/src/auto-render.js +263 -0
- package/src/linebreaking.js +1 -1
- package/src/postProcess.js +1 -1
- package/temml.js +6 -0
- package/contrib/auto-render/README.md +0 -89
- package/contrib/auto-render/auto-render.js +0 -128
- package/contrib/auto-render/dist/auto-render.js +0 -214
- package/contrib/auto-render/dist/auto-render.min.js +0 -1
- package/contrib/auto-render/splitAtDelimiters.js +0 -84
- package/contrib/auto-render/test/auto-render-spec.js +0 -234
- package/contrib/auto-render/test/auto-render.js +0 -214
- package/contrib/auto-render/test/test_page.html +0 -59
package/dist/temml.mjs
CHANGED
@@ -2008,7 +2008,7 @@ function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
2008
2008
|
}
|
2009
2009
|
block.push(node);
|
2010
2010
|
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
2011
|
-
!Object.
|
2011
|
+
!Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) {
|
2012
2012
|
const ch = node.children[0].text;
|
2013
2013
|
if (openDelims.indexOf(ch) > -1) {
|
2014
2014
|
level += 1;
|
@@ -13675,7 +13675,7 @@ class Style {
|
|
13675
13675
|
* https://mit-license.org/
|
13676
13676
|
*/
|
13677
13677
|
|
13678
|
-
const version = "0.
|
13678
|
+
const version = "0.11.00";
|
13679
13679
|
|
13680
13680
|
function postProcess(block) {
|
13681
13681
|
const labelMap = {};
|
@@ -13740,6 +13740,267 @@ function postProcess(block) {
|
|
13740
13740
|
});
|
13741
13741
|
}
|
13742
13742
|
|
13743
|
+
const findEndOfMath = function(delimiter, text, startIndex) {
|
13744
|
+
// Adapted from
|
13745
|
+
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
13746
|
+
let index = startIndex;
|
13747
|
+
let braceLevel = 0;
|
13748
|
+
|
13749
|
+
const delimLength = delimiter.length;
|
13750
|
+
|
13751
|
+
while (index < text.length) {
|
13752
|
+
const character = text[index];
|
13753
|
+
|
13754
|
+
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
13755
|
+
return index;
|
13756
|
+
} else if (character === "\\") {
|
13757
|
+
index++;
|
13758
|
+
} else if (character === "{") {
|
13759
|
+
braceLevel++;
|
13760
|
+
} else if (character === "}") {
|
13761
|
+
braceLevel--;
|
13762
|
+
}
|
13763
|
+
|
13764
|
+
index++;
|
13765
|
+
}
|
13766
|
+
|
13767
|
+
return -1;
|
13768
|
+
};
|
13769
|
+
|
13770
|
+
const escapeRegex = function(string) {
|
13771
|
+
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
13772
|
+
};
|
13773
|
+
|
13774
|
+
const amsRegex = /^\\(?:begin|(?:eq)?ref){/;
|
13775
|
+
|
13776
|
+
const splitAtDelimiters = function(text, delimiters) {
|
13777
|
+
let index;
|
13778
|
+
const data = [];
|
13779
|
+
|
13780
|
+
const regexLeft = new RegExp(
|
13781
|
+
"(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
|
13782
|
+
);
|
13783
|
+
|
13784
|
+
while (true) {
|
13785
|
+
index = text.search(regexLeft);
|
13786
|
+
if (index === -1) {
|
13787
|
+
break;
|
13788
|
+
}
|
13789
|
+
if (index > 0) {
|
13790
|
+
data.push({
|
13791
|
+
type: "text",
|
13792
|
+
data: text.slice(0, index)
|
13793
|
+
});
|
13794
|
+
text = text.slice(index); // now text starts with delimiter
|
13795
|
+
}
|
13796
|
+
// ... so this always succeeds:
|
13797
|
+
const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
|
13798
|
+
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
13799
|
+
if (index === -1) {
|
13800
|
+
break;
|
13801
|
+
}
|
13802
|
+
const rawData = text.slice(0, index + delimiters[i].right.length);
|
13803
|
+
const math = amsRegex.test(rawData)
|
13804
|
+
? rawData
|
13805
|
+
: text.slice(delimiters[i].left.length, index);
|
13806
|
+
data.push({
|
13807
|
+
type: "math",
|
13808
|
+
data: math,
|
13809
|
+
rawData,
|
13810
|
+
display: delimiters[i].display
|
13811
|
+
});
|
13812
|
+
text = text.slice(index + delimiters[i].right.length);
|
13813
|
+
}
|
13814
|
+
|
13815
|
+
if (text !== "") {
|
13816
|
+
data.push({
|
13817
|
+
type: "text",
|
13818
|
+
data: text
|
13819
|
+
});
|
13820
|
+
}
|
13821
|
+
|
13822
|
+
return data;
|
13823
|
+
};
|
13824
|
+
|
13825
|
+
const defaultDelimiters = [
|
13826
|
+
{ left: "$$", right: "$$", display: true },
|
13827
|
+
{ left: "\\(", right: "\\)", display: false },
|
13828
|
+
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
13829
|
+
// {left: "$", right: "$", display: false},
|
13830
|
+
// $ must come after $$
|
13831
|
+
|
13832
|
+
// Render AMS environments even if outside $$…$$ delimiters.
|
13833
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
13834
|
+
{ left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
|
13835
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
13836
|
+
{ left: "\\begin{align*}", right: "\\end{align*}", display: true },
|
13837
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
13838
|
+
{ left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
|
13839
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
13840
|
+
{ left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
|
13841
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
13842
|
+
// Ditto \ref & \eqref
|
13843
|
+
{ left: "\\ref{", right: "}", display: false },
|
13844
|
+
{ left: "\\eqref{", right: "}", display: false },
|
13845
|
+
|
13846
|
+
{ left: "\\[", right: "\\]", display: true }
|
13847
|
+
];
|
13848
|
+
|
13849
|
+
const firstDraftDelimiters = {
|
13850
|
+
"$": [
|
13851
|
+
{ left: "$$", right: "$$", display: true },
|
13852
|
+
{ left: "$`", right: "`$", display: false },
|
13853
|
+
{ left: "$", right: "$", display: false }
|
13854
|
+
],
|
13855
|
+
"(": [
|
13856
|
+
{ left: "\\[", right: "\\]", display: true },
|
13857
|
+
{ left: "\\(", right: "\\)", display: false }
|
13858
|
+
]
|
13859
|
+
};
|
13860
|
+
|
13861
|
+
const amsDelimiters = [
|
13862
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
13863
|
+
{ left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
|
13864
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
13865
|
+
{ left: "\\begin{align*}", right: "\\end{align*}", display: true },
|
13866
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
13867
|
+
{ left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
|
13868
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
13869
|
+
{ left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
|
13870
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
13871
|
+
{ left: "\\ref{", right: "}", display: false },
|
13872
|
+
{ left: "\\eqref{", right: "}", display: false }
|
13873
|
+
];
|
13874
|
+
|
13875
|
+
const delimitersFromKey = key => {
|
13876
|
+
if (key === "$" || key === "(") {
|
13877
|
+
return firstDraftDelimiters[key];
|
13878
|
+
} else if (key === "$+" || key === "(+") {
|
13879
|
+
const firstDraft = firstDraftDelimiters[key.slice(0, 1)];
|
13880
|
+
return firstDraft.concat(amsDelimiters)
|
13881
|
+
} else if (key === "ams") {
|
13882
|
+
return amsDelimiters
|
13883
|
+
} else if (key === "all") {
|
13884
|
+
return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters)
|
13885
|
+
} else {
|
13886
|
+
return defaultDelimiters
|
13887
|
+
}
|
13888
|
+
};
|
13889
|
+
|
13890
|
+
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
13891
|
+
* API, we should copy it before mutating.
|
13892
|
+
*/
|
13893
|
+
const renderMathInText = function(text, optionsCopy) {
|
13894
|
+
const data = splitAtDelimiters(text, optionsCopy.delimiters);
|
13895
|
+
if (data.length === 1 && data[0].type === "text") {
|
13896
|
+
// There is no formula in the text.
|
13897
|
+
// Let's return null which means there is no need to replace
|
13898
|
+
// the current text node with a new one.
|
13899
|
+
return null;
|
13900
|
+
}
|
13901
|
+
|
13902
|
+
const fragment = document.createDocumentFragment();
|
13903
|
+
|
13904
|
+
for (let i = 0; i < data.length; i++) {
|
13905
|
+
if (data[i].type === "text") {
|
13906
|
+
fragment.appendChild(document.createTextNode(data[i].data));
|
13907
|
+
} else {
|
13908
|
+
const span = document.createElement("span");
|
13909
|
+
let math = data[i].data;
|
13910
|
+
// Override any display mode defined in the settings with that
|
13911
|
+
// defined by the text itself
|
13912
|
+
optionsCopy.displayMode = data[i].display;
|
13913
|
+
try {
|
13914
|
+
if (optionsCopy.preProcess) {
|
13915
|
+
math = optionsCopy.preProcess(math);
|
13916
|
+
}
|
13917
|
+
// Importing render() from temml.js would be a circular dependency.
|
13918
|
+
// So call the global version.
|
13919
|
+
// eslint-disable-next-line no-undef
|
13920
|
+
temml.render(math, span, optionsCopy);
|
13921
|
+
} catch (e) {
|
13922
|
+
if (!(e instanceof ParseError)) {
|
13923
|
+
throw e;
|
13924
|
+
}
|
13925
|
+
optionsCopy.errorCallback(
|
13926
|
+
"Temml auto-render: Failed to parse `" + data[i].data + "` with ",
|
13927
|
+
e
|
13928
|
+
);
|
13929
|
+
fragment.appendChild(document.createTextNode(data[i].rawData));
|
13930
|
+
continue;
|
13931
|
+
}
|
13932
|
+
fragment.appendChild(span);
|
13933
|
+
}
|
13934
|
+
}
|
13935
|
+
|
13936
|
+
return fragment;
|
13937
|
+
};
|
13938
|
+
|
13939
|
+
const renderElem = function(elem, optionsCopy) {
|
13940
|
+
for (let i = 0; i < elem.childNodes.length; i++) {
|
13941
|
+
const childNode = elem.childNodes[i];
|
13942
|
+
if (childNode.nodeType === 3) {
|
13943
|
+
// Text node
|
13944
|
+
const frag = renderMathInText(childNode.textContent, optionsCopy);
|
13945
|
+
if (frag) {
|
13946
|
+
i += frag.childNodes.length - 1;
|
13947
|
+
elem.replaceChild(frag, childNode);
|
13948
|
+
}
|
13949
|
+
} else if (childNode.nodeType === 1) {
|
13950
|
+
// Element node
|
13951
|
+
const className = " " + childNode.className + " ";
|
13952
|
+
const shouldRender =
|
13953
|
+
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
|
13954
|
+
optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
|
13955
|
+
|
13956
|
+
if (shouldRender) {
|
13957
|
+
renderElem(childNode, optionsCopy);
|
13958
|
+
}
|
13959
|
+
}
|
13960
|
+
// Otherwise, it's something else, and ignore it.
|
13961
|
+
}
|
13962
|
+
};
|
13963
|
+
|
13964
|
+
const renderMathInElement = function(elem, options) {
|
13965
|
+
if (!elem) {
|
13966
|
+
throw new Error("No element provided to render");
|
13967
|
+
}
|
13968
|
+
|
13969
|
+
const optionsCopy = {};
|
13970
|
+
|
13971
|
+
// Object.assign(optionsCopy, option)
|
13972
|
+
for (const option in options) {
|
13973
|
+
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
13974
|
+
optionsCopy[option] = options[option];
|
13975
|
+
}
|
13976
|
+
}
|
13977
|
+
|
13978
|
+
if (optionsCopy.fences) {
|
13979
|
+
optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences);
|
13980
|
+
} else {
|
13981
|
+
optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters;
|
13982
|
+
}
|
13983
|
+
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
|
13984
|
+
"script",
|
13985
|
+
"noscript",
|
13986
|
+
"style",
|
13987
|
+
"textarea",
|
13988
|
+
"pre",
|
13989
|
+
"code",
|
13990
|
+
"option"
|
13991
|
+
];
|
13992
|
+
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
13993
|
+
// eslint-disable-next-line no-console
|
13994
|
+
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
|
13995
|
+
|
13996
|
+
// Enable sharing of global macros defined via `\gdef` between different
|
13997
|
+
// math elements within a single call to `renderMathInElement`.
|
13998
|
+
optionsCopy.macros = optionsCopy.macros || {};
|
13999
|
+
|
14000
|
+
renderElem(elem, optionsCopy);
|
14001
|
+
postProcess(elem);
|
14002
|
+
};
|
14003
|
+
|
13743
14004
|
/* eslint no-console:0 */
|
13744
14005
|
/**
|
13745
14006
|
* This is the main entry point for Temml. Here, we expose functions for
|
@@ -13859,7 +14120,7 @@ const renderToMathMLTree = function(expression, options) {
|
|
13859
14120
|
};
|
13860
14121
|
|
13861
14122
|
/** @type {import('./temml').default} */
|
13862
|
-
var temml = {
|
14123
|
+
var temml$1 = {
|
13863
14124
|
/**
|
13864
14125
|
* Current Temml version
|
13865
14126
|
*/
|
@@ -13874,6 +14135,11 @@ var temml = {
|
|
13874
14135
|
* for sending to the client.
|
13875
14136
|
*/
|
13876
14137
|
renderToString,
|
14138
|
+
/**
|
14139
|
+
* Finds all the math delimiters in a given element of a running HTML document
|
14140
|
+
* and converts the contents of each instance into a <math> element.
|
14141
|
+
*/
|
14142
|
+
renderMathInElement,
|
13877
14143
|
/**
|
13878
14144
|
* Post-process an entire HTML block.
|
13879
14145
|
* Writes AMS auto-numbers and implements \ref{}.
|
@@ -13916,4 +14182,4 @@ var temml = {
|
|
13916
14182
|
__defineMacro: defineMacro
|
13917
14183
|
};
|
13918
14184
|
|
13919
|
-
export { temml as default };
|
14185
|
+
export { temml$1 as default };
|
package/dist/temmlPostProcess.js
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "temml",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.11.00",
|
4
4
|
"description": "TeX to MathML conversion in JavaScript.",
|
5
5
|
"main": "dist/temml.js",
|
6
6
|
"engines": {
|
@@ -44,6 +44,6 @@
|
|
44
44
|
"minify": "terser test/temml.js -o site/assets/temml.min.js -c -m && terser contrib/mhchem/mhchem.js -o site/assets/mhchem.min.js -c -m",
|
45
45
|
"build": "rollup --config ./utils/rollupConfig.mjs && yarn minify && node utils/insertPlugins.js",
|
46
46
|
"docs": "node utils/buildDocs.js",
|
47
|
-
"dist": "yarn build && node ./utils/copyfiles.js
|
47
|
+
"dist": "yarn build && node ./utils/copyfiles.js"
|
48
48
|
}
|
49
49
|
}
|
@@ -0,0 +1,263 @@
|
|
1
|
+
import ParseError from "./ParseError.js"
|
2
|
+
import { postProcess } from "./postProcess.js";
|
3
|
+
|
4
|
+
const findEndOfMath = function(delimiter, text, startIndex) {
|
5
|
+
// Adapted from
|
6
|
+
// https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
|
7
|
+
let index = startIndex;
|
8
|
+
let braceLevel = 0;
|
9
|
+
|
10
|
+
const delimLength = delimiter.length;
|
11
|
+
|
12
|
+
while (index < text.length) {
|
13
|
+
const character = text[index];
|
14
|
+
|
15
|
+
if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
|
16
|
+
return index;
|
17
|
+
} else if (character === "\\") {
|
18
|
+
index++;
|
19
|
+
} else if (character === "{") {
|
20
|
+
braceLevel++;
|
21
|
+
} else if (character === "}") {
|
22
|
+
braceLevel--;
|
23
|
+
}
|
24
|
+
|
25
|
+
index++;
|
26
|
+
}
|
27
|
+
|
28
|
+
return -1;
|
29
|
+
};
|
30
|
+
|
31
|
+
const escapeRegex = function(string) {
|
32
|
+
return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
|
33
|
+
};
|
34
|
+
|
35
|
+
const amsRegex = /^\\(?:begin|(?:eq)?ref){/
|
36
|
+
|
37
|
+
const splitAtDelimiters = function(text, delimiters) {
|
38
|
+
let index;
|
39
|
+
const data = [];
|
40
|
+
|
41
|
+
const regexLeft = new RegExp(
|
42
|
+
"(" + delimiters.map((x) => escapeRegex(x.left)).join("|") + ")"
|
43
|
+
)
|
44
|
+
|
45
|
+
while (true) {
|
46
|
+
index = text.search(regexLeft);
|
47
|
+
if (index === -1) {
|
48
|
+
break;
|
49
|
+
}
|
50
|
+
if (index > 0) {
|
51
|
+
data.push({
|
52
|
+
type: "text",
|
53
|
+
data: text.slice(0, index)
|
54
|
+
});
|
55
|
+
text = text.slice(index); // now text starts with delimiter
|
56
|
+
}
|
57
|
+
// ... so this always succeeds:
|
58
|
+
const i = delimiters.findIndex((delim) => text.startsWith(delim.left));
|
59
|
+
index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
|
60
|
+
if (index === -1) {
|
61
|
+
break;
|
62
|
+
}
|
63
|
+
const rawData = text.slice(0, index + delimiters[i].right.length);
|
64
|
+
const math = amsRegex.test(rawData)
|
65
|
+
? rawData
|
66
|
+
: text.slice(delimiters[i].left.length, index);
|
67
|
+
data.push({
|
68
|
+
type: "math",
|
69
|
+
data: math,
|
70
|
+
rawData,
|
71
|
+
display: delimiters[i].display
|
72
|
+
});
|
73
|
+
text = text.slice(index + delimiters[i].right.length);
|
74
|
+
}
|
75
|
+
|
76
|
+
if (text !== "") {
|
77
|
+
data.push({
|
78
|
+
type: "text",
|
79
|
+
data: text
|
80
|
+
});
|
81
|
+
}
|
82
|
+
|
83
|
+
return data;
|
84
|
+
};
|
85
|
+
|
86
|
+
const defaultDelimiters = [
|
87
|
+
{ left: "$$", right: "$$", display: true },
|
88
|
+
{ left: "\\(", right: "\\)", display: false },
|
89
|
+
// LaTeX uses $…$, but it ruins the display of normal `$` in text:
|
90
|
+
// {left: "$", right: "$", display: false},
|
91
|
+
// $ must come after $$
|
92
|
+
|
93
|
+
// Render AMS environments even if outside $$…$$ delimiters.
|
94
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
95
|
+
{ left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
|
96
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
97
|
+
{ left: "\\begin{align*}", right: "\\end{align*}", display: true },
|
98
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
99
|
+
{ left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
|
100
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
101
|
+
{ left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
|
102
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
103
|
+
// Ditto \ref & \eqref
|
104
|
+
{ left: "\\ref{", right: "}", display: false },
|
105
|
+
{ left: "\\eqref{", right: "}", display: false },
|
106
|
+
|
107
|
+
{ left: "\\[", right: "\\]", display: true }
|
108
|
+
];
|
109
|
+
|
110
|
+
const firstDraftDelimiters = {
|
111
|
+
"$": [
|
112
|
+
{ left: "$$", right: "$$", display: true },
|
113
|
+
{ left: "$`", right: "`$", display: false },
|
114
|
+
{ left: "$", right: "$", display: false }
|
115
|
+
],
|
116
|
+
"(": [
|
117
|
+
{ left: "\\[", right: "\\]", display: true },
|
118
|
+
{ left: "\\(", right: "\\)", display: false }
|
119
|
+
]
|
120
|
+
}
|
121
|
+
|
122
|
+
const amsDelimiters = [
|
123
|
+
{ left: "\\begin{equation}", right: "\\end{equation}", display: true },
|
124
|
+
{ left: "\\begin{equation*}", right: "\\end{equation*}", display: true },
|
125
|
+
{ left: "\\begin{align}", right: "\\end{align}", display: true },
|
126
|
+
{ left: "\\begin{align*}", right: "\\end{align*}", display: true },
|
127
|
+
{ left: "\\begin{alignat}", right: "\\end{alignat}", display: true },
|
128
|
+
{ left: "\\begin{alignat*}", right: "\\end{alignat*}", display: true },
|
129
|
+
{ left: "\\begin{gather}", right: "\\end{gather}", display: true },
|
130
|
+
{ left: "\\begin{gather*}", right: "\\end{gather*}", display: true },
|
131
|
+
{ left: "\\begin{CD}", right: "\\end{CD}", display: true },
|
132
|
+
{ left: "\\ref{", right: "}", display: false },
|
133
|
+
{ left: "\\eqref{", right: "}", display: false }
|
134
|
+
];
|
135
|
+
|
136
|
+
const delimitersFromKey = key => {
|
137
|
+
if (key === "$" || key === "(") {
|
138
|
+
return firstDraftDelimiters[key];
|
139
|
+
} else if (key === "$+" || key === "(+") {
|
140
|
+
const firstDraft = firstDraftDelimiters[key.slice(0, 1)];
|
141
|
+
return firstDraft.concat(amsDelimiters)
|
142
|
+
} else if (key === "ams") {
|
143
|
+
return amsDelimiters
|
144
|
+
} else if (key === "all") {
|
145
|
+
return (firstDraftDelimiters["("]).concat(firstDraftDelimiters["$"]).concat(amsDelimiters)
|
146
|
+
} else {
|
147
|
+
return defaultDelimiters
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
|
152
|
+
* API, we should copy it before mutating.
|
153
|
+
*/
|
154
|
+
const renderMathInText = function(text, optionsCopy) {
|
155
|
+
const data = splitAtDelimiters(text, optionsCopy.delimiters);
|
156
|
+
if (data.length === 1 && data[0].type === "text") {
|
157
|
+
// There is no formula in the text.
|
158
|
+
// Let's return null which means there is no need to replace
|
159
|
+
// the current text node with a new one.
|
160
|
+
return null;
|
161
|
+
}
|
162
|
+
|
163
|
+
const fragment = document.createDocumentFragment();
|
164
|
+
|
165
|
+
for (let i = 0; i < data.length; i++) {
|
166
|
+
if (data[i].type === "text") {
|
167
|
+
fragment.appendChild(document.createTextNode(data[i].data));
|
168
|
+
} else {
|
169
|
+
const span = document.createElement("span");
|
170
|
+
let math = data[i].data;
|
171
|
+
// Override any display mode defined in the settings with that
|
172
|
+
// defined by the text itself
|
173
|
+
optionsCopy.displayMode = data[i].display;
|
174
|
+
try {
|
175
|
+
if (optionsCopy.preProcess) {
|
176
|
+
math = optionsCopy.preProcess(math);
|
177
|
+
}
|
178
|
+
// Importing render() from temml.js would be a circular dependency.
|
179
|
+
// So call the global version.
|
180
|
+
// eslint-disable-next-line no-undef
|
181
|
+
temml.render(math, span, optionsCopy);
|
182
|
+
} catch (e) {
|
183
|
+
if (!(e instanceof ParseError)) {
|
184
|
+
throw e;
|
185
|
+
}
|
186
|
+
optionsCopy.errorCallback(
|
187
|
+
"Temml auto-render: Failed to parse `" + data[i].data + "` with ",
|
188
|
+
e
|
189
|
+
);
|
190
|
+
fragment.appendChild(document.createTextNode(data[i].rawData));
|
191
|
+
continue;
|
192
|
+
}
|
193
|
+
fragment.appendChild(span);
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
return fragment;
|
198
|
+
};
|
199
|
+
|
200
|
+
const renderElem = function(elem, optionsCopy) {
|
201
|
+
for (let i = 0; i < elem.childNodes.length; i++) {
|
202
|
+
const childNode = elem.childNodes[i];
|
203
|
+
if (childNode.nodeType === 3) {
|
204
|
+
// Text node
|
205
|
+
const frag = renderMathInText(childNode.textContent, optionsCopy);
|
206
|
+
if (frag) {
|
207
|
+
i += frag.childNodes.length - 1;
|
208
|
+
elem.replaceChild(frag, childNode);
|
209
|
+
}
|
210
|
+
} else if (childNode.nodeType === 1) {
|
211
|
+
// Element node
|
212
|
+
const className = " " + childNode.className + " ";
|
213
|
+
const shouldRender =
|
214
|
+
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 &&
|
215
|
+
optionsCopy.ignoredClasses.every((x) => className.indexOf(" " + x + " ") === -1);
|
216
|
+
|
217
|
+
if (shouldRender) {
|
218
|
+
renderElem(childNode, optionsCopy);
|
219
|
+
}
|
220
|
+
}
|
221
|
+
// Otherwise, it's something else, and ignore it.
|
222
|
+
}
|
223
|
+
};
|
224
|
+
|
225
|
+
export const renderMathInElement = function(elem, options) {
|
226
|
+
if (!elem) {
|
227
|
+
throw new Error("No element provided to render");
|
228
|
+
}
|
229
|
+
|
230
|
+
const optionsCopy = {};
|
231
|
+
|
232
|
+
// Object.assign(optionsCopy, option)
|
233
|
+
for (const option in options) {
|
234
|
+
if (Object.prototype.hasOwnProperty.call(options, option)) {
|
235
|
+
optionsCopy[option] = options[option];
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
if (optionsCopy.fences) {
|
240
|
+
optionsCopy.delimiters = delimitersFromKey(optionsCopy.fences);
|
241
|
+
} else {
|
242
|
+
optionsCopy.delimiters = optionsCopy.delimiters || defaultDelimiters
|
243
|
+
}
|
244
|
+
optionsCopy.ignoredTags = optionsCopy.ignoredTags || [
|
245
|
+
"script",
|
246
|
+
"noscript",
|
247
|
+
"style",
|
248
|
+
"textarea",
|
249
|
+
"pre",
|
250
|
+
"code",
|
251
|
+
"option"
|
252
|
+
];
|
253
|
+
optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
|
254
|
+
// eslint-disable-next-line no-console
|
255
|
+
optionsCopy.errorCallback = optionsCopy.errorCallback || console.error;
|
256
|
+
|
257
|
+
// Enable sharing of global macros defined via `\gdef` between different
|
258
|
+
// math elements within a single call to `renderMathInElement`.
|
259
|
+
optionsCopy.macros = optionsCopy.macros || {};
|
260
|
+
|
261
|
+
renderElem(elem, optionsCopy);
|
262
|
+
postProcess(elem);
|
263
|
+
};
|
package/src/linebreaking.js
CHANGED
@@ -58,7 +58,7 @@ export default function setLineBreaks(expression, wrapMode, isDisplayMode) {
|
|
58
58
|
}
|
59
59
|
block.push(node);
|
60
60
|
if (node.type && node.type === "mo" && node.children.length === 1 &&
|
61
|
-
!Object.
|
61
|
+
!Object.prototype.hasOwnProperty.call(node.attributes, "movablelimits")) {
|
62
62
|
const ch = node.children[0].text
|
63
63
|
if (openDelims.indexOf(ch) > -1) {
|
64
64
|
level += 1
|
package/src/postProcess.js
CHANGED
package/temml.js
CHANGED
@@ -19,6 +19,7 @@ import { Span, TextNode } from "./src/domTree";
|
|
19
19
|
import { defineSymbol } from "./src/symbols";
|
20
20
|
import defineMacro from "./src/defineMacro";
|
21
21
|
import { postProcess, version } from "./src/postProcess";
|
22
|
+
import { renderMathInElement } from "./src/auto-render"
|
22
23
|
|
23
24
|
/**
|
24
25
|
* @type {import('./temml').render}
|
@@ -144,6 +145,11 @@ export default {
|
|
144
145
|
* for sending to the client.
|
145
146
|
*/
|
146
147
|
renderToString,
|
148
|
+
/**
|
149
|
+
* Finds all the math delimiters in a given element of a running HTML document
|
150
|
+
* and converts the contents of each instance into a <math> element.
|
151
|
+
*/
|
152
|
+
renderMathInElement,
|
147
153
|
/**
|
148
154
|
* Post-process an entire HTML block.
|
149
155
|
* Writes AMS auto-numbers and implements \ref{}.
|