arnaucube
arnaucube/arnaucube.comPublic Self-hosted repository
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 };