From 866f7944eb459e87e65d3aec691910b8dfcd3741 Mon Sep 17 00:00:00 2001 From: Jonathan Rehm Date: Tue, 15 Aug 2017 21:31:15 -0700 Subject: [PATCH] Meta data improvements * Add buttons to show/hide results from Douban & Google * Trigger search with "Enter" key * Use Underscore.js template instead of build HTML strings in JavaScript * Keep click event handler in JavaScript instead of using HTML's `onclick` * Normalize Douban & Google results * Update cover image & add cover URL to the form input --- cps/static/css/style.css | 27 ++- cps/static/js/get_meta.js | 158 +++++++++--------- .../js/libs/bootstrap-rating-input.min.js | 3 +- cps/templates/book_edit.html | 54 +++++- 4 files changed, 153 insertions(+), 89 deletions(-) diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 39ce8008..c2f200ca 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -55,6 +55,27 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te .block-label {display: block;} .fake-input {position: absolute; pointer-events: none; top: 0;} +input.pill { position: absolute; opacity: 0; } +input.pill + label { + border: 2px solid #45b29d; + border-radius: 15px; + color: #45b29d; + cursor: pointer; + display: inline-block; + padding: 3px 15px; + user-select: none; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +input.pill:checked + label { + background-color: #45b29d; + border-color: #fff; + color: #fff; +} +input.pill:not(:checked) + label .glyphicon { + display: none; +} + .author-bio img {margin: 0 1em 1em 0;} .author-link img {display: inline-block;max-width: 100px;} @@ -63,4 +84,8 @@ span.glyphicon.glyphicon-tags {padding-right: 5px;color: #999;vertical-align: te margin-left: 5px; } -.tags_click, .serie_click, .language_click {margin-right: 5px;} \ No newline at end of file +.tags_click, .serie_click, .language_click {margin-right: 5px;} + +#meta-info img { max-height: 150px; max-width: 100px; cursor: pointer; } + +.padded-bottom { margin-bottom: 15px; } diff --git a/cps/static/js/get_meta.js b/cps/static/js/get_meta.js index c4b552b3..8dd34cdf 100644 --- a/cps/static/js/get_meta.js +++ b/cps/static/js/get_meta.js @@ -4,7 +4,7 @@ * Google Books api document: https://developers.google.com/books/docs/v1/using * Douban Books api document: https://developers.douban.com/wiki/?title=book_v2 (Chinese Only) */ - /* global i18nMsg, tinymce */ + /* global _, i18nMsg, tinymce */ var dbResults = []; var ggResults = []; @@ -23,64 +23,93 @@ $(function () { var showFlag = 0; + var templates = { + bookResult: _.template( + $("#template-book-result").html() + ) + }; + + function populateForm (book) { + tinymce.get("description").setContent(book.description); + $("#bookAuthor").val(book.authors); + $("#book_title").val(book.title); + $("#tags").val(book.tags.join(",")); + $("#rating").data("rating").setValue(Math.round(book.rating)); + $(".cover img").attr("src", book.cover); + $("#cover_url").val(book.cover); + } + function showResult () { - var book; - var i; - var bookHtml; showFlag++; if (showFlag === 1) { $("#meta-info").html(""); } if (ggDone && dbDone) { if (!ggResults && !dbResults) { - $("#meta-info").html("

"+ msg.no_result +"

"); + $("#meta-info").html("

" + msg.no_result + "

