From cfec25c56658383951b2299832c5b29d1f3cc28b Mon Sep 17 00:00:00 2001 From: Arne Keller Date: Thu, 28 Jan 2021 17:50:56 +0100 Subject: [PATCH 1/2] MathJax: use shadow DOM, wait for content --- frontend/src/mathjax-setup.js | 109 +++++++++++++++++++++++++++------- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/frontend/src/mathjax-setup.js b/frontend/src/mathjax-setup.js index b26f6a8..fa88e06 100644 --- a/frontend/src/mathjax-setup.js +++ b/frontend/src/mathjax-setup.js @@ -6,27 +6,89 @@ window.MathJax = { }, startup: { ready: () => { + const mathjax = MathJax._.mathjax.mathjax; + const HTMLAdaptor = MathJax._.adaptors.HTMLAdaptor.HTMLAdaptor; + const HTMLHandler = MathJax._.handlers.html.HTMLHandler.HTMLHandler; + const AbstractHandler = MathJax._.core.Handler.AbstractHandler.prototype; + const startup = MathJax.startup; + + // + // Extend HTMLAdaptor to handle shadowDOM as the document + // + class ShadowAdaptor extends HTMLAdaptor { + create(kind, ns) { + const document = (this.document.createElement ? this.document : this.window.document); + return (ns ? + document.createElementNS(ns, kind) : + document.createElement(kind)); + } + + text(text) { + const document = (this.document.createTextNode ? this.document : this.window.document); + return document.createTextNode(text); + } + + head(doc) { + return doc.head || (doc.getElementById("mjx-document") || {}).firstChild || doc; + } + + body(doc) { + return doc.body || (doc.getElementById("mjx-document") || {}).lastChild || doc; + } + + root(doc) { + return doc.documentElement || doc.getElementById("mjx-document") || doc; + } + } + + // + // Extend HTMLHandler to handle shadowDOM as document + // + class ShadowHandler extends HTMLHandler { + create(document, options) { + const adaptor = this.adaptor; + if (typeof (document) === 'string') { + document = adaptor.parse(document, 'text/html'); + } else if ((document instanceof adaptor.window.HTMLElement || + document instanceof adaptor.window.DocumentFragment) && + !(document instanceof window.ShadowRoot)) { + let child = document; + document = adaptor.parse('', 'text/html'); + adaptor.append(adaptor.body(document), child); + } + // + // We can't use super.create() here, since that doesn't + // handle shadowDOM correctly, so call HTMLHandler's parent class + // directly instead. + // + return AbstractHandler.create.call(this, document, options); + } + } + + // + // Register the new handler and adaptor + // + startup.registerConstructor('HTMLHandler', ShadowHandler); + startup.registerConstructor('browserAdaptor', () => new ShadowAdaptor(window)); + + // + // A service function that creates a new MathDocument from the + // shadow root with the configured input and output jax, and then + // renders the document. The MathDocument is returned in case + // you need to rerender the shadowRoot later. + // MathJax.typesetShadow = function (root, callback) { - setTimeout(() => { - let tex = root.getElementById("tc-content").innerText; - root.getElementById("tc-content").innerText = ""; - MathJax.tex2svgPromise(tex, options).then(function (node) { - console.log(node); - // - // The promise returns the typeset node, which we add to the output - // Then update the document to include the adjusted CSS for the - // content of the new equation. - // - root.appendChild(node); - MathJax.startup.document.clear(); - MathJax.startup.document.updateDocument(); - // TODO: findSteps(node); - }).catch(function (err) { - // - // If there was an error, put the message into the output instead - // - root.appendChild(document.createElement('pre')).appendChild(document.createTextNode(err.message)); - });}, 1); // TODO: remove the delay? + if (root.getElementById("tc-content") == null) { + return; + } + const InputJax = startup.getInputJax(); + const OutputJax = startup.getOutputJax(); + const html = mathjax.document(root, {InputJax, OutputJax}); + html.render(); + if (callback != null) { + callback(html); + } + return html; } // @@ -46,3 +108,10 @@ window.MathJax = { } } }; + +(function () { + let script = document.createElement('script'); + script.src = 'http://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js'; + // script.async = true; + document.head.appendChild(script); +})(); \ No newline at end of file From 26ddf5fdd7a43cce01fc712051b544117d3b99f0 Mon Sep 17 00:00:00 2001 From: Arne Keller Date: Thu, 28 Jan 2021 17:59:50 +0100 Subject: [PATCH 2/2] MathJax typeset in connectedCallback --- frontend/src/mathjax-adapter.ts | 18 +++++++++--------- frontend/src/mathjax-setup.js | 7 ------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/frontend/src/mathjax-adapter.ts b/frontend/src/mathjax-adapter.ts index 40c4919..bde069c 100644 --- a/frontend/src/mathjax-adapter.ts +++ b/frontend/src/mathjax-adapter.ts @@ -9,15 +9,6 @@ declare let window: { }; export abstract class MathjaxAdapter extends LitElement { - constructor() { - super(); - if (window.MathJax === undefined || !window.MathJax.isInitialized) { - window.addEventListener('mathjax-initialized', () => this.execTypeset()); - } else { - this.execTypeset(); - } - } - protected execTypeset() { if (window.MathJax !== undefined) { window.MathJax.typesetShadow(this.shadowRoot, () => this.calculateSteps()); @@ -35,5 +26,14 @@ export abstract class MathjaxAdapter extends LitElement { protected abstract showStep(n: number): void; protected abstract calculateSteps(): void; + + connectedCallback() { + super.connectedCallback(); + if (window.MathJax === undefined || !window.MathJax.isInitialized) { + window.addEventListener('mathjax-initialized', () => this.execTypeset()); + } else { + this.execTypeset(); + } + } } diff --git a/frontend/src/mathjax-setup.js b/frontend/src/mathjax-setup.js index fa88e06..54b7fc8 100644 --- a/frontend/src/mathjax-setup.js +++ b/frontend/src/mathjax-setup.js @@ -108,10 +108,3 @@ window.MathJax = { } } }; - -(function () { - let script = document.createElement('script'); - script.src = 'http://cdn.jsdelivr.net/npm/mathjax@3.1.2/es5/tex-svg.js'; - // script.async = true; - document.head.appendChild(script); -})(); \ No newline at end of file