mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Wire up the footnote popup
This commit is contained in:
parent
cad17a5785
commit
9ed99e752f
@ -7,6 +7,7 @@ from gettext import gettext as _
|
|||||||
|
|
||||||
from dom import add_extra_css, build_rule, clear, svgicon
|
from dom import add_extra_css, build_rule, clear, svgicon
|
||||||
from read_book.comm import IframeWrapper
|
from read_book.comm import IframeWrapper
|
||||||
|
from read_book.resources import load_resources
|
||||||
|
|
||||||
CLASS_NAME = 'book-content-popup-container'
|
CLASS_NAME = 'book-content-popup-container'
|
||||||
TOP_LEVEL_DISPLAY = 'flex'
|
TOP_LEVEL_DISPLAY = 'flex'
|
||||||
@ -22,7 +23,7 @@ add_extra_css(def():
|
|||||||
sel += ' > div'
|
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')
|
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')
|
style += build_rule(sel, display='flex', justify_content='space-between', align_items='center')
|
||||||
|
|
||||||
sel += ' > a' # buttons
|
sel += ' > a' # buttons
|
||||||
@ -37,9 +38,10 @@ class ContentPopupOverlay:
|
|||||||
|
|
||||||
def __init__(self, view):
|
def __init__(self, view):
|
||||||
self.view = view
|
self.view = view
|
||||||
|
self.loaded_resources = {}
|
||||||
c = self.container
|
c = self.container
|
||||||
c.classList.add(CLASS_NAME)
|
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(
|
c.appendChild(E.div(
|
||||||
E.div(),
|
E.div(),
|
||||||
@ -51,6 +53,8 @@ class ContentPopupOverlay:
|
|||||||
)
|
)
|
||||||
handlers = {
|
handlers = {
|
||||||
'ready': self.on_iframe_ready,
|
'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.iframe_wrapper = IframeWrapper(handlers, iframe, 'popup', _('Loading data, please wait...'))
|
||||||
self.pending_load = None
|
self.pending_load = None
|
||||||
@ -98,6 +102,12 @@ class ContentPopupOverlay:
|
|||||||
bc.lastChild.addEventListener('click', self.hide)
|
bc.lastChild.addEventListener('click', self.hide)
|
||||||
header.appendChild(bc)
|
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):
|
def show_footnote(self, data):
|
||||||
self.current_footnote_data = data
|
self.current_footnote_data = data
|
||||||
width = 100 // data.cols_per_screen
|
width = 100 // data.cols_per_screen
|
||||||
@ -105,3 +115,27 @@ class ContentPopupOverlay:
|
|||||||
c.style.width = f'{width}vw'
|
c.style.width = f'{width}vw'
|
||||||
header = c.firstChild
|
header = c.firstChild
|
||||||
self.create_footnote_header(header)
|
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'
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __python__ import bound_methods, hash_literals
|
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):
|
def elem_roles(elem):
|
||||||
return {k.toLowerCase(): True for k in (elem.getAttribute('role') or '').split(' ')}
|
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.inline_displays = {'inline': True, 'inline-block': True}
|
||||||
is_footnote_link.vert_aligns = {'sub': True, 'super': True, 'top': True, 'bottom': 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)
|
||||||
|
@ -12,7 +12,7 @@ from read_book.flow_mode import (
|
|||||||
flow_to_scroll_fraction, handle_gesture as flow_handle_gesture,
|
flow_to_scroll_fraction, handle_gesture as flow_handle_gesture,
|
||||||
layout as flow_layout, scroll_by_page as flow_scroll_by_page
|
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 (
|
from read_book.globals import (
|
||||||
current_book, current_layout_mode, current_spine_item, set_boss,
|
current_book, current_layout_mode, current_spine_item, set_boss,
|
||||||
set_current_spine_item, set_layout_mode
|
set_current_spine_item, set_layout_mode
|
||||||
@ -104,7 +104,7 @@ class IframeBoss:
|
|||||||
if error_object is None:
|
if error_object is None:
|
||||||
# This happens for cross-domain errors (probably javascript injected
|
# This happens for cross-domain errors (probably javascript injected
|
||||||
# into the browser via extensions/ userscripts and the like). It also
|
# 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
|
# type of error
|
||||||
console.log(f'Unhandled error from external javascript, ignoring: {msg} {script_url} {line_number}')
|
console.log(f'Unhandled error from external javascript, ignoring: {msg} {script_url} {line_number}')
|
||||||
return
|
return
|
||||||
@ -387,5 +387,5 @@ def init():
|
|||||||
script = document.getElementById('bootstrap')
|
script = document.getElementById('bootstrap')
|
||||||
script.parentNode.removeChild(script) # free up some memory
|
script.parentNode.removeChild(script) # free up some memory
|
||||||
IframeBoss()
|
IframeBoss()
|
||||||
# elif window.iframe_type is 'popup':
|
elif window.iframe_type is 'popup':
|
||||||
# PopupIframeBoss()
|
PopupIframeBoss()
|
||||||
|
@ -326,9 +326,10 @@ class View:
|
|||||||
if not is_current_book:
|
if not is_current_book:
|
||||||
self.iframe_wrapper.reset()
|
self.iframe_wrapper.reset()
|
||||||
self.content_popup_overlay.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.book = current_book.book = book
|
||||||
self.ui.db.update_last_read_time(book)
|
self.ui.db.update_last_read_time(book)
|
||||||
self.loaded_resources = {}
|
|
||||||
pos = {'replace_history':True}
|
pos = {'replace_history':True}
|
||||||
unkey = username_key(get_interface_data().username)
|
unkey = username_key(get_interface_data().username)
|
||||||
name = book.manifest.spine[0]
|
name = book.manifest.spine[0]
|
||||||
@ -374,7 +375,13 @@ class View:
|
|||||||
if idx > -1:
|
if idx > -1:
|
||||||
self.currently_showing.bookpos = 'epubcfi(/{})'.format(2 * (idx +1))
|
self.currently_showing.bookpos = 'epubcfi(/{})'.format(2 * (idx +1))
|
||||||
self.set_margins()
|
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):
|
def goto_doc_boundary(self, start):
|
||||||
name = self.book.manifest.spine[0 if start else self.book.manifest.spine.length - 1]
|
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)
|
update_visible_toc_nodes(data.visible_anchors)
|
||||||
|
|
||||||
def show_spine_item(self, resource_data):
|
def show_spine_item(self, resource_data):
|
||||||
self.loaded_resources = resource_data
|
|
||||||
self.pending_load = resource_data
|
self.pending_load = resource_data
|
||||||
if self.iframe_wrapper.ready:
|
if self.iframe_wrapper.ready:
|
||||||
self.do_pending_load()
|
self.do_pending_load()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user