calibre/src/pyj/utils.pyj
Kovid Goyal 4fc76a6919
Finish up adding books implementation
Still needs to add the new book to the existing book list, so that the
book list does not need to be refreshed to see the book.
2018-01-25 14:04:15 +05:30

249 lines
7.7 KiB
Plaintext

# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals
from ajax import encode_query
from encodings import hexlify
from book_list.theme import get_font_family
is_ios = v'!!navigator.platform && /iPad|iPhone|iPod/.test(navigator.platform)'
def debounce(func, wait, immediate=False):
# Returns a function, that, as long as it continues to be invoked, will not
# be triggered. The function will be called after it stops being called for
# wait milliseconds. If `immediate` is True, trigger the function on the
# leading edge, instead of the trailing.
timeout = None
return def debounce_inner(): # noqa: unused-local
nonlocal timeout
context, args = this, arguments
def later():
nonlocal timeout
timeout = None
if not immediate:
func.apply(context, args)
call_now = immediate and not timeout
window.clearTimeout(timeout)
timeout = window.setTimeout(later, wait)
if call_now:
func.apply(context, args)
if Object.assign:
copy_hash = def (obj):
return Object.assign({}, obj)
else:
copy_hash = def (obj):
return {k:obj[k] for k in Object.keys(obj)}
def parse_url_params(url=None, allow_multiple=False):
cache = parse_url_params.cache
url = url or window.location.href
if cache[url]:
return copy_hash(parse_url_params.cache[url])
qs = url.indexOf('#')
ans = {}
if qs < 0:
cache[url] = ans
return copy_hash(ans)
q = url.slice(qs + 1, (url.length + 1))
if not q:
cache[url] = ans
return copy_hash(ans)
pairs = q.replace(/\+/g, " ").split("&")
for pair in pairs:
key, val = pair.partition('=')[::2]
key, val = decodeURIComponent(key), decodeURIComponent(val)
if allow_multiple:
if ans[key] is undefined:
ans[key] = v'[]'
ans[key].append(val)
else:
ans[key] = val
cache[url] = ans
return copy_hash(ans)
parse_url_params.cache = {}
def encode_query_with_path(query, path):
path = path or window.location.pathname
return path + encode_query(query, '#')
def request_full_screen(elem):
elem = elem or document.documentElement
if elem.requestFullScreen:
elem.requestFullScreen()
elif elem.webkitRequestFullScreen:
elem.webkitRequestFullScreen()
elif elem.mozRequestFullScreen:
elem.mozRequestFullScreen()
def full_screen_element():
return document.fullscreenElement or document.webkitFullscreenElement or document.mozFullScreenElement or document.msFullscreenElement
_roman = list(zip(
[1000,900,500,400,100,90,50,40,10,9,5,4,1],
["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"]
))
def roman(num):
if num <= 0 or num >= 4000 or int(num) is not num:
return num + ''
result = []
for d, r in _roman:
while num >= d:
result.append(r)
num -= d
return result.join('')
def fmt_sidx(val, fmt='{:.2f}', use_roman=True):
if val is undefined or val is None or val is '':
return '1'
if int(val) is float(val):
if use_roman:
return roman(val)
return int(val) + ''
return fmt.format(float(val))
def rating_to_stars(value, allow_half_stars=False, star='★', half='½'):
r = max(0, min(int(value or 0), 10))
if allow_half_stars:
ans = star.repeat(r // 2)
if r % 2:
ans += half
else:
ans = star.repeat(int(r/2.0))
return ans
def human_readable(size, sep=' '):
divisor, suffix = 1, "B"
for i, candidate in enumerate(('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB')):
if size < (1 << ((i + 1) * 10)):
divisor, suffix = (1 << (i * 10)), candidate
break
size = (float(size)/divisor) + ''
pos = size.find(".")
if pos > -1:
size = size[:pos + 2]
if size.endswith('.0'):
size = size[:-2]
return size + sep + suffix
def document_height():
html = document.documentElement
return max(document.body.scrollHeight, document.body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
def document_width():
html = document.documentElement
return max(document.body.scrollWidth, document.body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth)
_data_ns = None
def data_ns(name):
nonlocal _data_ns
if _data_ns is None:
rand = Uint8Array(12)
window.crypto.getRandomValues(rand)
_data_ns = 'data-' + hexlify(rand) + '-'
return _data_ns + name
def get_elem_data(elem, name, defval):
ans = elem.getAttribute(data_ns(name))
if ans is None:
return defval ? None
return JSON.parse(ans)
def set_elem_data(elem, name, val):
elem.setAttribute(data_ns(name), JSON.stringify(val))
def viewport_to_document(x, y, doc):
# Convert x, y from the viewport (window) co-ordinate system to the
# document (body) co-ordinate system
doc = doc or window.document
topdoc = window.document
while doc is not topdoc:
# We are in a frame
frame = doc.defaultView.frameElement
rect = frame.getBoundingClientRect()
x += rect.left
y += rect.top
doc = frame.ownerDocument
win = doc.defaultView
wx, wy = win.pageXOffset, win.pageYOffset
x += wx
y += wy
return x, y
def username_key(username):
return ('u' if username else 'n') + username
def html_escape(text):
repl = { '&': "&amp;", '"': "&quot;", '<': "&lt;", '>': "&gt;" }
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'[]'
seen = {}
for x in vals:
if not seen[x]:
seen[x] = True
ans.push(x)
return ans
def conditional_timeout(elem_id, timeout, func):
def ct_impl():
elem = document.getElementById(elem_id)
if elem:
func.call(elem)
window.setTimeout(ct_impl, timeout)
def simple_markup(html):
html = (html or '').replace(/\uffff/g, '').replace(
/<\s*(\/?[a-zA-Z1-6]+)[^>]*>/g, def (match, tag):
tag = tag.toLowerCase()
is_closing = '/' if tag[0] is '/' else ''
if is_closing:
tag = tag[1:]
if simple_markup.allowed_tags.indexOf(tag) < 0:
tag = 'span'
return f'\uffff{is_closing}{tag}\uffff'
)
div = document.createElement('b')
div.textContent = html
html = div.innerHTML
return html.replace(/\uffff(\/?[a-z1-6]+)\uffff/g, '<$1>')
simple_markup.allowed_tags = v"'a|b|i|br|hr|h1|h2|h3|h4|h5|h6|div|em|strong|span'.split('|')"
def safe_set_inner_html(elem, html):
elem.innerHTML = simple_markup(html)
return elem
def sandboxed_html(html, style, sandbox):
ans = document.createElement('iframe')
ans.setAttribute('sandbox', sandbox or '')
ans.setAttribute('seamless', '')
ans.style.width = '100%'
html = html or ''
css = 'html, body { margin: 0; padding: 0; font-family: __FONT__ } p:first-child { margin-top: 0; padding-top: 0; -webkit-margin-before: 0 }'.replace('__FONT__', get_font_family())
css += style or ''
final_html = f'<!DOCTYPE html><html><head><style>{css}</style></head><body>{html}</body></html>'
# Microsoft Edge does not support srcdoc not does it work using a data URI.
ans.srcdoc = final_html
return ans
if __name__ is '__main__':
from pythonize import strings
strings()
print(rating_to_stars(3, True))
print(fmt_sidx(10), fmt_sidx(1.2))
print(list(map(human_readable, [1, 1024.0, 1025, 1024*1024*2.3])))