calibre/src/pyj/session.pyj
Kovid Goyal 393e47dcdb
Add a remove highlight action to the popup bar
Only visible if a highlight is selected
2020-08-22 18:48:23 +05:30

313 lines
9.8 KiB
Plaintext

# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from uuid import short_uuid
from ajax import ajax_send
defaults = {
# Book list settings
'copy_to_library_dupes': 'add;overwrite',
'last_sort_order': {},
'show_all_metadata': False, # show all metadata fields in the book details panel
'sort': 'timestamp.desc', # comma separated list of items of the form: field.order
'view_mode': 'cover_grid',
# Tag Browser settings
'and_search_terms': False, # how to add search terms to the search expression from the Tag Browser
'collapse_at': 25, # number of items at which sub-groups are created, 0 to disable
'dont_collapse': '', # comma separated list of category names
'hide_empty_categories': 'no',
'partition_method': 'first letter', # other choices: 'disable', 'partition'
'sort_tags_by': 'name', # other choices: popularity, rating
# Book reader settings
'background_image_fade': 0,
'background_image_style': 'scaled',
'background_image': None,
'base_font_size': 16,
'book_scrollbar': False,
'columns_per_screen': {'portrait':0, 'landscape':0},
'controls_help_shown_count': 0,
'controls_help_shown_count_rtl_page_progression': 0,
'cover_preserve_aspect_ratio': True,
'current_color_scheme': 'system',
'footer': {'right': 'progress'},
'header': {},
'left-margin': {},
'right-margin': {},
'hide_tooltips': False,
'keyboard_shortcuts': {},
'lines_per_sec_auto': 1,
'lines_per_sec_smooth': 20,
'margin_bottom': 20,
'margin_left': 20,
'margin_right': 20,
'margin_top': 20,
'max_text_height': 0,
'max_text_width': 0,
'override_book_colors': 'never',
'paged_margin_clicks_scroll_by_screen': True,
'paged_wheel_scrolls_by_screen': False,
'paged_taps_scroll_by_screen': False,
'paged_pixel_scroll_threshold': 60,
'read_mode': 'paged',
'scroll_auto_boundary_delay': 5,
'scroll_stop_boundaries': False,
'standalone_font_settings': {},
'standalone_misc_settings': {},
'standalone_recently_opened': v'[]',
'user_color_schemes': {},
'user_stylesheet': '',
'word_actions': v'[]',
'highlight_style': None,
'custom_highlight_colors': v'[]',
'show_selection_bar': True,
'net_search_url': 'https://google.com/search?q={q}',
'selection_bar_actions': v"['copy', 'lookup', 'highlight', 'remove_highlight', 'search_net', 'clear']",
}
is_local_setting = {
'background_image_fade': True,
'background_image_style': True,
'background_image': True,
'base_font_size': True,
'columns_per_screen': True,
'controls_help_shown_count': True,
'controls_help_shown_count_rtl_page_progression': True,
'current_color_scheme': True,
'lines_per_sec_auto': True,
'lines_per_sec_smooth': True,
'margin_bottom': True,
'margin_left': True,
'margin_right': True,
'margin_top': True,
'max_text_height': True,
'max_text_width': True,
'override_book_colors': True,
'read_mode': 'paged',
'scroll_auto_boundary_delay': True,
'scroll_stop_boundaries': True,
'standalone_font_settings': True,
'standalone_misc_settings': True,
'standalone_recently_opened': True,
'user_stylesheet': True,
'highlight_style': True,
}
def session_defaults():
return defaults
def storage_available(which):
which = which or 'localStorage'
try:
storage = window[which]
x = '__storage__test__'
storage.setItem(x, x)
storage.removeItem(x)
return True
except:
return False
class FakeStorage:
def __init__(self):
self.data = {}
def getItem(self, key):
return self.data[key]
def setItem(self, key, value):
if jstype(value) is not 'string':
value = JSON.stringify(value)
self.data[key] = value
def clear(self):
self.data = {}
def get_session_storage():
if not get_session_storage.ans:
if storage_available('localStorage'):
get_session_storage.ans = window.localStorage
elif storage_available('sessionStorage'):
get_session_storage.ans = window.sessionStorage
console.error('localStorage not available using sessionStorage instead')
else:
get_session_storage.ans = FakeStorage()
console.error('sessionStorage and localStorage not available using a temp cache instead')
return get_session_storage.ans
class SessionData:
def __init__(self, global_prefix=None):
self.global_prefix = global_prefix or 'calibre-session-'
self.storage = get_session_storage()
self.overflow_storage = {}
self.has_overflow = False
def get(self, key, defval):
key = self.global_prefix + key
if self.has_overflow:
ans = self.overflow_storage[key]
if ans is undefined:
ans = self.storage.getItem(key)
else:
ans = self.storage.getItem(key)
if ans is undefined or ans is None:
if defval is undefined:
defval = None
return defval
return JSON.parse(ans)
def set(self, key, value):
key = self.global_prefix + key
if value is None:
self.storage.removeItem(key)
v'delete self.overflow_storage[key]'
return True
value = JSON.stringify(value)
try:
self.storage.setItem(key, value)
v'delete self.overflow_storage[key]'
return True
except:
self.overflow_storage[key] = value
self.has_overflow = True
console.error('session storage has overflowed, using a temp cache instead')
return False
def clear(self):
self.storage.clear()
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
def get_device_uuid():
if not get_device_uuid.ans:
s = local_storage()
ans = s.get('device_uuid')
if not ans:
ans = short_uuid()
s.set('device_uuid', ans)
get_device_uuid.ans = ans
return get_device_uuid.ans
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,
'default_library_id': None,
'default_book_list_mode': defaults.view_mode,
'library_map': None,
'search_the_net_urls': [],
'donate_link': 'https://calibre-ebook.com/donate',
'icon_map': {},
'icon_path': '',
'custom_list_template': None,
'num_per_page': 50,
}
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):
self.prefix = (username or '') + '-'
self.has_user = bool(username)
self.username = username
SessionData.__init__(self)
self.echo_changes = False
self.changes = {}
self.has_changes = False
self.push_timer_id = None
if saved_data:
for key in saved_data:
if not is_local_setting[key]:
self.set(key, saved_data[key])
self.echo_changes = True
def defval(self, key):
return defaults[key]
def get(self, key, defval):
if defval is undefined:
defval = defaults[key]
return SessionData.get(self, (self.prefix + key), defval)
def get_library_option(self, library_id, key, defval):
if not library_id:
return self.get(key, defval)
lkey = key + '-||-' + library_id
if defval is undefined:
defval = defaults[key]
return self.get(lkey, defval)
def set(self, key, value):
if self.echo_changes and self.has_user and not is_local_setting[key]:
self.changes[key] = value
self.has_changes = True
if self.push_timer_id is not None:
clearTimeout(self.push_timer_id)
self.push_timer_id = setTimeout(self.push_to_server.bind(self), 1000)
return SessionData.set(self, (self.prefix + key), value)
def set_library_option(self, library_id, key, value):
if library_id:
key = key + '-||-' + library_id
return self.set(key, value)
def push_to_server(self):
if self.has_changes:
ajax_send('interface-data/set-session-data', self.changes, def(end_type, xhr, ev):
if end_type is not 'load':
console.error('Failed to send session data to server: ' + xhr.error_html)
)
self.changes = {}
self.has_changes = False