diff --git a/resources/content_server/browse/browse.css b/resources/content_server/browse/browse.css
index d66ae744cf..d2e8106ba0 100644
--- a/resources/content_server/browse/browse.css
+++ b/resources/content_server/browse/browse.css
@@ -265,9 +265,66 @@ h2.library_name {
display: none;
}
+#booklist .load_data { display: none }
+
.loading img {
vertical-align: middle;
}
+#booklist .summary {
+ margin-bottom: 2ex;
+ border-bottom: solid 1px black;
+}
+
+#booklist div.left {
+ float: left;
+ height: 190px;
+ vertical-align: middle;
+ text-align: center;
+ margin-left: 1em;
+ margin-right: 2em;
+}
+
+#booklist div.left img {
+ display: block;
+ margin-bottom: 1ex;
+}
+
+#booklist div.right {
+ height: 190px;
+ overflow: auto;
+ margin-left: 1em;
+ margin-right: 1em;
+}
+
+#booklist div.right .stars {
+ float:right;
+ margin-right: 1em;
+}
+
+#booklist div.right .stars .rating_container {
+ display: block;
+}
+
+#booklist .title {
+ font-size: larger;
+}
+
+#booklist .formats a {
+ text-decoration: none;
+}
+
+#booklist .formats a:hover {
+ color: red;
+}
+
+#booklist .left .ui-button-text {
+ font-weight: bold;
+ padding-left: 0.25em;
+ padding-right: 0.25em;
+ padding-top: 0.25em;
+ padding-bottom: 0.25em;
+}
+
/* }}} */
diff --git a/resources/content_server/browse/browse.js b/resources/content_server/browse/browse.js
index f3f278fc48..5f2af3299e 100644
--- a/resources/content_server/browse/browse.js
+++ b/resources/content_server/browse/browse.js
@@ -89,11 +89,14 @@ function render_error(msg) {
}
// Category feed {{{
+
+function category_clicked() {
+ var href = $(this).find("span.href").html();
+ window.location = href;
+}
+
function category() {
- $(".category .category-item").click(function() {
- var href = $(this).find("span.href").html();
- window.location = href;
- });
+ $(".category .category-item").click(category_clicked);
$(".category a.navlink").button();
@@ -115,6 +118,7 @@ function category() {
this.children(".loaded").html(data);
this.children(".loaded").show();
this.children(".loading").hide();
+ this.find('.category-item').click(category_clicked);
},
context: ui.newContent,
dataType: "json",
@@ -132,4 +136,45 @@ function category() {
}
// }}}
+// Booklist {{{
+function load_page(elem) {
+ var ids = elem.find(".load_data").attr('title');
+ var href = elem.find(".load_data").html();
+ $.ajax({
+ url: href,
+ context: elem,
+ dataType: "json",
+ type: 'POST',
+ timeout: 600000, //milliseconds (10 minutes)
+ data: {'ids': ids},
+ error: function(xhr, stat, err) {
+ this.children(".loaded").html(render_error(stat));
+ this.children(".loaded").show();
+ this.children(".loading").hide();
+ },
+ success: function(data) {
+ this.children(".loaded").html(data);
+ this.children(".loaded").show();
+ this.children(".loading").hide();
+ this.find(".left a.read").button();
+ }
+ });
+ $("#booklist .page").hide();
+ elem.children(".loaded").hide();
+ elem.children(".loading").show();
+ elem.show();
+}
+
+function booklist() {
+ var test = $("#booklist #page0").html();
+ if (!test) {
+ $("#booklist").html(render_error("No books found"));
+ return;
+ }
+
+ load_page($("#booklist #page0"));
+
+}
+
+// }}}
diff --git a/resources/content_server/browse/summary.html b/resources/content_server/browse/summary.html
new file mode 100644
index 0000000000..0caf710a43
--- /dev/null
+++ b/resources/content_server/browse/summary.html
@@ -0,0 +1,14 @@
+
+
+
+
{stars}{series}
+
{title}
+
{authors}
+
+
{tags}
+
{other_formats}
+
+
diff --git a/src/calibre/library/comments.py b/src/calibre/library/comments.py
index 32ae65b31e..670d9f2564 100644
--- a/src/calibre/library/comments.py
+++ b/src/calibre/library/comments.py
@@ -42,6 +42,8 @@ def comments_to_html(comments):
Deprecated HTML returns as HTML via BeautifulSoup()
'''
+ if not comments:
+ return u''
if not isinstance(comments, unicode):
comments = comments.decode(preferred_encoding, 'replace')
diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py
index ffbd958688..e1415decdb 100644
--- a/src/calibre/library/server/browse.py
+++ b/src/calibre/library/server/browse.py
@@ -14,16 +14,20 @@ import cherrypy
from calibre.constants import filesystem_encoding
from calibre import isbytestring, force_unicode, prepare_string_for_xml as xml
from calibre.utils.ordered_dict import OrderedDict
+from calibre.utils.filenames import ascii_filename
+from calibre.utils.config import prefs
+from calibre.library.comments import comments_to_html
def render_book_list(ids):
pages = []
+ num = len(ids)
while ids:
page = list(ids[:25])
pages.append(page)
ids = ids[25:]
page_template = u'''\
-
+
/browse/booklist_page

{2}
@@ -41,7 +45,7 @@ def render_book_list(ids):
{pages}
'''
- return templ.format(_('Browsing %d books')%len(ids), pages=rpages)
+ return templ.format(_('Browsing %d books')%num, pages=rpages)
def utf8(x): # {{{
if isinstance(x, unicode):
@@ -49,11 +53,13 @@ def utf8(x): # {{{
return x
# }}}
-def render_rating(rating, container='span'): # {{{
+def render_rating(rating, container='span', prefix=None): # {{{
if rating < 0.1:
return '', ''
added = 0
- rstring = xml(_('Average rating: %.1f stars')% (rating if rating else 0.0),
+ if prefix is None:
+ prefix = _('Average rating')
+ rstring = xml(_('%s: %.1f stars')% (prefix, rating if rating else 0.0),
True)
ans = ['<%s class="rating">' % (container)]
for i in range(5):
@@ -89,7 +95,7 @@ def get_category_items(category, items, db, datatype): # {{{
id_ = xml(str(id_))
desc = ''
if i.count > 0:
- desc += '[' + _('%d items')%i.count + ']'
+ desc += '[' + _('%d books')%i.count + ']'
href = '/browse/matches/%s/%s'%(category, id_)
return templ.format(xml(name), rating,
xml(desc), xml(quote(href)), rstring)
@@ -193,6 +199,14 @@ class BrowseServer(object):
self.__browse_template__ = generate()
return self.__browse_template__
+ @property
+ def browse_summary_template(self):
+ if not hasattr(self, '__browse_summary_template__') or \
+ self.opts.develop:
+ self.__browse_summary_template__ = \
+ P('content_server/browse/summary.html', data=True).decode('utf-8')
+ return self.__browse_summary_template__
+
# Catalogs {{{
def browse_toplevel(self):
@@ -329,7 +343,6 @@ class BrowseServer(object):
return json.dumps(entries, ensure_ascii=False)
-
@Endpoint()
def browse_catalog(self, category=None, category_sort=None):
'Entry point for top-level, categories and sub-categories'
@@ -401,16 +414,57 @@ class BrowseServer(object):
ids = json.loads(ids)
except:
raise cherrypy.HTTPError(404, 'invalid ids')
+ summs = []
+ for id_ in ids:
+ try:
+ id_ = int(id_)
+ mi = self.db.get_metadata(id_, index_is_id=True)
+ except:
+ continue
+ fmts = self.db.formats(id_, index_is_id=True)
+ if not fmts:
+ fmts = ''
+ fmts = [x.lower() for x in fmts.split(',') if x]
+ pf = prefs['output_format'].lower()
+ fmt = pf if pf in fmts else fmts[0]
+ args = {'id':id_, 'mi':mi, 'read_string':_('Read'),}
+ for key in mi.all_field_keys():
+ val = mi.format_field(key)[1]
+ if not val:
+ val = ''
+ args[key] = xml(val, True)
+ fname = ascii_filename(args['title']) + ' - ' + ascii_filename(args['authors'])
+ args['href'] = '/get/%s/%s_%d.%s'%(
+ fmt, fname, id_, fmt)
+ args['comments'] = comments_to_html(mi.comments)
+ args['read_tooltip'] = \
+ _('Read %s in the %s format')%(args['title'], fmt.upper())
+ args['stars'] = ''
+ if mi.rating:
+ args['stars'] = render_rating(mi.rating/2.0, prefix=_('Rating'))[0]
+ if args['tags']:
+ args['tags'] = u'%s: '%_('Tags') + args['tags']
+ args['other_formats'] = ''
+ other_fmts = [x for x in fmts if x.lower() != fmt.lower()]
+
+ if other_fmts:
+ ofmts = [u'{3}'\
+ .format(fmt, fname, id_, fmt.upper()) for fmt in
+ other_fmts]
+ ofmts = ', '.join(ofmts)
+ args['other_formats'] = u'%s: ' % \
+ _('Other formats') + ofmts
+
+
+ summs.append(self.browse_summary_template.format(**args))
+
+
+ return json.dumps('\n'.join(summs), ensure_ascii=False)
# }}}
# Search {{{
- def browse_search(self, query=None, offset=0, sort=None):
- raise NotImplementedError()
- # }}}
-
- # Book {{{
- def browse_book(self, uuid=None):
+ def browse_search(self, query=None):
raise NotImplementedError()
# }}}