diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..ed5f1fba --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space + +[*.{js,py}] +indent_size = 4 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..eb405572 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,96 @@ +{ + "env": { + "browser": true, + "jquery": true + }, + "globals": { + "alert": true + }, + "rules": { + "arrow-parens": 2, + "block-scoped-var": 1, + "brace-style": 2, + "camelcase": 1, + "comma-spacing": 2, + "curly": [2, "multi-line", "consistent"], + "eqeqeq": 2, + "indent": [ + 2, + 4, + { + "SwitchCase": 1 + } + ], + "keyword-spacing": 2, + "linebreak-style": 2, + "new-cap": 2, + "no-dupe-args": 2, + "no-dupe-class-members": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-caller": 2, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-const-assign": 2, + "no-console": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-empty": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-boolean-cast": 2, + "no-extra-semi": 2, + "no-fallthrough": [ + 2, + { + "commentPattern": "break[\\s\\w]*omitted" + } + ], + "no-implied-eval": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-loop-func": 2, + "no-mixed-operators": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-str": 2, + "no-new": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-script-url": 2, + "no-sparse-arrays": 2, + "no-undef": 2, + "no-undefined": 2, + "no-unreachable": 2, + "no-unsafe-negation": 2, + "no-unused-vars": 2, + "no-use-before-define": [ + 2, + { + "classes": false, + "functions": false + } + ], + "quotes": [ + 2, + "double" + ], + "require-yield": 2, + "semi": [ + 2, + "always" + ], + "space-before-blocks": 2, + "space-infix-ops": 2, + "space-unary-ops": 2, + "use-isnan": 2, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "any" + ], + "yield-star-spacing": 2 + } +} diff --git a/.gitignore b/.gitignore index be017750..15411480 100644 --- a/.gitignore +++ b/.gitignore @@ -27,5 +27,4 @@ tags settings.yaml gdrive_credentials -#kindlegen -vendor/kindlegen +vendor diff --git a/cps/cache_buster.py b/cps/cache_buster.py new file mode 100644 index 00000000..2e2b9869 --- /dev/null +++ b/cps/cache_buster.py @@ -0,0 +1,60 @@ +# Inspired by https://github.com/ChrisTM/Flask-CacheBust +# Uses query strings so CSS font files are found without having to resort to absolute URLs + +import hashlib +import os + + +def init_cache_busting(app): + """ + Configure `app` to so that `url_for` adds a unique query string to URLs generated + for the `'static'` endpoint. + + This allows setting long cache expiration values on static resources + because whenever the resource changes, so does its URL. + """ + + static_folder = os.path.join(app.static_folder, '') # path to the static file folder, with trailing slash + + hash_table = {} # map of file hashes + + app.logger.debug('Computing cache-busting values...') + # compute file hashes + for dirpath, dirnames, filenames in os.walk(static_folder): + for filename in filenames: + # compute version component + rooted_filename = os.path.join(dirpath, filename) + with open(rooted_filename, 'r') as f: + file_hash = hashlib.md5(f.read()).hexdigest()[:7] + + # save version to tables + file_path = rooted_filename.replace(static_folder, "") + file_path = file_path.replace("\\", "/") # Convert Windows path to web path + hash_table[file_path] = file_hash + app.logger.debug('Finished computing cache-busting values') + + def bust_filename(filename): + return hash_table.get(filename, "") + + def unbust_filename(filename): + return filename.split("?", 1)[0] + + @app.url_defaults + def reverse_to_cache_busted_url(endpoint, values): + """ + Make `url_for` produce busted filenames when using the 'static' endpoint. + """ + if endpoint == "static": + file_hash = bust_filename(values["filename"]) + if file_hash: + values["q"] = file_hash + + def debusting_static_view(filename): + """ + Serve a request for a static file having a busted name. + """ + return original_static_view(filename=unbust_filename(filename)) + + # Replace the default static file view with our debusting view. + original_static_view = app.view_functions["static"] + app.view_functions["static"] = debusting_static_view diff --git a/cps/epub.py b/cps/epub.py index 50714b6f..dd9ad28b 100644 --- a/cps/epub.py +++ b/cps/epub.py @@ -43,13 +43,16 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): epub_metadata = {} - for s in ['title', 'description', 'creator', 'language']: + for s in ['title', 'description', 'creator', 'language', 'subject']: tmp = p.xpath('dc:%s/text()' % s, namespaces=ns) if len(tmp) > 0: epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0] else: epub_metadata[s] = "Unknown" + if epub_metadata['subject'] == "Unknown": + epub_metadata['subject'] = '' + if epub_metadata['description'] == "Unknown": description = tree.xpath("//*[local-name() = 'description']/text()") if len(description) > 0: @@ -68,6 +71,18 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): else: epub_metadata['language'] = "" + series = tree.xpath("/pkg:package/pkg:metadata/pkg:meta[@name='calibre:series']/@content", namespaces=ns) + if len(series) > 0: + epub_metadata['series'] = series[0] + else: + epub_metadata['series'] = '' + + series_id = tree.xpath("/pkg:package/pkg:metadata/pkg:meta[@name='calibre:series_index']/@content", namespaces=ns) + if len(series_id) > 0: + epub_metadata['series_id'] = series_id[0] + else: + epub_metadata['series_id'] = '1' + coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover-image']/@href", namespaces=ns) coverfile = None if len(coversection) > 0: @@ -101,7 +116,7 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): author=epub_metadata['creator'].encode('utf-8').decode('utf-8'), cover=coverfile, description=epub_metadata['description'], - tags="", - series="", - series_id="", + tags=epub_metadata['subject'].encode('utf-8').decode('utf-8'), + series=epub_metadata['series'].encode('utf-8').decode('utf-8'), + series_id=epub_metadata['series_id'].encode('utf-8').decode('utf-8'), languages=epub_metadata['language']) diff --git a/cps/helper.py b/cps/helper.py index e513ad8b..0c38b684 100755 --- a/cps/helper.py +++ b/cps/helper.py @@ -278,7 +278,7 @@ def get_valid_filename(value, replace_whitespace=True): else: value = unicode(re_slugify.sub('', value).strip()) if replace_whitespace: - #*+:\"/<>? werden durch _ ersetzt + #*+:\"/<>? are replaced by _ value = re.sub('[\*\+:\\\"/<>\?]+', u'_', value, flags=re.U) value = value[:128] diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 87f7b4c5..673623c5 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -55,10 +55,38 @@ 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;} +.author-link {display: inline-block; margin-top: 10px; width: 100px;} +.author-link img {display: block; height: 100%;} #remove-from-shelves .btn, #shelf-action-errors { margin-left: 5px; } + +.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/details.js b/cps/static/js/details.js index 74dbb08e..33335339 100644 --- a/cps/static/js/details.js +++ b/cps/static/js/details.js @@ -1,4 +1,6 @@ -$( document ).ready(function() { +/* global _ */ + +$(function() { $("#have_read_form").ajaxForm(); }); @@ -6,34 +8,51 @@ $("#have_read_cb").on("change", function() { $(this).closest("form").submit(); }); -$("#shelf-actions").on("click", "[data-shelf-action]", function (e) { - e.preventDefault(); +(function() { + var templates = { + add: _.template( + $("#template-shelf-add").html() + ), + remove: _.template( + $("#template-shelf-remove").html() + ) + }; + + $("#shelf-actions").on("click", "[data-shelf-action]", function (e) { + e.preventDefault(); - $.get(this.href) - .done(() => { - const $this = $(this); - switch ($this.data("shelf-action")) { - case "add": - $("#remove-from-shelves").append(` ${this.textContent}`); - break; - case "remove": - $("#add-to-shelves").append(`
  • ${this.textContent}
  • `); - break; - } - this.parentNode.removeChild(this); - }) - .fail((xhr) => { - const $msg = $("", { "class": "text-danger"}).text(xhr.responseText); - $("#shelf-action-status").html($msg); + $.get(this.href) + .done(function() { + var $this = $(this); + switch ($this.data("shelf-action")) { + case "add": + $("#remove-from-shelves").append( + templates.remove({ + add: this.href, + remove: $this.data("remove-href"), + content: this.textContent + }) + ); + break; + case "remove": + $("#add-to-shelves").append( + templates.add({ + add: $this.data("add-href"), + remove: this.href, + content: this.textContent + }) + ); + break; + } + this.parentNode.removeChild(this); + }.bind(this)) + .fail(function(xhr) { + var $msg = $("", { "class": "text-danger"}).text(xhr.responseText); + $("#shelf-action-status").html($msg); - setTimeout(() => { - $msg.remove(); - }, 10000); - }); -}); \ No newline at end of file + setTimeout(function() { + $msg.remove(); + }, 10000); + }); + }); +})(); diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index 5438e7b0..18acccc1 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -3,6 +3,7 @@ */ /* global Bloodhound, language, Modernizr, tinymce */ +if ($("#description").length) { tinymce.init({ selector: "#description", branding: false, @@ -10,6 +11,7 @@ tinymce.init({ language }); + if (!Modernizr.inputtypes.date) { $("#pubdate").datepicker({ format: "yyyy-mm-dd", @@ -26,13 +28,13 @@ if (!Modernizr.inputtypes.date) { .removeClass("hidden"); }).trigger("change"); } - +} /* Takes a prefix, query typeahead callback, Bloodhound typeahead adapter and returns the completions it gets from the bloodhound engine prefixed. */ function prefixedSource(prefix, query, cb, bhAdapter) { - bhAdapter(query, function(retArray){ + bhAdapter(query, function(retArray) { var matches = []; for (var i = 0; i < retArray.length; i++) { var obj = {name : prefix + retArray[i].name}; @@ -41,7 +43,7 @@ function prefixedSource(prefix, query, cb, bhAdapter) { cb(matches); }); } -function getPath(){ +function getPath() { var jsFileLocation = $("script[src*=edit_books]").attr("src"); // the js file path jsFileLocation = jsFileLocation.replace("/static/js/edit_books.js", ""); // the js folder path return jsFileLocation; @@ -49,7 +51,7 @@ function getPath(){ var authors = new Bloodhound({ name: "authors", - datumTokenizer(datum) { + datumTokenizer: function datumTokenizer(datum) { return [datum.name]; }, queryTokenizer: Bloodhound.tokenizers.whitespace, @@ -60,15 +62,15 @@ var authors = new Bloodhound({ var series = new Bloodhound({ name: "series", - datumTokenizer(datum) { + datumTokenizer: function datumTokenizer(datum) { return [datum.name]; }, - queryTokenizer(query) { + queryTokenizer: function queryTokenizer(query) { return [query]; }, remote: { url: getPath()+"/get_series_json?q=", - replace(url, query) { + replace: function replace(url, query) { return url+encodeURIComponent(query); } } @@ -77,10 +79,10 @@ var series = new Bloodhound({ var tags = new Bloodhound({ name: "tags", - datumTokenizer(datum) { + datumTokenizer: function datumTokenizer(datum) { return [datum.name]; }, - queryTokenizer(query) { + queryTokenizer: function queryTokenizer(query) { var tokens = query.split(","); tokens = [tokens[tokens.length-1].trim()]; return tokens; @@ -92,15 +94,15 @@ var tags = new Bloodhound({ var languages = new Bloodhound({ name: "languages", - datumTokenizer(datum) { + datumTokenizer: function datumTokenizer(datum) { return [datum.name]; }, - queryTokenizer(query) { + queryTokenizer: function queryTokenizer(query) { return [query]; }, remote: { url: getPath()+"/get_languages_json?q=", - replace(url, query) { + replace: function replace(url, query) { return url+encodeURIComponent(query); } } @@ -115,9 +117,9 @@ function sourceSplit(query, cb, split, source) { tokens.splice(tokens.length-1, 1); // remove last element var prefix = ""; var newSplit; - if (split === "&"){ + if (split === "&") { newSplit = " " + split + " "; - }else{ + } else { newSplit = split + " "; } for (var i = 0; i < tokens.length; i++) { @@ -127,76 +129,78 @@ function sourceSplit(query, cb, split, source) { } var promiseAuthors = authors.initialize(); - promiseAuthors.done(function(){ +promiseAuthors.done(function() { $("#bookAuthor").typeahead( - { - highlight: true, minLength: 1, - hint: true - }, { - name: "authors", - displayKey: "name", - source(query, cb){ - return sourceSplit(query, cb, "&", authors); //sourceSplit //("&") + { + highlight: true, minLength: 1, + hint: true + }, { + name: "authors", + displayKey: "name", + source: function source(query, cb) { + return sourceSplit(query, cb, "&", authors); //sourceSplit //("&") } - }); + } + ); }); var promiseSeries = series.initialize(); - promiseSeries.done(function(){ +promiseSeries.done(function() { $("#series").typeahead( - { - highlight: true, minLength: 0, - hint: true - }, { - name: "series", - displayKey: "name", - source: series.ttAdapter() - } + { + highlight: true, minLength: 0, + hint: true + }, { + name: "series", + displayKey: "name", + source: series.ttAdapter() + } ); }); var promiseTags = tags.initialize(); - promiseTags.done(function(){ +promiseTags.done(function() { $("#tags").typeahead( - { - highlight: true, minLength: 0, - hint: true - }, { - name: "tags", - displayKey: "name", - source(query, cb){ - return sourceSplit(query, cb, ",", tags); - } - }); - }); + { + highlight: true, minLength: 0, + hint: true + }, { + name: "tags", + displayKey: "name", + source: function source(query, cb) { + return sourceSplit(query, cb, ",", tags); + } + } + ); +}); var promiseLanguages = languages.initialize(); - promiseLanguages.done(function(){ +promiseLanguages.done(function() { $("#languages").typeahead( - { - highlight: true, minLength: 0, - hint: true - }, { - name: "languages", - displayKey: "name", - source(query, cb){ - return sourceSplit(query, cb, ",", languages); //(",") - } - }); - }); + { + highlight: true, minLength: 0, + hint: true + }, { + name: "languages", + displayKey: "name", + source: function source(query, cb) { + return sourceSplit(query, cb, ",", languages); //(",") + } + } + ); +}); -$("#search").on("change input.typeahead:selected", function(){ +$("#search").on("change input.typeahead:selected", function() { var form = $("form").serialize(); $.getJSON( getPath()+"/get_matching_tags", form, function( data ) { - $(".tags_click").each(function() { - if ($.inArray(parseInt($(this).children("input").first().val(), 10), data.tags) === -1 ) { - if (!($(this).hasClass("active"))) { - $(this).addClass("disabled"); - } - } - else { - $(this).removeClass("disabled"); - } - }); + $(".tags_click").each(function() { + if ($.inArray(parseInt($(this).children("input").first().val(), 10), data.tags) === -1 ) { + if (!($(this).hasClass("active"))) { + $(this).addClass("disabled"); + } + } else { + $(this).removeClass("disabled"); + } + }); }); }); diff --git a/cps/static/js/get_meta.js b/cps/static/js/get_meta.js index 829c36b1..8dd34cdf 100644 --- a/cps/static/js/get_meta.js +++ b/cps/static/js/get_meta.js @@ -4,11 +4,11 @@ * 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 = []; -$(document).ready(function () { +$(function () { var msg = i18nMsg; var douban = "https://api.douban.com"; var dbSearch = "/v2/book/search"; @@ -22,113 +22,138 @@ $(document).ready(function () { var ggDone = false; var showFlag = 0; - String.prototype.replaceAll = function (s1, s2) { - return this.replace(new RegExp(s1, "gm"), s2); + + 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) { - $("#metaModal #meta-info").html(""); + $("#meta-info").html(""); } if (ggDone && dbDone) { if (!ggResults && !dbResults) { - $("#metaModal #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

    " + - "
    " + - "
  • "; - $("#metaModal #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

    " + - "
    " + - "
  • "; - $("#metaModal #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; } } function ggSearchBook (title) { - title = title.replaceAll(/\s+/, "+"); - var url = google + ggSearch + "?q=" + title; $.ajax({ - url, + url: google + ggSearch + "?q=" + title.replace(/\s+/gm, "+"), type: "GET", dataType: "jsonp", jsonp: "callback", - success (data) { + success: function success(data) { ggResults = data.items; }, - complete () { + complete: function complete() { ggDone = true; showResult(); + $("#show-google").trigger("change"); } }); } function dbSearchBook (title) { - var url = douban + dbSearch + "?q=" + title + "&fields=all&count=10"; $.ajax({ - url, + url: douban + dbSearch + "?q=" + title + "&fields=all&count=10", type: "GET", dataType: "jsonp", jsonp: "callback", - success (data) { + success: function success(data) { dbResults = data.books; }, - error () { - $("#metaModal #meta-info").html("

    "+ msg.search_error+"!

    "); + error: function error() { + $("#meta-info").html("

    " + msg.search_error + "!

    "); }, - complete () { + complete: function complete() { dbDone = true; showResult(); + $("#show-douban").trigger("change"); } }); } function doSearch (keyword) { showFlag = 0; - $("#metaModal #meta-info").text(msg.loading); + $("#meta-info").text(msg.loading); // var keyword = $("#keyword").val(); if (keyword) { dbSearchBook(keyword); @@ -136,7 +161,8 @@ $(document).ready(function () { } } - $("#do-search").click(function () { + $("#meta-search").on("submit", function (e) { + e.preventDefault(); var keyword = $("#keyword").val(); if (keyword) { doSearch(keyword); @@ -152,35 +178,3 @@ $(document).ready(function () { }); }); - -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)); - return; - } -} 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/static/js/main.js b/cps/static/js/main.js index 7b7402b2..267c6d97 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -1,7 +1,3 @@ -var displaytext; -var updateTimerID; -var updateText; - // Generic control/related handler to show/hide fields based on a checkbox' value // e.g. // @@ -11,16 +7,18 @@ $(document).on("change", "input[type=\"checkbox\"][data-control]", function () { var name = $this.data("control"); var showOrHide = $this.prop("checked"); - $("[data-related=\""+name+"\"]").each(function () { + $("[data-related=\"" + name + "\"]").each(function () { $(this).toggle(showOrHide); }); }); $(function() { + var updateTimerID; + var updateText; // Allow ajax prefilters to be added/removed dynamically // eslint-disable-next-line new-cap - const preFilters = $.Callbacks(); + var preFilters = $.Callbacks(); $.ajaxPrefilter(preFilters.fire); function restartTimer() { @@ -30,29 +28,29 @@ $(function() { function updateTimer() { $.ajax({ - dataType: "json", - url: window.location.pathname+"/../../get_updater_status", - success(data) { - // console.log(data.status); - $("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]); - if (data.status >6){ + dataType: "json", + url: window.location.pathname + "/../../get_updater_status", + success: function success(data) { + // console.log(data.status); + $("#Updatecontent").html(updateText[data.status]); + if (data.status > 6) { + clearInterval(updateTimerID); + $("#spinner2").hide(); + $("#updateFinished").removeClass("hidden"); + $("#check_for_update").removeClass("hidden"); + $("#perform_update").addClass("hidden"); + } + }, + error: function error() { + // console.log('Done'); clearInterval(updateTimerID); $("#spinner2").hide(); - $("#UpdateprogressDialog #updateFinished").removeClass("hidden"); + $("#Updatecontent").html(updateText[7]); + $("#updateFinished").removeClass("hidden"); $("#check_for_update").removeClass("hidden"); $("#perform_update").addClass("hidden"); - } - }, - error() { - // console.log('Done'); - clearInterval(updateTimerID); - $("#spinner2").hide(); - $("#UpdateprogressDialog #Updatecontent").html(updateText[7]); - $("#UpdateprogressDialog #updateFinished").removeClass("hidden"); - $("#check_for_update").removeClass("hidden"); - $("#perform_update").addClass("hidden"); }, - timeout:2000 + timeout: 2000 }); } @@ -70,13 +68,13 @@ $(function() { // selector for the NEXT link (to page 2) itemSelector : ".load-more .book", animate : true, - extraScrollPx: 300, + extraScrollPx: 300 // selector for all items you'll retrieve - }, function(data){ + }, function(data) { $(".load-more .row").isotope( "appended", $(data), null ); }); - $("#sendbtn").click(function(){ + $("#sendbtn").click(function() { var $this = $(this); $this.text("Please wait..."); $this.addClass("disabled"); @@ -84,36 +82,39 @@ $(function() { $("#restart").click(function() { $.ajax({ dataType: "json", - url: window.location.pathname+"/../../shutdown", + url: window.location.pathname + "/../../shutdown", data: {"parameter":0}, - success(data) { + success: function success() { $("#spinner").show(); - displaytext=data.text; - setTimeout(restartTimer, 3000);} + setTimeout(restartTimer, 3000); + } }); }); $("#shutdown").click(function() { $.ajax({ dataType: "json", - url: window.location.pathname+"/../../shutdown", + url: window.location.pathname + "/../../shutdown", data: {"parameter":1}, - success(data) { - return alert(data.text);} + success: function success(data) { + return alert(data.text); + } }); }); $("#check_for_update").click(function() { - var buttonText = $("#check_for_update").html(); - $("#check_for_update").html("..."); + var $this = $(this); + var buttonText = $this.html(); + $this.html("..."); $.ajax({ dataType: "json", - url: window.location.pathname+"/../../get_update_status", - success(data) { - $("#check_for_update").html(buttonText); + url: window.location.pathname + "/../../get_update_status", + success: function success(data) { + $this.html(buttonText); if (data.status === true) { $("#check_for_update").addClass("hidden"); $("#perform_update").removeClass("hidden"); - $("#update_info").removeClass("hidden"); - $("#update_info").find("span").html(data.commit); + $("#update_info") + .removeClass("hidden") + .find("span").html(data.commit); } } }); @@ -121,22 +122,23 @@ $(function() { $("#restart_database").click(function() { $.ajax({ dataType: "json", - url: window.location.pathname+"/../../shutdown", + url: window.location.pathname + "/../../shutdown", data: {"parameter":2} }); }); $("#perform_update").click(function() { $("#spinner2").show(); $.ajax({ - type: "POST", - dataType: "json", - data: { start: "True"}, - url: window.location.pathname+"/../../get_updater_status", - success(data) { - updateText=data.text; - $("#UpdateprogressDialog #Updatecontent").html(updateText[data.status]); - // console.log(data.status); - updateTimerID=setInterval(updateTimer, 2000);} + type: "POST", + dataType: "json", + data: { start: "True"}, + url: window.location.pathname + "/../../get_updater_status", + success: function success(data) { + updateText = data.text; + $("#Updatecontent").html(updateText[data.status]); + // console.log(data.status); + updateTimerID = setInterval(updateTimer, 2000); + } }); }); @@ -144,10 +146,10 @@ $(function() { $("#bookDetailsModal") .on("show.bs.modal", function(e) { - const $modalBody = $(this).find(".modal-body"); + var $modalBody = $(this).find(".modal-body"); // Prevent static assets from loading multiple times - const useCache = (options) => { + var useCache = function(options) { options.async = true; options.cache = true; }; @@ -162,7 +164,7 @@ $(function() { $(this).find(".modal-body").html("..."); }); - $(window).resize(function(event) { + $(window).resize(function() { $(".discover .row").isotope("reLayout"); }); -}); \ No newline at end of file +}); diff --git a/cps/static/js/shelforder.js b/cps/static/js/shelforder.js index 5ae3acbf..36390fb3 100644 --- a/cps/static/js/shelforder.js +++ b/cps/static/js/shelforder.js @@ -1,31 +1,31 @@ /* global Sortable,sortTrue */ -var sortable = Sortable.create(sortTrue, { - group: "sorting", - sort: true +Sortable.create(sortTrue, { + group: "sorting", + sort: true }); -function sendData(path){ +// eslint-disable-next-line no-unused-vars +function sendData(path) { var elements; var counter; var maxElements; - var tmp=[]; + var tmp = []; - elements=Sortable.utils.find(sortTrue,"div"); - maxElements=elements.length; + elements = Sortable.utils.find(sortTrue, "div"); + maxElements = elements.length; var form = document.createElement("form"); form.setAttribute("method", "post"); form.setAttribute("action", path); - - for(counter=0;counter{{author.about|safe}}

    {% endif %} - - - Goodreads - + - {{_("via")}} Goodreads +
    {% endif %}
    + {% if author is not none %} +

    {{_("In Library")}}

    + {% endif %}
    {% if entries[0] %} {% for entry in entries %} @@ -62,4 +63,48 @@ {% endif %}
    + +{% if other_books is not none %} +
    +

    {{_("More by")}} {{ author.name|safe }}

    +
    + {% for entry in other_books %} +
    +
    + + + +
    +
    +

    {{entry.title|shortentitle}}

    +

    + {% for author in entry.authors %} + + {{author.name}} + + {% if not loop.last %} + & + {% endif %} + {% endfor %} +

    +
    + {% for number in range((entry.average_rating)|float|round|int(2)) %} + + {% if loop.last and loop.index < 5 %} + {% for numer in range(5 - loop.index) %} + + {% endfor %} + {% endif %} + {% endfor %} +
    +
    +
    + {% endfor %} +
    + + + Goodreads + +
    +{% endif %} {% endblock %} diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index 9bd1e644..36e31f5e 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -139,7 +139,7 @@ -