Mercurial > repos > public > wdown
comparison katex/contrib/auto-render.mjs @ 8:4a25b534c81c javascript-experiment
Add v8 engine and include katex
| author | Jonatan Werpers <jonatan@werpers.com> |
|---|---|
| date | Wed, 17 Jun 2020 21:43:52 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 7:a5aa39557726 | 8:4a25b534c81c |
|---|---|
| 1 import katex from '../katex.mjs'; | |
| 2 | |
| 3 /* eslint no-constant-condition:0 */ | |
| 4 const findEndOfMath = function findEndOfMath(delimiter, text, startIndex) { | |
| 5 // Adapted from | |
| 6 // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx | |
| 7 let index = startIndex; | |
| 8 let braceLevel = 0; | |
| 9 const delimLength = delimiter.length; | |
| 10 | |
| 11 while (index < text.length) { | |
| 12 const 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 | |
| 30 const splitAtDelimiters = function splitAtDelimiters(startData, leftDelim, rightDelim, display) { | |
| 31 const finalData = []; | |
| 32 | |
| 33 for (let i = 0; i < startData.length; i++) { | |
| 34 if (startData[i].type === "text") { | |
| 35 const text = startData[i].data; | |
| 36 let lookingForLeft = true; | |
| 37 let currIndex = 0; | |
| 38 let nextIndex; | |
| 39 nextIndex = text.indexOf(leftDelim); | |
| 40 | |
| 41 if (nextIndex !== -1) { | |
| 42 currIndex = nextIndex; | |
| 43 finalData.push({ | |
| 44 type: "text", | |
| 45 data: text.slice(0, currIndex) | |
| 46 }); | |
| 47 lookingForLeft = false; | |
| 48 } | |
| 49 | |
| 50 while (true) { | |
| 51 if (lookingForLeft) { | |
| 52 nextIndex = text.indexOf(leftDelim, currIndex); | |
| 53 | |
| 54 if (nextIndex === -1) { | |
| 55 break; | |
| 56 } | |
| 57 | |
| 58 finalData.push({ | |
| 59 type: "text", | |
| 60 data: text.slice(currIndex, nextIndex) | |
| 61 }); | |
| 62 currIndex = nextIndex; | |
| 63 } else { | |
| 64 nextIndex = findEndOfMath(rightDelim, text, currIndex + leftDelim.length); | |
| 65 | |
| 66 if (nextIndex === -1) { | |
| 67 break; | |
| 68 } | |
| 69 | |
| 70 finalData.push({ | |
| 71 type: "math", | |
| 72 data: text.slice(currIndex + leftDelim.length, nextIndex), | |
| 73 rawData: text.slice(currIndex, nextIndex + rightDelim.length), | |
| 74 display: display | |
| 75 }); | |
| 76 currIndex = nextIndex + rightDelim.length; | |
| 77 } | |
| 78 | |
| 79 lookingForLeft = !lookingForLeft; | |
| 80 } | |
| 81 | |
| 82 finalData.push({ | |
| 83 type: "text", | |
| 84 data: text.slice(currIndex) | |
| 85 }); | |
| 86 } else { | |
| 87 finalData.push(startData[i]); | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 return finalData; | |
| 92 }; | |
| 93 | |
| 94 /* eslint no-console:0 */ | |
| 95 | |
| 96 const splitWithDelimiters = function splitWithDelimiters(text, delimiters) { | |
| 97 let data = [{ | |
| 98 type: "text", | |
| 99 data: text | |
| 100 }]; | |
| 101 | |
| 102 for (let i = 0; i < delimiters.length; i++) { | |
| 103 const delimiter = delimiters[i]; | |
| 104 data = splitAtDelimiters(data, delimiter.left, delimiter.right, delimiter.display || false); | |
| 105 } | |
| 106 | |
| 107 return data; | |
| 108 }; | |
| 109 /* Note: optionsCopy is mutated by this method. If it is ever exposed in the | |
| 110 * API, we should copy it before mutating. | |
| 111 */ | |
| 112 | |
| 113 | |
| 114 const renderMathInText = function renderMathInText(text, optionsCopy) { | |
| 115 const data = splitWithDelimiters(text, optionsCopy.delimiters); | |
| 116 const fragment = document.createDocumentFragment(); | |
| 117 | |
| 118 for (let i = 0; i < data.length; i++) { | |
| 119 if (data[i].type === "text") { | |
| 120 fragment.appendChild(document.createTextNode(data[i].data)); | |
| 121 } else { | |
| 122 const span = document.createElement("span"); | |
| 123 let math = data[i].data; // Override any display mode defined in the settings with that | |
| 124 // defined by the text itself | |
| 125 | |
| 126 optionsCopy.displayMode = data[i].display; | |
| 127 | |
| 128 try { | |
| 129 if (optionsCopy.preProcess) { | |
| 130 math = optionsCopy.preProcess(math); | |
| 131 } | |
| 132 | |
| 133 katex.render(math, span, optionsCopy); | |
| 134 } catch (e) { | |
| 135 if (!(e instanceof katex.ParseError)) { | |
| 136 throw e; | |
| 137 } | |
| 138 | |
| 139 optionsCopy.errorCallback("KaTeX auto-render: Failed to parse `" + data[i].data + "` with ", e); | |
| 140 fragment.appendChild(document.createTextNode(data[i].rawData)); | |
| 141 continue; | |
| 142 } | |
| 143 | |
| 144 fragment.appendChild(span); | |
| 145 } | |
| 146 } | |
| 147 | |
| 148 return fragment; | |
| 149 }; | |
| 150 | |
| 151 const renderElem = function renderElem(elem, optionsCopy) { | |
| 152 for (let i = 0; i < elem.childNodes.length; i++) { | |
| 153 const childNode = elem.childNodes[i]; | |
| 154 | |
| 155 if (childNode.nodeType === 3) { | |
| 156 // Text node | |
| 157 const frag = renderMathInText(childNode.textContent, optionsCopy); | |
| 158 i += frag.childNodes.length - 1; | |
| 159 elem.replaceChild(frag, childNode); | |
| 160 } else if (childNode.nodeType === 1) { | |
| 161 // Element node | |
| 162 const className = ' ' + childNode.className + ' '; | |
| 163 const shouldRender = optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) === -1 && optionsCopy.ignoredClasses.every(x => className.indexOf(' ' + x + ' ') === -1); | |
| 164 | |
| 165 if (shouldRender) { | |
| 166 renderElem(childNode, optionsCopy); | |
| 167 } | |
| 168 } // Otherwise, it's something else, and ignore it. | |
| 169 | |
| 170 } | |
| 171 }; | |
| 172 | |
| 173 const renderMathInElement = function renderMathInElement(elem, options) { | |
| 174 if (!elem) { | |
| 175 throw new Error("No element provided to render"); | |
| 176 } | |
| 177 | |
| 178 const optionsCopy = {}; // Object.assign(optionsCopy, option) | |
| 179 | |
| 180 for (const option in options) { | |
| 181 if (options.hasOwnProperty(option)) { | |
| 182 optionsCopy[option] = options[option]; | |
| 183 } | |
| 184 } // default options | |
| 185 | |
| 186 | |
| 187 optionsCopy.delimiters = optionsCopy.delimiters || [{ | |
| 188 left: "$$", | |
| 189 right: "$$", | |
| 190 display: true | |
| 191 }, { | |
| 192 left: "\\(", | |
| 193 right: "\\)", | |
| 194 display: false | |
| 195 }, // LaTeX uses $…$, but it ruins the display of normal `$` in text: | |
| 196 // {left: "$", right: "$", display: false}, | |
| 197 // \[…\] must come last in this array. Otherwise, renderMathInElement | |
| 198 // will search for \[ before it searches for $$ or \( | |
| 199 // That makes it susceptible to finding a \\[0.3em] row delimiter and | |
| 200 // treating it as if it were the start of a KaTeX math zone. | |
| 201 { | |
| 202 left: "\\[", | |
| 203 right: "\\]", | |
| 204 display: true | |
| 205 }]; | |
| 206 optionsCopy.ignoredTags = optionsCopy.ignoredTags || ["script", "noscript", "style", "textarea", "pre", "code"]; | |
| 207 optionsCopy.ignoredClasses = optionsCopy.ignoredClasses || []; | |
| 208 optionsCopy.errorCallback = optionsCopy.errorCallback || console.error; // Enable sharing of global macros defined via `\gdef` between different | |
| 209 // math elements within a single call to `renderMathInElement`. | |
| 210 | |
| 211 optionsCopy.macros = optionsCopy.macros || {}; | |
| 212 renderElem(elem, optionsCopy); | |
| 213 }; | |
| 214 | |
| 215 export default renderMathInElement; |
