mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
More work on viewer search
This commit is contained in:
parent
e6bd7ad712
commit
0c815b507e
@ -5,6 +5,8 @@ from __python__ import bound_methods, hash_literals
|
||||
import traceback
|
||||
from aes import GCM
|
||||
from gettext import install, gettext as _
|
||||
from utils import html_escape
|
||||
|
||||
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, current_book
|
||||
from read_book.mathjax import apply_mathjax
|
||||
@ -193,6 +195,8 @@ class IframeBoss:
|
||||
self.scroll_to_anchor(ipos.anchor)
|
||||
elif ipos.type is 'cfi':
|
||||
self.jump_to_cfi(ipos.cfi)
|
||||
elif ipos.type is 'search':
|
||||
self.find(ipos.search_data, True)
|
||||
self.onscroll()
|
||||
document.documentElement.addEventListener('contextmenu', def(ev):
|
||||
ev.preventDefault()
|
||||
@ -274,12 +278,18 @@ class IframeBoss:
|
||||
if elem:
|
||||
scroll_to_elem(elem)
|
||||
|
||||
def find(self, data):
|
||||
def find(self, data, from_load):
|
||||
if data.searched_in_spine:
|
||||
window.getSelection().removeAllRanges()
|
||||
if window.find(data.text, False, data.backwards):
|
||||
if current_layout_mode() is not 'flow':
|
||||
snap_to_selection()
|
||||
else:
|
||||
self.send_message('find_in_spine', text=data.text, backwards=data.backwards)
|
||||
if from_load:
|
||||
self.send_message('error', title=_('Invisible text'), msg=_(
|
||||
'The text <i>{}</i> is present on this page but not visible').format(html_escape(data.text)))
|
||||
else:
|
||||
self.send_message('find_in_spine', text=data.text, backwards=data.backwards, searched_in_spine=data.searched_in_spine)
|
||||
|
||||
def init():
|
||||
script = document.getElementById('bootstrap')
|
||||
|
@ -264,3 +264,20 @@ def unserialize_html(serialized_data, proceed):
|
||||
else:
|
||||
proceeded = True
|
||||
proceed()
|
||||
|
||||
def text_from_serialized_html(data):
|
||||
serialized_data = JSON.parse(data)
|
||||
tag_map = serialized_data.tag_map
|
||||
ans = v'[]'
|
||||
stack = v'[tree[2]]'
|
||||
ignore_text = {'script':True, 'style':True}
|
||||
while stack.length:
|
||||
node = stack.pop()
|
||||
src = tag_map[node[0]]
|
||||
if not ignore_text[src.n] and src.x:
|
||||
ans.push(src.x)
|
||||
if src.l:
|
||||
ans.push(src.l)
|
||||
for v'var i = node.length - 1; i >= 1; i--':
|
||||
stack.push(node[i])
|
||||
return ans.join('')
|
||||
|
@ -8,6 +8,7 @@ from keycodes import get_key
|
||||
from elementmaker import E
|
||||
from gettext import gettext as _
|
||||
from book_list.theme import get_color
|
||||
from read_book.resources import text_from_serialized_html
|
||||
|
||||
CLASS_NAME = 'book-search-container'
|
||||
|
||||
@ -72,3 +73,25 @@ class SearchOverlay:
|
||||
|
||||
def find_previous(self):
|
||||
self.find(self.search_text, True)
|
||||
|
||||
|
||||
def find_in_serialized_html(data, text):
|
||||
haystack = text_from_serialized_html(data)
|
||||
return haystack.toLowerCase().indexOf(text) > -1
|
||||
|
||||
|
||||
def find_in_spine(names, book, db, text, proceed):
|
||||
text = text.toLowerCase()
|
||||
|
||||
def got_one(data, name, mimetype):
|
||||
if find_in_serialized_html(data, text):
|
||||
proceed(name)
|
||||
else:
|
||||
do_one()
|
||||
|
||||
def do_one():
|
||||
name = names.shift()
|
||||
if name:
|
||||
db.get_file(book, name, got_one)
|
||||
else:
|
||||
proceed(None)
|
||||
|
@ -5,13 +5,14 @@ from __python__ import bound_methods, hash_literals
|
||||
from dom import set_css, add_extra_css, build_rule, svgicon
|
||||
from elementmaker import E
|
||||
from gettext import gettext as _
|
||||
from utils import html_escape
|
||||
|
||||
from modals import error_dialog
|
||||
from modals import error_dialog, warning_dialog
|
||||
from book_list.globals import get_session_data, get_boss
|
||||
from read_book.globals import messenger, iframe_id, current_book, set_current_spine_item
|
||||
from read_book.resources import load_resources
|
||||
from read_book.overlay import Overlay
|
||||
from read_book.search import SearchOverlay
|
||||
from read_book.search import SearchOverlay, find_in_spine
|
||||
from read_book.prefs.colors import resolve_color_scheme
|
||||
from read_book.prefs.font_size import change_font_size_by
|
||||
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
||||
@ -121,10 +122,29 @@ class View:
|
||||
self.send_message('gesture_from_margin', gesture=gesture)
|
||||
|
||||
def find(self, text, backwards):
|
||||
self.send_message('find', text=text, backwards=backwards)
|
||||
self.send_message('find', text=text, backwards=backwards, searched_in_spine=False)
|
||||
|
||||
def on_find_in_spine(self, data):
|
||||
pass
|
||||
if data.searched_in_spine:
|
||||
warning_dialog(_('Not found'), _('The text: <i>{}</i> was not found in this book').format(html_escape(data.text)))
|
||||
return
|
||||
spine = self.book.manifest.spine
|
||||
idx = spine.indexOf(self.currently_showing.name)
|
||||
if idx < 0:
|
||||
error_dialog(_('Missing file'), _(
|
||||
'Could not search as the spine item {} is missing from the book'.format(self.currently_showing.name)))
|
||||
return
|
||||
names = v'[]'
|
||||
item_groups = [range(idx-1, -1, -1), range(spine.length-1, idx, -1)] if data.backwards else [range(idx + 1, spine.length), range(idx)]
|
||||
for items in item_groups:
|
||||
for i in items:
|
||||
names.push(spine[i])
|
||||
find_in_spine(names, self.book, self.ui.db, data.text, def(found_in):
|
||||
if found_in:
|
||||
self.show_name(found_in, initial_position={'type':'search', 'search_data':data, 'replace_history':True})
|
||||
else:
|
||||
self.send_message('find', text=data.text, backwards=data.backwards, searched_in_spine=True)
|
||||
)
|
||||
|
||||
def bump_font_size(self, data):
|
||||
delta = 2 if data.increase else -2
|
||||
|
@ -141,6 +141,10 @@ def viewport_to_document(x, y, doc):
|
||||
def username_key(username):
|
||||
return ('u' if username else 'n') + username
|
||||
|
||||
def html_escape(text):
|
||||
repl = { '&': "&", '"': """, '<': "<", '>': ">" }
|
||||
return String.prototype.replace.call(text, /[&"<>]/g, def (c): return repl[c];)
|
||||
|
||||
def uniq(vals):
|
||||
# Remove all duplicates from vals, while preserving order
|
||||
ans = v'[]'
|
||||
|
Loading…
x
Reference in New Issue
Block a user