diff --git a/.gitignore b/.gitignore index 6a108bef5d..59a739473b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ resources/template-functions.json resources/editor-functions.json resources/user-manual-translation-stats.json resources/content-server/index-generated.html +resources/content-server/calibre.appcache resources/content-server/locales.zip resources/content-server/mathjax.zip.xz resources/content-server/mathjax.version diff --git a/resources/content-server/index.html b/resources/content-server/index.html index 82c3ee7b93..3a576be39c 100644 --- a/resources/content-server/index.html +++ b/resources/content-server/index.html @@ -1,5 +1,5 @@ - + calibre diff --git a/src/calibre/utils/rapydscript.py b/src/calibre/utils/rapydscript.py index 0ec6edbdaa..06cc1061eb 100644 --- a/src/calibre/utils/rapydscript.py +++ b/src/calibre/utils/rapydscript.py @@ -1,23 +1,28 @@ #!/usr/bin/env python2 # vim:fileencoding=utf-8 -from __future__ import (unicode_literals, division, absolute_import, - print_function) +# License: GPLv3 Copyright: 2015, Kovid Goyal +from __future__ import absolute_import, division, print_function, unicode_literals -__license__ = 'GPL v3' -__copyright__ = '2015, Kovid Goyal ' - -import os, sys, atexit, errno, subprocess, glob, shutil, json, re -from io import BytesIO -from threading import local +import atexit +import errno +import glob +import json +import os +import re +import shutil +import subprocess +import sys from functools import partial -from threading import Thread -from Queue import Queue, Empty +from io import BytesIO +from Queue import Empty, Queue +from threading import Thread, local +from calibre import force_unicode +from calibre.constants import __appname__, __version__, cache_dir +from calibre.utils.terminal import ANSIStream from duktape import Context, JSError, to_python from lzma.xz import compress, decompress -from calibre import force_unicode -from calibre.constants import cache_dir, __appname__, __version__ -from calibre.utils.terminal import ANSIStream + COMPILER_PATH = 'rapydscript/compiler.js.xz' @@ -47,6 +52,8 @@ def update_rapydscript(): # }}} # Compiler {{{ + + tls = local() @@ -72,6 +79,7 @@ class CompileFailure(ValueError): def default_lib_dir(): return P('rapydscript/lib', allow_user_override=False) + _cache_dir = None @@ -125,6 +133,7 @@ def compile_pyj(data, filename='', beautify=True, private_scope=True, lib raise CompileFailure(result.stack) raise CompileFailure(repr(presult)) + has_external_compiler = None @@ -170,6 +179,20 @@ def compile_fast(data, filename=None, beautify=True, private_scope=True, libdir= return js.decode('utf-8') +def create_manifest(html): + import hashlib + from calibre.library.field_metadata import category_icon_map + h = hashlib.sha256(html) + for ci in category_icon_map.itervalues(): + h.update(I(ci, data=True)) + icons = {'icon/' + x for x in category_icon_map.itervalues()} + icons.add('favicon.png') + h.update(I('lt.png', data=True)) + manifest = '\n'.join(sorted(icons)) + return 'CACHE MANIFEST\n# {}\n{}\n\nNETWORK:\n*'.format( + h.hexdigest(), manifest).encode('utf-8') + + def compile_srv(): d = os.path.dirname base = d(d(d(d(os.path.abspath(__file__))))) @@ -195,8 +218,12 @@ def compile_srv(): js = compile_fast(f.read(), fname).replace('__RENDER_VERSION__', rv, 1).replace('__MATHJAX_VERSION__', mathjax_version, 1).encode('utf-8') with lopen(os.path.join(base, 'index.html'), 'rb') as f: html = f.read().replace(b'RESET_STYLES', reset, 1).replace(b'ICONS', icons, 1).replace(b'MAIN_JS', js, 1) + + manifest = create_manifest(html) with lopen(os.path.join(base, 'index-generated.html'), 'wb') as f: f.write(html) + with lopen(os.path.join(base, 'calibre.appcache'), 'wb') as f: + f.write(manifest) # }}} @@ -420,5 +447,6 @@ def main(args=sys.argv): def entry(): main(sys.argv[1:]) + if __name__ == '__main__': main() diff --git a/src/pyj/autoreload.pyj b/src/pyj/autoreload.pyj index 98c9f3f543..624888ddac 100644 --- a/src/pyj/autoreload.pyj +++ b/src/pyj/autoreload.pyj @@ -46,6 +46,18 @@ class Watcher: self.reload_app() def reload_app(self): + appcache = window.top.applicationCache + for which in 'cached error noupdate obsolete updateready'.split(' '): + appcache.addEventListener(which, self.cache_update_done, False) + try: + appcache.update() + except: # In chrome with devtools open, appcache is sometimes disabled/fails + window.location.reload(True) + + def cache_update_done(self): + appcache = window.top.applicationCache + if appcache.status is appcache.UPDATEREADY: + appcache.swapCache() window.location.reload(True) diff --git a/src/pyj/srv.pyj b/src/pyj/srv.pyj index c31d4bf03c..5df31941c0 100644 --- a/src/pyj/srv.pyj +++ b/src/pyj/srv.pyj @@ -2,6 +2,8 @@ # License: GPL v3 Copyright: 2015, Kovid Goyal from __python__ import hash_literals +from gettext import gettext as _ + import initialize # noqa: unused-import from ajax import ajax from autoreload import create_auto_reload_watcher @@ -14,6 +16,12 @@ is_running_in_iframe = False # Changed before script is loaded in the iframe if is_running_in_iframe: init() else: + window.applicationCache.addEventListener('updateready', def(): + if window.applicationCache.status is window.applicationCache.UPDATEREADY: + window.applicationCache.swapCache() + if window.confirm(_('The calibre web application has been updated. Do you want reload the site?')): + window.location.reload() + , False) script = document.currentScript or document.scripts[0] main_js(script.textContent) script.parentNode.removeChild(script) # save some memory