1<script type="text/javascript"> 2/* global katex */ 3 4var findEndOfMath = function(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 10 var delimLength = delimiter.length; 11 12 while (index < text.length) { 13 var character = text[index]; 14 15 if (braceLevel <= 0 && 16 text.slice(index, index + delimLength) === delimiter) { 17 return index; 18 } else if (character === "\\") { 19 index++; 20 } else if (character === "{") { 21 braceLevel++; 22 } else if (character === "}") { 23 braceLevel--; 24 } 25 26 index++; 27 } 28 29 return -1; 30}; 31 32var splitAtDelimiters = function(startData, leftDelim, rightDelim, display) { 33 var finalData = []; 34 35 for (var i = 0; i < startData.length; i++) { 36 if (startData[i].type === "text") { 37 var text = startData[i].data; 38 39 var lookingForLeft = true; 40 var currIndex = 0; 41 var nextIndex; 42 43 nextIndex = text.indexOf(leftDelim); 44 if (nextIndex !== -1) { 45 currIndex = nextIndex; 46 finalData.push({ 47 type: "text", 48 data: text.slice(0, currIndex) 49 }); 50 lookingForLeft = false; 51 } 52 53 while (true) { 54 if (lookingForLeft) { 55 nextIndex = text.indexOf(leftDelim, currIndex); 56 if (nextIndex === -1) { 57 break; 58 } 59 60 finalData.push({ 61 type: "text", 62 data: text.slice(currIndex, nextIndex) 63 }); 64 65 currIndex = nextIndex; 66 } else { 67 nextIndex = findEndOfMath( 68 rightDelim, 69 text, 70 currIndex + leftDelim.length); 71 if (nextIndex === -1) { 72 break; 73 } 74 75 finalData.push({ 76 type: "math", 77 data: text.slice( 78 currIndex + leftDelim.length, 79 nextIndex), 80 rawData: text.slice( 81 currIndex, 82 nextIndex + rightDelim.length), 83 display: display 84 }); 85 86 currIndex = nextIndex + rightDelim.length; 87 } 88 89 lookingForLeft = !lookingForLeft; 90 } 91 92 finalData.push({ 93 type: "text", 94 data: text.slice(currIndex) 95 }); 96 } else { 97 finalData.push(startData[i]); 98 } 99 } 100 101 return finalData; 102}; 103 104var splitWithDelimiters = function(text, delimiters) { 105 var data = [{type: "text", data: text}]; 106 for (var i = 0; i < delimiters.length; i++) { 107 var delimiter = delimiters[i]; 108 data = splitAtDelimiters( 109 data, delimiter.left, delimiter.right, 110 delimiter.display || false); 111 } 112 return data; 113}; 114 115var renderMathInText = function(text, delimiters) { 116 var data = splitWithDelimiters(text, delimiters); 117 118 var fragment = document.createDocumentFragment(); 119 120 for (var i = 0; i < data.length; i++) { 121 if (data[i].type === "text") { 122 fragment.appendChild(document.createTextNode(data[i].data)); 123 } else { 124 var span = document.createElement("span"); 125 var math = data[i].data; 126 try { 127 katex.render(math, span, { 128 displayMode: data[i].display 129 }); 130 } catch (e) { 131 if (!(e instanceof katex.ParseError)) { 132 throw e; 133 } 134 console.error( 135 "KaTeX auto-render: Failed to parse `" + data[i].data + 136 "` with ", 137 e 138 ); 139 fragment.appendChild(document.createTextNode(data[i].rawData)); 140 continue; 141 } 142 fragment.appendChild(span); 143 } 144 } 145 146 return fragment; 147}; 148 149var renderElem = function(elem, delimiters, ignoredTags) { 150 for (var i = 0; i < elem.childNodes.length; i++) { 151 var childNode = elem.childNodes[i]; 152 if (childNode.nodeType === 3) { 153 // Text node 154 var frag = renderMathInText(childNode.textContent, delimiters); 155 i += frag.childNodes.length - 1; 156 elem.replaceChild(frag, childNode); 157 } else if (childNode.nodeType === 1) { 158 // Element node 159 var shouldRender = ignoredTags.indexOf( 160 childNode.nodeName.toLowerCase()) === -1; 161 162 if (shouldRender) { 163 renderElem(childNode, delimiters, ignoredTags); 164 } 165 } 166 // Otherwise, it's something else, and ignore it. 167 } 168}; 169 170var defaultOptions = { 171 delimiters: [ 172 {left: "$$", right: "$$", display: true}, 173 {left: "\\[", right: "\\]", display: true}, 174 {left: "\\(", right: "\\)", display: false} 175 // LaTeX uses this, but it ruins the display of normal `$` in text: 176 // {left: "$", right: "$", display: false} 177 ], 178 179 ignoredTags: [ 180 "script", "noscript", "style", "textarea", "pre", "code" 181 ] 182}; 183 184var extend = function(obj) { 185 // Adapted from underscore.js' `_.extend`. See LICENSE.txt for license. 186 var source, prop; 187 for (var i = 1, length = arguments.length; i < length; i++) { 188 source = arguments[i]; 189 for (prop in source) { 190 if (Object.prototype.hasOwnProperty.call(source, prop)) { 191 obj[prop] = source[prop]; 192 } 193 } 194 } 195 return obj; 196}; 197 198var renderMathInElement = function(elem, options) { 199 if (!elem) { 200 throw new Error("No element provided to render"); 201 } 202 203 options = extend({}, defaultOptions, options); 204 205 renderElem(elem, options.delimiters, options.ignoredTags); 206}; 207 208renderMathInElement(document.body); 209 210</script> 211