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;