diff --git a/README.md b/README.md index 3f0792bf..5a796efb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Calibre-Web is a web app providing a clean interface for browsing, reading and d - Support for public user registration - Send eBooks to Kindle devices with the click of a button - Sync your Kobo devices through Calibre-Web with your Calibre library -- Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz) +- Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz, .djvu) - Upload new books in many formats, including audio formats (.mp3, .m4a, .m4b) - Support for Calibre Custom Columns - Ability to hide content based on categories and Custom Column content per user diff --git a/cps/about.py b/cps/about.py index 2e9cc699..10e68e69 100644 --- a/cps/about.py +++ b/cps/about.py @@ -37,6 +37,7 @@ try: except ImportError: from flask_login.__about__ import __version__ as flask_loginVersion try: + # pylint: disable=unused-import import unidecode # _() necessary to make babel aware of string for translation unidecode_version = _(u'installed') diff --git a/cps/cache_buster.py b/cps/cache_buster.py index 99614dfa..8c521fe1 100644 --- a/cps/cache_buster.py +++ b/cps/cache_buster.py @@ -49,7 +49,7 @@ def init_cache_busting(app): # compute version component rooted_filename = os.path.join(dirpath, filename) with open(rooted_filename, 'rb') as f: - file_hash = hashlib.md5(f.read()).hexdigest()[:7] + file_hash = hashlib.md5(f.read()).hexdigest()[:7] # nosec # save version to tables file_path = rooted_filename.replace(static_folder, "") @@ -64,6 +64,7 @@ def init_cache_busting(app): return filename.split("?", 1)[0] @app.url_defaults + # pylint: disable=unused-variable def reverse_to_cache_busted_url(endpoint, values): """ Make `url_for` produce busted filenames when using the 'static' endpoint. diff --git a/cps/constants.py b/cps/constants.py index 200bec8d..ac1157ef 100644 --- a/cps/constants.py +++ b/cps/constants.py @@ -104,7 +104,7 @@ LDAP_AUTH_SIMPLE = 0 DEFAULT_MAIL_SERVER = "mail.example.org" -DEFAULT_PASSWORD = "admin123" # nosec # noqa +DEFAULT_PASSWORD = "admin123" # nosec DEFAULT_PORT = 8083 env_CALIBRE_PORT = os.environ.get("CALIBRE_PORT", DEFAULT_PORT) try: diff --git a/cps/editbooks.py b/cps/editbooks.py index 1b1e13a2..42bda734 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -211,6 +211,74 @@ def delete_book_from_details(book_id): def delete_book_ajax(book_id, book_format): return delete_book(book_id,book_format, False) + +def delete_whole_book(book_id, book): + # delete book from Shelfs, Downloads, Read list + ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete() + ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete() + ub.delete_download(book_id) + ub.session_commit() + + # check if only this book links to: + # author, language, series, tags, custom columns + modify_database_object([u''], book.authors, db.Authors, calibre_db.session, 'author') + modify_database_object([u''], book.tags, db.Tags, calibre_db.session, 'tags') + modify_database_object([u''], book.series, db.Series, calibre_db.session, 'series') + modify_database_object([u''], book.languages, db.Languages, calibre_db.session, 'languages') + modify_database_object([u''], book.publishers, db.Publishers, calibre_db.session, 'publishers') + + cc = calibre_db.session.query(db.Custom_Columns). \ + filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() + for c in cc: + cc_string = "custom_column_" + str(c.id) + if not c.is_multiple: + if len(getattr(book, cc_string)) > 0: + if c.datatype == 'bool' or c.datatype == 'integer' or c.datatype == 'float': + del_cc = getattr(book, cc_string)[0] + getattr(book, cc_string).remove(del_cc) + log.debug('remove ' + str(c.id)) + calibre_db.session.delete(del_cc) + calibre_db.session.commit() + elif c.datatype == 'rating': + del_cc = getattr(book, cc_string)[0] + getattr(book, cc_string).remove(del_cc) + if len(del_cc.books) == 0: + log.debug('remove ' + str(c.id)) + calibre_db.session.delete(del_cc) + calibre_db.session.commit() + else: + del_cc = getattr(book, cc_string)[0] + getattr(book, cc_string).remove(del_cc) + log.debug('remove ' + str(c.id)) + calibre_db.session.delete(del_cc) + calibre_db.session.commit() + else: + modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id], + calibre_db.session, 'custom') + calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete() + + +def render_delete_book_result(book_format, jsonResponse, warning, book_id): + if book_format: + if jsonResponse: + return json.dumps([warning, {"location": url_for("editbook.edit_book", book_id=book_id), + "type": "success", + "format": book_format, + "message": _('Book Format Successfully Deleted')}]) + else: + flash(_('Book Format Successfully Deleted'), category="success") + return redirect(url_for('editbook.edit_book', book_id=book_id)) + else: + if jsonResponse: + return json.dumps([warning, {"location": url_for('web.index'), + "type": "success", + "format": book_format, + "message": _('Book Successfully Deleted')}]) + else: + flash(_('Book Successfully Deleted'), category="success") + return redirect(url_for('web.index')) + + def delete_book(book_id, book_format, jsonResponse): warning = {} if current_user.role_delete_books(): @@ -236,49 +304,7 @@ def delete_book(book_id, book_format, jsonResponse): else: flash(error, category="warning") if not book_format: - # delete book from Shelfs, Downloads, Read list - ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete() - ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete() - ub.delete_download(book_id) - ub.session_commit() - - # check if only this book links to: - # author, language, series, tags, custom columns - modify_database_object([u''], book.authors, db.Authors, calibre_db.session, 'author') - modify_database_object([u''], book.tags, db.Tags, calibre_db.session, 'tags') - modify_database_object([u''], book.series, db.Series, calibre_db.session, 'series') - modify_database_object([u''], book.languages, db.Languages, calibre_db.session, 'languages') - modify_database_object([u''], book.publishers, db.Publishers, calibre_db.session, 'publishers') - - cc = calibre_db.session.query(db.Custom_Columns).\ - filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() - for c in cc: - cc_string = "custom_column_" + str(c.id) - if not c.is_multiple: - if len(getattr(book, cc_string)) > 0: - if c.datatype == 'bool' or c.datatype == 'integer' or c.datatype == 'float': - del_cc = getattr(book, cc_string)[0] - getattr(book, cc_string).remove(del_cc) - log.debug('remove ' + str(c.id)) - calibre_db.session.delete(del_cc) - calibre_db.session.commit() - elif c.datatype == 'rating': - del_cc = getattr(book, cc_string)[0] - getattr(book, cc_string).remove(del_cc) - if len(del_cc.books) == 0: - log.debug('remove ' + str(c.id)) - calibre_db.session.delete(del_cc) - calibre_db.session.commit() - else: - del_cc = getattr(book, cc_string)[0] - getattr(book, cc_string).remove(del_cc) - log.debug('remove ' + str(c.id)) - calibre_db.session.delete(del_cc) - calibre_db.session.commit() - else: - modify_database_object([u''], getattr(book, cc_string), db.cc_classes[c.id], - calibre_db.session, 'custom') - calibre_db.session.query(db.Books).filter(db.Books.id == book_id).delete() + delete_whole_book(book_id, book) else: calibre_db.session.query(db.Data).filter(db.Data.book == book.id).\ filter(db.Data.format == book_format).delete() @@ -289,24 +315,7 @@ def delete_book(book_id, book_format, jsonResponse): else: # book not found log.error('Book with id "%s" could not be deleted: not found', book_id) - if book_format: - if jsonResponse: - return json.dumps([warning, {"location": url_for("editbook.edit_book", book_id=book_id), - "type": "success", - "format": book_format, - "message": _('Book Format Successfully Deleted')}]) - else: - flash(_('Book Format Successfully Deleted'), category="success") - return redirect(url_for('editbook.edit_book', book_id=book_id)) - else: - if jsonResponse: - return json.dumps([warning, {"location": url_for('web.index'), - "type": "success", - "format": book_format, - "message": _('Book Successfully Deleted')}]) - else: - flash(_('Book Successfully Deleted'), category="success") - return redirect(url_for('web.index')) + return render_delete_book_result(book_format, jsonResponse, warning, book_id) def render_edit_book(book_id): @@ -447,6 +456,59 @@ def edit_book_publisher(to_save, book): return changed +def edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string): + changed = False + if to_save[cc_string] == 'None': + to_save[cc_string] = None + elif c.datatype == 'bool': + to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0 + + if to_save[cc_string] != cc_db_value: + if cc_db_value is not None: + if to_save[cc_string] is not None: + setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string]) + changed = True + else: + del_cc = getattr(book, cc_string)[0] + getattr(book, cc_string).remove(del_cc) + calibre_db.session.delete(del_cc) + changed = True + else: + cc_class = db.cc_classes[c.id] + new_cc = cc_class(value=to_save[cc_string], book=book_id) + calibre_db.session.add(new_cc) + changed = True + return changed, to_save + + +def edit_cc_data_string(book, c, to_save, cc_db_value, cc_string): + changed = False + if c.datatype == 'rating': + to_save[cc_string] = str(int(float(to_save[cc_string]) * 2)) + if to_save[cc_string].strip() != cc_db_value: + if cc_db_value is not None: + # remove old cc_val + del_cc = getattr(book, cc_string)[0] + getattr(book, cc_string).remove(del_cc) + if len(del_cc.books) == 0: + calibre_db.session.delete(del_cc) + changed = True + cc_class = db.cc_classes[c.id] + new_cc = calibre_db.session.query(cc_class).filter( + cc_class.value == to_save[cc_string].strip()).first() + # if no cc val is found add it + if new_cc is None: + new_cc = cc_class(value=to_save[cc_string].strip()) + calibre_db.session.add(new_cc) + changed = True + calibre_db.session.flush() + new_cc = calibre_db.session.query(cc_class).filter( + cc_class.value == to_save[cc_string].strip()).first() + # add cc value to book + getattr(book, cc_string).append(new_cc) + return changed, to_save + + def edit_cc_data(book_id, book, to_save): changed = False cc = calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() @@ -459,51 +521,9 @@ def edit_cc_data(book_id, book, to_save): cc_db_value = None if to_save[cc_string].strip(): if c.datatype == 'int' or c.datatype == 'bool' or c.datatype == 'float': - if to_save[cc_string] == 'None': - to_save[cc_string] = None - elif c.datatype == 'bool': - to_save[cc_string] = 1 if to_save[cc_string] == 'True' else 0 - - if to_save[cc_string] != cc_db_value: - if cc_db_value is not None: - if to_save[cc_string] is not None: - setattr(getattr(book, cc_string)[0], 'value', to_save[cc_string]) - changed = True - else: - del_cc = getattr(book, cc_string)[0] - getattr(book, cc_string).remove(del_cc) - calibre_db.session.delete(del_cc) - changed = True - else: - cc_class = db.cc_classes[c.id] - new_cc = cc_class(value=to_save[cc_string], book=book_id) - calibre_db.session.add(new_cc) - changed = True - + changed, to_save = edit_cc_data_number(book_id, book, c, to_save, cc_db_value, cc_string) else: - if c.datatype == 'rating': - to_save[cc_string] = str(int(float(to_save[cc_string]) * 2)) - if to_save[cc_string].strip() != cc_db_value: - if cc_db_value is not None: - # remove old cc_val - del_cc = getattr(book, cc_string)[0] - getattr(book, cc_string).remove(del_cc) - if len(del_cc.books) == 0: - calibre_db.session.delete(del_cc) - changed = True - cc_class = db.cc_classes[c.id] - new_cc = calibre_db.session.query(cc_class).filter( - cc_class.value == to_save[cc_string].strip()).first() - # if no cc val is found add it - if new_cc is None: - new_cc = cc_class(value=to_save[cc_string].strip()) - calibre_db.session.add(new_cc) - changed = True - calibre_db.session.flush() - new_cc = calibre_db.session.query(cc_class).filter( - cc_class.value == to_save[cc_string].strip()).first() - # add cc value to book - getattr(book, cc_string).append(new_cc) + changed, to_save = edit_cc_data_string(book, c, to_save, cc_db_value, cc_string) else: if cc_db_value is not None: # remove old cc_val @@ -766,6 +786,7 @@ def merge_metadata(to_save, meta): to_save["description"] = to_save["description"] or Markup( getattr(meta, 'description', '')).unescape() + def identifier_list(to_save, book): """Generate a list of Identifiers from form information""" id_type_prefix = 'identifier-type-' @@ -780,6 +801,85 @@ def identifier_list(to_save, book): result.append(db.Identifiers(to_save[val_key], type_value, book.id)) return result + +def prepare_authors_on_upload(title, authr): + if title != _(u'Unknown') and authr != _(u'Unknown'): + entry = calibre_db.check_exists_book(authr, title) + if entry: + log.info("Uploaded book probably exists in library") + flash(_(u"Uploaded book probably exists in the library, consider to change before upload new: ") + + Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning") + + # handle authors + input_authors = authr.split('&') + # handle_authors(input_authors) + input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors)) + # Remove duplicates in authors list + input_authors = helper.uniq(input_authors) + + # we have all author names now + if input_authors == ['']: + input_authors = [_(u'Unknown')] # prevent empty Author + + sort_authors_list = list() + db_author = None + for inp in input_authors: + stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first() + if not stored_author: + if not db_author: + db_author = db.Authors(inp, helper.get_sorted_author(inp), "") + calibre_db.session.add(db_author) + calibre_db.session.commit() + sort_author = helper.get_sorted_author(inp) + else: + if not db_author: + db_author = stored_author + sort_author = stored_author.sort + sort_authors_list.append(sort_author) + sort_authors = ' & '.join(sort_authors_list) + return sort_authors, input_authors, db_author + + +def create_book_on_upload(modif_date, meta): + title = meta.title + authr = meta.author + sort_authors, input_authors, db_author = prepare_authors_on_upload(title, authr) + + title_dir = helper.get_valid_filename(title) + author_dir = helper.get_valid_filename(db_author.name) + + # combine path and normalize path from windows systems + path = os.path.join(author_dir, title_dir).replace('\\', '/') + + # Calibre adds books with utc as timezone + db_book = db.Books(title, "", sort_authors, datetime.utcnow(), datetime(101, 1, 1), + '1', datetime.utcnow(), path, meta.cover, db_author, [], "") + + modif_date |= modify_database_object(input_authors, db_book.authors, db.Authors, calibre_db.session, + 'author') + + # Add series_index to book + modif_date |= edit_book_series_index(meta.series_id, db_book) + + # add languages + modif_date |= edit_book_languages(meta.languages, db_book, upload=True) + + # handle tags + modif_date |= edit_book_tags(meta.tags, db_book) + + # handle series + modif_date |= edit_book_series(meta.series, db_book) + + # Add file to book + file_size = os.path.getsize(meta.file_path) + db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir) + db_book.data.append(db_data) + calibre_db.session.add(db_book) + + # flush content, get db_book.id available + calibre_db.session.flush() + return db_book, input_authors, title_dir + @editbook.route("/upload", methods=["GET", "POST"]) @login_required_if_no_ano @upload_required @@ -814,76 +914,8 @@ def upload(): flash(_(u"File %(filename)s could not saved to temp dir", filename= requested_file.filename), category="error") return Response(json.dumps({"location": url_for("web.index")}), mimetype='application/json') - title = meta.title - authr = meta.author - if title != _(u'Unknown') and authr != _(u'Unknown'): - entry = calibre_db.check_exists_book(authr, title) - if entry: - log.info("Uploaded book probably exists in library") - flash(_(u"Uploaded book probably exists in the library, consider to change before upload new: ") - + Markup(render_title_template('book_exists_flash.html', entry=entry)), category="warning") - - # handle authors - input_authors = authr.split('&') - # handle_authors(input_authors) - input_authors = list(map(lambda it: it.strip().replace(',', '|'), input_authors)) - # Remove duplicates in authors list - input_authors = helper.uniq(input_authors) - - # we have all author names now - if input_authors == ['']: - input_authors = [_(u'Unknown')] # prevent empty Author - - sort_authors_list=list() - db_author = None - for inp in input_authors: - stored_author = calibre_db.session.query(db.Authors).filter(db.Authors.name == inp).first() - if not stored_author: - if not db_author: - db_author = db.Authors(inp, helper.get_sorted_author(inp), "") - calibre_db.session.add(db_author) - calibre_db.session.commit() - sort_author = helper.get_sorted_author(inp) - else: - if not db_author: - db_author = stored_author - sort_author = stored_author.sort - sort_authors_list.append(sort_author) - sort_authors = ' & '.join(sort_authors_list) - - title_dir = helper.get_valid_filename(title) - author_dir = helper.get_valid_filename(db_author.name) - - # combine path and normalize path from windows systems - path = os.path.join(author_dir, title_dir).replace('\\', '/') - # Calibre adds books with utc as timezone - db_book = db.Books(title, "", sort_authors, datetime.utcnow(), datetime(101, 1, 1), - '1', datetime.utcnow(), path, meta.cover, db_author, [], "") - - modif_date |= modify_database_object(input_authors, db_book.authors, db.Authors, calibre_db.session, - 'author') - - # Add series_index to book - modif_date |= edit_book_series_index(meta.series_id, db_book) - - # add languages - modif_date |= edit_book_languages(meta.languages, db_book, upload=True) - - # handle tags - modif_date |= edit_book_tags(meta.tags, db_book) - - # handle series - modif_date |= edit_book_series(meta.series, db_book) - - # Add file to book - file_size = os.path.getsize(meta.file_path) - db_data = db.Data(db_book, meta.extension.upper()[1:], file_size, title_dir) - db_book.data.append(db_data) - calibre_db.session.add(db_book) - - # flush content, get db_book.id available - calibre_db.session.flush() + db_book, input_authors, title_dir = create_book_on_upload(modif_date, meta) # Comments needs book id therfore only possible after flush modif_date |= edit_book_comments(Markup(meta.description).unescape(), db_book) diff --git a/cps/error_handler.py b/cps/error_handler.py index e9cb601a..373a1434 100644 --- a/cps/error_handler.py +++ b/cps/error_handler.py @@ -60,14 +60,8 @@ def init_errorhandler(): if services.ldap: # Only way of catching the LDAPException upon logging in with LDAP server down @app.errorhandler(services.ldap.LDAPException) + # pylint: disable=unused-variable def handle_exception(e): log.debug('LDAP server not accessible while trying to login to opds feed') return error_http(FailedDependency()) - -# @app.errorhandler(InvalidRequestError) -#@app.errorhandler(OperationalError) -#def handle_db_exception(e): -# db.session.rollback() -# log.error('Database request error: %s',e) -# return internal_error(InternalServerError(e)) diff --git a/cps/gdrive.py b/cps/gdrive.py index 950f3ce2..158a5a4f 100644 --- a/cps/gdrive.py +++ b/cps/gdrive.py @@ -142,7 +142,7 @@ def on_received_watch_confirmation(): else: dbpath = os.path.join(config.config_calibre_dir, "metadata.db").encode() if not response['deleted'] and response['file']['title'] == 'metadata.db' \ - and response['file']['md5Checksum'] != hashlib.md5(dbpath): + and response['file']['md5Checksum'] != hashlib.md5(dbpath): # nosec tmp_dir = os.path.join(tempfile.gettempdir(), 'calibre_web') if not os.path.isdir(tmp_dir): os.mkdir(tmp_dir) diff --git a/cps/helper.py b/cps/helper.py index 88c0550b..e18ae33b 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -137,43 +137,8 @@ def send_registration_mail(e_mail, user_name, default_password, resend=False): return -def check_send_to_kindle_without_converter(entry): +def check_send_to_kindle_with_converter(formats): bookformats = list() - # no converter - only for mobi and pdf formats - for ele in iter(entry.data): - if ele.uncompressed_size < config.mail_size: - if 'MOBI' in ele.format: - bookformats.append({'format': 'Mobi', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Mobi')}) - if 'PDF' in ele.format: - bookformats.append({'format': 'Pdf', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Pdf')}) - if 'AZW' in ele.format: - bookformats.append({'format': 'Azw', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Azw')}) - return bookformats - -def check_send_to_kindle_with_converter(entry): - bookformats = list() - formats = list() - for ele in iter(entry.data): - if ele.uncompressed_size < config.mail_size: - formats.append(ele.format) - if 'MOBI' in formats: - bookformats.append({'format': 'Mobi', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Mobi')}) - if 'AZW' in formats: - bookformats.append({'format': 'Azw', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Azw')}) - if 'PDF' in formats: - bookformats.append({'format': 'Pdf', - 'convert': 0, - 'text': _('Send %(format)s to Kindle', format='Pdf')}) if 'EPUB' in formats and 'MOBI' not in formats: bookformats.append({'format': 'Mobi', 'convert': 1, @@ -193,12 +158,27 @@ def check_send_to_kindle(entry): """ returns all available book formats for sending to Kindle """ + formats = list() + bookformats = list() if len(entry.data): - if not config.config_converterpath: - book_formats = check_send_to_kindle_with_converter(entry) - else: - book_formats = check_send_to_kindle_with_converter(entry) - return book_formats + for ele in iter(entry.data): + if ele.uncompressed_size < config.mail_size: + formats.append(ele.format) + if 'MOBI' in formats: + bookformats.append({'format': 'Mobi', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Mobi')}) + if 'PDF' in formats: + bookformats.append({'format': 'Pdf', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Pdf')}) + if 'AZW' in formats: + bookformats.append({'format': 'Azw', + 'convert': 0, + 'text': _('Send %(format)s to Kindle', format='Azw')}) + if config.config_converterpath: + bookformats.extend(check_send_to_kindle_with_converter(formats)) + return bookformats else: log.error(u'Cannot find book entry %d', entry.id) return None @@ -207,7 +187,7 @@ def check_send_to_kindle(entry): # Check if a reader is existing for any of the book formats, if not, return empty list, otherwise return # list with supported formats def check_read_formats(entry): - EXTENSIONS_READER = {'TXT', 'PDF', 'EPUB', 'CBZ', 'CBT', 'CBR'} + EXTENSIONS_READER = {'TXT', 'PDF', 'EPUB', 'CBZ', 'CBT', 'CBR', 'DJVU'} bookformats = list() if len(entry.data): for ele in iter(entry.data): @@ -750,7 +730,7 @@ def format_runtime(runtime): # helper function to apply localize status information in tasklist entries def render_task_status(tasklist): renderedtasklist = list() - for __, user, added, task in tasklist: + for __, user, __, task in tasklist: if user == current_user.nickname or current_user.role_admin(): ret = {} if task.start_time: diff --git a/cps/kobo.py b/cps/kobo.py index b5d5397b..a9dd8865 100644 --- a/cps/kobo.py +++ b/cps/kobo.py @@ -918,7 +918,7 @@ def HandleAuthRequest(): if config.config_kobo_proxy: try: return redirect_or_proxy_request() - except: + except Exception: log.error("Failed to receive or parse response from Kobo's auth endpoint. Falling back to un-proxied mode.") return make_calibre_web_auth_response() diff --git a/cps/kobo_auth.py b/cps/kobo_auth.py index 8f18db49..23f60fe2 100644 --- a/cps/kobo_auth.py +++ b/cps/kobo_auth.py @@ -81,6 +81,7 @@ log = logger.create() def register_url_value_preprocessor(kobo): @kobo.url_value_preprocessor + # pylint: disable=unused-variable def pop_auth_token(__, values): g.auth_token = values.pop("auth_token") diff --git a/cps/oauth_bb.py b/cps/oauth_bb.py index 3fee2e04..1fd7c9b1 100644 --- a/cps/oauth_bb.py +++ b/cps/oauth_bb.py @@ -96,7 +96,113 @@ def logout_oauth_user(): session.pop(str(oauth_key) + '_oauth_user_id') -if ub.oauth_support: +def oauth_update_token(provider_id, token, provider_user_id): + session[provider_id + "_oauth_user_id"] = provider_user_id + session[provider_id + "_oauth_token"] = token + + # Find this OAuth token in the database, or create it + query = ub.session.query(ub.OAuth).filter_by( + provider=provider_id, + provider_user_id=provider_user_id, + ) + try: + oauth_entry = query.one() + # update token + oauth_entry.token = token + except NoResultFound: + oauth_entry = ub.OAuth( + provider=provider_id, + provider_user_id=provider_user_id, + token=token, + ) + ub.session.add(oauth_entry) + ub.session_commit() + + # Disable Flask-Dance's default behavior for saving the OAuth token + # Value differrs depending on flask-dance version + return backend_resultcode + + +def bind_oauth_or_register(provider_id, provider_user_id, redirect_url, provider_name): + query = ub.session.query(ub.OAuth).filter_by( + provider=provider_id, + provider_user_id=provider_user_id, + ) + try: + oauth_entry = query.first() + # already bind with user, just login + if oauth_entry.user: + login_user(oauth_entry.user) + log.debug(u"You are now logged in as: '%s'", oauth_entry.user.nickname) + flash(_(u"you are now logged in as: '%(nickname)s'", nickname= oauth_entry.user.nickname), + category="success") + return redirect(url_for('web.index')) + else: + # bind to current user + if current_user and current_user.is_authenticated: + oauth_entry.user = current_user + try: + ub.session.add(oauth_entry) + ub.session.commit() + flash(_(u"Link to %(oauth)s Succeeded", oauth=provider_name), category="success") + return redirect(url_for('web.profile')) + except Exception as e: + log.debug_or_exception(e) + ub.session.rollback() + else: + flash(_(u"Login failed, No User Linked With OAuth Account"), category="error") + log.info('Login failed, No User Linked With OAuth Account') + return redirect(url_for('web.login')) + # return redirect(url_for('web.login')) + # if config.config_public_reg: + # return redirect(url_for('web.register')) + # else: + # flash(_(u"Public registration is not enabled"), category="error") + # return redirect(url_for(redirect_url)) + except (NoResultFound, AttributeError): + return redirect(url_for(redirect_url)) + + +def get_oauth_status(): + status = [] + query = ub.session.query(ub.OAuth).filter_by( + user_id=current_user.id, + ) + try: + oauths = query.all() + for oauth_entry in oauths: + status.append(int(oauth_entry.provider)) + return status + except NoResultFound: + return None + + +def unlink_oauth(provider): + if request.host_url + 'me' != request.referrer: + pass + query = ub.session.query(ub.OAuth).filter_by( + provider=provider, + user_id=current_user.id, + ) + try: + oauth_entry = query.one() + if current_user and current_user.is_authenticated: + oauth_entry.user = current_user + try: + ub.session.delete(oauth_entry) + ub.session.commit() + logout_oauth_user() + flash(_(u"Unlink to %(oauth)s Succeeded", oauth=oauth_check[provider]), category="success") + except Exception as e: + log.debug_or_exception(e) + ub.session.rollback() + flash(_(u"Unlink to %(oauth)s Failed", oauth=oauth_check[provider]), category="error") + except NoResultFound: + log.warning("oauth %s for user %d not found", provider, current_user.id) + flash(_(u"Not Linked to %(oauth)s", oauth=provider), category="error") + return redirect(url_for('web.profile')) + +def generate_oauth_blueprints(): oauthblueprints = [] if not ub.session.query(ub.OAuthProvider).count(): for provider in ("github", "google"): @@ -141,8 +247,12 @@ if ub.oauth_support: app.register_blueprint(blueprint, url_prefix="/login") if element['active']: register_oauth_blueprint(element['id'], element['provider_name']) + return oauthblueprints +if ub.oauth_support: + oauthblueprints = generate_oauth_blueprints() + @oauth_authorized.connect_via(oauthblueprints[0]['blueprint']) def github_logged_in(blueprint, token): if not token: @@ -175,113 +285,6 @@ if ub.oauth_support: return oauth_update_token(str(oauthblueprints[1]['id']), token, google_user_id) - def oauth_update_token(provider_id, token, provider_user_id): - session[provider_id + "_oauth_user_id"] = provider_user_id - session[provider_id + "_oauth_token"] = token - - # Find this OAuth token in the database, or create it - query = ub.session.query(ub.OAuth).filter_by( - provider=provider_id, - provider_user_id=provider_user_id, - ) - try: - oauth_entry = query.one() - # update token - oauth_entry.token = token - except NoResultFound: - oauth_entry = ub.OAuth( - provider=provider_id, - provider_user_id=provider_user_id, - token=token, - ) - ub.session.add(oauth_entry) - ub.session_commit() - - # Disable Flask-Dance's default behavior for saving the OAuth token - # Value differrs depending on flask-dance version - return backend_resultcode - - - def bind_oauth_or_register(provider_id, provider_user_id, redirect_url, provider_name): - query = ub.session.query(ub.OAuth).filter_by( - provider=provider_id, - provider_user_id=provider_user_id, - ) - try: - oauth_entry = query.first() - # already bind with user, just login - if oauth_entry.user: - login_user(oauth_entry.user) - log.debug(u"You are now logged in as: '%s'", oauth_entry.user.nickname) - flash(_(u"you are now logged in as: '%(nickname)s'", nickname= oauth_entry.user.nickname), - category="success") - return redirect(url_for('web.index')) - else: - # bind to current user - if current_user and current_user.is_authenticated: - oauth_entry.user = current_user - try: - ub.session.add(oauth_entry) - ub.session.commit() - flash(_(u"Link to %(oauth)s Succeeded", oauth=provider_name), category="success") - return redirect(url_for('web.profile')) - except Exception as e: - log.debug_or_exception(e) - ub.session.rollback() - else: - flash(_(u"Login failed, No User Linked With OAuth Account"), category="error") - log.info('Login failed, No User Linked With OAuth Account') - return redirect(url_for('web.login')) - # return redirect(url_for('web.login')) - # if config.config_public_reg: - # return redirect(url_for('web.register')) - # else: - # flash(_(u"Public registration is not enabled"), category="error") - # return redirect(url_for(redirect_url)) - except (NoResultFound, AttributeError): - return redirect(url_for(redirect_url)) - - - def get_oauth_status(): - status = [] - query = ub.session.query(ub.OAuth).filter_by( - user_id=current_user.id, - ) - try: - oauths = query.all() - for oauth_entry in oauths: - status.append(int(oauth_entry.provider)) - return status - except NoResultFound: - return None - - - def unlink_oauth(provider): - if request.host_url + 'me' != request.referrer: - pass - query = ub.session.query(ub.OAuth).filter_by( - provider=provider, - user_id=current_user.id, - ) - try: - oauth_entry = query.one() - if current_user and current_user.is_authenticated: - oauth_entry.user = current_user - try: - ub.session.delete(oauth_entry) - ub.session.commit() - logout_oauth_user() - flash(_(u"Unlink to %(oauth)s Succeeded", oauth=oauth_check[provider]), category="success") - except Exception as e: - log.debug_or_exception(e) - ub.session.rollback() - flash(_(u"Unlink to %(oauth)s Failed", oauth=oauth_check[provider]), category="error") - except NoResultFound: - log.warning("oauth %s for user %d not found", provider, current_user.id) - flash(_(u"Not Linked to %(oauth)s", oauth=provider), category="error") - return redirect(url_for('web.profile')) - - # notify on OAuth provider error @oauth_error.connect_via(oauthblueprints[0]['blueprint']) def github_error(blueprint, error, error_description=None, error_uri=None): diff --git a/cps/server.py b/cps/server.py index ac821b31..a96858c9 100644 --- a/cps/server.py +++ b/cps/server.py @@ -22,7 +22,7 @@ import os import errno import signal import socket -import subprocess +import subprocess # nosec try: from gevent.pywsgi import WSGIServer @@ -253,13 +253,13 @@ class WebServer(object): if not self.restart: log.info("Performing shutdown of Calibre-Web") - # prevent irritiating log of pending tasks message from asyncio + # prevent irritating log of pending tasks message from asyncio logger.get('asyncio').setLevel(logger.logging.CRITICAL) return True log.info("Performing restart of Calibre-Web") args = self._get_args_for_reloading() - subprocess.call(args, close_fds=True) + subprocess.call(args, close_fds=True) # nosec return True def _killServer(self, __, ___): diff --git a/cps/services/SyncToken.py b/cps/services/SyncToken.py index da5d93a3..b54d8d95 100644 --- a/cps/services/SyncToken.py +++ b/cps/services/SyncToken.py @@ -22,6 +22,7 @@ from base64 import b64decode, b64encode from jsonschema import validate, exceptions, __version__ from datetime import datetime try: + # pylint: disable=unused-import from urllib import unquote except ImportError: from urllib.parse import unquote @@ -91,14 +92,14 @@ class SyncToken: def __init__( self, - raw_kobo_store_token="", # nosec + raw_kobo_store_token="", books_last_created=datetime.min, books_last_modified=datetime.min, archive_last_modified=datetime.min, reading_state_last_modified=datetime.min, tags_last_modified=datetime.min, books_last_id=-1 - ): + ): # nosec self.raw_kobo_store_token = raw_kobo_store_token self.books_last_created = books_last_created self.books_last_modified = books_last_modified diff --git a/cps/static/css/caliBlur.css b/cps/static/css/caliBlur.css index ffa5ecca..14e5c286 100644 --- a/cps/static/css/caliBlur.css +++ b/cps/static/css/caliBlur.css @@ -1783,6 +1783,12 @@ body > div.container-fluid > div > div.col-sm-10 > div.discover { margin-top: 0 } +.container-fluid .book .meta .series { + /* font-weight: 400; */ + /* font-size: 12px; */ + color: hsla(0, 0%, 100%, .45); +} + .container-fluid .book .meta > p { -o-text-overflow: ellipsis; text-overflow: ellipsis; diff --git a/cps/static/js/libs/djvu_html5/Djvu_html5.css b/cps/static/js/libs/djvu_html5/Djvu_html5.css new file mode 100644 index 00000000..77e37c97 --- /dev/null +++ b/cps/static/js/libs/djvu_html5/Djvu_html5.css @@ -0,0 +1,194 @@ +body { + margin: 0px; +} + +#djvuContainer { + position: absolute; + width: 100%; + height: 100%; + max-width: 100%; + text-align: center; + overflow: hidden; +} + +.toolbar { + position: relative; + display: inline-block; + padding-top: 10px; + + transform: translate(0, 0); + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + transition: transform 0.3s; + -webkit-transition: -webkit-transform 0.3s; +} + +.toolbarHidden { + transform: translate(0, -100%); + -webkit-transform: translate(0, -100%); + -ms-transform: translate(0, -100%); + transition: transform 1s; + -webkit-transition: transform 1s; +} + +.toolbarSquareButton { + float: left; + width: 40px; + height: 40px; + background-image: url("img/toolbar-buttons.png"); + background-repeat: no-repeat; + background-size: 500% 300%; +} + +.scrollbar { + position: absolute; + border-radius: 6px; + opacity: 0.6; + box-shadow: inset 0 0 0 1px black, inset 0 0 0 2px white, inset 0 0 0 10px #BBB; + transition: opacity 0.3s; +} +.scrollbar:hover { + box-shadow: inset 0 0 0 1px black, inset 0 0 0 2px white, inset 0 0 0 10px #999; +} +.scrollbarClicked, .scrollbarClicked:hover { + box-shadow: inset 0 0 0 1px black, inset 0 0 0 2px white, inset 0 0 0 10px #777; +} +.scrollbarHidden { + opacity: 0; + transition: opacity 0.6s; +} + +.scrollbarVertical { + right: 0px; + border-right: 1px solid transparent; + width: 13px; +} + +.scrollbarHorizontal { + bottom: 0px; + border-bottom: 1px solid transparent; + height: 13px; +} + +.content { + overflow: hidden; + position: absolute; + height: 100%; + width: 100%; +} + +.textLayer { + position: absolute; + height: 120%; + width: 120%; + overflow: scroll; + text-align: left; +} +_:-ms-lang(x), .textLayer { + height: 100%; + width: 100%; + -ms-overflow-style: none; +} +.textPage { + margin-top: 100vh; + margin-bottom: 100vh; + padding-right: 100vw; +} +.textPage span { + font-family: sans-serif; + color: #000; + color: rgba(0, 0, 0, 0); + white-space: nowrap; +} +.visibleTextPage span { + display: inline-block; + position: relative; + + top: 50%; + transform: translateY(-50%); + -webkit-transform: translateY(-50%); + -ms-transform: translateY(-50%); + /* border: 1px solid red; /* for easy debug */ +} + +.buttonZoomIn { + background-position: 25% 0; +} +.buttonZoomIn:hover { + background-position: 25% 50%; +} +.buttonZoomIn:disabled { + background-position: 25% 100%; +} + +.buttonZoomOut { + background-position: 0 0; +} +.buttonZoomOut:hover { + background-position: 0 50%; +} +.buttonZoomOut:disabled { + background-position: 0 100%; +} + +.buttonPagePrev { + background-position: 50% 0; +} +.buttonPagePrev:hover { + background-position: 50% 50%; +} +.buttonPagePrev:disabled { + background-position: 50% 100%; +} + +.buttonPageNext { + background-position: 75% 0; +} +.buttonPageNext:hover { + background-position: 75% 50%; +} +.buttonPageNext:disabled { + background-position: 75% 100%; +} + +.toolbarItem { + display: inline-block; + margin: 0 10px; +} + +.comboBox { + float: left; + position: relative; +} + +.comboBoxSelection { + width: 8.25ex; + margin: 10px 12px 0px 12px; +} + +.comboBoxText { + width: 5ex; + border: none; + padding: 0px; + outline: none; + + position: absolute; + margin: 10px 0px 0px 12px; + top: 2px; + left: 3px; +} + +.statusImage { + position: absolute; + left: 50%; + top: 50%; + width: 128px; + height: 128px; + margin: -72px 0 0 -64px; + background-image: url("img/status.png"); + background-repeat: no-repeat; +} + +.blankImage { + background-image: url("img/blank.jpg"); +} \ No newline at end of file diff --git a/cps/static/js/libs/djvu_html5/djvu_html5/091ECB3AE852C68866FBC86AA8FCDB1F.cache.js b/cps/static/js/libs/djvu_html5/djvu_html5/091ECB3AE852C68866FBC86AA8FCDB1F.cache.js new file mode 100644 index 00000000..602edae2 --- /dev/null +++ b/cps/static/js/libs/djvu_html5/djvu_html5/091ECB3AE852C68866FBC86AA8FCDB1F.cache.js @@ -0,0 +1,8 @@ +djvu_html5.onScriptDownloaded(["var $wnd = $wnd || window.parent;var __gwtModuleFunction = $wnd.djvu_html5;var $sendStats = __gwtModuleFunction.__sendStats;$sendStats('moduleStartup', 'moduleEvalStart');var $gwt_version = \"2.8.1\";var $strongName = '091ECB3AE852C68866FBC86AA8FCDB1F';var $gwt = {};var $doc = $wnd.document;var $moduleName, $moduleBase;function __gwtStartLoadingFragment(frag) {var fragFile = 'deferredjs/' + $strongName + '/' + frag + '.cache.js';return __gwtModuleFunction.__startLoadingFragment(fragFile);}function __gwtInstallCode(code) {return __gwtModuleFunction.__installRunAsyncCode(code);}function __gwt_isKnownPropertyValue(propName, propValue) {return __gwtModuleFunction.__gwt_isKnownPropertyValue(propName, propValue);}function __gwt_getMetaProperty(name) {return __gwtModuleFunction.__gwt_getMetaProperty(name);}var $stats = $wnd.__gwtStatsEvent ? function(a) {return $wnd.__gwtStatsEvent && $wnd.__gwtStatsEvent(a);} : null;var $sessionId = $wnd.__gwtStatsSessionId ? $wnd.__gwtStatsSessionId : null;function Lp(){}\nfunction Hp(){}\nfunction ub(){}\nfunction zd(){}\nfunction Gd(){}\nfunction jd(){}\nfunction dc(){}\nfunction nf(){}\nfunction sf(){}\nfunction zf(){}\nfunction Jf(){}\nfunction Of(){}\nfunction Tf(){}\nfunction Yf(){}\nfunction bg(){}\nfunction gg(){}\nfunction mg(){}\nfunction ug(){}\nfunction zg(){}\nfunction Eg(){}\nfunction Jg(){}\nfunction Ng(){}\nfunction ah(){}\nfunction _p(){}\nfunction oq(){}\nfunction qq(){}\nfunction Mq(){}\nfunction Mr(){}\nfunction Kr(){}\nfunction zs(){}\nfunction Bs(){}\nfunction ut(){}\nfunction gu(){}\nfunction vu(){}\nfunction Gx(){}\nfunction Mx(){}\nfunction Yx(){}\nfunction Sz(){}\nfunction XH(){}\nfunction eI(){}\nfunction NJ(){}\nfunction RJ(){}\nfunction FL(){}\nfunction HL(){}\nfunction bM(){}\nfunction dM(){}\nfunction fM(){}\nfunction hM(){}\nfunction jM(){}\nfunction lM(){}\nfunction nM(){}\nfunction pM(){}\nfunction rM(){}\nfunction iO(){}\nfunction EP(){}\nfunction JP(){}\nfunction OP(){}\nfunction cq(a){}\nfunction xu(a){}\nfunction Lz(a){}\nfunction Wz(a){}\nfunction jc(a){ic=a}\nfunction _q(){$q()}\nfunction EO(){AO()}\nfunction zu(){xu(this)}\nfunction Zz(){Wz(this)}\nfunction Ot(){this.a=0}\nfunction Op(a,b){a.a=b}\nfunction gf(a,b){a.a=b}\nfunction ef(a,b){a.d=b}\nfunction hf(a,b){a.b=b}\nfunction Pp(a,b){a.b=b}\nfunction ZB(a,b){a.b=b}\nfunction $B(a,b){a.c=b}\nfunction Xx(a,b){a.c=b}\nfunction Xr(a,b){a.a=b}\nfunction zM(a,b){a.a=b}\nfunction Ib(a,b){a.n=b}\nfunction pQ(a,b){a.e=b}\nfunction uD(a){this.a=a}\nfunction od(a){this.a=a}\nfunction qd(a){this.a=a}\nfunction Xp(a){this.a=a}\nfunction bq(a){this.a=a}\nfunction CB(a){this.a=a}\nfunction zC(a){this.a=a}\nfunction HF(a){this.a=a}\nfunction UF(a){this.d=a}\nfunction dt(a){this.c=a}\nfunction uH(a){this.c=a}\nfunction rx(a){this.b=a}\nfunction kI(a){this.b=a}\nfunction zI(a){this.b=a}\nfunction XI(a){this.c=a}\nfunction gG(a){this.a=a}\nfunction mG(a){this.a=a}\nfunction qG(a){this.a=a}\nfunction vG(a){this.a=a}\nfunction xJ(a){this.a=a}\nfunction BJ(a){this.a=a}\nfunction rL(a){this.a=a}\nfunction RM(a){this.a=a}\nfunction CN(a){this.a=a}\nfunction JN(a){this.a=a}\nfunction JQ(a){this.a=a}\nfunction LQ(a){this.a=a}\nfunction VQ(a){this.a=a}\nfunction XQ(a){this.a=a}\nfunction gO(a){this.a=a}\nfunction MO(a){this.a=a}\nfunction eP(a){this.a=a}\nfunction CP(a){this.a=a}\nfunction DR(a){this.a=a}\nfunction HR(a){this.a=a}\nfunction IR(a){this.a=a}\nfunction KR(a){this.a=a}\nfunction LR(a){this.a=a}\nfunction NR(a){this.a=a}\nfunction OR(a){this.a=a}\nfunction QR(a){this.a=a}\nfunction rg(){this.a={}}\nfunction $G(){KG(this)}\nfunction hK(){tF(this)}\nfunction ac(a){Ib(this,a)}\nfunction Hb(a,b){Ib(a,b)}\nfunction lg(a,b){zQ(b,a)}\nfunction Nt(a,b){a.a=b.a}\nfunction he(b,a){b.src=a}\nfunction rq(b,a){b.set(a)}\nfunction MN(a){return a}\nfunction yO(a){qO();oO=a}\nfunction Az(a){xz(this,a)}\nfunction hc(){this.a=Lc()}\nfunction Ff(){this.c=++Cf}\nfunction IC(){IC=Hp;IC()}\nfunction Vr(){Vr=Hp;we()}\nfunction Js(){Js=Hp;Qs()}\nfunction Wr(){Wr=Hp;new hK}\nfunction JA(){BA();new ZA}\nfunction fC(a){a.a=new uC}\nfunction rC(a){a.b=new zz}\nfunction Bq(a){return true}\nfunction ps(a,b){ie(a.n,b)}\nfunction Jb(a,b){Od(a.n,b)}\nfunction Kb(a,b){Mb(a.n,b)}\nfunction SA(a,b){MG(a.b,b)}\nfunction QN(a,b){MG(a.b,b)}\nfunction RN(a,b){MG(a.d,b)}\nfunction SN(a,b){MG(a.j,b)}\nfunction TN(a,b){MG(a.k,b)}\nfunction $P(a,b){MG(a.b,b)}\nfunction hs(a,b){Rr(a.d,b)}\nfunction JO(a,b){DO(a.a,b)}\nfunction qg(a,b,c){a.a[b]=c}\nfunction vD(a){this.a=Iz(a)}\nfunction th(){this.c=new dL}\nfunction Ey(){this.g=new ZA}\nfunction ZA(){this.b=new $G}\nfunction _B(){this.a=new $G}\nfunction GB(){this.e=new Yw}\nfunction zz(){xz(this,null)}\nfunction It(){Bt();Ct(this)}\nfunction kD(){zc.call(this)}\nfunction ND(){zc.call(this)}\nfunction pD(){zc.call(this)}\nfunction rD(){zc.call(this)}\nfunction CC(){zc.call(this)}\nfunction GC(){zc.call(this)}\nfunction IE(){zc.call(this)}\nfunction XJ(){zc.call(this)}\nfunction JL(){zc.call(this)}\nfunction KB(){GB.call(this)}\nfunction LB(){GB.call(this)}\nfunction WB(){GB.call(this)}\nfunction EC(){CC.call(this)}\nfunction HK(){HK=Hp;GK=JK()}\nfunction nK(){this.a=new hK}\nfunction xM(){this.a=new hK}\nfunction iP(){this.d=new $G}\nfunction ud(a){td();sd.jb(a)}\nfunction rO(a){qO();MG(pO,a)}\nfunction Jq(a){lr();tr(a,kT)}\nfunction Fq(a){return kr(a)}\nfunction kd(a){return a.hb()}\nfunction jN(a){cN(a);return a}\nfunction Cr(a,b){xr(a,b,a.n)}\nfunction Tr(a,b){xr(a,b,a.n)}\nfunction Xs(a,b){Zs(a,b,a.c)}\nfunction ks(a,b){os(a,b,b,-1)}\nfunction GO(a,b){_N(a.a.d,b)}\nfunction HO(a,b){aO(a.a.d,b)}\nfunction LO(a,b){dO(a.a.d,b)}\nfunction Dq(a,b){lr();tr(a,b)}\nfunction DM(a,b){!!b&&(a.c=b)}\nfunction Ju(a){return btoa(a)}\nfunction Ac(a){yc.call(this,a)}\nfunction OD(a){Cc.call(this,a)}\nfunction PD(a){Ac.call(this,a)}\nfunction eh(a){bh.call(this,a)}\nfunction dq(a){cq.call(this,a)}\nfunction Ir(a){eh.call(this,a)}\nfunction Or(a){ac.call(this,a)}\nfunction Gs(a){ac.call(this,a)}\nfunction LH(a){cN(a);this.a=a}\nfunction TH(){TH=Hp;SH=new XH}\nfunction Ec(){Ec=Hp;Dc=new ub}\nfunction _c(){_c=Hp;$c=new jd}\nfunction Vp(){Vp=Hp;Up=new _p}\nfunction $q(){$q=Hp;Zq=new Ff}\nfunction Gu(){Gu=Hp;Fu=new hK}\nfunction dI(){dI=Hp;cI=new eI}\nfunction Eu(){Eu=Hp;GE();Du=FE}\nfunction wC(a){yc.call(this,a)}\nfunction DC(a){Ac.call(this,a)}\nfunction HC(a){Ac.call(this,a)}\nfunction qD(a){Ac.call(this,a)}\nfunction sD(a){Ac.call(this,a)}\nfunction MD(a){Ac.call(this,a)}\nfunction JE(a){Ac.call(this,a)}\nfunction EE(a){DC.call(this,a)}\nfunction FC(a){DC.call(this,a)}\nfunction WD(a){qD.call(this,a)}\nfunction oJ(a){kI.call(this,a)}\nfunction IJ(a){DI.call(this,a)}\nfunction sJ(a){oJ.call(this,a)}\nfunction SM(a){RM.call(this,a)}\nfunction xE(){zC.call(this,'')}\nfunction CE(){zC.call(this,'')}\nfunction KL(){Ac.call(this,UT)}\nfunction Bp(){zp==null&&(zp=[])}\nfunction yI(){throw dp(new IE)}\nfunction WI(){throw dp(new IE)}\nfunction Qc(){Qc=Hp;!!(td(),sd)}\nfunction sN(){sN=Hp;pN={};rN={}}\nfunction UC(a){TC(a);return a.k}\nfunction Qd(a){a=qE(a);return a}\nfunction rh(a,b){a.e=b;return a}\nfunction Mt(a,b){a.a=b<<24>>24}\nfunction Pt(a){this.a=a<<24>>24}\nfunction ID(a){return a<0?-a:a}\nfunction je(a,b){return a.c-b.c}\nfunction pg(a,b){return a.a[b]}\nfunction Dh(a,b){return aD(a,b)}\nfunction JD(a,b){return a>b?a:b}\nfunction KD(a,b){return a0}\nfunction _v(a,b){a.d.e[a.c+a.b]=b}\nfunction $v(a,b){a.d.e[a.c+a.a]=b}\nfunction bw(a,b){a.d.e[a.c+a.e]=b}\nfunction qx(a,b,c){a.b.c[b+a.a]=c}\nfunction uF(a){return a.d.c+a.e.c}\nfunction lK(a,b){return lF(a.a,b)}\nfunction Bc(a,b){rc.call(this,a,b)}\nfunction ir(){Tg.call(this,null)}\nfunction Mz(){Lz(this);this.a=0}\nfunction Nz(){Lz(this);this.a=0}\nfunction ke(a,b){this.b=a;this.c=b}\nfunction xe(a,b){ke.call(this,a,b)}\nfunction Ne(a,b){ke.call(this,a,b)}\nfunction Pe(){Ne.call(this,'PX',0)}\nfunction Re(){Ne.call(this,'EM',2)}\nfunction Se(){Ne.call(this,'EX',3)}\nfunction Te(){Ne.call(this,'PT',4)}\nfunction Ue(){Ne.call(this,'PC',5)}\nfunction Ve(){Ne.call(this,'IN',6)}\nfunction We(){Ne.call(this,'CM',7)}\nfunction Xe(){Ne.call(this,'MM',8)}\nfunction _e(a,b){ke.call(this,a,b)}\nfunction Ah(a,b){ke.call(this,a,b)}\nfunction jp(a,b){return gp(a,b)==0}\nfunction mp(a,b){return gp(a,b)>=0}\nfunction pp(a,b){return gp(a,b)<=0}\nfunction dd(a){return !!a.a||!!a.f}\nfunction LK(){HK();return new GK}\nfunction $x(){$x=Hp;Zx=new uD(-1)}\nfunction zr(){this.f=new at(this)}\nfunction cs(a,b){this.a=a;this.b=b}\nfunction av(a,b){this.a=a;this.b=b}\nfunction cv(a,b){this.b=a;this.a=b}\nfunction Bx(a,b){this.b=a;this.a=b}\nfunction eA(a,b){this.a=a;this.b=b}\nfunction BB(a,b){this.b=a;this.a=b}\nfunction GG(a,b){this.d=a;this.e=b}\nfunction Qy(){this.b=0;this.a=null}\nfunction Rs(a,b){ke.call(this,a,b)}\nfunction jt(a,b){rc.call(this,a,b)}\nfunction gP(a,b){this.a=a;this.b=b}\nfunction mP(a,b){this.i=a;this.e=b}\nfunction oP(a,b){this.a=a;this.b=b}\nfunction qP(a,b){this.a=a;this.b=b}\nfunction HQ(a,b){this.a=a;this.b=b}\nfunction bS(a,b){this.b=a;this.a=b}\nfunction ae(a,b){a.innerText=b||''}\nfunction Od(b,a){b.className=a||''}\nfunction nt(b,a){b.responseType=a}\nfunction ie(b,a){b.selectedIndex=a}\nfunction JM(a,b,c){a.splice(b,0,c)}\nfunction tD(a,b){return wD(a.a,b.a)}\nfunction NK(a,b){return a.a.get(b)}\nfunction nE(a,b){return a.substr(b)}\nfunction dE(a,b){return cN(a),a===b}\nfunction wz(b,a){return b.array[a]}\nfunction Bi(a){return typeof a===jS}\nfunction Ci(a){return typeof a===kS}\nfunction Fi(a){return typeof a===lS}\nfunction np(a){return typeof a===kS}\nfunction Rw(a){return a.ymax-a.ymin}\nfunction Xw(a){return a.xmax-a.xmin}\nfunction FO(a){AO();console.log(a)}\nfunction Yc(a){$wnd.clearTimeout(a)}\nfunction Oq(a){$wnd.clearTimeout(a)}\nfunction xC(){wC.call(this,'UTF-8')}\nfunction Qe(){Ne.call(this,'PCT',1)}\nfunction UM(){RM.call(this,'UTF-8')}\nfunction Zc(){Mc!=0&&(Mc=0);Pc=-1}\nfunction lr(){if(!jr){sr();jr=true}}\nfunction AK(a){this.a=LK();this.b=a}\nfunction QK(a){this.a=LK();this.b=a}\nfunction AE(a,b){a.a+=''+b;return a}\nfunction BE(a,b){a.a+=''+b;return a}\nfunction _b(a,b){a.n['disabled']=!b}\nfunction gc(c,a,b){c.translate(a,b)}\nfunction KE(a,b){return bE(a.a,b.a)}\nfunction fE(a,b){return a.indexOf(b)}\nfunction vE(a){return wE(a,a.length)}\nfunction Hi(a){return a==null?null:a}\nfunction my(a,b){return nA(a.d,b)!=0}\nfunction ih(a){fh(ZS,a);return jh(a)}\nfunction Nq(a){$wnd.clearInterval(a)}\nfunction $E(a){return !a?null:a.wc()}\nfunction Fv(a,b){a.c=b;a.b=sv(a,a.k)}\nfunction lQ(a,b){b.a==a.n&&qQ(a,b.a)}\nfunction Tx(a,b){a.a=a.a+(b<<16>>16)}\nfunction Ux(a,b){a.a=a.a-(b<<16>>16)}\nfunction Wx(a,b){a.b=a.b-(b<<16>>16)}\nfunction Vx(a,b){a.b=a.b+(b<<16>>16)}\nfunction KG(a){a.a=Hh(ym,gS,1,0,5,1)}\nfunction kc(a){a.i=Hh(Am,gS,60,0,0,1)}\nfunction Be(){xe.call(this,'LEFT',2)}\nfunction Vs(){Rs.call(this,'LEFT',2)}\nfunction Ws(){Rs.call(this,'RIGHT',3)}\nfunction Ce(){xe.call(this,'RIGHT',3)}\nfunction Cs(){vs.call(this,$doc.body)}\nfunction uC(){Yw.call(this);rC(this)}\nfunction vB(a){ZA.call(this);this.a=a}\nfunction Tg(a){this.a=new _g;this.b=a}\nfunction Xz(a,b){a.a=b;a.c=0;return a}\nfunction qA(a,b){a.k=b;oA(a);return a}\nfunction Fs(a,b){a.n[zT]=b!=null?b:''}\nfunction ML(a){return a!=null?Ab(a):0}\nfunction Ph(a){return Qh(a.l,a.m,a.h)}\nfunction nD(a){return dE(kS,typeof a)}\nfunction gE(a){return dE(lS,typeof a)}\nfunction LD(a,b){return gp(a,b)<0?a:b}\nfunction lN(a,b){return parseInt(a,b)}\nfunction wD(a,b){return ab?1:0}\nfunction cK(a){return a<10?'0'+a:''+a}\nfunction xz(b,a){b.array=a?a.array:[]}\nfunction XG(a,b){HH(a.a,a.a.length,b)}\nfunction bL(a,b){if(a.a){nL(b);mL(b)}}\nfunction xN(a){if(!a.c)return;a.b=true}\nfunction xQ(a){this.a=a;Mq.call(this)}\nfunction _R(a){this.a=a;Mq.call(this)}\nfunction YA(a){dB(true);a.b.a.length=0}\nfunction dp(a){return a.backingJsObject}\nfunction _D(a,b){return a.charCodeAt(b)}\nfunction kP(a,b){a.a=b;IO(a.i.a,a.e,b)}\nfunction VJ(a,b){var c;c=a[yU];b[yU]=c}\nfunction ze(){xe.call(this,'CENTER',0)}\nfunction Ts(){Rs.call(this,'CENTER',0)}\nfunction cf(){_e.call(this,'HIDDEN',1)}\nfunction Hv(){tv.call(this,3,3,3,true)}\nfunction DE(a){zC.call(this,(cN(a),a))}\nfunction Ae(){xe.call(this,'JUSTIFY',1)}\nfunction bf(){_e.call(this,'VISIBLE',0)}\nfunction WM(a){if(!a){throw dp(new pD)}}\nfunction $M(a){if(!a){throw dp(new GC)}}\nfunction aN(a){if(!a){throw dp(new JL)}}\nfunction gN(a){if(!a){throw dp(new rD)}}\nfunction iN(a){if(!a){throw dp(new kD)}}\nfunction $e(){$e=Hp;Ze=new bf;Ye=new cf}\nfunction sq(a){return new Int16Array(a)}\nfunction tq(a){return new Int32Array(a)}\nfunction wq(a){return new Uint8Array(a)}\nfunction xq(a){return new Uint8Array(a)}\nfunction oN(a){return a.$H||(a.$H=++nN)}\nfunction Zh(a){return a.l+a.m*bT+a.h*cT}\nfunction Qh(a,b,c){return {l:a,m:b,h:c}}\nfunction kN(a,b){return a==b?0:a>1))}\nfunction Hq(a){zq=a;lr();a.setCapture()}\nfunction SI(a){zI.call(this,a);this.a=a}\nfunction DI(a){kI.call(this,a);this.a=a}\nfunction Us(){Rs.call(this,'JUSTIFY',1)}\nfunction Kv(){Kv=Hp;Jv=new Ov(-1,-1,-1)}\nfunction LJ(){LJ=Hp;JJ=new NJ;KJ=new RJ}\nfunction Hr(){Hr=Hp;Fr=new Kr;Gr=new Mr}\nfunction EL(){EL=Hp;CL=new FL;DL=new HL}\nfunction qO(){qO=Hp;nO=new Yw;pO=new $G}\nfunction ex(a,b){a.c=new hu(b);return a}\nfunction IP(a,b,c){a.c=b;a.d=c;return a}\nfunction aw(a,b,c){a.c=(sv(a.d,b)+c)*qv}\nfunction mK(a,b){return sF(a.a,b)!=null}\nfunction hE(a,b){return a.lastIndexOf(b)}\nfunction Ai(a,b){return a!=null&&ui(a,b)}\nfunction Id(b,a){return b.appendChild(a)}\nfunction Jd(b,a){return b.removeChild(a)}\nfunction Ld(b,a){return parseInt(b[a])|0}\nfunction hq(c,a,b){return a.replace(c,b)}\nfunction Su(a,b){return vi(oF(a.e,b),54)}\nfunction ou(a,b){return vi(oF(a.b,b),34)}\nfunction YG(a){return HM(a.a,a.a.length)}\nfunction oL(a){pL.call(this,a,null,null)}\nfunction bQ(a){this.a=a;TP.call(this,a)}\nfunction _g(){this.d=new hK;this.c=false}\nfunction Ig(){Ig=Hp;Hg=new Gf(WS,new Jg)}\nfunction Xf(){Xf=Hp;Wf=new Gf(JS,new Yf)}\nfunction TC(a){if(a.k!=null){return}eD(a)}\nfunction Ic(a){return a==null?null:a.name}\nfunction VN(a){return a.e?a.e.a.length:1}\nfunction oE(a,b,c){return a.substr(b,c-b)}\nfunction vq(c,a,b){return c.subarray(a,b)}\nfunction iv(a,b){return jv(a,qu(a.c,b).b)}\nfunction sv(a,b){return b*a.Sb()+a.border}\nfunction ec(d,a,b,c){d.drawImage(a,b,c)}\nfunction Zr(a,b){Yr(a,(mq(),new iq(b)))}\nfunction Qt(a,b){b-a.a.length>0&&Rt(a,b)}\nfunction lC(a,b){a.b=gC(b);a.c=a.b.buffer}\nfunction ON(a,b){this.order=a;this.data=b}\nfunction zc(){kc(this);mc(this);this.fb()}\nfunction Ny(){Ey.call(this);this.a=new ZA}\nfunction QB(){GB.call(this);this.d=new ZA}\nfunction QE(a){qD.call(this,a==null?rS:a)}\nfunction RE(a){qD.call(this,a==null?rS:a)}\nfunction wO(a){qO();if(lO!=a){lO=a;sO()}}\nfunction xd(a){td();return parseInt(a)||-1}\nfunction LC(a,b){IC();return a==b?0:a?1:-1}\nfunction KC(a){IC();return dE(jS,typeof a)}\nfunction jQ(a){return Ii(a.r/a.s*100+0.5)}\nfunction $O(a){return !a.i?0:a.i.a.length}\nfunction _K(a){tF(a.c);a.b.b=a.b;a.b.a=a.b}\nfunction WJ(a){var b;b=a[yU]|0;a[yU]=b+1}\nfunction Py(a,b){a.b=b;a.a=new Hv;return a}\nfunction yi(a){iN(a==null||Fi(a));return a}\nfunction $J(a){this.a=new $wnd.Date(wp(a))}\nfunction Tt(){this.a=Hh(Ji,GT,11,32,15,1)}\nfunction GD(){GD=Hp;FD=Hh(sm,gS,18,256,0,1)}\nfunction Xq(){Sq&&Pg((!Tq&&(Tq=new ir),Tq))}\nfunction Nv(){Kv();this.f=this.g=this.i=-51}\nfunction mN(b,c,d){try{b[c]=d}catch(a){}}\nfunction cE(a,b,c,d){return vE(d.Ac(a,b,c))}\nfunction iE(a,b,c){return a.lastIndexOf(b,c)}\nfunction Ei(a,b){return a&&b&&a instanceof b}\nfunction Rc(a,b,c){return a.apply(b,c);var d}\nfunction ee(b,a){return b.getElementById(a)}\nfunction Hc(a){return a==null?null:a.message}\nfunction Rg(a,b,c){return Vg(a.a,b,c),new ah}\nfunction Rq(a,b){return $wnd.setTimeout(a,b)}\nfunction ot(){return new $wnd.XMLHttpRequest}\nfunction mf(){mf=Hp;lf=new Gf('blur',new nf)}\nfunction Wd(a,b){a.fireEvent('on'+b.type,b)}\nfunction hd(a,b){a.a=ld(a.a,[b,true]);ed(a)}\nfunction gd(a,b){a.a=ld(a.a,[b,false]);ed(a)}\nfunction Xt(a,b){a.d=b;a.c=0;a.a=mS;return a}\nfunction zG(a,b){var c;c=a.e;a.e=b;return c}\nfunction RG(a,b){return SG(a,b,a.a.length-1)}\nfunction yH(a,b,c){return zH(a,a.length,b,c)}\nfunction fc(e,a,b,c,d){e.fillRect(a,b,c,d)}\nfunction LP(a){KP.call(this,a.a,a.b,a.c,a.d)}\nfunction BC(){Ac.call(this,'divide by zero')}\nfunction xw(){pw();tv.call(this,0,1,2,false)}\nfunction yN(a){hd((_c(),$c),a);return false}\nfunction GR(a,b){Fs(a.a.e.e,b+'%');rR(a.a.e)}\nfunction Ug(a,b){!a.a&&(a.a=new $G);MG(a.a,b)}\nfunction Pg(a){var b;if(Mg){b=new Ng;Sg(a,b)}}\nfunction XM(a,b){if(!a){throw dp(new qD(b))}}\nfunction _M(a,b){if(!a){throw dp(new HC(b))}}\nfunction dB(a){if(!a){throw dp(new FC('0'))}}\nfunction gs(a){this.n=a;this.d=new Sr(this.n)}\nfunction XK(a,b,c){this.a=a;this.b=b;this.c=c}\nfunction st(a,b,c){ke.call(this,a,b);this.a=c}\nfunction pL(a,b,c){this.c=a;GG.call(this,b,c)}\nfunction HH(a,b,c){YM(b,a.length);FH(a,0,b,c)}\nfunction WA(a,b){cB(b,a.b.a.length);TG(a.b,b)}\nfunction KM(a,b,c){IM(c,0,a,b,c.length,false)}\nfunction _d(a,b,c){c?a.add(b,c.index):a.add(b)}\nfunction FN(b,a){b.djvuWorker.postMessage(a)}\nfunction Pw(a){a.xmin=a.xmax=a.ymin=a.ymax=0}\nfunction hy(a,b){a.U[0]=a.U[1]=a.U[2]=b;a.V=0}\nfunction MG(a,b){a.a[a.a.length]=b;return true}\nfunction vi(a,b){iN(a==null||ui(a,b));return a}\nfunction lR(a,b){a.a=b;pQ(b,new HR(a));rR(a.e)}\nfunction QJ(a,b){return cN(b),MC(b,(cN(a),a))}\nfunction MJ(a,b){return cN(a),MC(a,(cN(b),b))}\nfunction JC(a,b){return LC((cN(a),a),(cN(b),b))}\nfunction mD(a,b){return oD((cN(a),a),(cN(b),b))}\nfunction aE(a,b){return kN((cN(a),a),(cN(b),b))}\nfunction AM(a){this.c=a;this.b=(GE(),kp(MM()))}\nfunction Jx(a){a.j=a.e=a.a=null;a.i=a.g=0;Eu()}\nfunction nv(){ev();this.b=new hK;this.c=new su}\nfunction zy(){$x();ly.call(this);this.e=new Ot}\nfunction yf(){yf=Hp;xf=new Gf('click',new zf)}\nfunction rf(){rf=Hp;qf=new Gf('change',new sf)}\nfunction If(){If=Hp;Hf=new Gf('keydown',new Jf)}\nfunction tg(){tg=Hp;sg=new Gf('scroll',new ug)}\nfunction fg(){fg=Hp;eg=new Gf('mouseup',new gg)}\nfunction tp(a,b){return hp(gi(np(a)?vp(a):a,b))}\nfunction Pq(a,b){return cS(function(){a.Bb(b)})}\nfunction LG(a,b,c){eN(b,a.a.length);JM(a.a,b,c)}\nfunction PG(a,b){bN(b,a.a.length);return a.a[b]}\nfunction IH(a,b){bN(b,a.a.length);return a.a[b]}\nfunction vw(a,b,c,d){a.k=c;a.i=d;a.e=b;return a}\nfunction GN(a){!a.border&&(a.border=0);return a}\nfunction Np(a){if(a.b){return a.b}return ZL(),QL}\nfunction rQ(a,b){if(!a.o)return;gQ(a,b*a.s/100)}\nfunction Gv(a,b){if(b!=a.k){a.k=b;a.b=sv(a,a.k)}}\nfunction wA(a,b){a.c=b.c;a.b=b.b;a.d=b.d;a.a=b.a}\nfunction nL(a){a.a.b=a.b;a.b.a=a.a;a.a=a.b=null}\nfunction rR(a){_b(a.b,a.Ec(-1));_b(a.c,a.Ec(1))}\nfunction aH(a){KG(this);KM(this.a,0,a.toArray())}\nfunction wQ(a){$wnd.history.replaceState(a,'',a)}\nfunction ws(a){us();try{Sb(a)}finally{mK(ts,a)}}\nfunction Kz(a){return Ci(a)?Ii((cN(a),a)):a.hc()}\nfunction Jh(a){return Array.isArray(a)&&a.Jc===Lp}\nfunction Wg(a,b,c,d){var e;e=Yg(a,b,c);e.add(d)}\nfunction Fh(a,b,c,d,e,f){return Gh(a,b,c,d,e,0,f)}\nfunction yg(){yg=Hp;xg=new Gf('touchend',new zg)}\nfunction Dg(){Dg=Hp;Cg=new Gf('touchmove',new Eg)}\nfunction ag(){ag=Hp;_f=new Gf('mouseover',new bg)}\nfunction Sf(){Sf=Hp;Rf=new Gf('mousemove',new Tf)}\nfunction Nf(){Nf=Hp;Mf=new Gf('mousedown',new Of)}\nfunction GE(){GE=Hp;FE=new dq(null);new dq(null)}\nfunction us(){us=Hp;rs=new zs;ss=new hK;ts=new nK}\nfunction EM(a){this.b=a;this.d=true;this.a=new $G}\nfunction at(a){this.b=a;this.a=Hh(Rk,gS,20,4,0,1)}\nfunction js(a){gs.call(this,(eE('span',$d(a)),a))}\nfunction lF(a,b){return Fi(b)?pF(a,b):!!xK(a.d,b)}\nfunction MK(a,b){return !(a.a.get(b)===undefined)}\nfunction WN(a,b){return a.e?vi(IH(a.e,b),38):null}\nfunction XN(a,b){return a.f?vi(IH(a.f,b),61):null}\nfunction Di(a){return a!=null&&Gi(a)&&!(a.Jc===Lp)}\nfunction zi(a){return !Array.isArray(a)&&a.Jc===Lp}\nfunction Gi(a){return typeof a===dS||typeof a===iS}\nfunction HD(a,b){return gp(a,b)<0?-1:gp(a,b)>0?1:0}\nfunction ru(a,b){return gA(a.c,vi(UA(a.d,b),34).b)}\nfunction Uq(a){Wq();return Vq(Mg?Mg:(Mg=new Ff),a)}\nfunction dN(a,b){if(a==null){throw dp(new PD(b))}}\nfunction kh(a,b){if(a==null){throw dp(new qD(b))}}\nfunction cB(a,b){if(a<0||a>=b){throw dp(new EC)}}\nfunction XA(a,b,c){cB(c,a.b.a.length);WG(a.b,c,b)}\nfunction ld(a,b){!a&&(a=[]);a[a.length]=b;return a}\nfunction YC(a,b,c){var d;d=XC(a,b);iD(c,d);return d}\nfunction nF(a,b){return Fi(b)?oF(a,b):$E(xK(a.d,b))}\nfunction ai(a,b){return Qh(a.l&b.l,a.m&b.m,a.h&b.h)}\nfunction fi(a,b){return Qh(a.l|b.l,a.m|b.m,a.h|b.h)}\nfunction li(a,b){return Qh(a.l^b.l,a.m^b.m,a.h^b.h)}\nfunction FP(a,b,c,d){GP(a,b,c*a.b,d.width,d.height)}\nfunction VA(a,b,c){cB(c,a.b.a.length+1);LG(a.b,c,b)}\nfunction UJ(a,b){if(b[yU]!=a[yU]){throw dp(new XJ)}}\nfunction cN(a){if(a==null){throw dp(new ND)}return a}\nfunction vN(){if(qN==256){pN=rN;rN={};qN=0}++qN}\nfunction yc(a){kc(this);this.f=a;mc(this);this.fb()}\nfunction uA(a){iA();tA.call(this);this.k=a;oA(this)}\nfunction Ur(){zr.call(this);Hb(this,Vd($doc,'div'))}\nfunction fq(a){dq.call(this,new cq(null));this.a=a}\nfunction Md(b,a){return b[a]==null?null:String(b[a])}\nfunction TA(a,b){return cB(b,a.b.a.length),PG(a.b,b)}\nfunction UA(a,b){cB(b,a.b.a.length);return PG(a.b,b)}\nfunction HM(a,b){var c;c=a.slice(0,b);return Mh(c,a)}\nfunction kK(a,b){var c;c=qF(a.a,b,a);return c==null}\nfunction XC(a,b){var c;c=new VC;c.g=a;c.d=b;return c}\nfunction yE(a,b){a.a+=String.fromCharCode(b);return a}\nfunction Kh(a,b,c){$M(c==null||Ch(a,c));return a[b]=c}\nfunction yy(a,b,c,d){a.c=c;a.d=new uA(b);a.a=d;a.b=0}\nfunction eu(a,b){var c;c=a.c+b;op(c,a.a)&&(a.a=xp(c))}\nfunction $C(a,b){var c;c=XC('',a);c.j=b;c.f=1;return c}\nfunction Ds(a){var b;b=Md(a.n,zT).length;b>0&&Es(a,b)}\nfunction hh(a){fh('decodedURL',a);return encodeURI(a)}\nfunction xp(a){if(np(a)){return a|0}return a.l|a.m<<22}\nfunction Uw(a){return a.xmin>=a.xmax||a.ymin>=a.ymax}\nfunction Xd(a){return Math.round(-a.wheelDelta/40)||0}\nfunction KA(a){return a==null||a.length==0?-1:LA(a,MT)}\nfunction Vq(a,b){return Rg((!Tq&&(Tq=new ir),Tq),a,b)}\nfunction qF(a,b,c){return Fi(b)?rF(a,b,c):yK(a.d,b,c)}\nfunction af(){$e();return Lh(Dh(qj,1),gS,80,0,[Ze,Ye])}\nfunction tt(){rt();return Lh(Dh(Tk,1),gS,106,0,[qt,pt])}\nfunction Qp(){Op(this,new bq(true));Pp(this,(ZL(),QL))}\nfunction Sp(){Op(this,new bq(false));Pp(this,(ZL(),QL))}\nfunction su(){this.b=new hK;this.a=new ZA;this.d=new ZA}\nfunction yL(a){this.c=a;this.b=a.a.b.a;VJ(a.a.c,this)}\nfunction Yw(){this.xmin=this.xmax=this.ymin=this.ymax=0}\nfunction XD(a,b,c){this.a=hS;this.d=a;this.b=b;this.c=c}\nfunction Yt(a,b){a.d=Yz(new Zz,b);a.c=0;a.a=mS;return a}\nfunction fv(a){var b;b=a.a;!b&&(a.a=b=new _B);return b}\nfunction wK(a,b){var c;c=a.a.get(b);return c==null?[]:c}\nfunction LL(a,b){return Hi(a)===Hi(b)||a!=null&&wb(a,b)}\nfunction pc(a,b){a.backingJsObject=b;b!=null&&mN(b,nS,a)}\nfunction Yp(a){a.a=wM(yM(),'');a.a.d=false;$p();Zp(a.a)}\nfunction vs(a){zr.call(this);this.n=a,undefined;Qb(this)}\nfunction Ov(a,b,c){Kv();this.Xb(a);this.Yb(b);this.Zb(c)}\nfunction $z(a){Wz(this);this.a=a.a;this.c=a.c;this.b=a.b}\nfunction xA(a,b,c,d){this.c=a;this.b=b;this.d=c;this.a=d}\nfunction xr(a,b,c){Tb(b);Xs(a.f,b);Id(c,Cq(b.n));Vb(b,a)}\nfunction Lv(a,b){$v(a,b.Tb());_v(a,b.Ub());bw(a,b.Vb())}\nfunction pF(a,b){return b==null?!!xK(a.d,null):MK(a.e,b)}\nfunction rE(a){return String.fromCharCode.apply(null,a)}\nfunction PC(a){return /\\d/.test(String.fromCharCode(a))}\nfunction Cq(a){return a.__gwt_resolve?a.__gwt_resolve():a}\nfunction WH(a){TH();return Ai(a,82)?new IJ(a):new DI(a)}\nfunction Bh(){zh();return Lh(Dh(Vj,1),gS,89,0,[yh,xh,wh])}\nfunction mC(){eC();fC(this);lC(this,Hh(Ji,GT,11,0,15,1))}\nfunction KP(a,b,c,d){this.a=a;this.b=b;this.c=c;this.d=d}\nfunction vt(a,b,c){this.a=a;this.d=b;this.c=null;this.b=c}\nfunction xK(a,b){return vK(a,b,wK(a,b==null?0:a.b.sc(b)))}\nfunction AH(a,b){ZM(b);return BH(a,Hh(Ji,GT,11,b,15,1),b)}\nfunction Wc(a){$wnd.setTimeout(function(){throw a},0)}\nfunction Hz(a){return dE(kS,typeof a)||a instanceof Number}\nfunction HA(a){switch(a){case 4:case 2:case 5:return;}}\nfunction EA(a){switch(a){case 1:case 2:case 3:return;}}\nfunction xi(a){iN(a==null||Gi(a)&&!(a.Jc===Lp));return a}\nfunction gv(a,b){var c,d;c=qu(a.c,b).b;d=hv(a,c);return d}\nfunction Qr(a){var b;b=a.c?Sd(a.a):a.a;return b.innerText}\nfunction qc(a,b){var c;c=UC(a.Hc);return b==null?c:c+': '+b}\nfunction oF(a,b){return b==null?$E(xK(a.d,null)):NK(a.e,b)}\nfunction bE(a,b){return aE(a.toLowerCase(),b.toLowerCase())}\nfunction fp(a,b){return hp(ai(np(a)?vp(a):a,np(b)?vp(b):b))}\nfunction sp(a,b){return hp(fi(np(a)?vp(a):a,np(b)?vp(b):b))}\nfunction yp(a,b){return hp(li(np(a)?vp(a):a,np(b)?vp(b):b))}\nfunction hN(a,b){if(a>b||a<0){throw dp(new EE(yS+a+zS+b))}}\nfunction Yr(a,b){!!a.a&&(a.n[wT]='',undefined);he(a.n,b.a)}\nfunction es(a,b){!!a.a&&(a.n[wT]='',undefined);he(a.n,b.a)}\nfunction FR(a,b){Fs(a.a.b.e,b+1+'');ps(a.a.b.d,b);rR(a.a.b)}\nfunction Ly(a){a.d=a.b=0;YA(a.a);Dy(a,null,false);YA(a.g)}\nfunction CK(a){this.e=a;this.b=this.e.a.entries();this.a=[]}\nfunction xs(){us();try{Jr(ts,rs)}finally{tF(ts.a);tF(ss)}}\nfunction Qs(){Qs=Hp;Ms=new Ts;Ns=new Us;Os=new Vs;Ps=new Ws}\nfunction we(){we=Hp;se=new ze;te=new Ae;ue=new Be;ve=new Ce}\nfunction kg(){kg=Hp;jg=new Gf(US,new mg);new Gf(VS,new mg)}\nfunction td(){td=Hp;var a,b;b=!yd();a=new Gd;sd=b?new zd:a}\nfunction Jy(a,b,c){var d;Ly(a);d=new zy;yy(d,b,c,a);return d}\nfunction Zp(a){var b,c;b=new Qp;MG(a.a,b);c=new Sp;MG(a.a,c)}\nfunction mL(a){var b;b=a.c.b.b;a.b=b;a.a=a.c.b;b.a=a.c.b.b=a}\nfunction vO(a){qO();a.xmin=a.xmax=a.ymin=a.ymax=0;Vw(a,a,nO)}\nfunction Ii(a){return Math.max(Math.min(a,mS),-2147483648)|0}\nfunction UQ(a,b){return wp(kp($wnd.Math.round((a+b/2)/b)))*b}\nfunction UE(a,b){return b===a?'(this Map)':b==null?rS:Kp(b)}\nfunction rF(a,b,c){return b==null?yK(a.d,null,c):OK(a.e,b,c)}\nfunction VG(a,b,c){var d;fN(b,c,a.a.length);d=c-b;LM(a.a,b,d)}\nfunction fu(a,b){var c;c=a.d.Ob(b);a.c=xp(ep(a.c,c));return c}\nfunction ZO(a,b){var c;c=vi(PG(a.i,b),51);return c.b?c.d:null}\nfunction aD(a,b){var c=a.a=a.a||[];return c[b]||(c[b]=a.kc(b))}\nfunction PN(a,b){ON.call(this,'context-init',a);this.data2=b}\nfunction rc(a,b){kc(this);this.e=b;this.f=a;mc(this);this.fb()}\nfunction lh(a,b){if(a==null||a.length==0){throw dp(new qD(b))}}\nfunction qu(a,b){return b=a.n.options.length){throw dp(new CC)}}\nfunction uQ(a){if(!a.o)return;gQ(a,(null.Kc()-a.p*2)/a.o.width)}\nfunction YJ(a,b){return HD(kp(a.a.getTime()),kp(b.a.getTime()))}\nfunction YF(a,b){this.a=a;UF.call(this,a);eN(b,a.size());this.b=b}\nfunction aG(a,b,c){fN(b,c,a.size());this.c=a;this.a=b;this.b=c-b}\nfunction TF(a){gN(a.c!=-1);a.d.removeAtIndex(a.c);a.b=a.c;a.c=-1}\nfunction Ct(a){a.c=Hh(al,IT,29,300,0,1);a.g=Hh(Ji,GT,11,1,15,1)}\nfunction PQ(a,b){var c;c=WN(a.a.d,b);aR(OQ(a,b),c.width,c.height)}\nfunction kQ(a,b,c){var d;kR(b.j,c.a);d=JD(0,KD(c.a-1,a.n));qQ(a,d)}\nfunction CM(a,b,c){var d;ZL();d=new AM(b);d.d=c;zM(d,a.b);BM(a,d)}\nfunction kE(a,b,c){c=uE(c);return a.replace(new RegExp(b,'g'),c)}\nfunction GF(a,b){if(Ai(b,22)){return SE(a.a,vi(b,22))}return false}\nfunction qL(a,b){if(Ai(b,22)){return SE(a.a,vi(b,22))}return false}\nfunction fh(a,b){if(null==b){throw dp(new PD(a+' cannot be null'))}}\nfunction iq(a){if(a==null){throw dp(new PD('uri is null'))}this.a=a}\nfunction ZM(a){if(a<0){throw dp(new MD('Negative array size: '+a))}}\nfunction IA(a){switch(a){case -4:case -3:case -2:case -1:return;}}\nfunction tc(b){if(!('stack' in b)){try{throw b}catch(a){}}return b}\nfunction jC(a,b){var c;c=Wt(b);if(c){return kC(a,c)}hC(a,b);return a}\nfunction _s(a,b){var c;c=Ys(a,b);if(c==-1){throw dp(new JL)}$s(a,c)}\nfunction rJ(a,b){var c;for(c=0;c>8]:a.j[255&b]+8}\nfunction sF(a,b){return Fi(b)?b==null?zK(a.d,null):PK(a.e,b):zK(a.d,b)}\nfunction Kq(a){if(!a.d){return}++a.b;a.c?Nq(a.d.a):Oq(a.d.a);a.d=null}\nfunction NL(a,b){!a.a?(a.a=new DE(a.d)):BE(a.a,a.b);AE(a.a,b);return a}\nfunction WG(a,b,c){var d;d=(bN(b,a.a.length),a.a[b]);a.a[b]=c;return d}\nfunction SG(a,b,c){for(;c>=0;--c){if(LL(b,a.a[c])){return c}}return -1}\nfunction ny(a,b,c){var d;Mt(a.e,b[c]);d=nA(a.d,a.e);b[c]=a.e.a;return d}\nfunction TG(a,b){var c;c=(bN(b,a.a.length),a.a[b]);LM(a.a,b,1);return c}\nfunction Oh(a){var b,c,d;b=a&$S;c=a>>22&$S;d=a<0?_S:0;return Qh(b,c,d)}\nfunction jh(a){var b=/%20/g;return encodeURIComponent(a).replace(b,'+')}\n", +"function Lc(){if(Date.now){return Date.now()}return (new Date).getTime()}\nfunction Tc(b){Qc();return function(){return Uc(b,this,arguments);var a}}\nfunction SK(a){this.d=a;this.b=this.d.a.entries();this.a=this.b.next()}\nfunction zR(a){this.a=a;sR.call(this,'buttonPagePrev','buttonPageNext')}\nfunction mc(a){if(a.k){a.backingJsObject!==oS&&a.fb();a.i=null}return a}\nfunction fx(a){var b;if(!dx(a)){throw dp(new KL)}b=a.d;a.d=null;return b}\nfunction Td(a){var b=a.parentNode;(!b||b.nodeType!=1)&&(b=null);return b}\nfunction gh(a){var b=/\\+/g;return decodeURIComponent(a.replace(b,'%20'))}\nfunction Wp(){var a;Yp(Up);if(!ic){a=FM((TC(_j),_j.k));jc(new Xp(a))}}\nfunction Mp(){$wnd.setTimeout(cS(it));Eq();Vp();Wp();BO(new EO)}\nfunction oQ(a){if(!a.j)return;!a.k&&(a.k=new xQ(a));Kq(a.k);Lq(a.k,500)}\nfunction MM(){if(Date.now){return Date.now()}return (new Date).getTime()}\nfunction _G(a){KG(this);XM(a>=0,'Initial capacity must not be negative')}\nfunction is(){gs.call(this,Vd($doc,'div'));this.n.className='gwt-Label'}\nfunction OL(a,b){this.b=', ';this.d=a;this.e=b;this.c=this.d+(''+this.e)}\nfunction Zw(a,b,c,d){this.xmin=a;this.ymin=b;this.xmax=a+c;this.ymax=b+d}\nfunction hx(){cx();this.a=Hh(Ji,GT,11,4,15,1);this.b=Hh(Ji,GT,11,4,15,1)}\nfunction xO(a,b){qO();if(!Qw(nO,a)||mO!=b){Pw(nO);Vw(nO,nO,a);mO=b;sO()}}\nfunction JH(a,b,c){var d;d=(bN(b,a.a.length),a.a[b]);Kh(a.a,b,c);return d}\nfunction XO(a,b){var c;c=vi(oF(a.d,b),69);!c&&rF(a.d,b,c=new iP);return c}\nfunction $t(a){var b;if(a.c>=a.a)return -1;b=a.d.Lb();b>=0&&++a.c;return b}\nfunction xL(a){UJ(a.c.a.c,a);aN(a.b!=a.c.a.b);a.a=a.b;a.b=a.b.a;return a.a}\nfunction _N(a,b){var c,d;for(d=b.Db();d.Gb();){c=vi(d.Hb(),41);sF(a.o,c)}}\nfunction Vc(a){a&&bd((_c(),$c));--Mc;if(a){if(Pc!=-1){Yc(Pc);Pc=-1}}}\nfunction de(a,b){a.currentStyle.direction=='rtl'&&(b=-b);a.scrollLeft=b}\nfunction Dr(a){a.style['left']='';a.style['top']='';a.style['position']=''}\nfunction CO(){!cc&&(cc=new dc);throw dp(new Ac('Canvas not supported!'))}\nfunction eN(a,b){if(a<0||a>b){throw dp(new DC('Index: '+a+', Size: '+b))}}\nfunction bN(a,b){if(a<0||a>=b){throw dp(new DC('Index: '+a+', Size: '+b))}}\nfunction $N(a,b){if(kT==kr(b.type)){null.Kc().Kc();Jd((us(),ys(null)).n,a)}}\nfunction sP(a,b){if(b.b==12)return vi(nF(a.g,b),70);return vi(nF(a.a,b),70)}\nfunction fR(a){a.c.c==null&&(a.c.c=dR(a.n));null.Kc();return null.Kc().Kc()}\nfunction Yq(){var a;if(Sq){a=new _q;!!Tq&&Sg(Tq,a);return null}return null}\nfunction yM(){var a;if(!uM){uM=new xM;a=new EM('');ZL();vM(uM,a)}return uM}\nfunction Ys(a,b){var c;for(c=0;c=0?a.e[b*qv+3]:0;return (c*(a.a-1)+(a.a-2))/255|0}\nfunction cL(a,b){var c;c=vi(sF(a.c,b),73);if(c){nL(c);return c.e}return null}\nfunction rp(a){var b;if(np(a)){b=0-a;if(!isNaN(b)){return b}}return hp(ei(a))}\nfunction au(a){var b,c;c=$t(a);if(c<0){return c}b=$t(a);return b>=0?c<<8|b:-1}\nfunction bu(a){var b,c;c=au(a);if(c<0){return c}b=$t(a);return b>=0?c<<8|b:-1}\nfunction du(a,b){var c,d;c=cu(Vt(a,b));d=a.d.Ob(b);a.c=xp(ep(a.c,d));return c}\nfunction Yz(a,b){a.a=YO(Vz,b,null);!a.a&&YO(Vz,b,new eA(a,b));a.c=0;return a}\nfunction rv(a,b,c){a.e=wq(b*c*qv);a.f=a.e.buffer;a.dataWidth=b;a.dataHeight=c}\nfunction iR(a){var b;b=a.b.d.n.selectedIndex;!!a.a&&qQ(a.a,b);a.b.d.n.blur()}\nfunction ct(a){if(a.b>=a.c.c){throw dp(new JL)}a.a=a.c.a[a.b];++a.b;return a.a}\nfunction WK(a){if(a.a.d!=a.c){return NK(a.a,a.b.value[0])}return a.b.value[1]}\nfunction QG(a,b,c){for(;c0?(cE(a,0,a.length,(QM(),PM))+eU).substr(0,4):eU}\nfunction Eh(a){return a.__elementTypeCategory$==null?10:a.__elementTypeCategory$}\nfunction $p(){var a,b;b=hr('logLevel');a=b==null?null:_L(b);if(a);else{ZL()}}\nfunction gC(a){var b,c;c=wq(a.length);for(b=0;b0&&(a.i.n=true);KO(a.i.a,a.e,b)}\nfunction ki(a){if(bi(a,(qi(),pi))<0){return -Zh(ei(a))}return a.l+a.m*bT+a.h*cT}\nfunction qi(){qi=Hp;mi=Qh($S,$S,524287);ni=Qh(0,0,aT);oi=Oh(1);Oh(2);pi=Oh(0)}\nfunction zh(){zh=Hp;yh=new Ah('RTL',0);xh=new Ah('LTR',1);wh=new Ah('DEFAULT',2)}\nfunction QM(){QM=Hp;PM=new UM;OM=new SM('ISO-LATIN-1');NM=new SM('ISO-8859-1')}\nfunction Wt(a){var b;b=null;(a.b==null||a.b.length!=4)&&(b=ex(new hx,a));return b}\nfunction Sd(a){var b=a.firstChild;while(b&&b.nodeType!=1)b=b.nextSibling;return b}\nfunction FB(a){switch(a){case 1:case 2:case 3:case 4:case 5:case 6:return;}}\nfunction qs(){ac.call(this,Vd($doc,'select'));this.n.className='gwt-ListBox'}\nfunction Ks(){var a;Js();Ls.call(this,(a=$doc.createElement('INPUT'),a.type=AT,a))}\nfunction sO(){var a,b;for(b=new uH(pO);b.a=a.border||b=14&&b<=16)));return a}\nfunction BP(a){var b;b=Ii($wnd.Math.floor(1/a));b=1>(12>24}\nfunction MB(a,b,c){var d;d=Lh(Dh(Li,1),KT,11,15,[b,c]);SA(a.d,d);return a.d.b.a.length}\nfunction Gt(a,b){var c;a.i=qA(new tA,b);for(c=0;c>24}}\nfunction rt(){rt=Hp;qt=new st('Default',0,'');pt=new st('ArrayBuffer',1,'arraybuffer')}\nfunction px(){mx();this.c=new Int16Array(nT);this.b=new Int8Array(64);this.a=new rx(this)}\nfunction dL(){tF(this);this.b=new oL(this);this.c=new hK;this.b.b=this.b;this.b.a=this.b}\nfunction cw(a,b){Kv();Nv.call(this);this.d=a;this.c=b*qv;this.a=a.d;this.b=a.g;this.e=a.o}\nfunction Ub(a,b){a.g&&(a.n.__listener=null,undefined);!!a.n&&Gb(a.n,b);a.n=b;a.g&&nr(a.n,a)}\nfunction GA(a,b){b=qE(b);if(b.length==0){a.a=null}else if(!dE(b,a.a)){a.a=b;CA(new xB(a.a))}}\nfunction yC(a,b){var c;c=a.a.length;bc&&(a.a+=vE(Hh(Ki,GT,11,b-c,15,1)))}\nfunction HP(a,b,c,d){var e,f;f=(d.width+a.b-1)/a.b|0;e=(d.height+a.b-1)/a.b|0;GP(a,b,c,f,e)}\nfunction Et(a,b,c){var d,e,f;f=1;e=1<>22);e=a.h+b.h+(d>>22);return Qh(c&$S,d&$S,e&_S)}\nfunction ji(a,b){var c,d,e;c=a.l-b.l;d=a.m-b.m+(c>>22);e=a.h-b.h+(d>>22);return Qh(c&$S,d&$S,e&_S)}\nfunction fz(a,b){var c,d;for(c=0,d=a.size();cb){return 1}if(a==b){return 0}return isNaN(a)?isNaN(b)?0:1:-1}\nfunction NC(a){if(dE(typeof a,lS)){return true}return a!=null&&a.$implements__java_lang_CharSequence}\nfunction Lh(a,b,c,d,e){e.Hc=a;e.Ic=b;e.Jc=Lp;e.__elementTypeId$=c;e.__elementTypeCategory$=d;return e}\nfunction Zv(a,b,c,d){a.d.e[a.c+a.a]=b<<24>>24;a.d.e[a.c+a.b]=c<<24>>24;a.d.e[a.c+a.e]=d<<24>>24}\nfunction fQ(a,b,c,d){if(b>=0&&b0&&(a.c=mS);d<0?(a.d=0):d>0&&(a.d=mS);qQ(a,b)}}\nfunction ky(a,b,c){var d;d=a.T.b.a.length;if(d<=b){while(d++a){throw dp(new qD('fromIndex: 0 > toIndex: '+a))}if(a>b){throw dp(new FC(yS+a+zS+b))}}\nfunction Dv(a,b){if(b<2||b>256){throw dp(new qD('(GBitmap::set_grays) Illegal number of gray levels'))}a.a=b}\nfunction St(a,b,c){if(0>b.length||c<0||c-b.length>0){throw dp(new CC)}Qt(a,a.b+c);HE(b,0,a.a,a.b,c);a.b+=c}\nfunction Th(a,b,c,d,e){var f;f=hi(a,b);c&&Wh(f);if(e){a=Vh(a,b);d?(Nh=ei(a)):(Nh=Qh(a.l,a.m,a.h))}return f}\nfunction _t(a,b){var c;if(b.length==0)return 0;c=a.d.Mb(b);c=KD(c,a.a-a.c);if(c>0){a.c+=c;return c}return -1}\nfunction wE(a,b){var c,d,e;hN(b,a.length);e='';for(d=0;d=0&&(a.n.style[nU]=b+'px',undefined);c>=0&&(a.n.style[SU]=c+'px',undefined)}\nfunction $s(a,b){var c;if(b<0||b>=a.c){throw dp(new CC)}--a.c;for(c=b;c0.999&&b<1.001){return}c=zw(b);for(d=0;d>24}}\nfunction ED(a){var b,c;if(a>-129&&a<128){b=a+128;c=(GD(),FD)[b];!c&&(c=FD[b]=new uD(a));return c}return new uD(a)}\nfunction By(a){var b,c,d;d=0;for(c=new uH(a.g.b);c.a=a.f){c=vi(TA(a.g,b-a.f),71)}else if(a.e){c=Cy(a.e,b)}else{throw dp(new sD(iU))}return c}\nfunction Ev(a,b){var c;if(a.border>>0).toString(16)}return a.toString()}\nfunction Pr(){Or.call(this,$doc.createElement(\"