diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index 88bd5d2af5..16fc659999 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -123,6 +123,7 @@ def interface_data(ctx, rd): key=lambda (field, name):sort_key(name)) ans['field_metadata'] = db.field_metadata.all_metadata() ans['icon_map'] = icon_map() + ans['icon_path'] = ctx.url_for('/icon', which='') mdata = ans['metadata'] = {} try: extra_books = set(int(x) for x in rd.query.get('extra_books', '').split(',')) diff --git a/src/calibre/srv/metadata.py b/src/calibre/srv/metadata.py index e632765d1f..34580d9795 100644 --- a/src/calibre/srv/metadata.py +++ b/src/calibre/srv/metadata.py @@ -10,6 +10,7 @@ from collections import namedtuple from datetime import datetime, time from functools import partial from threading import Lock +from urllib import quote from calibre.constants import config_dir from calibre.db.categories import Tag @@ -140,7 +141,7 @@ def icon_map(): custom_icons = JSONConfig('gui').get('tags_browser_category_icons', {}) for k, v in custom_icons.iteritems(): if os.access(os.path.join(config_dir, 'tb_icons', v), os.R_OK): - _icon_map[k] = '_' + v + _icon_map[k] = '_' + quote(v) _icon_map['file_type_icons'] = { k:'mimetypes/%s.png' % v for k, v in EXT_MAP.iteritems() } diff --git a/src/pyj/book_list/search.pyj b/src/pyj/book_list/search.pyj index 8e09897b3b..049a1f6fb2 100644 --- a/src/pyj/book_list/search.pyj +++ b/src/pyj/book_list/search.pyj @@ -2,7 +2,7 @@ # License: GPL v3 Copyright: 2015, Kovid Goyal from ajax import ajax -from dom import clear +from dom import clear, set_css from elementmaker import E from gettext import gettext as _ from widgets import create_button, BUTTON_VPADDING, create_spinner @@ -17,6 +17,7 @@ class SearchPanel: sp_counter += 1 self.container_id = 'search-panel-' + sp_counter self.interface_data = interface_data + self.tag_path = [] style = '' div = E.div( id=self.container_id, style='display:none', @@ -99,10 +100,46 @@ class SearchPanel: self.tag_browser_data = JSON.parse(xhr.responseText) except Exception as err: show_error(err + '') - container.innerHTML = 'Loaded' + return + self.render_tag_browser(container, clear_path=True) else: show_error(xhr.error_html) + def render_tag_browser(self, container, clear_path=False): + if clear_path: + self.tag_path = [] + clear(container) + set_css(container, padding='1rem', display='flex', flex_wrap='wrap') + parent = self.tag_browser_data.root + detailed_child = None + for child_index, state in self.tag_path: + q = parent.children and parent.children[child_index] + if not q: + break + parent, detailed_child = q, state + if detailed_child is not None and parent.children[detailed_child]: + self.render_child_menu(container, parent.children[detailed_child]) + else: + self.render_children(container, parent.children) + + def icon_for_node(self, node): + ans = self.interface_data.icon_map[node.category] or 'column.png' + return self.interface_data.icon_path + '/' + ans + + def render_children(self, container, children): + item_map = self.tag_browser_data.item_map + + for child in children: + node = item_map[child.id] + div = E.div( + E.div( + E.img(src=self.icon_for_node(node), style='vertical-align:middle; display:inline-block; max-height:4ex'), + '\xa0' + node.name) + ) + set_css(div, max_width='45vw', border='solid 1px currentColor', border_radius='20px', margin='0.5rem', padding='1ex', cursor='pointer') + set_css(div.firstChild, position='relative', top='50%', transform='translateY(-50%)', margin_left='0.5rem') + container.appendChild(div) + @property def container(self): return document.getElementById(self.container_id)