E-book viewer: Allow clicking links in popup footnotes. Fixes #1931646 [Footnotes/Endnotes don't return to text](https://bugs.launchpad.net/calibre/+bug/1931646)

This commit is contained in:
Kovid Goyal 2021-06-23 10:17:02 +05:30
parent cff4d491ea
commit 78dea8e439
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 64 additions and 3 deletions

View File

@ -58,6 +58,8 @@ class ContentPopupOverlay:
'ready': self.on_iframe_ready,
'error': self.view.on_iframe_error,
'content_loaded': self.on_content_loaded,
'print': self.on_print,
'link_activated': self.on_link_activated,
}
iframe_kw = {
'seamless': True, 'sandbox': 'allow-scripts', 'style': 'width: 100%; max-height: 70vh'
@ -72,6 +74,12 @@ class ContentPopupOverlay:
c = self.container
c.firstChild.appendChild(iframe)
def on_print(self, data):
print(data.string)
def on_link_activated(self, data):
self.view.link_in_content_popup_activated(data.name, data.frag, data.is_popup, data.title)
@property
def container(self):
return document.getElementById('book-content-popup-overlay')

View File

@ -4,6 +4,7 @@ from __python__ import bound_methods, hash_literals
from dom import clear
from iframe_comm import IframeClient
from read_book.globals import runtime, current_spine_item, set_current_spine_item
from read_book.resources import finalize_resources, unserialize_html
from read_book.settings import (
apply_settings, set_color_scheme_class, update_settings
@ -194,11 +195,12 @@ class PopupIframeBoss:
self.blob_url_map = {}
self.name = None
self.frag = None
self.link_attr = None
def initialize(self, data):
window.addEventListener('error', self.onerror)
window.addEventListener('error', self.on_error)
def onerror(self, evt):
def on_error(self, evt):
msg = evt.message
script_url = evt.filename
line_number = evt.lineno
@ -217,6 +219,16 @@ class PopupIframeBoss:
self.book = data.book
self.name = data.name
self.frag = data.frag
self.link_attr = 'data-' + self.book.manifest.link_uid
spine = self.book.manifest.spine
index = spine.indexOf(data.name)
set_current_spine_item({
'name':data.name,
'is_first':index is 0,
'is_last':index is spine.length - 1,
'index': index,
'initial_position':data.initial_position
})
update_settings(data.settings)
for name in self.blob_url_map:
window.URL.revokeObjectURL(self.blob_url_map[name])
@ -224,6 +236,35 @@ class PopupIframeBoss:
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, self.show_only_footnote, data.name)
def connect_links(self):
for a in document.body.querySelectorAll(f'a[{self.link_attr}]'):
a.addEventListener('click', self.link_activated)
if runtime.is_standalone_viewer:
# links with a target get turned into requests to open a new window by Qt
for a in document.body.querySelectorAll('a[target]'):
a.removeAttribute('target')
def link_activated(self, evt):
try:
data = JSON.parse(evt.currentTarget.getAttribute(self.link_attr))
except:
print('WARNING: Failed to parse link data {}, ignoring'.format(evt.currentTarget?.getAttribute?(self.link_attr)))
return
self.activate_link(data.name, data.frag, evt.currentTarget)
def activate_link(self, name, frag, target_elem):
if not name:
name = current_spine_item().name
try:
is_popup = is_footnote_link(target_elem, name, frag, current_spine_item().name, self.book.manifest.link_to_map or {})
title = target_elem.textContent
except:
import traceback
traceback.print_exc()
is_popup = False
title = ''
self.comm.send_message('link_activated', is_popup=is_popup, name=name, frag=frag, title=title)
def on_clear(self, data):
clear(document.head)
clear(document.body)
@ -242,6 +283,7 @@ class PopupIframeBoss:
if not self.comm.encrypted_communications:
window.setTimeout(self.content_loaded, 2)
return
self.connect_links()
# this is the loading styles used to suppress scrollbars during load
# added in unserialize_html
document.head.removeChild(document.head.firstChild)

View File

@ -154,6 +154,7 @@ class IframeBoss:
'scroll_to_anchor': self.on_scroll_to_anchor,
'scroll_to_frac': self.on_scroll_to_frac,
'scroll_to_ref': self.on_scroll_to_ref,
'fake_popup_activation': self.on_fake_popup_activation,
'set_reference_mode': self.set_reference_mode,
'toggle_autoscroll': self.toggle_autoscroll,
'fake_wheel_event': self.fake_wheel_event,
@ -718,7 +719,7 @@ class IframeBoss:
traceback.print_exc()
is_popup = False
if is_popup:
self.send_message('show_footnote', name=name, frag=frag, title=target_elem.textContent, cols_per_screen=calc_columns_per_screen())
self.on_fake_popup_activation({'name': name, 'frag': frag, 'title': target_elem.textContent})
return
if name is current_spine_item().name:
self.replace_history_on_next_cfi_update = False
@ -726,6 +727,9 @@ class IframeBoss:
else:
self.send_message('scroll_to_anchor', name=name, frag=frag)
def on_fake_popup_activation(self, data):
self.send_message('show_footnote', name=data.name, frag=data.frag, title=data.title, cols_per_screen=calc_columns_per_screen())
def scroll_to_anchor(self, frag):
if frag:
elem = document.getElementById(frag)

View File

@ -1062,6 +1062,13 @@ class View:
def on_scroll_to_anchor(self, data):
self.show_name(data.name, initial_position={'type':'anchor', 'anchor':data.frag, 'replace_history':False})
def link_in_content_popup_activated(self, name, frag, is_popup, title):
self.content_popup_overlay.hide()
if is_popup:
self.iframe_wrapper.send_message('fake_popup_activation', name=name, frag=frag, title=title)
else:
self.goto_named_destination(name, frag)
def goto_cfi(self, bookpos, add_to_history):
cfiname, internal_cfi = self.parse_cfi(bookpos, self.book)
if cfiname and internal_cfi: