| 45 | * ``` |
| 46 | */ |
| 47 | export class Latex extends Img { |
| 48 | private static svgContentsPool: Record<string, string> = {}; |
| 49 | |
| 50 | private readonly imageElement = document.createElement('img'); |
| 51 | |
| 52 | @initial({}) |
| 53 | @signal() |
| 54 | public declare readonly options: SimpleSignal<OptionList, this>; |
| 55 | |
| 56 | @signal() |
| 57 | public declare readonly tex: SimpleSignal<string, this>; |
| 58 | |
| 59 | public constructor(props: LatexProps) { |
| 60 | super({...props, src: null}); |
| 61 | } |
| 62 | |
| 63 | protected override image(): HTMLImageElement { |
| 64 | // Render props may change the look of the TeX, so we need to cache both |
| 65 | // source and render props together. |
| 66 | const src = `${this.tex()}::${JSON.stringify(this.options())}`; |
| 67 | if (Latex.svgContentsPool[src]) { |
| 68 | this.imageElement.src = Latex.svgContentsPool[src]; |
| 69 | if (!this.imageElement.complete) { |
| 70 | DependencyContext.collectPromise( |
| 71 | new Promise((resolve, reject) => { |
| 72 | this.imageElement.addEventListener('load', resolve); |
| 73 | this.imageElement.addEventListener('error', reject); |
| 74 | }), |
| 75 | ); |
| 76 | } |
| 77 | return this.imageElement; |
| 78 | } |
| 79 | |
| 80 | // Convert to TeX, look for any errors |
| 81 | const tex = this.tex(); |
| 82 | const svg = Adaptor.innerHTML(JaxDocument.convert(tex, this.options())); |
| 83 | if (svg.includes('data-mjx-error')) { |
| 84 | const errors = svg.match(/data-mjx-error="(.*?)"/); |
| 85 | if (errors && errors.length > 0) { |
| 86 | useLogger().error(`Invalid MathJax: ${errors[1]}`); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | // Encode to raw base64 image format |
| 91 | const text = `data:image/svg+xml;base64,${btoa( |
| 92 | `<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n${svg}`, |
| 93 | )}`; |
| 94 | Latex.svgContentsPool[src] = text; |
| 95 | const image = document.createElement('img'); |
| 96 | image.src = text; |
| 97 | image.src = text; |
| 98 | if (!image.complete) { |
| 99 | DependencyContext.collectPromise( |
| 100 | new Promise((resolve, reject) => { |
| 101 | image.addEventListener('load', resolve); |
| 102 | image.addEventListener('error', reject); |
| 103 | }), |
| 104 | ); |