# vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal 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}