mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
251 lines
7.7 KiB
Plaintext
251 lines
7.7 KiB
Plaintext
# vim:fileencoding=utf-8
|
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
|
from __python__ import bound_methods, hash_literals
|
|
|
|
import traceback
|
|
from elementmaker import E
|
|
from gettext import gettext as _
|
|
|
|
import initialize # noqa: unused-import
|
|
from ajax import ajax
|
|
from book_list.globals import set_session_data
|
|
from book_list.theme import get_color, get_font_family
|
|
from dom import get_widget_css, set_css
|
|
from modals import create_modal_container, error_dialog
|
|
from qt import from_python, to_python
|
|
from read_book.db import new_book
|
|
from read_book.globals import runtime, ui_operations
|
|
from read_book.iframe import main as iframe_main
|
|
from read_book.view import View
|
|
from session import session_defaults
|
|
from utils import encode_query_with_path
|
|
from viewer.constants import FAKE_HOST, FAKE_PROTOCOL
|
|
|
|
|
|
runtime.is_standalone_viewer = True
|
|
runtime.FAKE_HOST = FAKE_HOST
|
|
runtime.FAKE_PROTOCOL = FAKE_PROTOCOL
|
|
book = None
|
|
view = None
|
|
|
|
|
|
def file_received(name, file_data, proceed, end_type, xhr, ev):
|
|
if end_type is 'abort':
|
|
return
|
|
if end_type is not 'load':
|
|
show_error(_('Failed to load file from book'), _(
|
|
'Could not load the file: {} with error: {}').format(name, xhr.error_html))
|
|
return
|
|
if not xhr.responseType or xhr.responseType is 'text':
|
|
result = xhr.responseText
|
|
else if xhr.responseType is 'blob':
|
|
result = xhr.response
|
|
else:
|
|
show_error(_('Failed to load file from book'), _(
|
|
'Could not load the file: {} unknown response type: {}').format(name, xhr.responseType))
|
|
return
|
|
|
|
proceed(result, name, file_data.mimetype, book)
|
|
|
|
|
|
def get_file(book, name, proceed):
|
|
entry = book.manifest.files[name]
|
|
xhr = ajax('book/' + name, file_received.bind(None, name, entry, proceed), ok_code=0)
|
|
if entry.is_html or entry.mimetype.startswith('text/') or entry.mimetype is 'application/javascript':
|
|
xhr.responseType = 'text'
|
|
else:
|
|
xhr.responseType = 'blob'
|
|
xhr.send()
|
|
|
|
def mathjax_file_received(name, proceed, end_type, xhr, ev):
|
|
if end_type is 'abort':
|
|
return
|
|
if end_type is not 'load':
|
|
show_error(_('Failed to load MathJax file'), _(
|
|
'Could not load the file: {} with error: {}').format(name, xhr.error_html))
|
|
return
|
|
if not xhr.responseType or xhr.responseType is 'text':
|
|
result = xhr.responseText
|
|
else if xhr.responseType is 'blob' or xhr.responseType is 'json':
|
|
result = xhr.response
|
|
else:
|
|
show_error(_('Failed to load MathJax file'), _(
|
|
'Could not load the file: {} unknown response type: {}')
|
|
.format(name, xhr.responseType))
|
|
return
|
|
if name is 'manifest.json':
|
|
get_mathjax_files.manifest = result
|
|
get_mathjax_files_stage2.files_to_get = list(Object.keys(result))
|
|
get_mathjax_files_stage2.file_data = {}
|
|
get_mathjax_files_stage2(proceed)
|
|
return
|
|
|
|
get_mathjax_files_stage2.file_data[name] = result
|
|
get_mathjax_files_stage2.files_to_get.remove(name)
|
|
if not get_mathjax_files_stage2.files_to_get.length:
|
|
proceed(get_mathjax_files_stage2.file_data)
|
|
|
|
def get_mathjax_manifest(proceed):
|
|
xhr = ajax('mathjax/manifest.json', mathjax_file_received.bind(None, 'manifest.json', proceed), ok_code=0)
|
|
xhr.responseType = 'json'
|
|
xhr.send()
|
|
|
|
|
|
def get_mathjax_files_stage2(proceed):
|
|
if not get_mathjax_files_stage2.files_to_get.length:
|
|
proceed(get_mathjax_files_stage2.file_data)
|
|
return
|
|
for filename in get_mathjax_files_stage2.files_to_get:
|
|
xhr = ajax(f'mathjax/{filename}', mathjax_file_received.bind(None, filename, proceed), ok_code=0)
|
|
xhr.responseType = 'blob'
|
|
xhr.send()
|
|
|
|
|
|
|
|
def get_mathjax_files(proceed):
|
|
if not get_mathjax_files.manifest:
|
|
get_mathjax_manifest(proceed)
|
|
else:
|
|
get_mathjax_files_stage2(proceed)
|
|
|
|
|
|
|
|
def update_url_state(replace):
|
|
if view and view.currently_showing:
|
|
bookpos = view.currently_showing.bookpos
|
|
if bookpos:
|
|
query = {'bookpos': bookpos}
|
|
query = encode_query_with_path(query)
|
|
window.history.pushState(None, '', query)
|
|
|
|
|
|
def show_error(title, msg, details):
|
|
error_dialog(title, msg, details)
|
|
|
|
|
|
def manifest_received(key, initial_cfi, end_type, xhr, ev):
|
|
nonlocal book
|
|
if end_type is 'load':
|
|
book = new_book(key, {})
|
|
data = xhr.response
|
|
book.manifest = data[0]
|
|
book.metadata = book.manifest.metadata = data[1]
|
|
book.stored_files = {}
|
|
book.is_complete = True
|
|
v'delete book.manifest["metadata"]'
|
|
v'delete book.manifest["last_read_positions"]'
|
|
view.display_book(book, initial_cfi)
|
|
else:
|
|
error_dialog(_('Could not open book'), _(
|
|
'Failed to load book manifest, click "Show details" for more info'),
|
|
xhr.error_html or None)
|
|
|
|
|
|
class SessionData:
|
|
|
|
def __init__(self, prefs):
|
|
defaults = session_defaults()
|
|
self.data = {k: defaults[k] if prefs[k] is undefined else prefs[k] for k in defaults}
|
|
|
|
def get(self, key, defval):
|
|
ans = self.data[key]
|
|
if ans is undefined or ans is None:
|
|
if defval is undefined:
|
|
defval = None
|
|
return defval
|
|
return ans
|
|
|
|
def set(self, key, val):
|
|
if val is None:
|
|
self.data[key] = session_defaults()[key]
|
|
else:
|
|
self.data[key] = val
|
|
to_python.set_session_data(key, val)
|
|
|
|
def clear(self):
|
|
defaults = session_defaults()
|
|
self.data = {k: defaults[k] for k in defaults}
|
|
to_python.set_session_data('*', None)
|
|
|
|
|
|
def create_session_data(prefs):
|
|
sd = SessionData(prefs)
|
|
set_session_data(sd)
|
|
|
|
|
|
@from_python
|
|
def create_view(prefs):
|
|
nonlocal view
|
|
if view is None:
|
|
create_session_data(prefs)
|
|
view = View(document.getElementById('view'))
|
|
|
|
|
|
@from_python
|
|
def show_preparing_message(msg):
|
|
view.show_loading_message(msg)
|
|
|
|
|
|
@from_python
|
|
def start_book_load(key, initial_cfi):
|
|
xhr = ajax('manifest', manifest_received.bind(None, key, initial_cfi), ok_code=0)
|
|
xhr.responseType = 'json'
|
|
xhr.send()
|
|
|
|
|
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
|
if not error_object:
|
|
# cross domain error
|
|
return False
|
|
fname = script_url.rpartition('/')[-1] or script_url
|
|
msg += '<br><span style="font-size:smaller">' + 'Error at {}:{}:{}'.format(fname, line_number, column_number or '') + '</span>'
|
|
details = ''
|
|
console.log(error_object)
|
|
details = traceback.format_exception(error_object).join('')
|
|
error_dialog(_('Unhandled error'), msg, details)
|
|
return True
|
|
|
|
|
|
def redisplay_book():
|
|
view.redisplay_book()
|
|
|
|
|
|
def reload_book():
|
|
to_python.reload_book()
|
|
|
|
|
|
def forward_gesture(gesture):
|
|
view.forward_gesture(gesture)
|
|
|
|
|
|
def update_color_scheme():
|
|
view.update_color_scheme()
|
|
|
|
|
|
def update_font_size():
|
|
view.update_font_size()
|
|
|
|
|
|
if window is window.top:
|
|
# main
|
|
ui_operations.get_file = get_file
|
|
ui_operations.get_mathjax_files = get_mathjax_files
|
|
ui_operations.update_url_state = update_url_state
|
|
ui_operations.show_error = show_error
|
|
ui_operations.redisplay_book = redisplay_book
|
|
ui_operations.reload_book = reload_book
|
|
ui_operations.forward_gesture = forward_gesture
|
|
ui_operations.update_color_scheme = update_color_scheme
|
|
ui_operations.update_font_size = update_font_size
|
|
ui_operations.toggle_toc = def():
|
|
to_python.toggle_toc()
|
|
document.body.appendChild(E.div(id='view'))
|
|
window.onerror = onerror
|
|
create_modal_container()
|
|
document.body.style.fontFamily = get_font_family()
|
|
document.head.appendChild(E.style(get_widget_css()))
|
|
set_css(document.body, background_color=get_color('window-background'), color=get_color('window-foreground'))
|
|
else:
|
|
# iframe
|
|
iframe_main()
|