From 9ed99e752fdce6177eb8ecdc8e8d22047042cda4 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 16 Oct 2017 11:58:31 +0530 Subject: [PATCH] Wire up the footnote popup --- src/pyj/read_book/content_popup.pyj | 38 ++++++++++++++++++++-- src/pyj/read_book/footnotes.pyj | 49 +++++++++++++++++++++++++++++ src/pyj/read_book/iframe.pyj | 8 ++--- src/pyj/read_book/view.pyj | 12 +++++-- 4 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/pyj/read_book/content_popup.pyj b/src/pyj/read_book/content_popup.pyj index 64bc001af5..ff97f76a26 100644 --- a/src/pyj/read_book/content_popup.pyj +++ b/src/pyj/read_book/content_popup.pyj @@ -7,6 +7,7 @@ from gettext import gettext as _ from dom import add_extra_css, build_rule, clear, svgicon from read_book.comm import IframeWrapper +from read_book.resources import load_resources CLASS_NAME = 'book-content-popup-container' TOP_LEVEL_DISPLAY = 'flex' @@ -22,7 +23,7 @@ add_extra_css(def(): sel += ' > div' style += build_rule(sel, padding_bottom='1ex', margin_bottom='1ex', border_bottom='solid currentColor 2px', display='flex', justify_content='space-between', align_items='center') - sel += ' > div' # button container + sel += ' > div' # button container style += build_rule(sel, display='flex', justify_content='space-between', align_items='center') sel += ' > a' # buttons @@ -37,9 +38,10 @@ class ContentPopupOverlay: def __init__(self, view): self.view = view + self.loaded_resources = {} c = self.container c.classList.add(CLASS_NAME) - iframe = E.iframe(seamless=True, sandbox='allow-scripts') + iframe = E.iframe(seamless=True, sandbox='allow-scripts', style='width: 100%; max-height: 70vh') c.appendChild(E.div( E.div(), @@ -51,6 +53,8 @@ class ContentPopupOverlay: ) handlers = { 'ready': self.on_iframe_ready, + 'error': self.view.on_iframe_error, + 'content_loaded': self.on_content_loaded, } self.iframe_wrapper = IframeWrapper(handlers, iframe, 'popup', _('Loading data, please wait...')) self.pending_load = None @@ -98,6 +102,12 @@ class ContentPopupOverlay: bc.lastChild.addEventListener('click', self.hide) header.appendChild(bc) + def load_doc(self, name, done_callback): + def cb(resource_data): + self.loaded_resources = resource_data + done_callback(resource_data) + load_resources(self.view.ui.db, self.view.book, name, self.loaded_resources, cb) + def show_footnote(self, data): self.current_footnote_data = data width = 100 // data.cols_per_screen @@ -105,3 +115,27 @@ class ContentPopupOverlay: c.style.width = f'{width}vw' header = c.firstChild self.create_footnote_header(header) + self.load_doc(data.name, self.show_footnote_item) + self.iframe.style.height = '12ex' + self.iframe_wrapper.send_message('clear', text=_('Loading note, please wait...')) + + def show_footnote_item(self, resource_data): + self.pending_load = resource_data, self.show_footnote_item_stage2 + if self.iframe_wrapper.ready: + self.do_pending_load() + else: + self.iframe_wrapper.init() + + def do_pending_load(self): + if self.pending_load: + data, func = self.pending_load + self.pending_load = None + func(data) + + def show_footnote_item_stage2(self, resource_data): + self.iframe_wrapper.send_unencrypted_message('display', + resource_data=resource_data, book=self.view.book, name=self.current_footnote_data.name, + frag=self.current_footnote_data.frag) + + def on_content_loaded(self, data): + self.iframe.style.height = f'{data.height}px' diff --git a/src/pyj/read_book/footnotes.pyj b/src/pyj/read_book/footnotes.pyj index d3b47f3c41..1d43a0d6d6 100644 --- a/src/pyj/read_book/footnotes.pyj +++ b/src/pyj/read_book/footnotes.pyj @@ -2,6 +2,10 @@ # License: GPL v3 Copyright: 2017, Kovid Goyal from __python__ import bound_methods, hash_literals +from dom import clear +from read_book.comm import IframeClient +from read_book.resources import finalize_resources, unserialize_html + def elem_roles(elem): return {k.toLowerCase(): True for k in (elem.getAttribute('role') or '').split(' ')} @@ -68,3 +72,48 @@ def is_footnote_link(a, dest_name, dest_frag, src_name, link_to_map): is_footnote_link.inline_displays = {'inline': True, 'inline-block': True} is_footnote_link.vert_aligns = {'sub': True, 'super': True, 'top': True, 'bottom': True} + + +class PopupIframeBoss: + + def __init__(self): + handlers = { + 'initialize': self.initialize, + 'clear': self.on_clear, + 'display': self.display, + } + self.comm = IframeClient(handlers) + self.blob_url_map = {} + self.name = None + self.frag = None + + def initialize(self, data): + window.onerror = self.onerror + + def onerror(self, msg, script_url, line_number, column_number, error_object): + if error_object is None: + # This happens for cross-domain errors (probably javascript injected + # into the browser via extensions/ userscripts and the like). It also + # happens all the time when using Chrome on Safari + console.log(f'Unhandled error from external javascript, ignoring: {msg} {script_url} {line_number}') + else: + console.log(error_object) + + def display(self, data): + self.book = data.book + self.name = data.name + self.frag = data.frag + for name in self.blob_url_map: + window.URL.revokeObjectURL(self.blob_url_map[name]) + root_data, self.mathjax, self.blob_url_map = finalize_resources(self.book, data.name, data.resource_data) + self.resource_urls = unserialize_html(root_data, self.content_loaded) + + def on_clear(self, data): + clear(document.head) + clear(document.body) + document.body.textContent = data.text + self.name = None + self.frag = None + + def content_loaded(self): + self.comm.send_message('content_loaded', height=document.documentElement.scrollHeight + 25) diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 6cdf56d9fe..20caace186 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -12,7 +12,7 @@ from read_book.flow_mode import ( flow_to_scroll_fraction, handle_gesture as flow_handle_gesture, layout as flow_layout, scroll_by_page as flow_scroll_by_page ) -from read_book.footnotes import is_footnote_link +from read_book.footnotes import is_footnote_link, PopupIframeBoss from read_book.globals import ( current_book, current_layout_mode, current_spine_item, set_boss, set_current_spine_item, set_layout_mode @@ -104,7 +104,7 @@ class IframeBoss: if error_object is None: # This happens for cross-domain errors (probably javascript injected # into the browser via extensions/ userscripts and the like). It also - # happens all the time when using Chrom on Safari, so ignore this + # happens all the time when using Chrome on Safari, so ignore this # type of error console.log(f'Unhandled error from external javascript, ignoring: {msg} {script_url} {line_number}') return @@ -387,5 +387,5 @@ def init(): script = document.getElementById('bootstrap') script.parentNode.removeChild(script) # free up some memory IframeBoss() - # elif window.iframe_type is 'popup': - # PopupIframeBoss() + elif window.iframe_type is 'popup': + PopupIframeBoss() diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 859da68d2b..4d83ff09d5 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -326,9 +326,10 @@ class View: if not is_current_book: self.iframe_wrapper.reset() self.content_popup_overlay.iframe_wrapper.reset() + self.loaded_resources = {} + self.content_popup_overlay.loaded_resources = {} self.book = current_book.book = book self.ui.db.update_last_read_time(book) - self.loaded_resources = {} pos = {'replace_history':True} unkey = username_key(get_interface_data().username) name = book.manifest.spine[0] @@ -374,7 +375,13 @@ class View: if idx > -1: self.currently_showing.bookpos = 'epubcfi(/{})'.format(2 * (idx +1)) self.set_margins() - load_resources(self.ui.db, self.book, name, self.loaded_resources, self.show_spine_item) + self.load_doc(name, self.show_spine_item) + + def load_doc(self, name, done_callback): + def cb(resource_data): + self.loaded_resources = resource_data + done_callback(resource_data) + load_resources(self.ui.db, self.book, name, self.loaded_resources, cb) def goto_doc_boundary(self, start): name = self.book.manifest.spine[0 if start else self.book.manifest.spine.length - 1] @@ -467,7 +474,6 @@ class View: update_visible_toc_nodes(data.visible_anchors) def show_spine_item(self, resource_data): - self.loaded_resources = resource_data self.pending_load = resource_data if self.iframe_wrapper.ready: self.do_pending_load()