diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index f194c9252..c2c40cf5a 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -553,9 +553,12 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (url == "/" ) return build_homepage(request); - if (isEndpointUrl(url, "skin")) + if (isEndpointUrl(url, "viewer") || isEndpointUrl(url, "skin")) return handle_skin(request); + if (url == "/viewer_settings.js") + return handle_viewer_settings(request); + if (isEndpointUrl(url, "content")) return handle_content(request); @@ -623,7 +626,7 @@ InternalServer::get_matching_if_none_match_etag(const RequestContext& r) const std::unique_ptr InternalServer::build_homepage(const RequestContext& request) { - return ContentResponse::build(*this, m_indexTemplateString, get_default_data(), "text/html; charset=utf-8", true); + return ContentResponse::build(*this, m_indexTemplateString, get_default_data(), "text/html; charset=utf-8"); } /** @@ -653,8 +656,7 @@ std::unique_ptr InternalServer::handle_suggest(const RequestContext& r if (archive == nullptr) { return HTTP404Response(*this, request) - + noSuchBookErrorMsg(bookName) - + TaskbarInfo(bookName); + + noSuchBookErrorMsg(bookName); } const auto queryString = request.get_optional_param("term", std::string()); @@ -714,13 +716,30 @@ std::unique_ptr InternalServer::handle_suggest(const RequestContext& r return std::move(response); } +std::unique_ptr InternalServer::handle_viewer_settings(const RequestContext& request) +{ + if (m_verbose.load()) { + printf("** running handle_viewer_settings\n"); + } + + const kainjow::mustache::object data{ + {"enable_toolbar", m_withTaskbar ? "true" : "false" }, + {"enable_link_blocking", m_blockExternalLinks ? "true" : "false" }, + {"enable_library_button", m_withLibraryButton ? "true" : "false" } + }; + return ContentResponse::build(*this, RESOURCE::templates::viewer_settings_js, data, "application/javascript; charset=utf-8"); +} + std::unique_ptr InternalServer::handle_skin(const RequestContext& request) { if (m_verbose.load()) { printf("** running handle_skin\n"); } - auto resourceName = request.get_url().substr(1); + const bool isRequestForViewer = request.get_url() == "/viewer"; + auto resourceName = isRequestForViewer + ? "viewer.html" + : request.get_url().substr(1); try { auto response = ContentResponse::build( *this, @@ -777,11 +796,15 @@ std::unique_ptr InternalServer::handle_search(const RequestContext& re "404-page-heading", cssUrl); response += nonParameterizedMessage("no-search-results"); + // XXX: Now this has to be handled by the iframe-based viewer which + // XXX: has to resolve if the book selection resulted in a single book. + /* if(bookIds.size() == 1) { auto bookId = *bookIds.begin(); auto bookName = mp_nameMapper->getNameForId(bookId); response += TaskbarInfo(bookName, mp_library->getArchiveById(bookId).get()); } + */ return response; } @@ -811,16 +834,18 @@ std::unique_ptr InternalServer::handle_search(const RequestContext& re renderer.setSearchProtocolPrefix(m_root + "/search"); renderer.setPageLength(pageLength); if (request.get_requested_format() == "xml") { - return ContentResponse::build(*this, renderer.getXml(), "application/rss+xml; charset=utf-8", - /*isHomePage =*/false, - /*raw =*/true); + return ContentResponse::build(*this, renderer.getXml(), "application/rss+xml; charset=utf-8"); } auto response = ContentResponse::build(*this, renderer.getHtml(), "text/html; charset=utf-8"); + // XXX: Now this has to be handled by the iframe-based viewer which + // XXX: has to resolve if the book selection resulted in a single book. + /* if(bookIds.size() == 1) { auto bookId = *bookIds.begin(); auto bookName = mp_nameMapper->getNameForId(bookId); response->set_taskbar(bookName, mp_library->getArchiveById(bookId).get()); } + */ return std::move(response); } catch (const Error& e) { return HTTP400Response(*this, request) @@ -852,8 +877,7 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re if (archive == nullptr) { return HTTP404Response(*this, request) - + noSuchBookErrorMsg(bookName) - + TaskbarInfo(bookName); + + noSuchBookErrorMsg(bookName); } try { @@ -861,8 +885,7 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re return build_redirect(bookName, getFinalItem(*archive, entry)); } catch(zim::EntryNotFound& e) { return HTTP404Response(*this, request) - + nonParameterizedMessage("random-article-failure") - + TaskbarInfo(bookName, archive.get()); + + nonParameterizedMessage("random-article-failure"); } } @@ -1010,8 +1033,7 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r const std::string searchURL = m_root + "/search?pattern=" + kiwix::urlEncode(pattern, true); return HTTP404Response(*this, request) + urlNotFoundMsg - + suggestSearchMsg(searchURL, kiwix::urlDecode(pattern)) - + TaskbarInfo(bookName); + + suggestSearchMsg(searchURL, kiwix::urlDecode(pattern)); } auto urlStr = url.substr(prefixLength + bookName.size()); @@ -1027,9 +1049,6 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r return build_redirect(bookName, getFinalItem(*archive, entry)); } auto response = ItemResponse::build(*this, request, entry.getItem()); - try { - dynamic_cast(*response).set_taskbar(bookName, archive.get()); - } catch (std::bad_cast& e) {} if (m_verbose.load()) { printf("Found %s\n", entry.getPath().c_str()); @@ -1044,8 +1063,7 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r std::string searchURL = m_root + "/search?content=" + bookName + "&pattern=" + kiwix::urlEncode(pattern, true); return HTTP404Response(*this, request) + urlNotFoundMsg - + suggestSearchMsg(searchURL, kiwix::urlDecode(pattern)) - + TaskbarInfo(bookName, archive.get()); + + suggestSearchMsg(searchURL, kiwix::urlDecode(pattern)); } } @@ -1093,13 +1111,13 @@ std::unique_ptr InternalServer::handle_raw(const RequestContext& reque try { if (kind == "meta") { auto item = archive->getMetadataItem(itemPath); - return ItemResponse::build(*this, request, item, /*raw=*/true); + return ItemResponse::build(*this, request, item); } else { auto entry = archive->getEntryByPath(itemPath); if (entry.isRedirect()) { return build_redirect(bookName, entry.getItem(true)); } - return ItemResponse::build(*this, request, entry.getItem(), /*raw=*/true); + return ItemResponse::build(*this, request, entry.getItem()); } } catch (zim::EntryNotFound& e ) { if (m_verbose.load()) { @@ -1136,9 +1154,7 @@ std::unique_ptr InternalServer::handle_locally_customized_resource(con return ContentResponse::build(*this, resourceData, - crd.mimeType, - /*isHomePage=*/false, - /*raw=*/true); + crd.mimeType); } } diff --git a/src/server/internalServer.h b/src/server/internalServer.h index 82574378c..a73a7d424 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -126,6 +126,7 @@ class InternalServer { std::unique_ptr handle_request(const RequestContext& request); std::unique_ptr build_redirect(const std::string& bookName, const zim::Item& item) const; std::unique_ptr build_homepage(const RequestContext& request); + std::unique_ptr handle_viewer_settings(const RequestContext& request); std::unique_ptr handle_skin(const RequestContext& request); std::unique_ptr handle_catalog(const RequestContext& request); std::unique_ptr handle_catalog_v2(const RequestContext& request); @@ -183,8 +184,8 @@ class InternalServer { std::unique_ptr m_customizedResources; friend std::unique_ptr Response::build(const InternalServer& server); - friend std::unique_ptr ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype, bool isHomePage, bool raw); - friend std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item, bool raw); + friend std::unique_ptr ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype); + friend std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item); }; } diff --git a/src/server/internalServer_catalog_v2.cpp b/src/server/internalServer_catalog_v2.cpp index bc7f1adae..b082dd1c1 100644 --- a/src/server/internalServer_catalog_v2.cpp +++ b/src/server/internalServer_catalog_v2.cpp @@ -158,7 +158,11 @@ std::unique_ptr InternalServer::handle_catalog_v2_illustration(const R auto book = mp_library->getBookByIdThreadSafe(bookId); auto size = request.get_argument("size"); auto illustration = book.getIllustration(size); - return ContentResponse::build(*this, illustration->getData(), illustration->mimeType); + return ContentResponse::build( + *this, + illustration->getData(), + illustration->mimeType + ); } catch(...) { return HTTP404Response(*this, request) + urlNotFoundMsg; diff --git a/src/server/response.cpp b/src/server/response.cpp index 4b53914c2..7067ff207 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -140,9 +140,6 @@ std::unique_ptr ContentResponseBlueprint::generateResponseObjec { auto r = ContentResponse::build(m_server, m_template, m_data, m_mimeType); r->set_code(m_httpStatusCode); - if ( m_taskbarInfo ) { - r->set_taskbar(m_taskbarInfo->bookName, m_taskbarInfo->archive); - } return r; } @@ -236,29 +233,12 @@ HTTP500Response::HTTP500Response(const InternalServer& server, std::unique_ptr HTTP500Response::generateResponseObject() const { - // We want a 500 response to be a minimalistic one (so that the server doesn't - // have to provide additional resources required for its proper rendering) - // ";raw=true" in the MIME-type below disables response decoration - // (see ContentResponse::contentDecorationAllowed()) - const std::string mimeType = "text/html;charset=utf-8;raw=true"; + const std::string mimeType = "text/html;charset=utf-8"; auto r = ContentResponse::build(m_server, m_template, m_data, mimeType); r->set_code(m_httpStatusCode); return r; } -ContentResponseBlueprint& ContentResponseBlueprint::operator+(const TaskbarInfo& taskbarInfo) -{ - this->m_taskbarInfo.reset(new TaskbarInfo(taskbarInfo)); - return *this; -} - -ContentResponseBlueprint& ContentResponseBlueprint::operator+=(const TaskbarInfo& taskbarInfo) -{ - // operator+() is already a state-modifying operator (akin to operator+=) - return *this + taskbarInfo; -} - - std::unique_ptr Response::build_416(const InternalServer& server, size_t resourceLength) { auto response = Response::build(server); @@ -337,52 +317,6 @@ void print_response_info(int retCode, MHD_Response* response) } -void ContentResponse::introduce_taskbar(const std::string& lang) -{ - i18n::GetTranslatedString t(lang); - kainjow::mustache::object data{ - {"root", m_root}, - {"content", m_bookName}, - {"hascontent", (!m_bookName.empty() && !m_bookTitle.empty())}, - {"title", m_bookTitle}, - {"withlibrarybutton", m_withLibraryButton}, - {"LIBRARY_BUTTON_TEXT", t("library-button-text")}, - {"HOME_BUTTON_TEXT", t("home-button-text", {{"BOOK_TITLE", m_bookTitle}}) }, - {"RANDOM_PAGE_BUTTON_TEXT", t("random-page-button-text") }, - {"SEARCHBOX_TOOLTIP", t("searchbox-tooltip", {{"BOOK_TITLE", m_bookTitle}}) }, - }; - auto head_content = render_template(RESOURCE::templates::head_taskbar_html, data); - m_content = prependToFirstOccurence( - m_content, - "", - head_content); - - auto taskbar_part = render_template(RESOURCE::templates::taskbar_part_html, data); - m_content = appendToFirstOccurence( - m_content, - "]*>", - taskbar_part); -} - - -void ContentResponse::inject_externallinks_blocker() -{ - kainjow::mustache::data data; - data.set("root", m_root); - auto script_tag = render_template(RESOURCE::templates::external_blocker_part_html, data); - m_content = prependToFirstOccurence( - m_content, - "", - script_tag); -} - -void ContentResponse::inject_root_link(){ - m_content = prependToFirstOccurence( - m_content, - "", - ""); -} - bool ContentResponse::can_compress(const RequestContext& request) const { @@ -391,16 +325,6 @@ ContentResponse::can_compress(const RequestContext& request) const && (m_content.size() > KIWIX_MIN_CONTENT_SIZE_TO_COMPRESS); } -bool -ContentResponse::contentDecorationAllowed() const -{ - if (m_raw) { - return false; - } - return (startsWith(m_mimeType, "text/html") - && m_mimeType.find(";raw=true") == std::string::npos); -} - MHD_Response* Response::create_mhd_response(const RequestContext& request) { @@ -411,17 +335,6 @@ Response::create_mhd_response(const RequestContext& request) MHD_Response* ContentResponse::create_mhd_response(const RequestContext& request) { - if (contentDecorationAllowed()) { - inject_root_link(); - - if (m_withTaskbar) { - introduce_taskbar(request.get_user_language()); - } - if (m_blockExternalLinks) { - inject_externallinks_blocker(); - } - } - const bool isCompressed = can_compress(request) && compress(m_content); MHD_Response* response = MHD_create_response_from_buffer( @@ -461,24 +374,11 @@ MHD_Result Response::send(const RequestContext& request, MHD_Connection* connect return ret; } -void ContentResponse::set_taskbar(const std::string& bookName, const zim::Archive* archive) -{ - m_bookName = bookName; - m_bookTitle = archive ? getArchiveTitle(*archive) : ""; -} - - -ContentResponse::ContentResponse(const std::string& root, bool verbose, bool raw, bool withTaskbar, bool withLibraryButton, bool blockExternalLinks, const std::string& content, const std::string& mimetype) : +ContentResponse::ContentResponse(const std::string& root, bool verbose, const std::string& content, const std::string& mimetype) : Response(verbose), m_root(root), m_content(content), - m_mimeType(mimetype), - m_raw(raw), - m_withTaskbar(withTaskbar), - m_withLibraryButton(withLibraryButton), - m_blockExternalLinks(blockExternalLinks), - m_bookName(""), - m_bookTitle("") + m_mimeType(mimetype) { add_header(MHD_HTTP_HEADER_CONTENT_TYPE, m_mimeType); } @@ -486,17 +386,11 @@ ContentResponse::ContentResponse(const std::string& root, bool verbose, bool raw std::unique_ptr ContentResponse::build( const InternalServer& server, const std::string& content, - const std::string& mimetype, - bool isHomePage, - bool raw) + const std::string& mimetype) { return std::unique_ptr(new ContentResponse( server.m_root, server.m_verbose.load(), - raw, - server.m_withTaskbar && !isHomePage, - server.m_withLibraryButton, - server.m_blockExternalLinks, content, mimetype)); } @@ -505,11 +399,10 @@ std::unique_ptr ContentResponse::build( const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, - const std::string& mimetype, - bool isHomePage) + const std::string& mimetype) { auto content = render_template(template_str, data); - return ContentResponse::build(server, content, mimetype, isHomePage); + return ContentResponse::build(server, content, mimetype); } ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) : @@ -522,14 +415,14 @@ ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::strin add_header(MHD_HTTP_HEADER_CONTENT_TYPE, m_mimeType); } -std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item, bool raw) +std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item) { const std::string mimetype = get_mime_type(item); auto byteRange = request.get_range().resolve(item.getSize()); const bool noRange = byteRange.kind() == ByteRange::RESOLVED_FULL_CONTENT; if (noRange && is_compressible_mime_type(mimetype)) { // Return a contentResponse - auto response = ContentResponse::build(server, item.getData(), mimetype, /*isHomePage=*/false, raw); + auto response = ContentResponse::build(server, item.getData(), mimetype); response->set_cacheable(); response->m_byteRange = byteRange; return std::move(response); diff --git a/src/server/response.h b/src/server/response.h index 85dc8cb8f..55c8fde4a 100644 --- a/src/server/response.h +++ b/src/server/response.h @@ -83,60 +83,32 @@ class ContentResponse : public Response { ContentResponse( const std::string& root, bool verbose, - bool raw, - bool withTaskbar, - bool withLibraryButton, - bool blockExternalLinks, const std::string& content, const std::string& mimetype); + static std::unique_ptr build( const InternalServer& server, const std::string& content, - const std::string& mimetype, - bool isHomePage = false, - bool raw = false); + const std::string& mimetype); + static std::unique_ptr build( const InternalServer& server, const std::string& template_str, kainjow::mustache::data data, - const std::string& mimetype, - bool isHomePage = false); - - void set_taskbar(const std::string& bookName, const zim::Archive* archive); + const std::string& mimetype); private: MHD_Response* create_mhd_response(const RequestContext& request); - void introduce_taskbar(const std::string& lang); - void inject_externallinks_blocker(); - void inject_root_link(); bool can_compress(const RequestContext& request) const; - bool contentDecorationAllowed() const; private: std::string m_root; std::string m_content; std::string m_mimeType; - bool m_raw; - bool m_withTaskbar; - bool m_withLibraryButton; - bool m_blockExternalLinks; - std::string m_bookName; - std::string m_bookTitle; }; -struct TaskbarInfo -{ - const std::string bookName; - const zim::Archive* const archive; - - TaskbarInfo(const std::string& bookName, const zim::Archive* a = nullptr) - : bookName(bookName) - , archive(a) - {} -}; - class ContentResponseBlueprint { public: // functions @@ -165,9 +137,6 @@ class ContentResponseBlueprint } - ContentResponseBlueprint& operator+(const TaskbarInfo& taskbarInfo); - ContentResponseBlueprint& operator+=(const TaskbarInfo& taskbarInfo); - protected: // functions std::string getMessage(const std::string& msgId) const; virtual std::unique_ptr generateResponseObject() const; @@ -179,7 +148,6 @@ class ContentResponseBlueprint const std::string m_mimeType; const std::string m_template; kainjow::mustache::data m_data; - std::unique_ptr m_taskbarInfo; }; struct HTTPErrorResponse : ContentResponseBlueprint @@ -191,8 +159,6 @@ struct HTTPErrorResponse : ContentResponseBlueprint const std::string& headingMsgId, const std::string& cssUrl = ""); - using ContentResponseBlueprint::operator+; - using ContentResponseBlueprint::operator+=; HTTPErrorResponse& operator+(const std::string& msg); HTTPErrorResponse& operator+(const ParameterizedMessage& errorDetails); HTTPErrorResponse& operator+=(const ParameterizedMessage& errorDetails); @@ -238,7 +204,7 @@ struct HTTP500Response : HTTPErrorResponse class ItemResponse : public Response { public: ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange); - static std::unique_ptr build(const InternalServer& server, const RequestContext& request, const zim::Item& item, bool raw = false); + static std::unique_ptr build(const InternalServer& server, const RequestContext& request, const zim::Item& item); private: MHD_Response* create_mhd_response(const RequestContext& request); diff --git a/src/tools/regexTools.cpp b/src/tools/regexTools.cpp index 36305cbbc..472dd4199 100644 --- a/src/tools/regexTools.cpp +++ b/src/tools/regexTools.cpp @@ -75,41 +75,3 @@ std::string replaceRegex(const std::string& content, uresult.toUTF8String(tmp); return tmp; } - -std::string appendToFirstOccurence(const std::string& content, - const std::string& regex, - const std::string& replacement) -{ - ucnv_setDefaultName("UTF-8"); - icu::UnicodeString ucontent(content.c_str()); - icu::UnicodeString ureplacement(replacement.c_str()); - auto matcher = buildMatcher(regex, ucontent); - if (matcher->find()) { - UErrorCode status = U_ZERO_ERROR; - ucontent.insert(matcher->end(status), ureplacement); - std::string tmp; - ucontent.toUTF8String(tmp); - return tmp; - } - - return content; -} - -std::string prependToFirstOccurence(const std::string& content, - const std::string& regex, - const std::string& replacement) -{ - ucnv_setDefaultName("UTF-8"); - icu::UnicodeString ucontent(content.c_str()); - icu::UnicodeString ureplacement(replacement.c_str()); - auto matcher = buildMatcher(regex, ucontent); - if (matcher->find()) { - UErrorCode status = U_ZERO_ERROR; - ucontent.insert(matcher->start(status), ureplacement); - std::string tmp; - ucontent.toUTF8String(tmp); - return tmp; - } - - return content; -} diff --git a/src/tools/regexTools.h b/src/tools/regexTools.h index 50ce28647..e4c61a43c 100644 --- a/src/tools/regexTools.h +++ b/src/tools/regexTools.h @@ -26,11 +26,5 @@ bool matchRegex(const std::string& content, const std::string& regex); std::string replaceRegex(const std::string& content, const std::string& replacement, const std::string& regex); -std::string appendToFirstOccurence(const std::string& content, - const std::string& regex, - const std::string& replacement); -std::string prependToFirstOccurence(const std::string& content, - const std::string& regex, - const std::string& replacement); #endif diff --git a/static/resources_list.txt b/static/resources_list.txt index 653fadd2f..4ff6cc3ce 100644 --- a/static/resources_list.txt +++ b/static/resources_list.txt @@ -4,7 +4,6 @@ skin/magnet.png skin/download.png skin/hash.png skin/search-icon.svg -skin/taskbar.js skin/iso6391To3.js skin/isotope.pkgd.min.js skin/index.js @@ -13,17 +12,16 @@ skin/taskbar.css skin/index.css skin/fonts/Poppins.ttf skin/fonts/Roboto.ttf -skin/block_external.js skin/search_results.css +skin/blank.html +skin/viewer.js +viewer.html templates/search_result.html templates/search_result.xml templates/error.html templates/error.xml templates/index.html templates/suggestion.json -templates/head_taskbar.html -templates/taskbar_part.html -templates/external_blocker_part.html templates/captured_external.html templates/catalog_entries.xml templates/catalog_v2_root.xml @@ -32,6 +30,7 @@ templates/catalog_v2_entry.xml templates/catalog_v2_categories.xml templates/catalog_v2_languages.xml templates/url_of_search_results_css +templates/viewer_settings.js opensearchdescription.xml ft_opensearchdescription.xml catalog_v2_searchdescription.xml diff --git a/static/skin/blank.html b/static/skin/blank.html new file mode 100644 index 000000000..8256d9c1d --- /dev/null +++ b/static/skin/blank.html @@ -0,0 +1,11 @@ + + + + + Blank page + + + + + + diff --git a/static/skin/block_external.js b/static/skin/block_external.js deleted file mode 100644 index 6bc32baec..000000000 --- a/static/skin/block_external.js +++ /dev/null @@ -1,74 +0,0 @@ -const root = document.querySelector( `link[type='root']` ).getAttribute("href"); -// `block_path` variable used by openzim/warc2zim to detect whether URL blocking is enabled or not -var block_path = `${root}/catch/external`; -// called only on external links -function capture_event(e, target) { target.setAttribute("href", encodeURI(block_path + "?source=" + target.href)); } - -// called on all link clicks. filters external and call capture_event -function on_click_event(e) { - var target = findParent("a", e.target); - if (target !== null && "href" in target) { - var href = target.href; - if (window.location.pathname.indexOf(block_path) == 0) // already in catch page - return; - if (href.indexOf(window.location.origin) == 0) - return; - if (href.substr(0, 2) == "//") - return capture_event(e, target); - if (href.substr(0, 5) == "http:") - return capture_event(e, target); - if (href.substr(0, 6) == "https:") - return capture_event(e, target); - return; - } -} - -// script entrypoint (called on document ready) -function run() { live('a', 'click', on_click_event); } - -// find first parent with tagname -function findParent(tagname, el) { - while (el) { - if ((el.nodeName || el.tagName).toLowerCase() === tagname.toLowerCase()) { - return el; - } - el = el.parentNode; - } - return null; -} - -// matches polyfill -this.Element && function(ElementPrototype) { - ElementPrototype.matches = ElementPrototype.matches || - ElementPrototype.matchesSelector || - ElementPrototype.webkitMatchesSelector || - ElementPrototype.msMatchesSelector || - function(selector) { - var node = this, nodes = (node.parentNode || node.document).querySelectorAll(selector), i = -1; - while (nodes[++i] && nodes[i] != node); - return !!nodes[i]; - } -}(Element.prototype); - -// helper for enabling IE 8 event bindings -function addEvent(el, type, handler) { - if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler); -} - -// live binding helper using matchesSelector -function live(selector, event, callback, context) { - addEvent(context || document, event, function(e) { - var found, el = e.target || e.srcElement; - while (el && el.matches && el !== context && !(found = el.matches(selector))) el = el.parentElement; - if (found) callback.call(el, e); - }); -} - -// in case the document is already rendered -if (document.readyState!='loading') run(); -// modern browsers -else if (document.addEventListener) document.addEventListener('DOMContentLoaded', run); -// IE <= 8 -else document.attachEvent('onreadystatechange', function(){ - if (document.readyState=='complete') run(); -}); diff --git a/static/skin/index.js b/static/skin/index.js index fd1c338ea..7f29b316e 100644 --- a/static/skin/index.js +++ b/static/skin/index.js @@ -110,6 +110,9 @@ } catch { downloadLink = ''; } + const bookName = link.split('/').pop(); + const viewerLink = `${root}/viewer#${bookName}`; + const humanFriendlyZimSize = humanFriendlySize(zimSize); const divTag = document.createElement('div'); @@ -122,7 +125,7 @@ const languageAttr = langCode != '' ? `title="${language}" aria-label="${language}"` : 'style="background-color: transparent"'; divTag.innerHTML = `
- +