mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-11-27 00:35:00 -05:00
146 lines
5.3 KiB
Plaintext
146 lines
5.3 KiB
Plaintext
# vim:fileencoding=utf-8
|
||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||
from __python__ import hash_literals
|
||
|
||
from complete import create_search_bar
|
||
from dom import set_css, svgicon, ensure_id
|
||
from elementmaker import E
|
||
from gettext import gettext as _
|
||
from modals import error_dialog
|
||
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 iter_toc_nodes(node, callback):
|
||
if callback(node):
|
||
return
|
||
for child in node.children:
|
||
if callback(child):
|
||
return
|
||
|
||
def get_highlighted_toc_nodes(toc, spine, parent_map, id_map):
|
||
data = update_visible_toc_nodes.data
|
||
ans = {}
|
||
if data.has_visible:
|
||
ans = data.visible_anchors
|
||
else:
|
||
if data.before:
|
||
ans[data.before] = True
|
||
else:
|
||
nodes_before = v'[]'
|
||
iter_toc_nodes(toc, def(node):
|
||
if node.dest is current_spine_item():
|
||
return True
|
||
nodes_before.push(node)
|
||
)
|
||
if nodes_before.length:
|
||
ans[nodes_before[-1].id] = 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):
|
||
|
||
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):
|
||
li.dataset.tocDest = node.dest or ''
|
||
li.dataset.tocFrag = node.frag 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)
|
||
|
||
def do_search(text):
|
||
container = document.getElementById(this)
|
||
a = find_text_in_tree(container, text)
|
||
if not text:
|
||
return
|
||
if not a:
|
||
return error_dialog(_('No matches found'), _(
|
||
'The text "{}" was not found in the Table of Contents').format(text))
|
||
scroll_tree_item_into_view(a)
|
||
|
||
def create_toc_panel(book, container, onclick, onclose):
|
||
container.appendChild(E.div(
|
||
style='display: flex; justify-content: space-between; padding: 1ex 1em; border-bottom: solid 1px currentColor',
|
||
E.h2(_('Table of Contents')),
|
||
E.div(svgicon('close'), style='cursor:pointer', onclick=def(event):event.preventDefault(), event.stopPropagation(), onclose(event);, class_='simple-link'),
|
||
))
|
||
def handle_click(event, li):
|
||
if event.button is 0:
|
||
onclick(li.dataset.tocDest, li.dataset.tocFrag)
|
||
toc_panel = create_toc_tree(book.manifest.toc, handle_click)
|
||
toc_panel_id = ensure_id(toc_panel)
|
||
set_css(container, display='flex', flex_direction='column')
|
||
set_css(toc_panel, flex_grow='10')
|
||
container.appendChild(toc_panel)
|
||
search_button = E.div(class_='simple-link', svgicon('search'))
|
||
t = _('Search Table of Contents')
|
||
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')
|
||
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)
|
||
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 < 0:
|
||
before = anchor_id
|
||
elif visibility is 0:
|
||
has_visible = True
|
||
visible_anchors[anchor_id] = True
|
||
elif visibility > 0:
|
||
after = anchor_id
|
||
break
|
||
|
||
return {'visible_anchors':visible_anchors, 'has_visible':has_visible, 'before':before, 'after':after}
|