From 097ac879eabb57b7dcf310dbe0bae956e2512013 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Mon, 12 Oct 2020 09:31:58 +0200 Subject: [PATCH 01/10] render read --- cps/static/css/style.css | 23 +++++++++++++++++++---- cps/templates/grid.html | 6 ++++-- cps/templates/index.html | 3 +++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 2294d326..6b3cba15 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -123,12 +123,19 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d position: relative; } -.container-fluid .book .cover img { +.container-fluid .book .cover span.img { + bottom: 0; + height: 100%; + position: absolute; +} + +.container-fluid .book .cover span img { + position: relative; + top: 0; + left: 0; + height: 100%; border: 1px solid #fff; box-sizing: border-box; - height: 100%; - bottom: 0; - position: absolute; -webkit-box-shadow: 0 5px 8px -6px #777; -moz-box-shadow: 0 5px 8px -6px #777; box-shadow: 0 5px 8px -6px #777; @@ -203,6 +210,14 @@ span.glyphicon.glyphicon-tags { left: 2px; background-color: #777; } +.cover .read{ + left: auto; + right: 2px; + width: 17px; + height: 17px; + display: inline-block; + padding: 2px; +} .cover-height { max-height: 100px;} .col-sm-2 a .cover-small { diff --git a/cps/templates/grid.html b/cps/templates/grid.html index 4aa0b7df..c44af387 100644 --- a/cps/templates/grid.html +++ b/cps/templates/grid.html @@ -28,8 +28,10 @@
- {{ entry[0].name }} - {{entry.count}} + + {{ entry[0].name }} + {{entry.count}} +
diff --git a/cps/templates/index.html b/cps/templates/index.html index e60f92fe..319fd60f 100644 --- a/cps/templates/index.html +++ b/cps/templates/index.html @@ -79,7 +79,10 @@
From 754b9832e95081b22e22e91dd57b65cf595adf37 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 17 Oct 2020 13:30:19 +0200 Subject: [PATCH 02/10] fix height container --- cps/static/css/style.css | 1 + 1 file changed, 1 insertion(+) diff --git a/cps/static/css/style.css b/cps/static/css/style.css index 6b3cba15..abe0ca48 100644 --- a/cps/static/css/style.css +++ b/cps/static/css/style.css @@ -111,6 +111,7 @@ a, .danger,.book-remove, .editable-empty, .editable-empty:hover { color: #45b29d display: block; max-width: 100%; height: auto; + max-height: 100%; } .container-fluid .discover{ margin-bottom: 50px; } From b2594468b41b3ce99f732543bd79ee13a4463258 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 17 Oct 2020 16:49:06 +0200 Subject: [PATCH 03/10] add helper to get all read books --- cps/helper.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cps/helper.py b/cps/helper.py index 82d0b232..71dd2cc4 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -819,3 +819,18 @@ def get_download_link(book_id, book_format, client): return do_download_file(book, book_format, client, data1, headers) else: abort(404) + + +def get_readbooks_ids(): + if not config.config_read_column: + readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id))\ + .filter(ub.ReadBook.is_read == True).all() + return frozenset([x.book_id for x in readBooks]) + else: + try: + readBooks = calibre_db.session.query(db.cc_classes[config.config_read_column])\ + .filter(db.cc_classes[config.config_read_column].value == True).all() + return frozenset([x.book for x in readBooks]) + except KeyError: + log.error("Custom Column No.%d is not existing in calibre database", config.config_read_column) + return [] \ No newline at end of file From 7d28963a32769f9749d2478241281422e3809bee Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 17 Oct 2020 16:49:38 +0200 Subject: [PATCH 04/10] connect read books to all render --- cps/web.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cps/web.py b/cps/web.py index aa1cac1a..a3eb37d5 100644 --- a/cps/web.py +++ b/cps/web.py @@ -59,7 +59,8 @@ from . import calibre_db from .gdriveutils import getFileFromEbooksFolder, do_gdrive_download from .helper import check_valid_domain, render_task_status, \ get_cc_columns, get_book_cover, get_download_link, send_mail, generate_random_password, \ - send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password + send_registration_mail, check_send_to_kindle, check_read_formats, tags_filters, reset_password, \ + get_readbooks_ids from .pagination import Pagination from .redirect import redirect_back @@ -602,7 +603,7 @@ def get_matching_tags(): def render_title_template(*args, **kwargs): sidebar = ub.get_sidebar_config(kwargs) return render_template(instance=config.config_calibre_web_title, sidebar=sidebar, - accept=constants.EXTENSIONS_UPLOAD, + accept=constants.EXTENSIONS_UPLOAD, read_book_ids=get_readbooks_ids(), *args, **kwargs) From 4d81d3613ca7baa9416c82193186efc4f31b8e99 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Sat, 17 Oct 2020 16:49:57 +0200 Subject: [PATCH 05/10] display the check when books are read --- cps/templates/author.html | 5 ++++- cps/templates/discover.html | 5 ++++- cps/templates/index.html | 7 +++++-- cps/templates/search.html | 5 ++++- cps/templates/shelf.html | 5 ++++- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/cps/templates/author.html b/cps/templates/author.html index 0015d6e2..fadc9a04 100644 --- a/cps/templates/author.html +++ b/cps/templates/author.html @@ -40,7 +40,10 @@
diff --git a/cps/templates/discover.html b/cps/templates/discover.html index 9abe3666..ed95bc03 100644 --- a/cps/templates/discover.html +++ b/cps/templates/discover.html @@ -8,7 +8,10 @@
{% if entry.has_cover is defined %} - {{ entry.title }} + + {{ entry.title }} + {% if entry.id in read_book_ids %}{% endif %} + {% endif %}
diff --git a/cps/templates/index.html b/cps/templates/index.html index 319fd60f..4f341e1e 100644 --- a/cps/templates/index.html +++ b/cps/templates/index.html @@ -8,7 +8,10 @@
diff --git a/cps/templates/search.html b/cps/templates/search.html index cba430a4..e619e856 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -41,7 +41,10 @@
{% if entry.has_cover is defined %} - {{ entry.title }} + + {{ entry.title }} + {% if entry.id in read_book_ids %}{% endif %} + {% endif %}
diff --git a/cps/templates/shelf.html b/cps/templates/shelf.html index a2655f96..db1c3bcf 100644 --- a/cps/templates/shelf.html +++ b/cps/templates/shelf.html @@ -18,7 +18,10 @@
From 27dcbcd7e197699a800225f8f51dd784fd89fa57 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 27 Dec 2020 10:24:51 +0100 Subject: [PATCH 06/10] paged and orderable shelfs Fix for non writable settings db with non configured calibre-web --- cps/db.py | 2 ++ cps/shelf.py | 34 ++++++++++++++++++++++++++++++---- cps/static/js/main.js | 13 +++++++++++++ cps/templates/shelf.html | 6 +++--- cps/templates/shelf_order.html | 2 +- cps/templates/shelfdown.html | 25 ++++++------------------- cps/ub.py | 20 ++++++++++++-------- 7 files changed, 67 insertions(+), 35 deletions(-) diff --git a/cps/db.py b/cps/db.py index 2e428f72..f30fb609 100644 --- a/cps/db.py +++ b/cps/db.py @@ -445,6 +445,8 @@ class CalibreDB(): cls.config = config cls.dispose() + # toDo: if db changed -> delete shelfs, delete download books, delete read boks, kobo sync?? + if not config.config_calibre_dir: config.invalidate() return False diff --git a/cps/shelf.py b/cps/shelf.py index be102ea9..6dae333c 100644 --- a/cps/shelf.py +++ b/cps/shelf.py @@ -381,27 +381,53 @@ def order_shelf(shelf_id): title=_(u"Change order of Shelf: '%(name)s'", name=shelf.name), shelf=shelf, page="shelforder") +def change_shelf_order(shelf_id, order): + result = calibre_db.session.query(db.Books).join(ub.BookShelf,ub.BookShelf.book_id == db.Books.id)\ + .filter(ub.BookShelf.shelf == shelf_id).order_by(*order).all() + for index, entry in enumerate(result): + book = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ + .filter(ub.BookShelf.book_id == entry.id).first() + book.order = index + try: + ub.session.commit() + except OperationalError: + ub.session.rollback() def render_show_shelf(shelf_type, shelf_id, page_no, sort_param): shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() # check user is allowed to access shelf if shelf and check_shelf_view_permissions(shelf): + if shelf_type == 1: + # order = [ub.BookShelf.order.asc()] + if sort_param == 'pubnew': + change_shelf_order(shelf_id, [db.Books.pubdate.desc()]) + if sort_param == 'pubold': + change_shelf_order(shelf_id, [db.Books.pubdate]) + if sort_param == 'abc': + change_shelf_order(shelf_id, [db.Books.sort]) + if sort_param == 'zyx': + change_shelf_order(shelf_id, [db.Books.sort.desc()]) + if sort_param == 'new': + change_shelf_order(shelf_id, [db.Books.timestamp.desc()]) + if sort_param == 'old': + change_shelf_order(shelf_id, [db.Books.timestamp]) + if sort_param == 'authaz': + change_shelf_order(shelf_id, [db.Books.author_sort.asc()]) + if sort_param == 'authza': + change_shelf_order(shelf_id, [db.Books.author_sort.desc()]) page = "shelf.html" pagesize = 0 - order = [ub.BookShelf.order.asc()] else: pagesize = sys.maxsize page = 'shelfdown.html' - order = [ub.BookShelf.order.asc()] result, __, pagination = calibre_db.fill_indexpage(page_no, pagesize, db.Books, ub.BookShelf.shelf == shelf_id, - order, + [ub.BookShelf.order.asc()], ub.BookShelf,ub.BookShelf.book_id == db.Books.id) - # delete chelf entries where book is not existent anymore, can happen if book is deleted outside calibre-web wrong_entries = calibre_db.session.query(ub.BookShelf)\ .join(db.Books, ub.BookShelf.book_id == db.Books.id, isouter=True)\ diff --git a/cps/static/js/main.js b/cps/static/js/main.js index d02d3b58..d891d30e 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -497,6 +497,19 @@ $(function() { ); }); + $("#toggle_order_shelf").click(function() { + $("#new").toggleClass("disabled"); + $("#old").toggleClass("disabled"); + $("#asc").toggleClass("disabled"); + $("#desc").toggleClass("disabled"); + $("#auth_az").toggleClass("disabled"); + $("#auth_za").toggleClass("disabled"); + $("#pub_new").toggleClass("disabled"); + $("#pub_old").toggleClass("disabled"); + var alternative_text = $("#toggle_order_shelf").data('alt-text'); + $("#toggle_order_shelf")[0].attributes['data-alt-text'].value = $("#toggle_order_shelf").html(); + $("#toggle_order_shelf").html(alternative_text); + }); $("#btndeluser").click(function() { ConfirmDialog( diff --git a/cps/templates/shelf.html b/cps/templates/shelf.html index 32e3da4f..2110d905 100644 --- a/cps/templates/shelf.html +++ b/cps/templates/shelf.html @@ -8,8 +8,10 @@ {% if g.user.is_authenticated %} {% if (g.user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
{{ _('Delete this Shelf') }}
- {{ _('Edit Shelf') }} + {{ _('Edit Shelf Properties') }} {% if entries.__len__() %} + {{ _('Arrange books manually') }} + {% endif %} {% endif %} diff --git a/cps/templates/shelf_order.html b/cps/templates/shelf_order.html index c5a698d2..1e49f29a 100644 --- a/cps/templates/shelf_order.html +++ b/cps/templates/shelf_order.html @@ -37,7 +37,7 @@
{% endfor %}
- + {{_('Back')}}
{% endblock %} diff --git a/cps/templates/shelfdown.html b/cps/templates/shelfdown.html index 9ec154be..77251e02 100644 --- a/cps/templates/shelfdown.html +++ b/cps/templates/shelfdown.html @@ -55,27 +55,14 @@
- {% if g.user.role_download() %} + {% if g.user.role_download() %} {% if entry.data|length %}
- {% if entry.data|length < 2 %} - - {% for format in entry.data %} - - {{format.format}} ({{ format.uncompressed_size|filesizeformat }}) - - {% endfor %} - {% else %} - - - {% endif %} + {% for format in entry.data %} + + {{format.format}} ({{ format.uncompressed_size|filesizeformat }}) + + {% endfor %}
{% endif %} {% endif %} diff --git a/cps/ub.py b/cps/ub.py index dbc3b419..f11b77b9 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -452,7 +452,7 @@ def migrate_Database(session): if not engine.dialect.has_table(engine.connect(), "archived_book"): ArchivedBook.__table__.create(bind=engine) if not engine.dialect.has_table(engine.connect(), "registration"): - ReadBook.__table__.create(bind=engine) + Registration.__table__.create(bind=engine) with engine.connect() as conn: conn.execute("insert into registration (domain, allow) values('%.%',1)") session.commit() @@ -501,12 +501,16 @@ def migrate_Database(session): for book_shelf in session.query(BookShelf).all(): book_shelf.date_added = datetime.datetime.now() session.commit() - # Handle table exists, but no content - cnt = session.query(Registration).count() - if not cnt: - with engine.connect() as conn: - conn.execute("insert into registration (domain, allow) values('%.%',1)") - session.commit() + try: + # Handle table exists, but no content + cnt = session.query(Registration).count() + if not cnt: + with engine.connect() as conn: + conn.execute("insert into registration (domain, allow) values('%.%',1)") + session.commit() + except exc.OperationalError: # Database is not writeable + print('Settings database is not writeable. Exiting...') + sys.exit(2) try: session.query(exists().where(BookShelf.order)).scalar() except exc.OperationalError: # Database is not compatible, some columns are missing @@ -591,7 +595,7 @@ def migrate_Database(session): session.commit() except exc.OperationalError: print('Settings database is not writeable. Exiting...') - sys.exit(1) + sys.exit(2) def clean_database(session): From 2a63c357436b9494db225bc41166ab1d8326f46c Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 27 Dec 2020 11:27:15 +0100 Subject: [PATCH 07/10] Activate serverside filepicker with parameter in unconfigured state --- cps/admin.py | 44 ++++++++++++++++++++-------------- cps/cli.py | 4 ++++ cps/templates/config_edit.html | 9 ++++++- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/cps/admin.py b/cps/admin.py index 5831234f..9c016e5b 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -39,6 +39,7 @@ from sqlalchemy.exc import IntegrityError, OperationalError, InvalidRequestError from sqlalchemy.sql.expression import func, or_ from . import constants, logger, helper, services +from .cli import filepicker from . import db, calibre_db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils from .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash from .gdriveutils import is_gdrive_ready, gdrive_support @@ -118,7 +119,7 @@ def before_request(): g.shelves_access = ub.session.query(ub.Shelf).filter( or_(ub.Shelf.is_public == 1, ub.Shelf.user_id == current_user.id)).order_by(ub.Shelf.name).all() if not config.db_configured and request.endpoint not in ( - 'admin.basic_configuration', 'login') and '/static/' not in request.path: + 'admin.basic_configuration', 'login', 'admin.config_pathchooser') and '/static/' not in request.path: return redirect(url_for('admin.basic_configuration')) @@ -192,7 +193,7 @@ def admin(): @admin_required def configuration(): if request.method == "POST": - return _configuration_update_helper() + return _configuration_update_helper(True) return _configuration_result() @@ -587,10 +588,11 @@ def list_restriction(res_type): return response @admi.route("/basicconfig/pathchooser/") -# @unconfigured -@login_required +@unconfigured def config_pathchooser(): - return pathchooser() + if filepicker: + return pathchooser() + abort(403) @admi.route("/ajax/pathchooser/") @login_required @@ -599,7 +601,7 @@ def ajax_pathchooser(): return pathchooser() def pathchooser(): - browse_for = "folder" # if request.endpoint == "admin.pathchooser" else "file" + browse_for = "folder" folder_only = request.args.get('folder', False) == "true" file_filter = request.args.get('filter', "") path = os.path.normpath(request.args.get('path', "")) @@ -685,8 +687,8 @@ def pathchooser(): def basic_configuration(): logout_user() if request.method == "POST": - return _configuration_update_helper() - return _configuration_result() + return _configuration_update_helper(configured=filepicker) + return _configuration_result(configured=filepicker) def _config_int(to_save, x, func=int): @@ -841,7 +843,7 @@ def _configuration_ldap_helper(to_save, gdriveError): return reboot_required, None -def _configuration_update_helper(): +def _configuration_update_helper(configured): reboot_required = False db_change = False to_save = request.form.to_dict() @@ -861,11 +863,15 @@ def _configuration_update_helper(): reboot_required |= _config_string(to_save, "config_keyfile") if config.config_keyfile and not os.path.isfile(config.config_keyfile): - return _configuration_result(_('Keyfile Location is not Valid, Please Enter Correct Path'), gdriveError) + return _configuration_result(_('Keyfile Location is not Valid, Please Enter Correct Path'), + gdriveError, + configured) reboot_required |= _config_string(to_save, "config_certfile") if config.config_certfile and not os.path.isfile(config.config_certfile): - return _configuration_result(_('Certfile Location is not Valid, Please Enter Correct Path'), gdriveError) + return _configuration_result(_('Certfile Location is not Valid, Please Enter Correct Path'), + gdriveError, + configured) _config_checkbox_int(to_save, "config_uploading") # Reboot on config_anonbrowse with enabled ldap, as decoraters are changed in this case @@ -930,10 +936,10 @@ def _configuration_update_helper(): if "config_rarfile_location" in to_save: unrar_status = helper.check_unrar(config.config_rarfile_location) if unrar_status: - return _configuration_result(unrar_status, gdriveError) + return _configuration_result(unrar_status, gdriveError, configured) except (OperationalError, InvalidRequestError): ub.session.rollback() - _configuration_result(_(u"Settings DB is not Writeable"), gdriveError) + _configuration_result(_(u"Settings DB is not Writeable"), gdriveError, configured) try: metadata_db = os.path.join(config.config_calibre_dir, "metadata.db") @@ -941,11 +947,13 @@ def _configuration_update_helper(): gdriveutils.downloadFile(None, "metadata.db", metadata_db) db_change = True except Exception as e: - return _configuration_result('%s' % e, gdriveError) + return _configuration_result('%s' % e, gdriveError, configured) if db_change: if not calibre_db.setup_db(config, ub.app_DB_path): - return _configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), gdriveError) + return _configuration_result(_('DB Location is not Valid, Please Enter Correct Path'), + gdriveError, + configured) if not os.access(os.path.join(config.config_calibre_dir, "metadata.db"), os.W_OK): flash(_(u"DB is not Writeable"), category="warning") @@ -954,10 +962,10 @@ def _configuration_update_helper(): if reboot_required: web_server.stop(True) - return _configuration_result(None, gdriveError) + return _configuration_result(None, gdriveError, configured) -def _configuration_result(error_flash=None, gdriveError=None): +def _configuration_result(error_flash=None, gdriveError=None, configured=True): gdrive_authenticate = not is_gdrive_ready() gdrivefolders = [] if gdriveError is None: @@ -978,7 +986,7 @@ def _configuration_result(error_flash=None, gdriveError=None): return render_title_template("config_edit.html", config=config, provider=oauthblueprints, show_back_button=show_back_button, show_login_button=show_login_button, - show_authenticate_google_drive=gdrive_authenticate, + show_authenticate_google_drive=gdrive_authenticate, filepicker=configured, gdriveError=gdriveError, gdrivefolders=gdrivefolders, feature_support=feature_support, title=_(u"Basic Configuration"), page="config") diff --git a/cps/cli.py b/cps/cli.py index c94cb89d..65a4185a 100644 --- a/cps/cli.py +++ b/cps/cli.py @@ -45,6 +45,7 @@ parser.add_argument('-v', '--version', action='version', help='Shows version num version=version_info()) parser.add_argument('-i', metavar='ip-address', help='Server IP-Address to listen') parser.add_argument('-s', metavar='user:pass', help='Sets specific username to new password') +parser.add_argument('-f', action='store_true', help='Enables filepicker in unconfigured mode') args = parser.parse_args() if sys.version_info < (3, 0): @@ -110,3 +111,6 @@ if ipadress: # handle and check user password argument user_password = args.s or None + +# Handles enableing of filepicker +filepicker = args.f or None diff --git a/cps/templates/config_edit.html b/cps/templates/config_edit.html index b86fd36b..cf3c7bcd 100644 --- a/cps/templates/config_edit.html +++ b/cps/templates/config_edit.html @@ -16,12 +16,19 @@
-
+
+ {% if filepicker %} + {% endif %}
+ {% if not filepicker %} +
+ +
+ {% endif %} {% if feature_support['gdrive'] %}
From 1e351eb01d73f00a6cde8bf976650279374aca46 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 27 Dec 2020 18:59:33 +0100 Subject: [PATCH 08/10] Search for read status --- cps/templates/search_form.html | 8 ++++++++ cps/web.py | 25 +++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/cps/templates/search_form.html b/cps/templates/search_form.html index e713fb93..98ab392c 100644 --- a/cps/templates/search_form.html +++ b/cps/templates/search_form.html @@ -31,6 +31,14 @@
+
+ + +
diff --git a/cps/web.py b/cps/web.py index 10eb11f3..21bc41a0 100644 --- a/cps/web.py +++ b/cps/web.py @@ -618,7 +618,8 @@ def render_read_books(page, are_read, as_xml=False, order=None): db_filter = and_(ub.ReadBook.user_id == int(current_user.id), ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED) else: - db_filter = coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED + db_filter = and_(ub.ReadBook.user_id == int(current_user.id), + coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED) entries, random, pagination = calibre_db.fill_indexpage(page, 0, db.Books, db_filter, @@ -1030,6 +1031,7 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): rating_low = term.get("ratinghigh") rating_high = term.get("ratinglow") description = term.get("comment") + read_status = term.get("read_status") if author_name: author_name = author_name.strip().lower().replace(',', '|') if book_title: @@ -1047,7 +1049,7 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \ include_languages_inputs or exclude_languages_inputs or author_name or book_title or \ publisher or pub_start or pub_end or rating_low or rating_high or description or cc_present or \ - include_extension_inputs or exclude_extension_inputs: + include_extension_inputs or exclude_extension_inputs or read_status: searchterm.extend((author_name.replace('|', ','), book_title, publisher)) if pub_start: try: @@ -1076,6 +1078,8 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): searchterm.extend([_(u"Rating <= %(rating)s", rating=rating_high)]) if rating_low: searchterm.extend([_(u"Rating >= %(rating)s", rating=rating_low)]) + if read_status: + searchterm.extend([_(u"Read Status = %(status)s", status=read_status)]) searchterm.extend(ext for ext in include_extension_inputs) searchterm.extend(ext for ext in exclude_extension_inputs) # handle custom columns @@ -1092,6 +1096,23 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): q = q.filter(db.Books.pubdate >= pub_start) if pub_end: q = q.filter(db.Books.pubdate <= pub_end) + if read_status: + if config.config_read_column: + if read_status=="True": + q = q.join(db.cc_classes[config.config_read_column], isouter=True) \ + .filter(db.cc_classes[config.config_read_column].value == True) + else: + q = q.join(db.cc_classes[config.config_read_column], isouter=True) \ + .filter(coalesce(db.cc_classes[config.config_read_column].value, False) != True) + else: + if read_status == "True": + q = q.join(ub.ReadBook, db.Books.id==ub.ReadBook.book_id, isouter=True)\ + .filter(ub.ReadBook.user_id == int(current_user.id), + ub.ReadBook.read_status == ub.ReadBook.STATUS_FINISHED) + else: + q = q.join(ub.ReadBook, db.Books.id == ub.ReadBook.book_id, isouter=True) \ + .filter(ub.ReadBook.user_id == int(current_user.id), + coalesce(ub.ReadBook.read_status, 0) != ub.ReadBook.STATUS_FINISHED) if publisher: q = q.filter(db.Books.publishers.any(func.lower(db.Publishers.name).ilike("%" + publisher + "%"))) for tag in include_tag_inputs: From 7e0ed537b7a58e849c23f6477b5be760a752a893 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 27 Dec 2020 19:12:27 +0100 Subject: [PATCH 09/10] irst steps Advanced search with mulitselects --- cps/static/css/libs/bootstrap-select.min.css | 6 + cps/static/js/libs/bootstrap-select.min.js | 9 ++ .../libs/bootstrap-select/defaults-cs.min.js | 8 ++ .../libs/bootstrap-select/defaults-de.min.js | 8 ++ .../libs/bootstrap-select/defaults-es.min.js | 8 ++ .../libs/bootstrap-select/defaults-fi.min.js | 8 ++ .../libs/bootstrap-select/defaults-fr.min.js | 8 ++ .../libs/bootstrap-select/defaults-hu.min.js | 8 ++ .../libs/bootstrap-select/defaults-it.min.js | 8 ++ .../libs/bootstrap-select/defaults-ja.min.js | 8 ++ .../libs/bootstrap-select/defaults-km.min.js | 8 ++ .../libs/bootstrap-select/defaults-nl.min.js | 8 ++ .../libs/bootstrap-select/defaults-pl.min.js | 8 ++ .../libs/bootstrap-select/defaults-ru.min.js | 8 ++ .../libs/bootstrap-select/defaults-sv.min.js | 8 ++ .../libs/bootstrap-select/defaults-tr.min.js | 8 ++ .../defaults-zh_Hans_CN.min.js | 8 ++ cps/templates/search_form.html | 135 +++++++++--------- 18 files changed, 200 insertions(+), 70 deletions(-) create mode 100644 cps/static/css/libs/bootstrap-select.min.css create mode 100644 cps/static/js/libs/bootstrap-select.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-cs.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-de.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-es.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-fi.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-fr.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-hu.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-it.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-ja.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-km.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-nl.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-pl.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-ru.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-sv.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-tr.min.js create mode 100644 cps/static/js/libs/bootstrap-select/defaults-zh_Hans_CN.min.js diff --git a/cps/static/css/libs/bootstrap-select.min.css b/cps/static/css/libs/bootstrap-select.min.css new file mode 100644 index 00000000..59708ed5 --- /dev/null +++ b/cps/static/css/libs/bootstrap-select.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */@-webkit-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@-o-keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}@keyframes bs-notify-fadeOut{0%{opacity:.9}100%{opacity:0}}.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select select:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select select:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu .notify.fadeOut{-webkit-animation:.3s linear 750ms forwards bs-notify-fadeOut;-o-animation:.3s linear 750ms forwards bs-notify-fadeOut;animation:.3s linear 750ms forwards bs-notify-fadeOut}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none} \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select.min.js b/cps/static/js/libs/bootstrap-select.min.js new file mode 100644 index 00000000..92e3a32e --- /dev/null +++ b/cps/static/js/libs/bootstrap-select.min.js @@ -0,0 +1,9 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(z){"use strict";var d=["sanitize","whiteList","sanitizeFn"],r=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},l=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==z.inArray(i,t))return-1===z.inArray(i,r)||Boolean(e.nodeValue.match(l)||e.nodeValue.match(a));for(var s=z(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function L(e){return parseInt(e,10)||0}z.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var I,x,y,$,S=(I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},x="(?:"+Object.keys(I).join("|")+")",y=RegExp(x),$=RegExp(x,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace($,E):e});function E(e){return I[e]}var C={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},N=27,D=13,H=32,W=9,B=38,M=40,R={success:!1,major:"3"};try{R.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),R.major=R.full[0],R.success=!0}catch(e){}var U=0,j=".bs.select",V={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},F={MENU:"."+V.MENU},_={span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};_.a.setAttribute("role","option"),"4"===R.major&&(_.a.className="dropdown-item"),_.subtext.className="text-muted",_.text=_.span.cloneNode(!1),_.text.className="text",_.checkMark=_.span.cloneNode(!1);var G=new RegExp(B+"|"+M),q=new RegExp("^"+W+"$|"+N),K={li:function(e,t,i){var s=_.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},a:function(e,t,i){var s=_.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&s.classList.add.apply(s.classList,t.split(" ")),i&&s.setAttribute("style",i),s},text:function(e,t){var i,s,n=_.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=_.whitespace.cloneNode(!1);(s=(!0===t?_.i:_.span).cloneNode(!1)).className=this.options.iconBase+" "+e.icon,_.fragment.appendChild(s),_.fragment.appendChild(o)}e.subtext&&((i=_.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},Y.prototype={constructor:Y,init:function(){var i=this,e=this.$element.attr("id");U++,this.selectId="bs-select-"+U,this.$element[0].classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$element[0].classList.contains("show-tick")&&(this.options.showTick=!0),this.$newElement=this.createDropdown(),this.buildData(),this.$element.after(this.$newElement).prependTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(F.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element[0].classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(V.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch?(this.liveSearchListener(),this.focusedParent=this.$searchbox[0]):this.focusedParent=this.$menuInner[0],this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+j,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$element.trigger("hide"+j,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+j,e)},"show.bs.dropdown":function(e){i.$element.trigger("show"+j,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+j,e)}}),i.$element[0].hasAttribute("required")&&this.$element.on("invalid"+j,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+j+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+j+".invalid")}).on("rendered"+j,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+j)}),i.$button.on("blur"+j,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+j)})}),setTimeout(function(){i.buildList(),i.$element.trigger("loaded"+j)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t=this.multiple?' aria-multiselectable="true"':"",i="",s=this.autofocus?" autofocus":"";R.major<4&&this.$element.parent().hasClass("input-group")&&(i=" input-group-btn");var n,o="",r="",l="",a="";return this.options.header&&(o='
'+this.options.header+"
"),this.options.liveSearch&&(r=''),this.multiple&&this.options.actionsBox&&(l='
"),this.multiple&&this.options.doneButton&&(a='
"),n='",z(n)},setPositionData:function(){this.selectpicker.view.canHighlight=[];for(var e=this.selectpicker.view.size=0;e=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(A,e,t){var L,N,D=this,i=0,H=[];if(this.selectpicker.isSearching=A,this.selectpicker.current=A?this.selectpicker.search:this.selectpicker.main,this.setPositionData(),e)if(t)i=this.$menuInner[0].scrollTop;else if(!D.multiple){var s=D.$element[0],n=(s.options[s.selectedIndex]||{}).liIndex;if("number"==typeof n&&!1!==D.options.size){var o=D.selectpicker.main.data[n],r=o&&o.position;r&&(i=r-(D.sizeInfo.menuInnerHeight+D.sizeInfo.liHeight)/2)}}function l(e,t){var i,s,n,o,r,l,a,c,d=D.selectpicker.current.elements.length,h=[],p=!0,u=D.isVirtual();D.selectpicker.view.scrollTop=e,i=Math.ceil(D.sizeInfo.menuInnerHeight/D.sizeInfo.liHeight*1.5),s=Math.round(d/i)||1;for(var f=0;fd-1?0:D.selectpicker.current.data[d-1].position-D.selectpicker.current.data[D.selectpicker.view.position1-1].position,b.firstChild.style.marginTop=v+"px",b.firstChild.style.marginBottom=g+"px"):(b.firstChild.style.marginTop=0,b.firstChild.style.marginBottom=0),b.firstChild.appendChild(w),!0===u&&D.sizeInfo.hasScrollBar){var C=b.firstChild.offsetWidth;if(t&&CD.sizeInfo.selectWidth)b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px";else if(C>D.sizeInfo.menuInnerInnerWidth){D.$menu[0].style.minWidth=0;var O=b.firstChild.offsetWidth;O>D.sizeInfo.menuInnerInnerWidth&&(D.sizeInfo.menuInnerInnerWidth=O,b.firstChild.style.minWidth=D.sizeInfo.menuInnerInnerWidth+"px"),D.$menu[0].style.minWidth=""}}}if(D.prevActiveIndex=D.activeIndex,D.options.liveSearch){if(A&&t){var z,T=0;D.selectpicker.view.canHighlight[T]||(T=1+D.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=D.selectpicker.view.visibleElements[T],D.defocusItem(D.selectpicker.view.currentActive),D.activeIndex=(D.selectpicker.current.data[T]||{}).index,D.focusItem(z)}}else D.$menuInner.trigger("focus")}l(i,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){D.noScroll||l(this.scrollTop,t),D.noScroll=!1}),z(window).off("resize"+j+"."+this.selectId+".createView").on("resize"+j+"."+this.selectId+".createView",function(){D.$newElement.hasClass(V.SHOW)&&l(D.$menuInner[0].scrollTop)})},focusItem:function(e,t,i){if(e){t=t||this.selectpicker.main.data[this.activeIndex];var s=e.firstChild;s&&(s.setAttribute("aria-setsize",this.selectpicker.view.size),s.setAttribute("aria-posinset",t.posinset),!0!==i&&(this.focusedParent.setAttribute("aria-activedescendant",s.id),e.classList.add("active"),s.classList.add("active")))}},defocusItem:function(e){e&&(e.classList.remove("active"),e.firstChild&&e.firstChild.classList.remove("active"))},setPlaceholder:function(){var e=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),e=!0;var t=this.$element[0],i=!1,s=!this.selectpicker.view.titleOption.parentNode;if(s)this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",i=void 0===z(t.options[t.selectedIndex]).attr("selected")&&void 0===this.$element.data("selected");!s&&0===this.selectpicker.view.titleOption.index||t.insertBefore(this.selectpicker.view.titleOption,t.firstChild),i&&(t.selectedIndex=0)}return e},buildData:function(){var p=':not([hidden]):not([data-hidden="true"])',u=[],f=0,e=this.setPlaceholder()?1:0;this.options.hideDisabled&&(p+=":not(:disabled)");var t=this.$element[0].querySelectorAll("select > *"+p);function m(e){var t=u[u.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",u.push(e))}function v(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)m({optID:t.optID});else{var i=u.length,s=e.style.cssText,n=s?S(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.optionClass=o.trim(),t.inlineStyle=n,t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.selected=!!e.selected,t.disabled=t.disabled||!!e.disabled,u.push(t)}}function i(e,t){var i=t[e],s=t[e-1],n=t[e+1],o=i.querySelectorAll("option"+p);if(o.length){var r,l,a={display:S(i.label),subtext:i.getAttribute("data-subtext"),icon:i.getAttribute("data-icon"),type:"optgroup-label",optgroupClass:" "+(i.className||"")};f++,s&&m({optID:f}),a.optID=f,u.push(a);for(var c=0,d=o.length;c li")},render:function(){var e,t=this,i=this.$element[0],s=this.setPlaceholder()&&0===i.selectedIndex,n=O(i,this.options.hideDisabled),o=n.length,r=this.$button[0],l=r.querySelector(".filter-option-inner-inner"),a=document.createTextNode(this.options.multipleSeparator),c=_.fragment.cloneNode(!1),d=!1;if(r.classList.toggle("bs-placeholder",t.multiple?!o:!T(i,n)),this.tabIndex(),"static"===this.options.selectedTextFormat)c=K.text.call(this,{text:this.options.title},!0);else if(!1===(this.multiple&&-1!==this.options.selectedTextFormat.indexOf("count")&&1")).length&&o>e[1]||1===e.length&&2<=o))){if(!s){for(var h=0;h option"+m+", optgroup"+m+" option"+m).length,g="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,v):this.options.countSelectedText;c=K.text.call(this,{text:g.replace("{0}",o.toString()).replace("{1}",v.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),c.childNodes.length||(c=K.text.call(this,{text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),r.title=c.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&d&&P([c],t.options.whiteList,t.options.sanitizeFn),l.innerHTML="",l.appendChild(c),R.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var b=r.querySelector(".filter-expand"),w=l.cloneNode(!0);w.className="filter-expand",b?r.replaceChild(w,b):r.appendChild(w)}this.$element.trigger("rendered"+j)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),R.major<4&&(n.classList.add("bs3"),n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!Object.keys(this.sizeInfo).length){var t=document.createElement("div"),i=document.createElement("div"),s=document.createElement("div"),n=document.createElement("ul"),o=document.createElement("li"),r=document.createElement("li"),l=document.createElement("li"),a=document.createElement("a"),c=document.createElement("span"),d=this.options.header&&0this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot,!0===this.selectpicker.isSearching&&(a=this.selectpicker.dropup),this.$newElement.toggleClass(V.DROPUP,a),this.selectpicker.dropup=a),"auto"===this.options.size)n=3this.options.size){for(var b=0;bthis.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth),"auto"===this.options.dropdownAlignRight&&this.$menu.toggleClass(V.MENURIGHT,this.sizeInfo.selectOffsetLeft>this.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRightthis.options.size&&i.off("resize"+j+"."+this.selectId+".setMenuSize scroll"+j+"."+this.selectId+".setMenuSize")}this.createView(!1,!0,e)},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+j,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=z('
');function e(e){var t={},i=r.options.display||!!z.fn.dropdown.Constructor.Default&&z.fn.dropdown.Constructor.Default.display;r.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(V.DROPUP,e.hasClass(V.DROPUP)),s=e.offset(),l.is("body")?n={top:0,left:0}:((n=l.offset()).top+=parseInt(l.css("borderTopWidth"))-l.scrollTop(),n.left+=parseInt(l.css("borderLeftWidth"))-l.scrollLeft()),o=e.hasClass(V.DROPUP)?0:e[0].offsetHeight,(R.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,r.$bsContainer.css(t)}var s,n,o,r=this,l=z(this.options.container);this.$button.on("click.bs.dropdown.data-api",function(){r.isDisabled()||(e(r.$newElement),r.$bsContainer.appendTo(r.options.container).toggleClass(V.SHOW,!r.$button.hasClass(V.SHOW)).append(r.$menu))}),z(window).off("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId).on("resize"+j+"."+this.selectId+" scroll"+j+"."+this.selectId,function(){r.$newElement.hasClass(V.SHOW)&&e(r.$newElement)}),this.$element.on("hide"+j,function(){r.$menu.data("height",r.$menu.height()),r.$bsContainer.detach()})},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length)for(var i=0;i
');y[2]&&($=$.replace("{var}",y[2][1"+$+"
")),d=!1,C.$element.trigger("maxReached"+j)),g&&w&&(E.append(z("
"+S+"
")),d=!1,C.$element.trigger("maxReachedGrp"+j)),setTimeout(function(){C.setSelected(r,!1)},10),E[0].classList.add("fadeOut"),setTimeout(function(){E.remove()},1050)}}}else c&&(c.selected=!1),h.selected=!0,C.setSelected(r,!0);!C.multiple||C.multiple&&1===C.options.maxOptions?C.$button.trigger("focus"):C.options.liveSearch&&C.$searchbox.trigger("focus"),d&&(!C.multiple&&a===s.selectedIndex||(A=[h.index,p.prop("selected"),l],C.$element.triggerNative("change")))}}),this.$menu.on("click","li."+V.DISABLED+" a, ."+V.POPOVERHEADER+", ."+V.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),C.options.liveSearch&&!z(e.target).hasClass("close")?C.$searchbox.trigger("focus"):C.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus")}),this.$menu.on("click","."+V.POPOVERHEADER+" .close",function(){C.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),z(this).hasClass("bs-select-all")?C.selectAll():C.deselectAll()}),this.$element.on("change"+j,function(){C.render(),C.$element.trigger("changed"+j,A),A=null}).on("focus"+j,function(){C.options.mobile||C.$button.trigger("focus")})},liveSearchListener:function(){var u=this,f=document.createElement("li");this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&u.$searchbox.val("")}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox.val();if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),r=u.options.liveSearchNormalize;r&&(i=w(i));for(var l=0;l=a.selectpicker.view.canHighlight.length&&(t=0),a.selectpicker.view.canHighlight[t+f]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+f+1).indexOf(!0))),e.preventDefault();var m=f+t;e.which===B?0===f&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,m=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[m]).position-n.height)u+a.sizeInfo.menuInnerHeight),s=a.selectpicker.main.elements[v],a.activeIndex=b[x],a.focusItem(s),s&&s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),r.trigger("focus")}}i&&(e.which===H&&!a.selectpicker.keydown.keyHistory||e.which===D||e.which===W&&a.options.selectOnTab)&&(e.which!==H&&e.preventDefault(),a.options.liveSearch&&e.which===H||(a.$menuInner.find(".active a").trigger("click",!0),r.trigger("focus"),a.options.liveSearch||(e.preventDefault(),z(document).data("spaceSelect",!0))))}},mobile:function(){this.$element[0].classList.add("mobile-device")},refresh:function(){var e=z.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.setStyle(),this.render(),this.buildData(),this.buildList(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+j)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(j).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),z(window).off(j+"."+this.selectId)}};var J=z.fn.selectpicker;z.fn.selectpicker=Z,z.fn.selectpicker.Constructor=Y,z.fn.selectpicker.noConflict=function(){return z.fn.selectpicker=J,this};var Q=z.fn.dropdown.Constructor._dataApiKeydownHandler||z.fn.dropdown.Constructor.prototype.keydown;z(document).off("keydown.bs.dropdown.data-api").on("keydown.bs.dropdown.data-api",':not(.bootstrap-select) > [data-toggle="dropdown"]',Q).on("keydown.bs.dropdown.data-api",":not(.bootstrap-select) > .dropdown-menu",Q).on("keydown"+j,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',Y.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),z(window).on("load"+j+".data-api",function(){z(".selectpicker").each(function(){var e=z(this);Z.call(e,e.data())})})}(e)}); +//# sourceMappingURL=bootstrap-select.min.js.map \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select/defaults-cs.min.js b/cps/static/js/libs/bootstrap-select/defaults-cs.min.js new file mode 100644 index 00000000..be309a10 --- /dev/null +++ b/cps/static/js/libs/bootstrap-select/defaults-cs.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,n){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return n(e)}):"object"==typeof module&&module.exports?module.exports=n(require("jquery")):n(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"Vyberte ze seznamu",noneResultsText:"Pro hled\xe1n\xed {0} nebyly nalezeny \u017e\xe1dn\xe9 v\xfdsledky",countSelectedText:"Vybran\xe9 {0} z {1}",maxOptionsText:["Limit p\u0159ekro\u010den ({n} {var} max)","Limit skupiny p\u0159ekro\u010den ({n} {var} max)",["polo\u017eek","polo\u017eka"]],multipleSeparator:", ",selectAllText:"Vybrat v\u0161e",deselectAllText:"Zru\u0161it v\xfdb\u011br"}}); \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select/defaults-de.min.js b/cps/static/js/libs/bootstrap-select/defaults-de.min.js new file mode 100644 index 00000000..e625440b --- /dev/null +++ b/cps/static/js/libs/bootstrap-select/defaults-de.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"Bitte w\xe4hlen...",noneResultsText:"Keine Ergebnisse f\xfcr {0}",countSelectedText:function(e,t){return 1==e?"{0} Element ausgew\xe4hlt":"{0} Elemente ausgew\xe4hlt"},maxOptionsText:function(e,t){return[1==e?"Limit erreicht ({n} Element max.)":"Limit erreicht ({n} Elemente max.)",1==t?"Gruppen-Limit erreicht ({n} Element max.)":"Gruppen-Limit erreicht ({n} Elemente max.)"]},selectAllText:"Alles ausw\xe4hlen",deselectAllText:"Nichts ausw\xe4hlen",multipleSeparator:", "}}); \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select/defaults-es.min.js b/cps/static/js/libs/bootstrap-select/defaults-es.min.js new file mode 100644 index 00000000..25efec39 --- /dev/null +++ b/cps/static/js/libs/bootstrap-select/defaults-es.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,o){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return o(e)}):"object"==typeof module&&module.exports?module.exports=o(require("jquery")):o(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"No hay selecci\xf3n",noneResultsText:"No hay resultados {0}",countSelectedText:"Seleccionados {0} de {1}",maxOptionsText:["L\xedmite alcanzado ({n} {var} max)","L\xedmite del grupo alcanzado({n} {var} max)",["elementos","element"]],multipleSeparator:", ",selectAllText:"Seleccionar Todos",deselectAllText:"Desmarcar Todos"}}); \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select/defaults-fi.min.js b/cps/static/js/libs/bootstrap-select/defaults-fi.min.js new file mode 100644 index 00000000..bee14048 --- /dev/null +++ b/cps/static/js/libs/bootstrap-select/defaults-fi.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"Ei valintoja",noneResultsText:"Ei hakutuloksia {0}",countSelectedText:function(e,t){return 1==e?"{0} valittu":"{0} valitut"},maxOptionsText:function(e,t){return["Valintojen maksimim\xe4\xe4r\xe4 ({n} saavutettu)","Ryhm\xe4n maksimim\xe4\xe4r\xe4 ({n} saavutettu)"]},selectAllText:"Valitse kaikki",deselectAllText:"Poista kaikki",multipleSeparator:", "}}); \ No newline at end of file diff --git a/cps/static/js/libs/bootstrap-select/defaults-fr.min.js b/cps/static/js/libs/bootstrap-select/defaults-fr.min.js new file mode 100644 index 00000000..d8931590 --- /dev/null +++ b/cps/static/js/libs/bootstrap-select/defaults-fr.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.14 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2020 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"Aucune s\xe9lection",noneResultsText:"Aucun r\xe9sultat pour {0}",countSelectedText:function(e,t){return 1{{_('Yes')}} -
- -
-
- {% for tag in tags %} - - {% endfor %} +
+
+
+
+ +
+
+
+
- -
-
- {% for tag in tags %} - - {% endfor %} +
+
+
+
-
- -
-
- {% for serie in series %} - - {% endfor %} -
-
- -
-
- {% for serie in series %} - - {% endfor %} +
+
+
{% if languages %} - -
-
+
+
+
+ {{language.name}} - + {% endfor %} -
+
- -
-
+
+
+ {{language.name}} - + {% endfor %} -
-
- {% endif%} - -
-
- {% for extension in extensions %} - - {% endfor %} +
- -
-
+ {% endif%} +
+
+
+ {{extension.format}} - - {% endfor %} -
+ + {% endfor %} + +
+
+
+ +
@@ -197,10 +189,13 @@ - + +{% if not g.user.locale == 'en' %} + +{% endif %} {% endblock %} {% block header %} + {% endblock %} From d33b0587cb20954d0fe4b629e89ec0a0da134d72 Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Mon, 28 Dec 2020 13:44:17 +0100 Subject: [PATCH 10/10] Advanced search tags are now multiselects (#1240) --- cps/static/js/edit_books.js | 18 +++++++++++++----- cps/templates/search_form.html | 20 ++++++++++---------- cps/web.py | 6 ++++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index 35515aa1..5f9154fc 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -249,18 +249,26 @@ promisePublishers.done(function() { ); }); -$("#search").on("change input.typeahead:selected", function() { +$("#search").on("change input.typeahead:selected", function(event) { + if (event.target.type == "search" && event.target.tagName == "INPUT") { + return; + } 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"); + if ($.inArray(parseInt($(this).val(), 10), data.tags) === -1) { + if(!$(this).prop("selected")) { + $(this).prop("disabled", true); } } else { - $(this).removeClass("disabled"); + $(this).prop("disabled", false); } }); + $("#include_tag option:selected").each(function () { + $("#exclude_tag").find("[value="+$(this).val()+"]").prop("disabled", true); + }); + $('#include_tag').selectpicker("refresh"); + $('#exclude_tag').selectpicker("refresh"); }); }); diff --git a/cps/templates/search_form.html b/cps/templates/search_form.html index e39cf661..e61fe067 100644 --- a/cps/templates/search_form.html +++ b/cps/templates/search_form.html @@ -42,17 +42,17 @@
- {% for tag in tags %} - + {% endfor %}
- {% for tag in tags %} - + {% endfor %}
@@ -60,7 +60,7 @@
- {% for serie in series %} {% endfor %} @@ -68,7 +68,7 @@
- {% for serie in series %} {% endfor %} @@ -79,7 +79,7 @@
- {% for language in languages %} {% endfor %} @@ -87,7 +87,7 @@
- {% for language in languages %} {% endfor %} @@ -98,7 +98,7 @@
- {% for extension in extensions %} {% endfor %} @@ -106,7 +106,7 @@
- {% for extension in extensions %} {% endfor %} diff --git a/cps/web.py b/cps/web.py index 21bc41a0..6d993e29 100644 --- a/cps/web.py +++ b/cps/web.py @@ -335,8 +335,6 @@ def get_matching_tags(): title_input = request.args.get('book_title') or '' include_tag_inputs = request.args.getlist('include_tag') or '' exclude_tag_inputs = request.args.getlist('exclude_tag') or '' - # include_extension_inputs = request.args.getlist('include_extension') or '' - # exclude_extension_inputs = request.args.getlist('exclude_extension') or '' q = q.filter(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + author_input + "%")), func.lower(db.Books.title).ilike("%" + title_input + "%")) if len(include_tag_inputs) > 0: @@ -1067,8 +1065,12 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): pub_start = u"" tag_names = calibre_db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all() searchterm.extend(tag.name for tag in tag_names) + tag_names = calibre_db.session.query(db.Tags).filter(db.Tags.id.in_(exclude_tag_inputs)).all() + searchterm.extend(tag.name for tag in tag_names) serie_names = calibre_db.session.query(db.Series).filter(db.Series.id.in_(include_series_inputs)).all() searchterm.extend(serie.name for serie in serie_names) + serie_names = calibre_db.session.query(db.Series).filter(db.Series.id.in_(exclude_series_inputs)).all() + searchterm.extend(serie.name for serie in serie_names) language_names = calibre_db.session.query(db.Languages).\ filter(db.Languages.id.in_(include_languages_inputs)).all() if language_names: