mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Start work on section tracking for the new viewer
This commit is contained in:
parent
1782fbae51
commit
f5c1f90dfa
@ -99,18 +99,23 @@ class TOC(object):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return b'\n'.join([x.encode('utf-8') for x in self.get_lines()])
|
return b'\n'.join([x.encode('utf-8') for x in self.get_lines()])
|
||||||
|
|
||||||
@property
|
def to_dict(self, node_counter=None):
|
||||||
def as_dict(self):
|
|
||||||
ans = {
|
ans = {
|
||||||
'title':self.title, 'dest':self.dest, 'frag':self.frag,
|
'title':self.title, 'dest':self.dest, 'frag':self.frag,
|
||||||
'children':[c.as_dict for c in self.children]
|
'children':[c.to_dict(node_counter) for c in self.children]
|
||||||
}
|
}
|
||||||
if self.dest_exists is not None:
|
if self.dest_exists is not None:
|
||||||
ans['dest_exists'] = self.dest_exists
|
ans['dest_exists'] = self.dest_exists
|
||||||
if self.dest_error is not None:
|
if self.dest_error is not None:
|
||||||
ans['dest_error'] = self.dest_error
|
ans['dest_error'] = self.dest_error
|
||||||
|
if node_counter is not None:
|
||||||
|
ans['id'] = next(node_counter)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_dict(self):
|
||||||
|
return self.to_dict()
|
||||||
|
|
||||||
|
|
||||||
def child_xpath(tag, name):
|
def child_xpath(tag, name):
|
||||||
return tag.xpath('./*[calibre:lower-case(local-name()) = "%s"]'%name)
|
return tag.xpath('./*[calibre:lower-case(local-name()) = "%s"]'%name)
|
||||||
@ -748,4 +753,3 @@ def create_inline_toc(container, title=None):
|
|||||||
f.write(raw)
|
f.write(raw)
|
||||||
set_guide_item(container, 'toc', title, name, frag='calibre_generated_inline_toc')
|
set_guide_item(container, 'toc', title, name, frag='calibre_generated_inline_toc')
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ def abspath(x):
|
|||||||
x = '\\\\?\\' + os.path.abspath(x)
|
x = '\\\\?\\' + os.path.abspath(x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
_books_cache_dir = None
|
_books_cache_dir = None
|
||||||
|
|
||||||
|
|
||||||
@ -50,9 +51,10 @@ def books_cache_dir():
|
|||||||
|
|
||||||
|
|
||||||
def book_hash(library_uuid, book_id, fmt, size, mtime):
|
def book_hash(library_uuid, book_id, fmt, size, mtime):
|
||||||
raw = dumps((library_uuid, book_id, fmt.upper(), size, mtime), RENDER_VERSION)
|
raw = dumps((library_uuid, book_id, fmt.upper(), size, mtime, RENDER_VERSION))
|
||||||
return sha1(raw).hexdigest().decode('ascii')
|
return sha1(raw).hexdigest().decode('ascii')
|
||||||
|
|
||||||
|
|
||||||
staging_cleaned = False
|
staging_cleaned = False
|
||||||
|
|
||||||
|
|
||||||
@ -82,6 +84,7 @@ def queue_job(ctx, copy_format_to, bhash, fmt, book_id, size, mtime):
|
|||||||
queued_jobs[bhash] = job_id
|
queued_jobs[bhash] = job_id
|
||||||
return job_id
|
return job_id
|
||||||
|
|
||||||
|
|
||||||
last_final_clean_time = 0
|
last_final_clean_time = 0
|
||||||
|
|
||||||
|
|
||||||
@ -175,6 +178,7 @@ def book_file(ctx, rd, book_id, fmt, size, mtime, name):
|
|||||||
raise
|
raise
|
||||||
raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
|
raise HTTPNotFound('No book file with hash: %s and name: %s' % (bhash, name))
|
||||||
|
|
||||||
|
|
||||||
mathjax_lock = Lock()
|
mathjax_lock = Lock()
|
||||||
mathjax_manifest = None
|
mathjax_manifest = None
|
||||||
|
|
||||||
|
@ -55,6 +55,7 @@ def decode_url(x):
|
|||||||
parts = x.split('#', 1)
|
parts = x.split('#', 1)
|
||||||
return decode_component(parts[0]), (parts[1] if len(parts) > 1 else '')
|
return decode_component(parts[0]), (parts[1] if len(parts) > 1 else '')
|
||||||
|
|
||||||
|
|
||||||
absolute_units = frozenset('px mm cm pt in pc q'.split())
|
absolute_units = frozenset('px mm cm pt in pc q'.split())
|
||||||
length_factors = {'mm':2.8346456693, 'cm':28.346456693, 'in': 72, 'pc': 12, 'q':0.708661417325}
|
length_factors = {'mm':2.8346456693, 'cm':28.346456693, 'in': 72, 'pc': 12, 'q':0.708661417325}
|
||||||
|
|
||||||
@ -114,6 +115,21 @@ def has_ancestor(elem, q):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def anchor_map(root):
|
||||||
|
ans = []
|
||||||
|
seen = set()
|
||||||
|
for elem in root.xpath('//*[@id or @name]'):
|
||||||
|
eid = elem.get('id')
|
||||||
|
if not eid and elem.tag.endswith('}a'):
|
||||||
|
eid = elem.get('name')
|
||||||
|
if eid:
|
||||||
|
elem.set('id', eid)
|
||||||
|
if eid and eid not in seen:
|
||||||
|
ans.append(eid)
|
||||||
|
seen.add(eid)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def get_length(root):
|
def get_length(root):
|
||||||
strip_space = re.compile(r'\s+')
|
strip_space = re.compile(r'\s+')
|
||||||
ans = 0
|
ans = 0
|
||||||
@ -136,6 +152,19 @@ def get_length(root):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
def toc_anchor_map(toc):
|
||||||
|
ans = defaultdict(list)
|
||||||
|
|
||||||
|
def process_node(node):
|
||||||
|
name = node['dest']
|
||||||
|
if name:
|
||||||
|
ans[name].append({'id':node['id'], 'frag':node['frag']})
|
||||||
|
tuple(map(process_node, node['children']))
|
||||||
|
|
||||||
|
process_node(toc)
|
||||||
|
return dict(ans)
|
||||||
|
|
||||||
|
|
||||||
class Container(ContainerBase):
|
class Container(ContainerBase):
|
||||||
|
|
||||||
tweak_mode = True
|
tweak_mode = True
|
||||||
@ -150,10 +179,11 @@ class Container(ContainerBase):
|
|||||||
name == 'mimetype'
|
name == 'mimetype'
|
||||||
}
|
}
|
||||||
raster_cover_name, titlepage_name = self.create_cover_page(input_fmt.lower())
|
raster_cover_name, titlepage_name = self.create_cover_page(input_fmt.lower())
|
||||||
|
toc = get_toc(self).to_dict(count())
|
||||||
|
|
||||||
self.book_render_data = data = {
|
self.book_render_data = data = {
|
||||||
'version': RENDER_VERSION,
|
'version': RENDER_VERSION,
|
||||||
'toc':get_toc(self).as_dict,
|
'toc':toc,
|
||||||
'spine':[name for name, is_linear in self.spine_names],
|
'spine':[name for name, is_linear in self.spine_names],
|
||||||
'link_uid': uuid4(),
|
'link_uid': uuid4(),
|
||||||
'book_hash': book_hash,
|
'book_hash': book_hash,
|
||||||
@ -163,6 +193,7 @@ class Container(ContainerBase):
|
|||||||
'has_maths': False,
|
'has_maths': False,
|
||||||
'total_length': 0,
|
'total_length': 0,
|
||||||
'spine_length': 0,
|
'spine_length': 0,
|
||||||
|
'toc_anchor_map': toc_anchor_map(toc),
|
||||||
}
|
}
|
||||||
# Mark the spine as dirty since we have to ensure it is normalized
|
# Mark the spine as dirty since we have to ensure it is normalized
|
||||||
for name in data['spine']:
|
for name in data['spine']:
|
||||||
@ -188,6 +219,7 @@ class Container(ContainerBase):
|
|||||||
ans['has_maths'] = hm = check_for_maths(root)
|
ans['has_maths'] = hm = check_for_maths(root)
|
||||||
if hm:
|
if hm:
|
||||||
self.book_render_data['has_maths'] = True
|
self.book_render_data['has_maths'] = True
|
||||||
|
ans['anchor_map'] = anchor_map(root)
|
||||||
return ans
|
return ans
|
||||||
data['files'] = {name:manifest_data(name) for name in set(self.name_path_map) - excluded_names}
|
data['files'] = {name:manifest_data(name) for name in set(self.name_path_map) - excluded_names}
|
||||||
self.commit()
|
self.commit()
|
||||||
@ -467,5 +499,6 @@ def html_as_dict(root):
|
|||||||
def render(pathtoebook, output_dir, book_hash=None):
|
def render(pathtoebook, output_dir, book_hash=None):
|
||||||
Container(pathtoebook, output_dir, book_hash=book_hash)
|
Container(pathtoebook, output_dir, book_hash=book_hash)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
render(sys.argv[-2], sys.argv[-1])
|
render(sys.argv[-2], sys.argv[-1])
|
||||||
|
@ -5,7 +5,7 @@ from __python__ import hash_literals, bound_methods
|
|||||||
from dom import set_css
|
from dom import set_css
|
||||||
from read_book.globals import get_boss
|
from read_book.globals import get_boss
|
||||||
from keycodes import get_key
|
from keycodes import get_key
|
||||||
from utils import document_height, document_width
|
from utils import document_height, document_width, viewport_to_document
|
||||||
|
|
||||||
def flow_to_scroll_fraction(frac):
|
def flow_to_scroll_fraction(frac):
|
||||||
window.scrollTo(0, document_height() * frac)
|
window.scrollTo(0, document_height() * frac)
|
||||||
@ -162,3 +162,27 @@ def handle_gesture(gesture):
|
|||||||
scroll_by_page(True)
|
scroll_by_page(True)
|
||||||
elif gesture.type is 'next-page':
|
elif gesture.type is 'next-page':
|
||||||
scroll_by_page(False)
|
scroll_by_page(False)
|
||||||
|
|
||||||
|
anchor_funcs = {
|
||||||
|
'pos_for_elem': def pos_for_elem(elem):
|
||||||
|
if not elem:
|
||||||
|
return 0, 0
|
||||||
|
br = elem.getBoundingClientRect()
|
||||||
|
x, y = viewport_to_document(br.left, br.top, elem.ownerDocument)
|
||||||
|
return y, x
|
||||||
|
,
|
||||||
|
'visibility': def visibility(pos):
|
||||||
|
y, x = pos
|
||||||
|
if y < window.pageYOffset:
|
||||||
|
return -1
|
||||||
|
if y < window.pageYOffset + window.innerHeight:
|
||||||
|
if x < window.pageXOffset:
|
||||||
|
return -1
|
||||||
|
if x < window.pageXOffset + window.innerWidth:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
,
|
||||||
|
'cmp': def cmp(a, b):
|
||||||
|
return (a[0] - b[0]) or (a[1] - b[1])
|
||||||
|
,
|
||||||
|
}
|
||||||
|
@ -35,22 +35,25 @@ messenger = Messenger()
|
|||||||
iframe_id = 'read-book-iframe'
|
iframe_id = 'read-book-iframe'
|
||||||
uid = 'calibre-' + hexlify(random_bytes(12))
|
uid = 'calibre-' + hexlify(random_bytes(12))
|
||||||
|
|
||||||
_layout_mode = 'flow'
|
|
||||||
def current_layout_mode():
|
def current_layout_mode():
|
||||||
return _layout_mode
|
return current_layout_mode.value
|
||||||
|
current_layout_mode.value = 'flow'
|
||||||
|
|
||||||
def set_layout_mode(val):
|
def set_layout_mode(val):
|
||||||
nonlocal _layout_mode
|
current_layout_mode.value = val
|
||||||
_layout_mode = val
|
|
||||||
|
|
||||||
_current_spine_item = None
|
|
||||||
def current_spine_item():
|
def current_spine_item():
|
||||||
return _current_spine_item
|
return current_spine_item.value
|
||||||
|
current_spine_item.value = None
|
||||||
|
|
||||||
def set_current_spine_item(val):
|
def set_current_spine_item(val):
|
||||||
nonlocal _current_spine_item
|
current_spine_item.value = val
|
||||||
_current_spine_item = val
|
|
||||||
|
|
||||||
|
def toc_anchor_map():
|
||||||
|
return toc_anchor_map.value
|
||||||
|
|
||||||
|
def set_toc_anchor_map(val):
|
||||||
|
toc_anchor_map.value = val
|
||||||
|
|
||||||
default_color_schemes = {
|
default_color_schemes = {
|
||||||
'white':{'foreground':'#000000', 'background':'#ffffff', 'name':_('White')},
|
'white':{'foreground':'#000000', 'background':'#ffffff', 'name':_('White')},
|
||||||
|
@ -8,16 +8,17 @@ from gettext import install, gettext as _
|
|||||||
from read_book.cfi import at_current, scroll_to as scroll_to_cfi
|
from read_book.cfi import at_current, scroll_to as scroll_to_cfi
|
||||||
from read_book.globals import set_boss, set_current_spine_item, current_layout_mode, current_spine_item, set_layout_mode
|
from read_book.globals import set_boss, set_current_spine_item, current_layout_mode, current_spine_item, set_layout_mode
|
||||||
from read_book.mathjax import apply_mathjax
|
from read_book.mathjax import apply_mathjax
|
||||||
|
from read_book.toc import update_visible_toc_anchors
|
||||||
from read_book.resources import finalize_resources, unserialize_html
|
from read_book.resources import finalize_resources, unserialize_html
|
||||||
from read_book.flow_mode import (
|
from read_book.flow_mode import (
|
||||||
flow_to_scroll_fraction, flow_onwheel, flow_onkeydown, layout as flow_layout, handle_gesture as flow_handle_gesture,
|
flow_to_scroll_fraction, flow_onwheel, flow_onkeydown, layout as flow_layout, handle_gesture as flow_handle_gesture,
|
||||||
scroll_by_page as flow_scroll_by_page
|
scroll_by_page as flow_scroll_by_page, anchor_funcs as flow_anchor_funcs
|
||||||
)
|
)
|
||||||
from read_book.paged_mode import (
|
from read_book.paged_mode import (
|
||||||
layout as paged_layout, scroll_to_fraction as paged_scroll_to_fraction,
|
layout as paged_layout, scroll_to_fraction as paged_scroll_to_fraction,
|
||||||
onwheel as paged_onwheel, onkeydown as paged_onkeydown, scroll_to_elem,
|
onwheel as paged_onwheel, onkeydown as paged_onkeydown, scroll_to_elem,
|
||||||
jump_to_cfi as paged_jump_to_cfi, handle_gesture as paged_handle_gesture,
|
jump_to_cfi as paged_jump_to_cfi, handle_gesture as paged_handle_gesture,
|
||||||
scroll_by_page as paged_scroll_by_page
|
scroll_by_page as paged_scroll_by_page, anchor_funcs as paged_anchor_funcs
|
||||||
)
|
)
|
||||||
from read_book.settings import apply_settings, opts
|
from read_book.settings import apply_settings, opts
|
||||||
from read_book.touch import create_handlers as create_touch_handlers
|
from read_book.touch import create_handlers as create_touch_handlers
|
||||||
@ -104,6 +105,7 @@ class IframeBoss:
|
|||||||
self._handle_gesture = flow_handle_gesture
|
self._handle_gesture = flow_handle_gesture
|
||||||
self.to_scroll_fraction = flow_to_scroll_fraction
|
self.to_scroll_fraction = flow_to_scroll_fraction
|
||||||
self.jump_to_cfi = scroll_to_cfi
|
self.jump_to_cfi = scroll_to_cfi
|
||||||
|
self.anchor_funcs = flow_anchor_funcs
|
||||||
else:
|
else:
|
||||||
self.do_layout = paged_layout
|
self.do_layout = paged_layout
|
||||||
self.handle_wheel = paged_onwheel
|
self.handle_wheel = paged_onwheel
|
||||||
@ -111,6 +113,7 @@ class IframeBoss:
|
|||||||
self.to_scroll_fraction = paged_scroll_to_fraction
|
self.to_scroll_fraction = paged_scroll_to_fraction
|
||||||
self.jump_to_cfi = paged_jump_to_cfi
|
self.jump_to_cfi = paged_jump_to_cfi
|
||||||
self._handle_gesture = paged_handle_gesture
|
self._handle_gesture = paged_handle_gesture
|
||||||
|
self.anchor_funcs = paged_anchor_funcs
|
||||||
apply_settings(data.settings)
|
apply_settings(data.settings)
|
||||||
set_current_spine_item({'name':data.name, 'is_first':index is 0, 'is_last':index is spine.length - 1, 'initial_position':data.initial_position})
|
set_current_spine_item({'name':data.name, 'is_first':index is 0, 'is_last':index is spine.length - 1, 'initial_position':data.initial_position})
|
||||||
self.last_cfi = None
|
self.last_cfi = None
|
||||||
@ -174,7 +177,7 @@ class IframeBoss:
|
|||||||
|
|
||||||
def content_loaded_stage2(self):
|
def content_loaded_stage2(self):
|
||||||
self.connect_links()
|
self.connect_links()
|
||||||
window.addEventListener('scroll', debounce(self.update_cfi, 1000))
|
window.addEventListener('scroll', debounce(self.onscroll, 1000))
|
||||||
window.addEventListener('resize', debounce(self.onresize, 500))
|
window.addEventListener('resize', debounce(self.onresize, 500))
|
||||||
window.addEventListener('wheel', self.onwheel)
|
window.addEventListener('wheel', self.onwheel)
|
||||||
window.addEventListener('keydown', self.onkeydown)
|
window.addEventListener('keydown', self.onkeydown)
|
||||||
@ -188,7 +191,7 @@ class IframeBoss:
|
|||||||
self.scroll_to_anchor(ipos.anchor)
|
self.scroll_to_anchor(ipos.anchor)
|
||||||
elif ipos.type is 'cfi':
|
elif ipos.type is 'cfi':
|
||||||
self.jump_to_cfi(ipos.cfi)
|
self.jump_to_cfi(ipos.cfi)
|
||||||
self.update_cfi()
|
self.onscroll()
|
||||||
self.send_message('content_loaded')
|
self.send_message('content_loaded')
|
||||||
|
|
||||||
def update_cfi(self):
|
def update_cfi(self):
|
||||||
@ -203,6 +206,14 @@ class IframeBoss:
|
|||||||
self.send_message('update_cfi', cfi=cfi, replace_history=self.replace_history_on_next_cfi_update)
|
self.send_message('update_cfi', cfi=cfi, replace_history=self.replace_history_on_next_cfi_update)
|
||||||
self.replace_history_on_next_cfi_update = True
|
self.replace_history_on_next_cfi_update = True
|
||||||
|
|
||||||
|
def update_toc_position(self):
|
||||||
|
visible_anchors = update_visible_toc_anchors(self.book.manifest.toc_anchor_map, self.anchor_funcs)
|
||||||
|
self.send_message('update_toc_position', visible_anchors=visible_anchors)
|
||||||
|
|
||||||
|
def onscroll(self):
|
||||||
|
self.update_cfi()
|
||||||
|
self.update_toc_position()
|
||||||
|
|
||||||
def onresize(self):
|
def onresize(self):
|
||||||
if current_layout_mode() is not 'flow':
|
if current_layout_mode() is not 'flow':
|
||||||
self.do_layout()
|
self.do_layout()
|
||||||
@ -211,6 +222,7 @@ class IframeBoss:
|
|||||||
if cfi:
|
if cfi:
|
||||||
paged_jump_to_cfi('/' + cfi)
|
paged_jump_to_cfi('/' + cfi)
|
||||||
self.update_cfi()
|
self.update_cfi()
|
||||||
|
self.update_toc_position()
|
||||||
|
|
||||||
def onwheel(self, evt):
|
def onwheel(self, evt):
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
|
@ -513,3 +513,25 @@ def handle_gesture(gesture):
|
|||||||
scroll_by_page(True, False)
|
scroll_by_page(True, False)
|
||||||
elif gesture.type is 'next-page':
|
elif gesture.type is 'next-page':
|
||||||
scroll_by_page(False, False)
|
scroll_by_page(False, False)
|
||||||
|
|
||||||
|
|
||||||
|
anchor_funcs = {
|
||||||
|
'pos_for_elem': def pos_for_elem(elem):
|
||||||
|
if not elem:
|
||||||
|
return 0
|
||||||
|
br = elem.getBoundingClientRect()
|
||||||
|
x = viewport_to_document(br.left, br.top, elem.ownerDocument)[0]
|
||||||
|
return column_at(x)
|
||||||
|
,
|
||||||
|
'visibility': def visibility(pos):
|
||||||
|
first = column_at(window.pageXOffset + 10)
|
||||||
|
if pos < first:
|
||||||
|
return -1
|
||||||
|
if pos < first + cols_per_screen:
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
,
|
||||||
|
'cmp': def cmp(a, b):
|
||||||
|
return a - b
|
||||||
|
,
|
||||||
|
}
|
||||||
|
@ -8,13 +8,50 @@ from elementmaker import E
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from modals import error_dialog
|
from modals import error_dialog
|
||||||
from widgets import create_tree, find_text_in_tree, scroll_tree_item_into_view
|
from widgets import create_tree, find_text_in_tree, scroll_tree_item_into_view
|
||||||
|
from read_book.globals import toc_anchor_map, set_toc_anchor_map, current_spine_item, current_layout_mode
|
||||||
|
|
||||||
|
|
||||||
|
def update_visible_toc_nodes(visible_anchors):
|
||||||
|
update_visible_toc_nodes.data = visible_anchors
|
||||||
|
update_visible_toc_nodes.data = {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_highlighted_toc_nodes(toc, parent_map, id_map):
|
||||||
|
data = update_visible_toc_nodes.data
|
||||||
|
ans = {}
|
||||||
|
if data.has_visible:
|
||||||
|
ans = data.visible_anchors
|
||||||
|
elif data.before:
|
||||||
|
ans[data.before] = True
|
||||||
|
for node_id in Object.keys(ans):
|
||||||
|
pid = parent_map[node_id]
|
||||||
|
while pid:
|
||||||
|
ans[pid] = True
|
||||||
|
pid = parent_map[pid]
|
||||||
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def create_toc_tree(toc, onclick):
|
def create_toc_tree(toc, onclick):
|
||||||
|
|
||||||
|
parent_map, id_map = {}, {}
|
||||||
|
|
||||||
|
def process_node(node, parent):
|
||||||
|
id_map[node.id] = node
|
||||||
|
parent_map[node.id] = parent
|
||||||
|
for c in node.children:
|
||||||
|
process_node(c, node)
|
||||||
|
|
||||||
|
process_node(toc)
|
||||||
|
highlighted_toc_nodes = get_highlighted_toc_nodes(toc, parent_map, id_map)
|
||||||
|
|
||||||
def populate_data(node, li, a):
|
def populate_data(node, li, a):
|
||||||
li.dataset.tocDest = node.dest or ''
|
li.dataset.tocDest = node.dest or ''
|
||||||
li.dataset.tocFrag = node.frag or ''
|
li.dataset.tocFrag = node.frag or ''
|
||||||
a.textContent = node.title or ''
|
title = node.title or ''
|
||||||
|
if highlighted_toc_nodes[node.id]:
|
||||||
|
a.appendChild(E.b(E.i(title)))
|
||||||
|
else:
|
||||||
|
a.textContent = title
|
||||||
|
|
||||||
return create_tree(toc, populate_data, onclick)
|
return create_tree(toc, populate_data, onclick)
|
||||||
|
|
||||||
@ -47,3 +84,45 @@ def create_toc_panel(book, container, onclick, onclose):
|
|||||||
search_bar = create_search_bar(do_search.bind(toc_panel_id), 'search-book-toc', button=search_button, placeholder=t)
|
search_bar = create_search_bar(do_search.bind(toc_panel_id), 'search-book-toc', button=search_button, placeholder=t)
|
||||||
set_css(search_bar, flex_grow='10', margin_right='1em')
|
set_css(search_bar, flex_grow='10', margin_right='1em')
|
||||||
container.appendChild(E.div(style='margin: 1ex 1em; display: flex;', search_bar, search_button))
|
container.appendChild(E.div(style='margin: 1ex 1em; display: flex;', search_bar, search_button))
|
||||||
|
|
||||||
|
|
||||||
|
def current_toc_anchor_map(tam, anchor_funcs):
|
||||||
|
current_map = toc_anchor_map()
|
||||||
|
if not (current_map and current_map.layout_mode is current_layout_mode() and current_map.width is window.innerWidth and current_map.height is window.innerHeight):
|
||||||
|
name = current_spine_item().name
|
||||||
|
am = {}
|
||||||
|
anchors = v'[]'
|
||||||
|
for anchor in (tam[name] or v'[]'):
|
||||||
|
val = anchor_funcs.pos_for_elem()
|
||||||
|
if anchor.frag:
|
||||||
|
elem = document.getElementById(anchor.frag)
|
||||||
|
if elem:
|
||||||
|
val = anchor_funcs.pos_for_elem(elem)
|
||||||
|
am[anchor.id] = val
|
||||||
|
anchors.push(anchor.id)
|
||||||
|
anchors.sort(def (a, b): anchor_funcs.cmp(am[a], am[b]);)
|
||||||
|
sort_map = {aid: i for i, aid in enumerate(anchors)}
|
||||||
|
|
||||||
|
current_map = {'layout_mode': current_layout_mode, 'width': window.innerWidth, 'height': window.innerHeight, 'pos_map': am, 'sort_map':sort_map}
|
||||||
|
set_toc_anchor_map(current_map)
|
||||||
|
return current_map
|
||||||
|
|
||||||
|
|
||||||
|
def update_visible_toc_anchors(toc_anchor_map, anchor_funcs):
|
||||||
|
tam = current_toc_anchor_map(toc_anchor_map, anchor_funcs)
|
||||||
|
prev = before = after = None
|
||||||
|
visible_anchors = {}
|
||||||
|
has_visible = False
|
||||||
|
for anchor_id in tam.pos_map:
|
||||||
|
pos = tam.pos_map[anchor_id]
|
||||||
|
visibility = anchor_funcs.visibility(pos)
|
||||||
|
if visibility is 0:
|
||||||
|
before = prev
|
||||||
|
has_visible = True
|
||||||
|
visible_anchors[anchor_id] = True
|
||||||
|
elif visibility > 0:
|
||||||
|
after = anchor_id
|
||||||
|
break
|
||||||
|
prev = anchor_id
|
||||||
|
|
||||||
|
return {'visible_anchors':visible_anchors, 'has_visible':has_visible, 'before':before, 'after':after}
|
||||||
|
@ -12,6 +12,7 @@ from read_book.overlay import Overlay
|
|||||||
from read_book.prefs.colors import resolve_color_scheme
|
from read_book.prefs.colors import resolve_color_scheme
|
||||||
from read_book.prefs.font_size import change_font_size_by
|
from read_book.prefs.font_size import change_font_size_by
|
||||||
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
||||||
|
from read_book.toc import update_visible_toc_nodes
|
||||||
from book_list.theme import get_color
|
from book_list.theme import get_color
|
||||||
from utils import parse_url_params, username_key
|
from utils import parse_url_params, username_key
|
||||||
|
|
||||||
@ -82,6 +83,7 @@ class View:
|
|||||||
'goto_doc_boundary': self.goto_doc_boundary,
|
'goto_doc_boundary': self.goto_doc_boundary,
|
||||||
'scroll_to_anchor': self.on_scroll_to_anchor,
|
'scroll_to_anchor': self.on_scroll_to_anchor,
|
||||||
'update_cfi': self.on_update_cfi,
|
'update_cfi': self.on_update_cfi,
|
||||||
|
'update_toc_position': self.on_update_toc_position,
|
||||||
'content_loaded': self.on_content_loaded,
|
'content_loaded': self.on_content_loaded,
|
||||||
'show_chrome': self.show_chrome,
|
'show_chrome': self.show_chrome,
|
||||||
'bump_font_size': self.bump_font_size,
|
'bump_font_size': self.bump_font_size,
|
||||||
@ -314,6 +316,9 @@ class View:
|
|||||||
self.book.last_read_position[unkey] = data.cfi
|
self.book.last_read_position[unkey] = data.cfi
|
||||||
self.ui.db.update_last_read_time(self.book)
|
self.ui.db.update_last_read_time(self.book)
|
||||||
|
|
||||||
|
def on_update_toc_position(self, data):
|
||||||
|
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.loaded_resources = resource_data
|
||||||
# Re-init the iframe to ensure any changes made to the environment by the last spine item are lost
|
# Re-init the iframe to ensure any changes made to the environment by the last spine item are lost
|
||||||
|
Loading…
x
Reference in New Issue
Block a user