arnaucube.com/blogo-input/js/katex/contrib/auto-render.mjs
blogo-input/js/katex/contrib/auto-render.mjsRaw
1import katex from '../katex.mjs';
2
3/* eslint no-constant-condition:0 */
4var findEndOfMath = function findEndOfMath(delimiter, text, startIndex) {
5 // Adapted from
6 // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
7 var index = startIndex;
8 var braceLevel = 0;
9 var delimLength = delimiter.length;
10
11 while (index < text.length) {
12 var character = text[index];
13
14 if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
15 return index;
16 } else if (character === "\\") {
17 index++;
18 } else if (character === "{") {
19 braceLevel++;
20 } else if (character === "}") {
21 braceLevel--;
22 }
23
24 index++;
25 }
26
27 return -1;
28};
29
30var escapeRegex = function escapeRegex(string) {
31 return string.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
32};
33
34var amsRegex = /^\\begin{/;
35
36var splitAtDelimiters = function splitAtDelimiters(text, delimiters) {
37 var index;
38 var data = [];
39 var regexLeft = new RegExp("(" + delimiters.map(x => escapeRegex(x.left)).join("|") + ")");
40
41 while (true) {
42 index = text.search(regexLeft);
43
44 if (index === -1) {
45 break;
46 }
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 } // ... so this always succeeds:
55
56
57 var i = delimiters.findIndex(delim => text.startsWith(delim.left));
58 index = findEndOfMath(delimiters[i].right, text, delimiters[i].left.length);
59
60 if (index === -1) {
61 break;
62 }
63
64 var rawData = text.slice(0, index + delimiters[i].right.length);
65 var math = amsRegex.test(rawData) ? rawData : text.slice(delimiters[i].left.length, index);
66 data.push({
67 type: "math",
68 data: math,
69 rawData,
70 display: delimiters[i].display
71 });
72 text = text.slice(index + delimiters[i].right.length);
73 }
74
75 if (text !== "") {
76 data.push({
77 type: "text",
78 data: text
79 });
80 }
81
82 return data;
83};
84
85/* eslint no-console:0 */
86/* Note: optionsCopy is mutated by this method. If it is ever exposed in the
87 * API, we should copy it before mutating.
88 */
89
90var renderMathInText = function renderMathInText(text, optionsCopy) {
91 var data = splitAtDelimiters(text, optionsCopy.delimiters);
92
93 if (data.length === 1 && data[0].type === 'text') {
94 // There is no formula in the text.
95 // Let's return null which means there is no need to replace
96 // the current text node with a new one.
97 return null;
98 }
99
100 var fragment = document.createDocumentFragment();
101
102 for (var i = 0; i < data.length; i++) {
103 if (data[i].type === "text") {
104 fragment.appendChild(document.createTextNode(data[i].data));
105 } else {
106 var span = document.createElement("span");
107 var math = data[i].data; // Override any display mode defined in the settings with that
108 // defined by the text itself
109
110 optionsCopy.displayMode = data[i].display;
111
112 try {
113 if (optionsCopy.preProcess) {
114 math = optionsCopy.preProcess(math);
115 }
116
117 katex.render(math, span, optionsCopy);
118 } catch (e) {
119 if (!(e instanceof katex.ParseError)) {
120 throw e;
121 }
122
123 optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e);
124 fragment.appendChild(document.createTextNode(data[i].rawData));
125 continue;
126 }
127
128 fragment.appendChild(span);
129 }
130 }
131
132 return fragment;
133};
134
135var renderElem = function renderElem(elem, optionsCopy) {
136 for (var i = 0; i < elem.childNodes.length; i++) {
137 var childNode = elem.childNodes[i];
138
139 if (childNode.nodeType === 3) {
140 // Text node
141 var frag = renderMathInText(childNode.textContent, optionsCopy);
142
143 if (frag) {
144 i += frag.childNodes.length - 1;
145 elem.replaceChild(frag, childNode);
146 }
147 } else if (childNode.nodeType === 1) {
148 (function () {
149 // Element node
150 var className = ' ' + childNode.className + ' ';
151 var shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1);
152
153 if (shouldRender) {
154 renderElem(childNode, optionsCopy);
155 }
156 })();
157 } // Otherwise, it's something else, and ignore it.
158
159 }
160};
161
162var renderMathInElement = function renderMathInElement(elem, options) {
163 if (!elem) {
164 throw new Error("No element provided to render");
165 }
166
167 var optionsCopy = {}; // Object.assign(optionsCopy, option)
168
169 for (var option in options) {
170 if (options.hasOwnProperty(option)) {
171 optionsCopy[option] = options[option];
172 }
173 } // default options
174
175
176 optionsCopy.delimiters = optionsCopy.delimiters || [{
177 left: "$$",
178 right: "$$",
179 display: true
180 }, {
181 left: "\\(",
182 right: "\\)",
183 display: false
184 }, // LaTeX uses $…$, but it ruins the display of normal `$` in text:
185 // {left: "$", right: "$", display: false},
186 // $ must come after $$
187 // Render AMS environments even if outside $$…$$ delimiters.
188 {
189 left: "\\begin{equation}",
190 right: "\\end{equation}",
191 display: true
192 }, {
193 left: "\\begin{align}",
194 right: "\\end{align}",
195 display: true
196 }, {
197 left: "\\begin{alignat}",
198 right: "\\end{alignat}",
199 display: true
200 }, {
201 left: "\\begin{gather}",
202 right: "\\end{gather}",
203 display: true
204 }, {
205 left: "\\begin{CD}",
206 right: "\\end{CD}",
207 display: true
208 }, {
209 left: "\\[",
210 right: "\\]",
211 display: true
212 }];
213 optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code", "option"];
214 optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || [];
215 optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different
216 // math elements within a single call to `renderMathInElement`.
217
218 optionsCopy.macros = optionsCopy.macros || {};
219 renderElem(elem, optionsCopy);
220};
221
222export { renderMathInElement as default };