From 9782d39ced94a2d970e336ffab602fd44920687c Mon Sep 17 00:00:00 2001 From: hikaru-y Date: Fri, 2 Jul 2021 05:45:19 +0900 Subject: [PATCH] Preload images to be displayed on answer side --- qt/aqt/browser/previewer.py | 13 ++++++++++-- qt/aqt/data/web/js/reviewer.ts | 36 +++++++++++++++++++++++++++++++++- qt/aqt/reviewer.py | 5 ++++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/qt/aqt/browser/previewer.py b/qt/aqt/browser/previewer.py index 4d0425983..e7e2b7532 100644 --- a/qt/aqt/browser/previewer.py +++ b/qt/aqt/browser/previewer.py @@ -171,6 +171,7 @@ class Previewer(QDialog): return c = self.card() func = "_showQuestion" + ans_txt = "" if not c: txt = tr.qt_misc_please_select_1_card() bodyclass = "" @@ -188,10 +189,11 @@ class Previewer(QDialog): # need to force reload even if answer txt = c.question(reload=True) + ans_txt = c.answer() if self._state == "answer": func = "_showAnswer" - txt = c.answer() + txt = ans_txt txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt) bodyclass = theme_manager.body_classes_for_card_ord(c.ord) @@ -218,7 +220,14 @@ class Previewer(QDialog): txt = self.mw.prepare_card_text_for_display(txt) txt = gui_hooks.card_will_show(txt, c, f"preview{self._state.capitalize()}") self._last_state = self._state_and_mod() - self._web.eval(f"{func}({json.dumps(txt)},'{bodyclass}');") + + js: str + if self._state == "question": + ans_txt = self.mw.col.media.escape_media_filenames(ans_txt) + js = f"{func}({json.dumps(txt)}, {json.dumps(ans_txt)}, '{bodyclass}');" + else: + js = f"{func}({json.dumps(txt)}, '{bodyclass}');" + self._web.eval(js) self._card_changed = False def _on_show_both_sides(self, toggle: bool) -> None: diff --git a/qt/aqt/data/web/js/reviewer.ts b/qt/aqt/data/web/js/reviewer.ts index d28c6e6b1..a46d876f4 100644 --- a/qt/aqt/data/web/js/reviewer.ts +++ b/qt/aqt/data/web/js/reviewer.ts @@ -100,7 +100,7 @@ async function _updateQA( await _runHook(onShownHook); } -function _showQuestion(q: string, bodyclass: string): void { +function _showQuestion(q: string, a: string, bodyclass: string): void { _queueAction(() => _updateQA( q, @@ -117,6 +117,8 @@ function _showQuestion(q: string, bodyclass: string): void { if (typeans) { typeans.focus(); } + // preload images + allImagesLoaded().then(() => preloadAnswerImages(q, a)); } ) ); @@ -203,3 +205,35 @@ function imageLoaded(img: HTMLImageElement): Promise { function scrollToAnswer(): void { document.getElementById("answer")?.scrollIntoView(); } + +function injectPreloadLink(href: string, as: string): void { + const link = document.createElement("link"); + link.rel = "preload"; + link.href = href; + link.as = as; + document.head.appendChild(link); +} + +function clearPreloadLinks(): void { + document.head + .querySelectorAll("link[rel='preload']") + .forEach((link) => link.remove()); +} + +function extractImageSrcs(html: string): string[] { + const fragment = document.createRange().createContextualFragment(html); + const srcs = [...fragment.querySelectorAll("img[src]")].map( + (img) => (img as HTMLImageElement).src + ); + return srcs; +} + +function preloadAnswerImages(qHtml: string, aHtml: string): void { + clearPreloadLinks(); + const aSrcs = extractImageSrcs(aHtml); + if (aSrcs.length) { + const qSrcs = extractImageSrcs(qHtml); + const diff = aSrcs.filter((src) => !qSrcs.includes(src)); + diff.forEach((src) => injectPreloadLink(src, "image")); + } +} diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 326019f3a..38ff1c083 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -339,8 +339,11 @@ class Reviewer: self._run_state_mutation_hook() bodyclass = theme_manager.body_classes_for_card_ord(c.ord) + a = self.mw.col.media.escape_media_filenames(c.answer()) - self.web.eval(f"_showQuestion({json.dumps(q)},'{bodyclass}');") + self.web.eval( + f"_showQuestion({json.dumps(q)}, {json.dumps(a)}, '{bodyclass}');" + ) self._update_flag_icon() self._update_mark_icon() self._showAnswerButton()