"); return; } } if (ggDone && ggResults.length > 0) { - for (i = 0; i < ggResults.length; i++) { - book = ggResults[i]; - var bookCover; - if (book.volumeInfo.imageLinks) { - bookCover = book.volumeInfo.imageLinks.thumbnail; - } else { - bookCover = "/static/generic_cover.jpg"; - } - bookHtml = "
  • " + - "\"Cover\"" + - "
    " + - "

    " + book.volumeInfo.title + "

    " + - "

    "+ msg.author +":" + book.volumeInfo.authors + "

    " + - "

    "+ msg.publisher + ":" + book.volumeInfo.publisher + "

    " + - "

    "+ msg.description + ":" + book.volumeInfo.description + "

    " + - "

    "+ msg.source + ":Google Books

    " + - "
    " + - "
  • "; - $("#book-list").append(bookHtml); - } + ggResults.forEach(function(result) { + var book = { + id: result.id, + title: result.volumeInfo.title, + authors: result.volumeInfo.authors || [], + description: result.volumeInfo.description || "", + publisher: result.volumeInfo.publisher || "", + publishedDate: result.volumeInfo.publishedDate || "", + tags: result.volumeInfo.categories || [], + rating: result.volumeInfo.averageRating || 0, + cover: result.volumeInfo.imageLinks ? + result.volumeInfo.imageLinks.thumbnail : + "/static/generic_cover.jpg", + url: "https://books.google.com/books?id=" + result.id, + source: { + id: "google", + description: "Google Books", + url: "https://books.google.com/" + } + }; + + var $book = $(templates.bookResult(book)); + $book.find("img").on("click", function () { + populateForm(book); + }); + + $("#book-list").append($book); + }); ggDone = false; } if (dbDone && dbResults.length > 0) { - for (i = 0; i < dbResults.length; i++) { - book = dbResults[i]; - bookHtml = "
  • " + - "\"Cover\"" + - "
    " + - "

    " + book.title + "

    " + - "

    " + msg.author + ":" + book.author + "

    " + - "

    " + msg.publisher + ":" + book.publisher + "

    " + - "

    " + msg.description + ":" + book.summary + "

    " + - "

    " + msg.source + ":Douban Books

    " + - "
    " + - "
  • "; - $("#book-list").append(bookHtml); - } + dbResults.forEach(function(result) { + var book = { + id: result.id, + title: result.title, + authors: result.author || [], + description: result.summary, + publisher: result.publisher || "", + publishedDate: result.pubdate || "", + tags: result.tags.map(function(tag) { + return tag.title; + }), + rating: result.rating.average || 0, + cover: result.image, + url: "https://book.douban.com/subject/" + result.id, + source: { + id: "douban", + description: "Douban Books", + url: "https://book.douban.com/" + } + }; + + var $book = $(templates.bookResult(book)); + $book.find("img").on("click", function () { + populateForm(book); + }); + + $("#book-list").append($book); + }); dbDone = false; } } @@ -97,6 +126,7 @@ $(function () { complete: function complete() { ggDone = true; showResult(); + $("#show-google").trigger("change"); } }); } @@ -111,11 +141,12 @@ $(function () { dbResults = data.books; }, error: function error() { - $("#meta-info").html("

    "+ msg.search_error+"!

    "); + $("#meta-info").html("

    " + msg.search_error + "!

    "); }, complete: function complete() { dbDone = true; showResult(); + $("#show-douban").trigger("change"); } }); } @@ -130,7 +161,8 @@ $(function () { } } - $("#do-search").click(function () { + $("#meta-search").on("submit", function (e) { + e.preventDefault(); var keyword = $("#keyword").val(); if (keyword) { doSearch(keyword); @@ -146,35 +178,3 @@ $(function () { }); }); - -// eslint-disable-next-line no-unused-vars -function getMeta (source, id) { - var meta; - var tags; - if (source === "google") { - meta = ggResults[id]; - tinymce.get("description").setContent(meta.volumeInfo.description); - $("#bookAuthor").val(meta.volumeInfo.authors.join(" & ")); - $("#book_title").val(meta.volumeInfo.title); - if (meta.volumeInfo.categories) { - tags = meta.volumeInfo.categories.join(","); - $("#tags").val(tags); - } - if (meta.volumeInfo.averageRating) { - $("#rating").val(Math.round(meta.volumeInfo.averageRating)); - } - return; - } - if (source === "douban") { - meta = dbResults[id]; - tinymce.get("description").setContent(meta.summary); - $("#bookAuthor").val(meta.author.join(" & ")); - $("#book_title").val(meta.title); - tags = ""; - for (var i = 0; i < meta.tags.length; i++) { - tags = tags + meta.tags[i].title + ","; - } - $("#tags").val(tags); - $("#rating").val(Math.round(meta.rating.average / 2)); - } -} diff --git a/cps/static/js/libs/bootstrap-rating-input.min.js b/cps/static/js/libs/bootstrap-rating-input.min.js index 0398742e..7ece4d3d 100644 --- a/cps/static/js/libs/bootstrap-rating-input.min.js +++ b/cps/static/js/libs/bootstrap-rating-input.min.js @@ -1 +1,2 @@ -!function(a){"use strict";function b(a){return"[data-value"+(a?"="+a:"")+"]"}function c(a,b,c){var d=c.activeIcon,e=c.inactiveIcon;a.removeClass(b?e:d).addClass(b?d:e)}function d(b,c){var d=a.extend({},i,b.data(),c);return d.inline=""===d.inline||d.inline,d.readonly=""===d.readonly||d.readonly,d.clearable===!1?d.clearableLabel="":d.clearableLabel=d.clearable,d.clearable=""===d.clearable||d.clearable,d}function e(b,c){if(c.inline)var d=a('');else var d=a('
    ');d.addClass(b.attr("class")),d.removeClass("rating");for(var e=c.min;e<=c.max;e++)d.append('');return c.clearable&&!c.readonly&&d.append(" ").append(''+c.clearableLabel+""),d}var f="rating-clear",g="."+f,h="hidden",i={min:1,max:5,"empty-value":0,iconLib:"glyphicon",activeIcon:"glyphicon-star",inactiveIcon:"glyphicon-star-empty",clearable:!1,clearableIcon:"glyphicon-remove",clearableRemain:!1,inline:!1,readonly:!1},j=function(a,b){var c=this.$input=a;this.options=d(c,b);var f=this.$el=e(c,this.options);c.addClass(h).before(f),c.attr("type","hidden"),this.highlight(c.val())};j.VERSION="0.4.0",j.DEFAULTS=i,j.prototype={clear:function(){this.setValue(this.options["empty-value"])},setValue:function(a){this.highlight(a),this.updateInput(a)},highlight:function(a,d){var e=this.options,f=this.$el;if(a>=this.options.min&&a<=this.options.max){var i=f.find(b(a));c(i.prevAll("i").andSelf(),!0,e),c(i.nextAll("i"),!1,e)}else c(f.find(b()),!1,e);d||(this.options.clearableRemain?f.find(g).removeClass(h):a&&a!=this.options["empty-value"]?f.find(g).removeClass(h):f.find(g).addClass(h))},updateInput:function(a){var b=this.$input;b.val()!=a&&b.val(a).change()}};var k=a.fn.rating=function(c){return this.filter("input[type=number]").each(function(){var d=a(this),e="object"==typeof c&&c||{},f=new j(d,e);f.options.readonly||f.$el.on("mouseenter",b(),function(){f.highlight(a(this).data("value"),!0)}).on("mouseleave",b(),function(){f.highlight(d.val(),!0)}).on("click",b(),function(){f.setValue(a(this).data("value"))}).on("click",g,function(){f.clear()})})};k.Constructor=j,a(function(){a("input.rating[type=number]").each(function(){a(this).rating()})})}(jQuery); \ No newline at end of file +/** @link https://github.com/javiertoledo/bootstrap-rating-input */ +!function(a){"use strict";function n(a){return"[data-value"+(a?"="+a:"")+"]"}function e(a,n,e){var i=e.activeIcon,t=e.inactiveIcon;a.removeClass(n?t:i).addClass(n?i:t)}function i(n,e){var i=a.extend({},s,n.data(),e);return i.inline=""===i.inline||i.inline,i.readonly=""===i.readonly||i.readonly,!1===i.clearable?i.clearableLabel="":i.clearableLabel=i.clearable,i.clearable=""===i.clearable||i.clearable,i}function t(n,e){if(e.inline)i=a('');else var i=a('
    ');i.addClass(n.attr("class")),i.removeClass("rating");for(var t=e.min;t<=e.max;t++)i.append('');return e.clearable&&!e.readonly&&i.append(" ").append(''+e.clearableLabel+""),i}var l="rating-clear",o="."+l,s={min:1,max:5,"empty-value":0,iconLib:"glyphicon",activeIcon:"glyphicon-star",inactiveIcon:"glyphicon-star-empty",clearable:!1,clearableIcon:"glyphicon-remove",clearableRemain:!1,inline:!1,readonly:!1},r=function(a,n){var e=this.$input=a;this.options=i(e,n);var l=this.$el=t(e,this.options);e.addClass("hidden").before(l),e.attr("type","hidden"),this.highlight(e.val())};r.VERSION="0.4.0",r.DEFAULTS=s,r.prototype={clear:function(){this.setValue(this.options["empty-value"])},setValue:function(a){this.highlight(a),this.updateInput(a)},highlight:function(a,i){var t=this.options,l=this.$el;if(a>=this.options.min&&a<=this.options.max){var s=l.find(n(a));e(s.prevAll("i").addBack(),!0,t),e(s.nextAll("i"),!1,t)}else e(l.find(n()),!1,t);i||(this.options.clearableRemain?l.find(o).removeClass("hidden"):a&&a!=this.options["empty-value"]?l.find(o).removeClass("hidden"):l.find(o).addClass("hidden"))},updateInput:function(a){var n=this.$input;n.val()!=a&&n.val(a).change()}},(a.fn.rating=function(e){return this.filter("input[type=number]").each(function(){var i=a(this),t=new r(i,"object"==typeof e&&e||{});t.options.readonly||(t.$el.on("mouseenter",n(),function(){t.highlight(a(this).data("value"),!0)}).on("mouseleave",n(),function(){t.highlight(i.val(),!0)}).on("click",n(),function(){t.setValue(a(this).data("value"))}).on("click",o,function(){t.clear()}),i.data("rating",t))})}).Constructor=r,a(function(){a("input.rating[type=number]").each(function(){a(this).rating()})})}(jQuery); diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index f124a988..36e31f5e 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -154,22 +154,35 @@ {% endif %}