diff --git a/src/pyj/book_list/constants.pyj b/src/pyj/book_list/constants.pyj new file mode 100644 index 0000000000..a6c5041084 --- /dev/null +++ b/src/pyj/book_list/constants.pyj @@ -0,0 +1,7 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2017, Kovid Goyal +from __python__ import hash_literals, bound_methods + + +book_list_container_id = 'book-list-container' +read_book_container_id = 'read-book-container' diff --git a/src/pyj/book_list/globals.pyj b/src/pyj/book_list/globals.pyj index 5c11f4a056..7a946901da 100644 --- a/src/pyj/book_list/globals.pyj +++ b/src/pyj/book_list/globals.pyj @@ -28,3 +28,9 @@ def get_current_query(): def set_current_query(val): nonlocal current_query current_query = val + + +def main_js(newval): + if newval is not undefined: + main_js.ans = newval + return main_js.ans diff --git a/src/pyj/book_list/main.pyj b/src/pyj/book_list/main.pyj new file mode 100644 index 0000000000..db36a9d582 --- /dev/null +++ b/src/pyj/book_list/main.pyj @@ -0,0 +1,87 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2017, Kovid Goyal +from __python__ import hash_literals, bound_methods + +import traceback +from elementmaker import E +from ajax import ajax +from dom import set_css, get_widget_css +from modals import create_modal_container, error_dialog +from session import get_interface_data, UserSessionData, update_interface_data, get_translations +from gettext import gettext as _, install +from utils import parse_url_params + +from book_list.constants import book_list_container_id, read_book_container_id +from book_list.theme import get_color + + +def remove_initial_progress_bar(): + p = document.getElementById('page_load_progress') + if p: + p.parentNode.removeChild(p) + +def onerror(msg, script_url, line_number, column_number, error_object): + console.log(error_object) + try: + fname = script_url.rpartition('/')[-1] or script_url + msg = msg + '
' + 'Error at {}:{}:{}'.format(fname, line_number, column_number or '') + '' + details = '' + if error_object: + details = traceback.format_exception(error_object).join('') + error_dialog(_('Unhandled error'), msg, details) + return True + except: + console.log('There was an error in the unhandled exception handler') + +def init_ui(): + window.onerror = onerror + translations = get_translations() + if translations: + install(translations) + remove_initial_progress_bar() + document.head.appendChild(E.style(get_widget_css())) + set_css(document.body, background_color=get_color('window-background'), color=get_color('window-foreground')) + document.body.appendChild(E.div(id='containers')) + document.body.lastChild.appendChild(E.div(id=book_list_container_id, style='display: none')) + document.body.lastChild.appendChild(E.div(id=read_book_container_id, style='display: none')) + create_modal_container() + +def on_data_loaded(end_type, xhr, ev): + remove_initial_progress_bar() + if end_type is 'load': + data = JSON.parse(xhr.responseText) + update_interface_data(data) + if data.translations: + get_translations(data.translations) + init_ui() + else: + p = E.p(style='color:red; font-weight: bold; font-size:1.5em') + if xhr.status is 401: + p.innerHTML = _('You are not authorized to view this site') + else: + p.innerHTML = xhr.error_html + document.body.appendChild(p) + +def on_data_load_progress(loaded, total): + p = document.querySelector('#page_load_progress > progress') + p.max = total + p.value = loaded + +def load_interface_data(): + temp = UserSessionData(None, {}) # So that settings for anonymous users are preserved + query = {} + library_id = temp.get('library_id') + if library_id: + query.library_id = library_id + query.sort = temp.get_library_option(library_id, 'sort') + url_query = parse_url_params() + for key in url_query: + query[key] = url_query[key] + ajax('interface-data/init', on_data_loaded, on_data_load_progress, query=query).send() + + +def main(): + if get_interface_data().is_default: + load_interface_data() + else: + init_ui() diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 9997633533..992852ece4 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -8,7 +8,7 @@ from gettext import gettext as _ from utils import html_escape from modals import error_dialog, warning_dialog -from book_list.globals import get_session_data, get_boss +from book_list.globals import get_session_data, get_boss, main_js 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 @@ -193,8 +193,8 @@ class View: side_margin('left', margin_left), side_margin('right', margin_right) def create_src_doc(self): - iframe_script = self.ui.interface_data.main_js.replace(/is_running_in_iframe\s*=\s*false/, 'is_running_in_iframe = true') - self.ui.interface_data.main_js = None + iframe_script = main_js().replace(/is_running_in_iframe\s*=\s*false/, 'is_running_in_iframe = true') + main_js(None) self.src_doc = self.iframe.srcdoc = LOADING_DOC.replace( '__BS__', _('Bootstrapping book reader...')).replace( '__SCRIPT__', iframe_script) diff --git a/src/pyj/session.pyj b/src/pyj/session.pyj index a0c9a300e5..18fdfac65c 100644 --- a/src/pyj/session.pyj +++ b/src/pyj/session.pyj @@ -1,6 +1,6 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2015, Kovid Goyal -from __python__ import hash_literals +from __python__ import hash_literals, bound_methods from ajax import ajax_send @@ -55,8 +55,6 @@ def storage_available(which): except: return False -session_storage = None - class FakeStorage: def __init__(self): @@ -74,17 +72,16 @@ class FakeStorage: self.data = {} def get_session_storage(): - nonlocal session_storage - if session_storage is None: + if not get_session_storage.ans: if storage_available('localStorage'): - session_storage = window.localStorage + get_session_storage.ans = window.localStorage elif storage_available('sessionStorage'): - session_storage = window.sessionStorage + get_session_storage.ans = window.sessionStorage console.error('localStorage not available using sessionStorage instead') else: - session_storage = FakeStorage() + get_session_storage.ans = FakeStorage() console.error('sessionStorage and localStorage not available using a temp cache instead') - return session_storage + return get_session_storage.ans class SessionData: @@ -130,11 +127,56 @@ class SessionData: self.overflow_storage = {} self.has_overflow = False + def local_storage(): if not local_storage.storage: local_storage.storage = SessionData('calibre-local-') return local_storage.storage +default_interface_data = { + 'username': None, + 'output_format': 'EPUB', + 'input_formats': {'EPUB', 'MOBI', 'AZW3'}, + 'gui_pubdate_display_format': 'MMM yyyy', + 'gui_timestamp_display_format': 'dd MMM yyyy', + 'gui_last_modified_display_format': 'dd MMM yyyy', + 'use_roman_numerals_for_series_number': True, + 'allow_console_print':False, +} + +def get_interface_data(): + if not get_interface_data.storage: + get_interface_data.storage = SessionData('calibre-interface-data-') + ans = get_interface_data.storage.get('current') + if ans: + ans.is_default = False + else: + ans = {'is_default': True} + for k in default_interface_data: + ans[k] = default_interface_data[k] + return ans + + +def update_interface_data(new_data): + data = get_interface_data() + for k in default_interface_data: + nval = new_data[k] + if k is not undefined: + data[k] = nval + if not get_interface_data.storage: + get_interface_data.storage = SessionData('calibre-interface-data-') + get_interface_data.storage.set('current', data) + + +def get_translations(newval): + if not get_translations.storage: + get_translations.storage = SessionData('calibre-translations-') + if newval: + get_translations.storage.set('current', newval) + else: + return get_translations.storage.get('current') + + class UserSessionData(SessionData): def __init__(self, username, saved_data): diff --git a/src/pyj/srv.pyj b/src/pyj/srv.pyj index 414317446a..8ef60d169d 100644 --- a/src/pyj/srv.pyj +++ b/src/pyj/srv.pyj @@ -3,76 +3,25 @@ from __python__ import hash_literals import initialize # noqa: unused-import -from ajax import ajax, set_allow_console_print -from elementmaker import E -from gettext import gettext as _, install -from session import UserSessionData -from utils import parse_url_params +from ajax import ajax -from book_list.boss import Boss -from book_list.globals import set_session_data +from book_list.globals import main_js +from book_list.main import main from read_book.iframe import init -def on_library_loaded(end_type, xhr, ev): - nonlocal main_js - p = document.getElementById('page_load_progress') - p.parentNode.removeChild(p) - if end_type is 'load': - interface_data = JSON.parse(xhr.responseText) - interface_data.main_js = main_js - set_allow_console_print(interface_data.allow_console_print) - main_js = None - script = document.getElementById('main_js') - if script: - script.parentNode.removeChild(script) # Free up some memory - if interface_data.translations: - install(interface_data.translations) - sd = UserSessionData(interface_data.username, interface_data.user_session_data) - set_session_data(sd) - sd.set('library_id', interface_data.library_id) - Boss(interface_data) - else: - p = E.p(style='color:red; font-weight: bold; font-size:1.5em') - if xhr.status is 401: - p.innerHTML = _('You are not authorized to view this site') - else: - p.innerHTML = xhr.error_html - document.body.appendChild(p) - -def on_library_load_progress(loaded, total): - p = document.querySelector('#page_load_progress > progress') - p.max = total - p.value = loaded - -def load_book_list(): - temp = UserSessionData(None, {}) # So that settings for anonymous users are preserved - query = {} - library_id = temp.get('library_id') - if library_id: - query.library_id = library_id - query.sort = temp.get_library_option(library_id, 'sort') - url_query = parse_url_params() - for key in url_query: - query[key] = url_query[key] - ajax('interface-data/init', on_library_loaded, on_library_load_progress, query=query).send() - -def on_load(): - print('calibre loaded at:', Date().toString()) - load_book_list() - is_running_in_iframe = False # Changed before script is loaded in the iframe if is_running_in_iframe: init() else: script = document.currentScript or document.scripts[0] - main_js = script.textContent + main_js(script.textContent) script.parentNode.removeChild(script) # save some memory script = undefined # We wait for all page elements to load, since this is a single page app # with a largely empty starting document, we can use this to preload any resources # we know are going to be needed immediately. - window.addEventListener('load', on_load) + window.addEventListener('load', main) ajax('auto-reload', def(end_type, xhr, event): if end_type is 'load':