mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-12-07 21:55:07 -05:00
155 lines
6.0 KiB
Plaintext
155 lines
6.0 KiB
Plaintext
# vim:fileencoding=utf-8
|
||
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
|
||
from __python__ import hash_literals
|
||
|
||
from gettext import gettext as _
|
||
|
||
|
||
default_timeout = 60
|
||
|
||
|
||
def set_default_timeout(val):
|
||
nonlocal default_timeout
|
||
default_timeout = val
|
||
|
||
|
||
def encode_query_component(x):
|
||
ans = encodeURIComponent(x)
|
||
# The following exceptions are to make epubcfi() look better
|
||
ans = ans.replace(/%2[fF]/g, '/')
|
||
ans = ans.replace(/%40/g, '@')
|
||
ans = ans.replace(/%5[bB]/g, '[')
|
||
ans = ans.replace(/%5[dD]/g, ']')
|
||
ans = ans.replace(/%5[eE]/g, '^')
|
||
ans = ans.replace(/%3[aA]/g, ':')
|
||
return ans
|
||
|
||
def encode_query(query, qchar):
|
||
if not query:
|
||
return ''
|
||
qchar = qchar or '?'
|
||
keys = Object.keys(query).sort()
|
||
ans = ''
|
||
if keys.length:
|
||
for k in keys:
|
||
val = query[k]
|
||
if val is undefined or val is None or val is '':
|
||
continue
|
||
ans += qchar + encodeURIComponent(k) + '=' + encode_query_component(val.toString())
|
||
qchar = '&'
|
||
return ans
|
||
|
||
|
||
def absolute_path(path):
|
||
if path[0] is not '/':
|
||
slash = '' if window.location.pathname[-1] is '/' else '/'
|
||
path = window.location.pathname + slash + path
|
||
return path
|
||
|
||
|
||
def workaround_qt_bug(xhr, end_type, ok_code=200):
|
||
if end_type is 'error' and xhr.status is ok_code and window.navigator.userAgent.indexOf('calibre-viewer') is 0:
|
||
end_type = 'load'
|
||
return end_type
|
||
|
||
|
||
def ajax(path, on_complete, on_progress=None, bypass_cache=True, method='GET', query=None, timeout=None, ok_code=200, progress_totals_needed=True):
|
||
# Run an AJAX request. on_complete must be a function that accepts three
|
||
# arguments: end_type, xhr, ev where end_type is one of 'abort', 'error',
|
||
# 'load', 'timeout'. In case end_type is anything other than 'load' you can
|
||
# get a descriptive error message via `xhr.error_html`. on_progress, if
|
||
# provided must be a function accepting three arguments: loaded, total, xhr
|
||
# where loaded is the number of bytes received, total is the total size.
|
||
#
|
||
# Returns the xhr object, call xhr.send() to start the request.
|
||
if timeout is None:
|
||
timeout = default_timeout
|
||
query = query or {}
|
||
xhr = XMLHttpRequest()
|
||
eq = encode_query(query)
|
||
has_query = eq.length > 0
|
||
path = absolute_path(path) + eq
|
||
if bypass_cache:
|
||
path += ('&' if has_query else '?') + Date().getTime()
|
||
|
||
xhr.request_path = path
|
||
xhr.error_html = ''
|
||
|
||
def set_error(event, is_network_error):
|
||
if is_network_error:
|
||
xhr.error_html = str.format(_('Failed to communicate with "{}", network error. Is the server running and accessible?'), xhr.request_path)
|
||
elif event is 'timeout':
|
||
xhr.error_html = str.format(_('Failed to communicate with "{}", timed out after: {} seconds'), xhr.request_path, timeout)
|
||
elif event is 'abort':
|
||
xhr.error_html = str.format(_('Failed to communicate with "{}", aborted'), xhr.request_path)
|
||
else:
|
||
try:
|
||
rtext = xhr.responseText or ''
|
||
except:
|
||
rtext = ''
|
||
xhr.error_html = str.format(_('Failed to communicate with "{}", with status: [{} ({})] {}<br><br>{}'), xhr.request_path, xhr.status, event, xhr.statusText, rtext[:200])
|
||
|
||
def progress_callback(ev):
|
||
if ev.lengthComputable:
|
||
on_progress(ev.loaded, ev.total, xhr)
|
||
elif ev.loaded:
|
||
if not progress_totals_needed:
|
||
on_progress(ev.loaded, undefined, xhr)
|
||
return
|
||
ul = xhr.getResponseHeader('Calibre-Uncompressed-Length')
|
||
if ul:
|
||
try:
|
||
ul = int(ul)
|
||
except Exception:
|
||
return
|
||
on_progress(ev.loaded, ul, xhr)
|
||
|
||
def complete_callback(end_type, ev):
|
||
is_network_error = ev if end_type is 'error' else False
|
||
if xhr.status is not ok_code and end_type is 'load':
|
||
end_type = 'error'
|
||
end_type = workaround_qt_bug(xhr, end_type, ok_code)
|
||
if end_type is not 'load':
|
||
set_error(end_type, is_network_error)
|
||
on_complete(end_type, xhr, ev)
|
||
|
||
if on_progress:
|
||
xhr.addEventListener('progress', progress_callback)
|
||
xhr.addEventListener('abort', def(ev): complete_callback('abort', ev);)
|
||
xhr.addEventListener('error', def(ev): complete_callback('error', ev);)
|
||
xhr.addEventListener('load', def(ev): complete_callback('load', ev);)
|
||
xhr.addEventListener('timeout', def(ev): complete_callback('timeout', ev);)
|
||
xhr.open(method, path)
|
||
xhr.timeout = timeout * 1000 # IE requires timeout to be set after open
|
||
return xhr
|
||
|
||
def ajax_send(path, data, on_complete, on_progress=None, query=None, timeout=None, ok_code=200):
|
||
# Unfortunately, browsers do not allow sending of data with HTTP GET, except
|
||
# as query parameters, so we have to use POST
|
||
xhr = ajax(path, on_complete, on_progress, False, 'POST', query, timeout, ok_code)
|
||
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
|
||
xhr.send(JSON.stringify(data))
|
||
return xhr
|
||
|
||
|
||
def ajax_send_file(path, file, on_complete, on_progress, timeout=None, ok_code=200):
|
||
xhr = ajax(path, on_complete, on_progress, False, 'POST', timeout, ok_code)
|
||
if file.type:
|
||
xhr.overrideMimeType(file.type)
|
||
xhr.send(file)
|
||
return xhr
|
||
|
||
|
||
def console_print(*args):
|
||
ρσ_print(*args) # noqa: undef
|
||
data = ' '.join(map(str, args)) + '\n'
|
||
xhr = ajax('console-print', def():pass;, method='POST', progress_totals_needed=False)
|
||
xhr.send(data)
|
||
|
||
# TODO: Implement AJAX based switch user by:
|
||
# 1) POST to a logout URL with an incorrect username and password
|
||
# 2) Have the server accept that incorrect username/password
|
||
# 3) Navigate to / which should cause the browser to re-prompt for username and password
|
||
# (only problem is login dialog will likely pre-show the wrong username) To workaround that,
|
||
# You can consider using a dedicated ajax based login form, see http://www.peej.co.uk/articles/http-auth-with-html-forms.html)
|