www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

auto-render.js (3463B)


      1 /* eslint no-console:0 */
      2 /* global katex */
      3 
      4 const splitAtDelimiters = require("./splitAtDelimiters");
      5 
      6 const splitWithDelimiters = function(text, delimiters) {
      7     let data = [{type: "text", data: text}];
      8     for (let i = 0; i < delimiters.length; i++) {
      9         const delimiter = delimiters[i];
     10         data = splitAtDelimiters(
     11             data, delimiter.left, delimiter.right,
     12             delimiter.display || false);
     13     }
     14     return data;
     15 };
     16 
     17 const renderMathInText = function(text, delimiters) {
     18     const data = splitWithDelimiters(text, delimiters);
     19 
     20     const fragment = document.createDocumentFragment();
     21 
     22     for (let i = 0; i < data.length; i++) {
     23         if (data[i].type === "text") {
     24             fragment.appendChild(document.createTextNode(data[i].data));
     25         } else {
     26             const span = document.createElement("span");
     27             const math = data[i].data;
     28             try {
     29                 katex.render(math, span, {
     30                     displayMode: data[i].display,
     31                 });
     32             } catch (e) {
     33                 if (!(e instanceof katex.ParseError)) {
     34                     throw e;
     35                 }
     36                 console.error(
     37                     "KaTeX auto-render: Failed to parse `" + data[i].data +
     38                     "` with ",
     39                     e
     40                 );
     41                 fragment.appendChild(document.createTextNode(data[i].rawData));
     42                 continue;
     43             }
     44             fragment.appendChild(span);
     45         }
     46     }
     47 
     48     return fragment;
     49 };
     50 
     51 const renderElem = function(elem, delimiters, ignoredTags) {
     52     for (let i = 0; i < elem.childNodes.length; i++) {
     53         const childNode = elem.childNodes[i];
     54         if (childNode.nodeType === 3) {
     55             // Text node
     56             const frag = renderMathInText(childNode.textContent, delimiters);
     57             i += frag.childNodes.length - 1;
     58             elem.replaceChild(frag, childNode);
     59         } else if (childNode.nodeType === 1) {
     60             // Element node
     61             const shouldRender = ignoredTags.indexOf(
     62                 childNode.nodeName.toLowerCase()) === -1;
     63 
     64             if (shouldRender) {
     65                 renderElem(childNode, delimiters, ignoredTags);
     66             }
     67         }
     68         // Otherwise, it's something else, and ignore it.
     69     }
     70 };
     71 
     72 const defaultOptions = {
     73     delimiters: [
     74         {left: "$$", right: "$$", display: true},
     75         {left: "\\[", right: "\\]", display: true},
     76         {left: "\\(", right: "\\)", display: false},
     77         // LaTeX uses this, but it ruins the display of normal `$` in text:
     78         // {left: "$", right: "$", display: false},
     79     ],
     80 
     81     ignoredTags: [
     82         "script", "noscript", "style", "textarea", "pre", "code",
     83     ],
     84 };
     85 
     86 const extend = function(obj) {
     87     // Adapted from underscore.js' `_.extend`. See LICENSE.txt for license.
     88     let source;
     89     let prop;
     90     const length = arguments.length;
     91     for (let i = 1; i < length; i++) {
     92         source = arguments[i];
     93         for (prop in source) {
     94             if (Object.prototype.hasOwnProperty.call(source, prop)) {
     95                 obj[prop] = source[prop];
     96             }
     97         }
     98     }
     99     return obj;
    100 };
    101 
    102 const renderMathInElement = function(elem, options) {
    103     if (!elem) {
    104         throw new Error("No element provided to render");
    105     }
    106 
    107     options = extend({}, defaultOptions, options);
    108 
    109     renderElem(elem, options.delimiters, options.ignoredTags);
    110 };
    111 
    112 module.exports = renderMathInElement;