From a6c65efd36962f5f9fc1e1d33a2cd4b21de53232 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 7 Jan 2022 14:22:32 +1000 Subject: [PATCH] another attempt at enforcing script load order See discussion on https://github.com/ankitects/anki/commit/36e20fd110151cdf532b7483369d18fd82888441#commitcomment-62861929 --- ts/reviewer/index.ts | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/ts/reviewer/index.ts b/ts/reviewer/index.ts index 5aca4410e..4d3e83dc5 100644 --- a/ts/reviewer/index.ts +++ b/ts/reviewer/index.ts @@ -59,7 +59,34 @@ export function _queueAction(action: Callback): void { _updatingQueue = _updatingQueue.then(action); } -function setInnerHTML(element: Element, html: string): void { +// Setting innerHTML does not evaluate the contents of script tags, so we need +// to add them again in order to trigger the download/evaluation. Promise resolves +// when download/evaluation has completed. +function replaceScript(oldScript: HTMLScriptElement): Promise { + return new Promise((resolve) => { + const newScript = document.createElement("script"); + let mustWaitForNetwork = true; + if (oldScript.src) { + newScript.addEventListener("load", () => resolve()); + newScript.addEventListener("error", () => resolve()); + } else { + mustWaitForNetwork = false; + } + + for (const attribute of oldScript.attributes) { + newScript.setAttribute(attribute.name, attribute.value); + } + + newScript.appendChild(document.createTextNode(oldScript.innerHTML)); + oldScript.replaceWith(newScript); + + if (!mustWaitForNetwork) { + resolve(); + } + }); +} + +async function setInnerHTML(element: Element, html: string): Promise { for (const oldVideo of element.getElementsByTagName("video")) { oldVideo.pause(); @@ -73,14 +100,7 @@ function setInnerHTML(element: Element, html: string): void { element.innerHTML = html; for (const oldScript of element.getElementsByTagName("script")) { - const newScript = document.createElement("script"); - - for (const attribute of oldScript.attributes) { - newScript.setAttribute(attribute.name, attribute.value); - } - - newScript.appendChild(document.createTextNode(oldScript.innerHTML)); - oldScript.parentNode!.replaceChild(newScript, oldScript); + await replaceScript(oldScript); } } @@ -120,9 +140,9 @@ export async function _updateQA( qa.style.opacity = "0"; try { - setInnerHTML(qa, html); + await setInnerHTML(qa, html); } catch (error) { - setInnerHTML(qa, renderError("html")(error)); + await setInnerHTML(qa, renderError("html")(error)); } await _runHook(onUpdateHook);