@internetarchive/bookreader 5.0.0-18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +58 -0
- package/.gitattributes +2 -0
- package/.github/ISSUE_TEMPLATE/bug.md +32 -0
- package/.github/ISSUE_TEMPLATE/feature-request.md +30 -0
- package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +15 -0
- package/.github/dependabot.yml +8 -0
- package/.github/workflows/node.js.yml +37 -0
- package/.github/workflows/npm-publish.yml +47 -0
- package/.testcaferc.json +5 -0
- package/BookReader/BookReader.css +2983 -0
- package/BookReader/BookReader.js +3 -0
- package/BookReader/BookReader.js.LICENSE.txt +117 -0
- package/BookReader/BookReader.js.map +1 -0
- package/BookReader/bookreader-component-bundle.js +1436 -0
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +27 -0
- package/BookReader/bookreader-component-bundle.js.map +1 -0
- package/BookReader/icons/1up.svg +1 -0
- package/BookReader/icons/2up.svg +1 -0
- package/BookReader/icons/advance.svg +3 -0
- package/BookReader/icons/chevron-right.svg +1 -0
- package/BookReader/icons/close-circle-dark.svg +1 -0
- package/BookReader/icons/close-circle.svg +1 -0
- package/BookReader/icons/fullscreen.svg +1 -0
- package/BookReader/icons/fullscreen_exit.svg +1 -0
- package/BookReader/icons/hamburger.svg +1 -0
- package/BookReader/icons/left-arrow.svg +1 -0
- package/BookReader/icons/magnify-minus.svg +1 -0
- package/BookReader/icons/magnify-plus.svg +1 -0
- package/BookReader/icons/magnify.svg +1 -0
- package/BookReader/icons/pause.svg +1 -0
- package/BookReader/icons/play.svg +1 -0
- package/BookReader/icons/playback-speed.svg +1 -0
- package/BookReader/icons/read-aloud.svg +1 -0
- package/BookReader/icons/review.svg +3 -0
- package/BookReader/icons/thumbnails.svg +1 -0
- package/BookReader/icons/volume-full.svg +1 -0
- package/BookReader/images/BRicons.png +0 -0
- package/BookReader/images/BRicons.svg +5 -0
- package/BookReader/images/BRicons_ia.png +0 -0
- package/BookReader/images/back_pages.png +0 -0
- package/BookReader/images/book_bottom_icon.png +0 -0
- package/BookReader/images/book_down_icon.png +0 -0
- package/BookReader/images/book_left_icon.png +0 -0
- package/BookReader/images/book_leftmost_icon.png +0 -0
- package/BookReader/images/book_right_icon.png +0 -0
- package/BookReader/images/book_rightmost_icon.png +0 -0
- package/BookReader/images/book_top_icon.png +0 -0
- package/BookReader/images/book_up_icon.png +0 -0
- package/BookReader/images/books_graphic.svg +1 -0
- package/BookReader/images/booksplit.png +0 -0
- package/BookReader/images/control_pause_icon.png +0 -0
- package/BookReader/images/control_play_icon.png +0 -0
- package/BookReader/images/embed_icon.png +0 -0
- package/BookReader/images/icon-home-ia.png +0 -0
- package/BookReader/images/icon_OL-logo-xs.png +0 -0
- package/BookReader/images/icon_alert-xs.png +0 -0
- package/BookReader/images/icon_book.svg +1 -0
- package/BookReader/images/icon_bookmark.svg +1 -0
- package/BookReader/images/icon_close-pop.png +0 -0
- package/BookReader/images/icon_download.png +0 -0
- package/BookReader/images/icon_gear.svg +1 -0
- package/BookReader/images/icon_hamburger.svg +1 -0
- package/BookReader/images/icon_home.png +0 -0
- package/BookReader/images/icon_home.svg +1 -0
- package/BookReader/images/icon_home_ia.png +0 -0
- package/BookReader/images/icon_indicator.png +0 -0
- package/BookReader/images/icon_info.svg +1 -0
- package/BookReader/images/icon_one_page.svg +1 -0
- package/BookReader/images/icon_pause.svg +1 -0
- package/BookReader/images/icon_play.svg +1 -0
- package/BookReader/images/icon_playback-rate.svg +1 -0
- package/BookReader/images/icon_return.png +0 -0
- package/BookReader/images/icon_search_button.svg +1 -0
- package/BookReader/images/icon_share.svg +1 -0
- package/BookReader/images/icon_skip-ahead.svg +1 -0
- package/BookReader/images/icon_skip-back.svg +2 -0
- package/BookReader/images/icon_speaker.svg +1 -0
- package/BookReader/images/icon_speaker_open.svg +1 -0
- package/BookReader/images/icon_thumbnails.svg +1 -0
- package/BookReader/images/icon_toc.svg +1 -0
- package/BookReader/images/icon_two_pages.svg +1 -0
- package/BookReader/images/icon_zoomer.png +0 -0
- package/BookReader/images/loading.gif +0 -0
- package/BookReader/images/logo_icon.png +0 -0
- package/BookReader/images/marker_chap-off.png +0 -0
- package/BookReader/images/marker_chap-off.svg +1 -0
- package/BookReader/images/marker_chap-off_ia.png +0 -0
- package/BookReader/images/marker_chap-on.png +0 -0
- package/BookReader/images/marker_chap-on.svg +1 -0
- package/BookReader/images/marker_srch-on.svg +1 -0
- package/BookReader/images/marker_srchchap-off.png +0 -0
- package/BookReader/images/marker_srchchap-on.png +0 -0
- package/BookReader/images/nav_control-dn.png +0 -0
- package/BookReader/images/nav_control-dn_ia.png +0 -0
- package/BookReader/images/nav_control-up.png +0 -0
- package/BookReader/images/nav_control-up_ia.png +0 -0
- package/BookReader/images/nav_control.png +0 -0
- package/BookReader/images/one_page_mode_icon.png +0 -0
- package/BookReader/images/paper-badge.png +0 -0
- package/BookReader/images/print_icon.png +0 -0
- package/BookReader/images/progressbar.gif +0 -0
- package/BookReader/images/right_edges.png +0 -0
- package/BookReader/images/slider.png +0 -0
- package/BookReader/images/slider_ia.png +0 -0
- package/BookReader/images/thumbnail_mode_icon.png +0 -0
- package/BookReader/images/transparent.png +0 -0
- package/BookReader/images/two_page_mode_icon.png +0 -0
- package/BookReader/images/zoom_in_icon.png +0 -0
- package/BookReader/images/zoom_out_icon.png +0 -0
- package/BookReader/jquery-1.10.1.js +2 -0
- package/BookReader/jquery-1.10.1.js.LICENSE.txt +24 -0
- package/BookReader/plugins/plugin.archive_analytics.js +2 -0
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -0
- package/BookReader/plugins/plugin.autoplay.js +2 -0
- package/BookReader/plugins/plugin.autoplay.js.map +1 -0
- package/BookReader/plugins/plugin.chapters.js +2 -0
- package/BookReader/plugins/plugin.chapters.js.map +1 -0
- package/BookReader/plugins/plugin.iframe.js +2 -0
- package/BookReader/plugins/plugin.iframe.js.map +1 -0
- package/BookReader/plugins/plugin.mobile_nav.js +2 -0
- package/BookReader/plugins/plugin.mobile_nav.js.map +1 -0
- package/BookReader/plugins/plugin.resume.js +2 -0
- package/BookReader/plugins/plugin.resume.js.map +1 -0
- package/BookReader/plugins/plugin.search.js +2 -0
- package/BookReader/plugins/plugin.search.js.map +1 -0
- package/BookReader/plugins/plugin.text_selection.js +2 -0
- package/BookReader/plugins/plugin.text_selection.js.map +1 -0
- package/BookReader/plugins/plugin.tts.js +3 -0
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +27 -0
- package/BookReader/plugins/plugin.tts.js.map +1 -0
- package/BookReader/plugins/plugin.url.js +2 -0
- package/BookReader/plugins/plugin.url.js.map +1 -0
- package/BookReader/plugins/plugin.vendor-fullscreen.js +2 -0
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -0
- package/BookReaderDemo/BookReaderDemo.css +41 -0
- package/BookReaderDemo/BookReaderJSAdvanced.js +115 -0
- package/BookReaderDemo/BookReaderJSAutoplay.js +56 -0
- package/BookReaderDemo/BookReaderJSSimple.js +55 -0
- package/BookReaderDemo/IIIFBookReader.js +207 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +1 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +1 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +1 -0
- package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +1 -0
- package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
- package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
- package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
- package/BookReaderDemo/bookreader-template-bundle.js +7178 -0
- package/BookReaderDemo/demo-advanced.html +32 -0
- package/BookReaderDemo/demo-autoplay.html +38 -0
- package/BookReaderDemo/demo-embed-iframe-src.html +84 -0
- package/BookReaderDemo/demo-embed.html +26 -0
- package/BookReaderDemo/demo-fullscreen-mobile.html +36 -0
- package/BookReaderDemo/demo-fullscreen.html +33 -0
- package/BookReaderDemo/demo-iiif.html +34 -0
- package/BookReaderDemo/demo-iiif.js +26 -0
- package/BookReaderDemo/demo-internetarchive.html +74 -0
- package/BookReaderDemo/demo-multiple.html +43 -0
- package/BookReaderDemo/demo-preview-pages.html +1092 -0
- package/BookReaderDemo/demo-simple.html +34 -0
- package/BookReaderDemo/demo-vendor-fullscreen.html +36 -0
- package/BookReaderDemo/immersion-1up.html +64 -0
- package/BookReaderDemo/immersion-mode.html +35 -0
- package/BookReaderDemo/toggle_controls.html +53 -0
- package/BookReaderDemo/view_mode.html +39 -0
- package/BookReaderDemo/viewmode-cycle.html +41 -0
- package/CHANGELOG.md +540 -0
- package/CONTRIBUTING.md +7 -0
- package/LICENSE +661 -0
- package/README.md +205 -0
- package/babel.config.js +18 -0
- package/codecov.yml +17 -0
- package/index.html +31 -0
- package/jsconfig.json +14 -0
- package/karma.conf.js +23 -0
- package/package.json +129 -0
- package/screenshot.png +0 -0
- package/scripts/postversion.js +10 -0
- package/scripts/preversion.js +14 -0
- package/scripts/version.js +26 -0
- package/src/BookNavigator/BookModel.js +14 -0
- package/src/BookNavigator/BookNavigator.js +468 -0
- package/src/BookNavigator/assets/book-loader.js +27 -0
- package/src/BookNavigator/assets/bookmark-colors.js +15 -0
- package/src/BookNavigator/assets/button-base.js +61 -0
- package/src/BookNavigator/assets/icon_checkmark.js +6 -0
- package/src/BookNavigator/assets/icon_close.js +3 -0
- package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
- package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
- package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
- package/src/BookNavigator/assets/icon_volumes.js +11 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +64 -0
- package/src/BookNavigator/bookmarks/bookmark-edit.js +215 -0
- package/src/BookNavigator/bookmarks/bookmarks-list.js +285 -0
- package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +53 -0
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +500 -0
- package/src/BookNavigator/br-fullscreen-mgr.js +83 -0
- package/src/BookNavigator/delete-modal-actions.js +49 -0
- package/src/BookNavigator/downloads/downloads-provider.js +76 -0
- package/src/BookNavigator/downloads/downloads.js +138 -0
- package/src/BookNavigator/search/a-search-result.js +55 -0
- package/src/BookNavigator/search/search-provider.js +180 -0
- package/src/BookNavigator/search/search-results.js +360 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +93 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
- package/src/BookNavigator/volumes/volumes-provider.js +83 -0
- package/src/BookNavigator/volumes/volumes.js +178 -0
- package/src/BookReader/BookModel.js +518 -0
- package/src/BookReader/DebugConsole.js +54 -0
- package/src/BookReader/ImageCache.js +116 -0
- package/src/BookReader/Mode1Up.js +90 -0
- package/src/BookReader/Mode1UpLit.js +434 -0
- package/src/BookReader/Mode2Up.js +1372 -0
- package/src/BookReader/ModeSmoothZoom.js +177 -0
- package/src/BookReader/ModeThumb.js +336 -0
- package/src/BookReader/Navbar/Navbar.js +339 -0
- package/src/BookReader/PageContainer.js +120 -0
- package/src/BookReader/ReduceSet.js +26 -0
- package/src/BookReader/Toolbar/Toolbar.js +384 -0
- package/src/BookReader/events.js +20 -0
- package/src/BookReader/options.js +320 -0
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/classes.js +36 -0
- package/src/BookReader/utils.js +240 -0
- package/src/BookReader.js +2546 -0
- package/src/BookReaderComponent/BookReaderComponent.js +112 -0
- package/src/ItemNavigator/ItemNavigator.js +376 -0
- package/src/ItemNavigator/providers/sharing.js +33 -0
- package/src/assets/icons/1up.svg +12 -0
- package/src/assets/icons/2up.svg +15 -0
- package/src/assets/icons/advance.svg +26 -0
- package/src/assets/icons/chevron-right.svg +1 -0
- package/src/assets/icons/close-circle-dark.svg +1 -0
- package/src/assets/icons/close-circle.svg +1 -0
- package/src/assets/icons/fullscreen.svg +17 -0
- package/src/assets/icons/fullscreen_exit.svg +17 -0
- package/src/assets/icons/hamburger.svg +15 -0
- package/src/assets/icons/left-arrow.svg +12 -0
- package/src/assets/icons/magnify-minus.svg +16 -0
- package/src/assets/icons/magnify-plus.svg +17 -0
- package/src/assets/icons/magnify.svg +15 -0
- package/src/assets/icons/pause.svg +23 -0
- package/src/assets/icons/play.svg +22 -0
- package/src/assets/icons/playback-speed.svg +34 -0
- package/src/assets/icons/read-aloud.svg +22 -0
- package/src/assets/icons/review.svg +22 -0
- package/src/assets/icons/thumbnails.svg +17 -0
- package/src/assets/icons/volume-full.svg +22 -0
- package/src/assets/images/BRicons.png +0 -0
- package/src/assets/images/BRicons.svg +94 -0
- package/src/assets/images/BRicons_ia.png +0 -0
- package/src/assets/images/back_pages.png +0 -0
- package/src/assets/images/book_bottom_icon.png +0 -0
- package/src/assets/images/book_down_icon.png +0 -0
- package/src/assets/images/book_left_icon.png +0 -0
- package/src/assets/images/book_leftmost_icon.png +0 -0
- package/src/assets/images/book_right_icon.png +0 -0
- package/src/assets/images/book_rightmost_icon.png +0 -0
- package/src/assets/images/book_top_icon.png +0 -0
- package/src/assets/images/book_up_icon.png +0 -0
- package/src/assets/images/books_graphic.svg +177 -0
- package/src/assets/images/booksplit.png +0 -0
- package/src/assets/images/control_pause_icon.png +0 -0
- package/src/assets/images/control_play_icon.png +0 -0
- package/src/assets/images/embed_icon.png +0 -0
- package/src/assets/images/icon-home-ia.png +0 -0
- package/src/assets/images/icon_OL-logo-xs.png +0 -0
- package/src/assets/images/icon_alert-xs.png +0 -0
- package/src/assets/images/icon_book.svg +12 -0
- package/src/assets/images/icon_bookmark.svg +12 -0
- package/src/assets/images/icon_close-pop.png +0 -0
- package/src/assets/images/icon_download.png +0 -0
- package/src/assets/images/icon_gear.svg +14 -0
- package/src/assets/images/icon_hamburger.svg +20 -0
- package/src/assets/images/icon_home.png +0 -0
- package/src/assets/images/icon_home.svg +21 -0
- package/src/assets/images/icon_home_ia.png +0 -0
- package/src/assets/images/icon_indicator.png +0 -0
- package/src/assets/images/icon_info.svg +11 -0
- package/src/assets/images/icon_one_page.svg +8 -0
- package/src/assets/images/icon_pause.svg +1 -0
- package/src/assets/images/icon_play.svg +1 -0
- package/src/assets/images/icon_playback-rate.svg +15 -0
- package/src/assets/images/icon_return.png +0 -0
- package/src/assets/images/icon_search_button.svg +8 -0
- package/src/assets/images/icon_share.svg +9 -0
- package/src/assets/images/icon_skip-ahead.svg +6 -0
- package/src/assets/images/icon_skip-back.svg +13 -0
- package/src/assets/images/icon_speaker.svg +18 -0
- package/src/assets/images/icon_speaker_open.svg +10 -0
- package/src/assets/images/icon_thumbnails.svg +12 -0
- package/src/assets/images/icon_toc.svg +5 -0
- package/src/assets/images/icon_two_pages.svg +9 -0
- package/src/assets/images/icon_zoomer.png +0 -0
- package/src/assets/images/loading.gif +0 -0
- package/src/assets/images/logo_icon.png +0 -0
- package/src/assets/images/marker_chap-off.png +0 -0
- package/src/assets/images/marker_chap-off.svg +11 -0
- package/src/assets/images/marker_chap-off_ia.png +0 -0
- package/src/assets/images/marker_chap-on.png +0 -0
- package/src/assets/images/marker_chap-on.svg +11 -0
- package/src/assets/images/marker_srch-on.svg +11 -0
- package/src/assets/images/marker_srchchap-off.png +0 -0
- package/src/assets/images/marker_srchchap-on.png +0 -0
- package/src/assets/images/nav_control-dn.png +0 -0
- package/src/assets/images/nav_control-dn_ia.png +0 -0
- package/src/assets/images/nav_control-up.png +0 -0
- package/src/assets/images/nav_control-up_ia.png +0 -0
- package/src/assets/images/nav_control.png +0 -0
- package/src/assets/images/one_page_mode_icon.png +0 -0
- package/src/assets/images/paper-badge.png +0 -0
- package/src/assets/images/print_icon.png +0 -0
- package/src/assets/images/progressbar.gif +0 -0
- package/src/assets/images/right_edges.png +0 -0
- package/src/assets/images/slider.png +0 -0
- package/src/assets/images/slider_ia.png +0 -0
- package/src/assets/images/thumbnail_mode_icon.png +0 -0
- package/src/assets/images/transparent.png +0 -0
- package/src/assets/images/two_page_mode_icon.png +0 -0
- package/src/assets/images/zoom_in_icon.png +0 -0
- package/src/assets/images/zoom_out_icon.png +0 -0
- package/src/css/BookReader.scss +89 -0
- package/src/css/_BRBookmarks.scss +29 -0
- package/src/css/_BRComponent.scss +13 -0
- package/src/css/_BRfloat.scss +197 -0
- package/src/css/_BRicon.scss +48 -0
- package/src/css/_BRmain.scss +251 -0
- package/src/css/_BRnav.scss +382 -0
- package/src/css/_BRpages.scss +139 -0
- package/src/css/_BRsearch.scss +226 -0
- package/src/css/_BRtoolbar.scss +84 -0
- package/src/css/_BRvendor.scss +5 -0
- package/src/css/_MobileNav.scss +194 -0
- package/src/css/_TextSelection.scss +32 -0
- package/src/css/_colorbox.scss +52 -0
- package/src/css/_controls.scss +244 -0
- package/src/css/_icons.scss +121 -0
- package/src/dragscrollable-br.js +261 -0
- package/src/jquery-wrapper.js +4 -0
- package/src/plugins/plugin.archive_analytics.js +86 -0
- package/src/plugins/plugin.autoplay.js +129 -0
- package/src/plugins/plugin.chapters.js +251 -0
- package/src/plugins/plugin.iframe.js +48 -0
- package/src/plugins/plugin.mobile_nav.js +287 -0
- package/src/plugins/plugin.resume.js +68 -0
- package/src/plugins/plugin.text_selection.js +291 -0
- package/src/plugins/plugin.url.js +198 -0
- package/src/plugins/plugin.vendor-fullscreen.js +247 -0
- package/src/plugins/search/plugin.search.js +439 -0
- package/src/plugins/search/view.js +440 -0
- package/src/plugins/tts/AbstractTTSEngine.js +242 -0
- package/src/plugins/tts/FestivalTTSEngine.js +169 -0
- package/src/plugins/tts/PageChunk.js +107 -0
- package/src/plugins/tts/PageChunkIterator.js +163 -0
- package/src/plugins/tts/WebTTSEngine.js +352 -0
- package/src/plugins/tts/plugin.tts.js +335 -0
- package/src/plugins/tts/tooltip_dict.js +15 -0
- package/src/plugins/tts/utils.js +91 -0
- package/src/util/browserSniffing.js +30 -0
- package/src/util/debouncer.js +26 -0
- package/src/util/docCookies.js +67 -0
- package/src/util/strings.js +34 -0
- package/tests/BookReader/BookModel.test.js +312 -0
- package/tests/BookReader/BookReaderPublicFunctions.test.js +164 -0
- package/tests/BookReader/DebugConsole.test.js +25 -0
- package/tests/BookReader/ImageCache.test.js +150 -0
- package/tests/BookReader/Mode1UpLit.test.js +87 -0
- package/tests/BookReader/Mode2Up.test.js +245 -0
- package/tests/BookReader/ModeSmoothZoom.test.js +149 -0
- package/tests/BookReader/Navbar/Navbar.test.js +169 -0
- package/tests/BookReader/PageContainer.test.js +187 -0
- package/tests/BookReader/ReduceSet.test.js +38 -0
- package/tests/BookReader/Toolbar/Toolbar.test.js +26 -0
- package/tests/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
- package/tests/BookReader/utils/classes.test.js +88 -0
- package/tests/BookReader/utils.test.js +136 -0
- package/tests/BookReader.options.test.js +39 -0
- package/tests/BookReader.test.js +301 -0
- package/tests/e2e/README.md +75 -0
- package/tests/e2e/autoplay.test.js +13 -0
- package/tests/e2e/base.test.js +35 -0
- package/tests/e2e/helpers/base.js +263 -0
- package/tests/e2e/helpers/debug.js +13 -0
- package/tests/e2e/helpers/desktopSearch.js +72 -0
- package/tests/e2e/helpers/mobileSearch.js +85 -0
- package/tests/e2e/helpers/mockSearch.js +93 -0
- package/tests/e2e/helpers/rightToLeft.js +29 -0
- package/tests/e2e/ia-production/ia-prod-base.js +17 -0
- package/tests/e2e/models/BookReader.js +11 -0
- package/tests/e2e/models/Navigation.js +56 -0
- package/tests/e2e/rightToLeft.test.js +20 -0
- package/tests/e2e/viewmode.test.js +37 -0
- package/tests/karma/BookNavigator/book-navigator.test.js +180 -0
- package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +133 -0
- package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +222 -0
- package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +64 -0
- package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
- package/tests/karma/BookNavigator/search/search-provider.test.js +23 -0
- package/tests/karma/BookNavigator/search/search-results.test.js +240 -0
- package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +40 -0
- package/tests/karma/BookNavigator/visual-adjustments.test.js +201 -0
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +160 -0
- package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
- package/tests/plugins/plugin.archive_analytics.test.js +23 -0
- package/tests/plugins/plugin.autoplay.test.js +52 -0
- package/tests/plugins/plugin.chapters.test.js +130 -0
- package/tests/plugins/plugin.iframe.test.js +42 -0
- package/tests/plugins/plugin.mobile_nav.test.js +66 -0
- package/tests/plugins/plugin.resume.test.js +98 -0
- package/tests/plugins/plugin.text_selection.test.js +193 -0
- package/tests/plugins/plugin.url.test.js +129 -0
- package/tests/plugins/plugin.vendor-fullscreen.test.js +65 -0
- package/tests/plugins/search/plugin.search.test.js +173 -0
- package/tests/plugins/search/plugin.search.view.test.js +106 -0
- package/tests/plugins/tts/AbstractTTSEngine.test.js +153 -0
- package/tests/plugins/tts/FestivalTTSEngine.test.js +59 -0
- package/tests/plugins/tts/PageChunk.test.js +57 -0
- package/tests/plugins/tts/PageChunkIterator.test.js +179 -0
- package/tests/plugins/tts/WebTTSEngine.test.js +126 -0
- package/tests/plugins/tts/utils.test.js +133 -0
- package/tests/util/browserSniffing.test.js +56 -0
- package/tests/util/docCookies.test.js +15 -0
- package/tests/util/strings.test.js +63 -0
- package/tests/utils.js +80 -0
- package/webpack.config.js +85 -0
@@ -0,0 +1,518 @@
|
|
1
|
+
// @ts-check
|
2
|
+
import { DEFAULT_OPTIONS } from './options.js';
|
3
|
+
import { clamp } from './utils.js';
|
4
|
+
/** @typedef {import('./options.js').PageData} PageData */
|
5
|
+
/** @typedef {import('../BookReader.js').default} BookReader */
|
6
|
+
|
7
|
+
// URI to display when a page is not viewable.
|
8
|
+
// TODO Render configurable html for the user instead.
|
9
|
+
// FIXME Don't reference files on archive.org
|
10
|
+
const UNVIEWABLE_PAGE_URI = '/bookreader/static/preview-default.png';
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Contains information about the Book/Document independent of the way it is
|
14
|
+
* being rendering. Nothing here should reference e.g. the mode, zoom, etc.
|
15
|
+
* It's just information about the book and its pages (usually as specified
|
16
|
+
* in the BookReader data option.)
|
17
|
+
*/
|
18
|
+
export class BookModel {
|
19
|
+
/**
|
20
|
+
* @param {BookReader} br
|
21
|
+
*/
|
22
|
+
constructor(br) {
|
23
|
+
this.br = br;
|
24
|
+
this.reduceSet = br.reduceSet;
|
25
|
+
this.ppi = br.options?.ppi ?? DEFAULT_OPTIONS.ppi;
|
26
|
+
|
27
|
+
/** @type {{width: number, height: number}} memoize storage */
|
28
|
+
this._medianPageSize = null;
|
29
|
+
/** @deprecated @type {{width: number, height: number}} memoize storage */
|
30
|
+
this._medianPageSizePixels = null;
|
31
|
+
/** @type {[PageData[], number]} */
|
32
|
+
this._getDataFlattenedCached = null;
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @deprecated Use getMedianPageSizeInches
|
37
|
+
* Memoized
|
38
|
+
* @return {{width: number, height: number}}
|
39
|
+
*/
|
40
|
+
getMedianPageSize() {
|
41
|
+
if (this._medianPageSizePixels) {
|
42
|
+
return this._medianPageSizePixels;
|
43
|
+
}
|
44
|
+
|
45
|
+
// A little expensive but we just do it once
|
46
|
+
const widths = [];
|
47
|
+
const heights = [];
|
48
|
+
for (let i = 0; i < this.getNumLeafs(); i++) {
|
49
|
+
widths.push(this.getPageWidth(i));
|
50
|
+
heights.push(this.getPageHeight(i));
|
51
|
+
}
|
52
|
+
|
53
|
+
widths.sort();
|
54
|
+
heights.sort();
|
55
|
+
this._medianPageSizePixels = {
|
56
|
+
width: widths[Math.floor(widths.length / 2)],
|
57
|
+
height: heights[Math.floor(heights.length / 2)]
|
58
|
+
};
|
59
|
+
return this._medianPageSizePixels;
|
60
|
+
}
|
61
|
+
|
62
|
+
/** Get median width/height of page in inches. Memoized for performance. */
|
63
|
+
getMedianPageSizeInches() {
|
64
|
+
if (this._medianPageSize) {
|
65
|
+
return this._medianPageSize;
|
66
|
+
}
|
67
|
+
|
68
|
+
const widths = [];
|
69
|
+
const heights = [];
|
70
|
+
for (const page of this.pagesIterator()) {
|
71
|
+
widths.push(page.widthInches);
|
72
|
+
heights.push(page.heightInches);
|
73
|
+
}
|
74
|
+
|
75
|
+
widths.sort();
|
76
|
+
heights.sort();
|
77
|
+
|
78
|
+
this._medianPageSize = {
|
79
|
+
width: widths[Math.floor(widths.length / 2)],
|
80
|
+
height: heights[Math.floor(heights.length / 2)]
|
81
|
+
};
|
82
|
+
return this._medianPageSize;
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Returns the page width for the given index, or first or last page if out of range
|
87
|
+
* @deprecated see getPageWidth
|
88
|
+
* @param {PageIndex} index
|
89
|
+
*/
|
90
|
+
_getPageWidth(index) {
|
91
|
+
// Synthesize a page width for pages not actually present in book.
|
92
|
+
// May or may not be the best approach.
|
93
|
+
// If index is out of range we return the width of first or last page
|
94
|
+
index = clamp(index, 0, this.getNumLeafs() - 1);
|
95
|
+
return this.getPageWidth(index);
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Returns the page height for the given index, or first or last page if out of range
|
100
|
+
* @deprecated see getPageHeight
|
101
|
+
* @param {PageIndex} index
|
102
|
+
*/
|
103
|
+
_getPageHeight(index) {
|
104
|
+
const clampedIndex = clamp(index, 0, this.getNumLeafs() - 1);
|
105
|
+
return this.getPageHeight(clampedIndex);
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Returns the *highest* index the given page number, or undefined
|
110
|
+
* @param {PageNumString} pageNum
|
111
|
+
* @return {PageIndex|undefined}
|
112
|
+
*/
|
113
|
+
getPageIndex(pageNum) {
|
114
|
+
const pageIndices = this.getPageIndices(pageNum);
|
115
|
+
return pageIndices.length ? pageIndices[pageIndices.length - 1] : undefined;
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Returns an array (possibly empty) of the indices with the given page number
|
120
|
+
* @param {PageNumString} pageNum
|
121
|
+
* @return {PageIndex[]}
|
122
|
+
*/
|
123
|
+
getPageIndices(pageNum) {
|
124
|
+
const indices = [];
|
125
|
+
|
126
|
+
// Check for special "nXX" page number
|
127
|
+
if (pageNum.slice(0,1) == 'n') {
|
128
|
+
try {
|
129
|
+
const pageIntStr = pageNum.slice(1, pageNum.length);
|
130
|
+
const pageIndex = parseInt(pageIntStr);
|
131
|
+
indices.push(pageIndex);
|
132
|
+
return indices;
|
133
|
+
} catch (err) {
|
134
|
+
// Do nothing... will run through page names and see if one matches
|
135
|
+
}
|
136
|
+
}
|
137
|
+
|
138
|
+
for (let i = 0; i < this.getNumLeafs(); i++) {
|
139
|
+
if (this.getPageNum(i) == pageNum) {
|
140
|
+
indices.push(i);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
return indices;
|
145
|
+
}
|
146
|
+
|
147
|
+
/**
|
148
|
+
* Returns the name of the page as it should be displayed in the user interface
|
149
|
+
* @param {PageIndex} index
|
150
|
+
* @return {string}
|
151
|
+
*/
|
152
|
+
getPageName(index) {
|
153
|
+
return 'Page ' + this.getPageNum(index);
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* @return {number} the total number of leafs (like an array length)
|
158
|
+
*/
|
159
|
+
getNumLeafs() {
|
160
|
+
// For deprecated interface support, if numLeafs is set, use that.
|
161
|
+
if (this.br.numLeafs !== undefined)
|
162
|
+
return this.br.numLeafs;
|
163
|
+
return this._getDataFlattened().length;
|
164
|
+
}
|
165
|
+
|
166
|
+
/**
|
167
|
+
* @param {PageIndex} index
|
168
|
+
* @return {Number|undefined}
|
169
|
+
*/
|
170
|
+
getPageWidth(index) {
|
171
|
+
return this.getPageProp(index, 'width');
|
172
|
+
}
|
173
|
+
|
174
|
+
/**
|
175
|
+
* @param {PageIndex} index
|
176
|
+
* @return {Number|undefined}
|
177
|
+
*/
|
178
|
+
getPageHeight(index) {
|
179
|
+
return this.getPageProp(index, 'height');
|
180
|
+
}
|
181
|
+
|
182
|
+
/**
|
183
|
+
* @param {PageIndex} index
|
184
|
+
* @param {number} reduce - not used in default implementation
|
185
|
+
* @param {number} rotate - not used in default implementation
|
186
|
+
* @return {string|undefined}
|
187
|
+
*/
|
188
|
+
// eslint-disable-next-line no-unused-vars
|
189
|
+
getPageURI(index, reduce, rotate) {
|
190
|
+
return !this.getPageProp(index, 'viewable', true) ? UNVIEWABLE_PAGE_URI : this.getPageProp(index, 'uri');
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @param {PageIndex} index
|
195
|
+
* @return {'L' | 'R'}
|
196
|
+
*/
|
197
|
+
getPageSide(index) {
|
198
|
+
return this.getPageProp(index, 'pageSide') || (index % 2 === 0 ? 'R' : 'L');
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* @param {PageIndex} index
|
203
|
+
* @return {PageNumString}
|
204
|
+
*/
|
205
|
+
getPageNum(index) {
|
206
|
+
const pageNum = this.getPageProp(index, 'pageNum');
|
207
|
+
return pageNum === undefined ? `n${index}` : pageNum;
|
208
|
+
}
|
209
|
+
|
210
|
+
/**
|
211
|
+
* Generalized property accessor.
|
212
|
+
* @param {PageIndex} index
|
213
|
+
* @param {keyof PageData} propName
|
214
|
+
* @param {*} [fallbackValue] return if undefined
|
215
|
+
* @return {*|undefined}
|
216
|
+
*/
|
217
|
+
getPageProp(index, propName, fallbackValue = undefined) {
|
218
|
+
return this._getDataProp(index, propName, fallbackValue);
|
219
|
+
}
|
220
|
+
|
221
|
+
/**
|
222
|
+
* This function returns the left and right indices for the user-visible
|
223
|
+
* spread that contains the given index.
|
224
|
+
* @note Can return indices out of range of what's in the book.
|
225
|
+
* @param {PageIndex} pindex
|
226
|
+
* @return {[PageIndex, PageIndex]} eg [0, 1]
|
227
|
+
*/
|
228
|
+
getSpreadIndices(pindex) {
|
229
|
+
if (this.br.pageProgression == 'rl') {
|
230
|
+
return this.getPageSide(pindex) == 'R' ? [pindex + 1, pindex] : [pindex, pindex - 1];
|
231
|
+
} else {
|
232
|
+
return this.getPageSide(pindex) == 'L' ? [pindex, pindex + 1] : [pindex - 1, pindex];
|
233
|
+
}
|
234
|
+
}
|
235
|
+
|
236
|
+
/**
|
237
|
+
* Single images in the Internet Archive scandata.xml metadata are (somewhat incorrectly)
|
238
|
+
* given a "leaf" number. Some of these images from the scanning process should not
|
239
|
+
* be displayed in the BookReader (for example colour calibration cards). Since some
|
240
|
+
* of the scanned images will not be displayed in the BookReader (those marked with
|
241
|
+
* addToAccessFormats false in the scandata.xml) leaf numbers and BookReader page
|
242
|
+
* indexes are generally not the same. This function returns the BookReader page
|
243
|
+
* index given a scanned leaf number.
|
244
|
+
*
|
245
|
+
* This function is used, for example, to map between search results (that use the
|
246
|
+
* leaf numbers) and the displayed pages in the BookReader.
|
247
|
+
* @param {LeafNum} leafNum
|
248
|
+
* @return {PageIndex}
|
249
|
+
*/
|
250
|
+
leafNumToIndex(leafNum) {
|
251
|
+
const index = this._getDataFlattened()
|
252
|
+
.findIndex(d => d.leafNum == leafNum);
|
253
|
+
// If no match is found, fall back to the leafNum provide (leafNum == index)
|
254
|
+
return index > -1 ? index : leafNum;
|
255
|
+
}
|
256
|
+
|
257
|
+
/**
|
258
|
+
* Parses the pageString format
|
259
|
+
* @param {PageString} pageString
|
260
|
+
* @return {PageIndex|undefined}
|
261
|
+
*/
|
262
|
+
parsePageString(pageString) {
|
263
|
+
let pageIndex;
|
264
|
+
// Check for special "leaf"
|
265
|
+
const leafMatch = /^leaf(\d+)/.exec(pageString);
|
266
|
+
if (leafMatch) {
|
267
|
+
pageIndex = this.leafNumToIndex(parseInt(leafMatch[1], 10));
|
268
|
+
if (pageIndex === null) {
|
269
|
+
pageIndex = undefined; // to match return type of getPageIndex
|
270
|
+
}
|
271
|
+
} else {
|
272
|
+
pageIndex = this.getPageIndex(pageString);
|
273
|
+
}
|
274
|
+
return pageIndex;
|
275
|
+
}
|
276
|
+
|
277
|
+
/**
|
278
|
+
* @param {number} index use negatives to get page relative to end
|
279
|
+
* @param loop whether to loop (i.e. -1 == last page)
|
280
|
+
*/
|
281
|
+
getPage(index, loop = true) {
|
282
|
+
const numLeafs = this.getNumLeafs();
|
283
|
+
if (!loop && (index < 0 || index >= numLeafs)) {
|
284
|
+
return undefined;
|
285
|
+
}
|
286
|
+
if (index < 0 && index >= -numLeafs) {
|
287
|
+
index += numLeafs;
|
288
|
+
}
|
289
|
+
index = index % numLeafs;
|
290
|
+
return new PageModel(this, index);
|
291
|
+
}
|
292
|
+
|
293
|
+
/**
|
294
|
+
* @param {object} [arg0]
|
295
|
+
* @param {number} [arg0.start] inclusive
|
296
|
+
* @param {number} [arg0.end] exclusive
|
297
|
+
* @param {boolean} [arg0.combineConsecutiveUnviewables] Yield only first unviewable
|
298
|
+
* of a chunk of unviewable pages instead of each page
|
299
|
+
*/
|
300
|
+
* pagesIterator({ start = 0, end = Infinity, combineConsecutiveUnviewables = false } = {}) {
|
301
|
+
start = Math.max(0, start);
|
302
|
+
end = Math.min(end, this.getNumLeafs());
|
303
|
+
|
304
|
+
for (let i = start; i < end; i++) {
|
305
|
+
const page = this.getPage(i);
|
306
|
+
if (combineConsecutiveUnviewables && page.isConsecutiveUnviewable) continue;
|
307
|
+
|
308
|
+
yield page;
|
309
|
+
}
|
310
|
+
}
|
311
|
+
|
312
|
+
/**
|
313
|
+
* Flatten the nested structure (make 1d array), and also add pageSide prop
|
314
|
+
* @return {PageData[]}
|
315
|
+
*/
|
316
|
+
_getDataFlattened() {
|
317
|
+
if (this._getDataFlattenedCached && this._getDataFlattenedCached[1] === this.br.data.length)
|
318
|
+
return this._getDataFlattenedCached[0];
|
319
|
+
|
320
|
+
let prevPageSide = null;
|
321
|
+
/** @type {number|null} */
|
322
|
+
let unviewablesChunkStart = null;
|
323
|
+
let index = 0;
|
324
|
+
// @ts-ignore TS doesn't know about flatMap for some reason
|
325
|
+
const flattened = this.br.data.flatMap(spread => {
|
326
|
+
return spread.map(page => {
|
327
|
+
if (!page.pageSide) {
|
328
|
+
if (prevPageSide === null) {
|
329
|
+
page.pageSide = spread.length === 2 ? 'L' : 'R';
|
330
|
+
} else {
|
331
|
+
page.pageSide = prevPageSide === 'L' ? 'R' : 'L';
|
332
|
+
}
|
333
|
+
}
|
334
|
+
prevPageSide = page.pageSide;
|
335
|
+
|
336
|
+
if (page.viewable === false) {
|
337
|
+
if (unviewablesChunkStart === null) {
|
338
|
+
page.unviewablesStart = unviewablesChunkStart = index;
|
339
|
+
} else {
|
340
|
+
page.unviewablesStart = unviewablesChunkStart;
|
341
|
+
}
|
342
|
+
} else {
|
343
|
+
unviewablesChunkStart = null;
|
344
|
+
}
|
345
|
+
|
346
|
+
index++;
|
347
|
+
return page;
|
348
|
+
});
|
349
|
+
});
|
350
|
+
|
351
|
+
// length is used as a cache breaker
|
352
|
+
this._getDataFlattenedCached = [flattened, this.br.data.length];
|
353
|
+
return flattened;
|
354
|
+
}
|
355
|
+
|
356
|
+
/**
|
357
|
+
* Helper. Return a prop for a given index. Returns `fallbackValue` if index is invalid or
|
358
|
+
* property not on page.
|
359
|
+
* @param {PageIndex} index
|
360
|
+
* @param {keyof PageData} prop
|
361
|
+
* @param {*} fallbackValue return if property not on the record
|
362
|
+
* @return {*}
|
363
|
+
*/
|
364
|
+
_getDataProp(index, prop, fallbackValue = undefined) {
|
365
|
+
const dataf = this._getDataFlattened();
|
366
|
+
const invalidIndex = isNaN(index) || index < 0 || index >= dataf.length;
|
367
|
+
if (invalidIndex || 'undefined' == typeof(dataf[index][prop]))
|
368
|
+
return fallbackValue;
|
369
|
+
return dataf[index][prop];
|
370
|
+
}
|
371
|
+
}
|
372
|
+
|
373
|
+
/**
|
374
|
+
* A controlled schema for page data.
|
375
|
+
*/
|
376
|
+
export class PageModel {
|
377
|
+
/**
|
378
|
+
* @param {BookModel} book
|
379
|
+
* @param {PageIndex} index
|
380
|
+
*/
|
381
|
+
constructor(book, index) {
|
382
|
+
// TODO: Get default from config
|
383
|
+
this.ppi = book._getDataProp(index, 'ppi', book.ppi);
|
384
|
+
this.book = book;
|
385
|
+
this.index = index;
|
386
|
+
this.width = book.getPageWidth(index);
|
387
|
+
this.widthInches = this.width / this.ppi;
|
388
|
+
this.height = book.getPageHeight(index);
|
389
|
+
this.heightInches = this.height / this.ppi;
|
390
|
+
this.pageSide = book.getPageSide(index);
|
391
|
+
this.leafNum = book._getDataProp(index, 'leafNum', this.index);
|
392
|
+
|
393
|
+
/** @type {boolean} */
|
394
|
+
this.isViewable = book._getDataProp(index, 'viewable', true);
|
395
|
+
/** @type {PageIndex} The first in the series of unviewable pages this is in. */
|
396
|
+
this.unviewablesStart = book._getDataProp(index, 'unviewablesStart') || null;
|
397
|
+
/**
|
398
|
+
* Consecutive unviewable pages are pages in an unviewable "chunk" which are not the first
|
399
|
+
* of that chunk.
|
400
|
+
*/
|
401
|
+
this.isConsecutiveUnviewable = !this.isViewable && this.unviewablesStart != this.index;
|
402
|
+
|
403
|
+
this._rawData = this.book._getDataFlattened()[this.index];
|
404
|
+
}
|
405
|
+
|
406
|
+
/**
|
407
|
+
* Updates the page to no longer be unviewable. Assumes the
|
408
|
+
* Page's URI is already set/correct.
|
409
|
+
*/
|
410
|
+
makeViewable(newViewableState = true) {
|
411
|
+
if (this.isViewable == newViewableState) return;
|
412
|
+
|
413
|
+
if (newViewableState) {
|
414
|
+
this._rawData.viewable = true;
|
415
|
+
delete this._rawData.unviewablesStart;
|
416
|
+
// Update any subsequent page to now point to the right "start"
|
417
|
+
for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
|
418
|
+
if (page.isViewable) break;
|
419
|
+
page._rawData.unviewablesStart = this.index + 1;
|
420
|
+
}
|
421
|
+
} else {
|
422
|
+
this._rawData.viewable = false;
|
423
|
+
this._rawData.unviewablesStart = (this.prev && !this.prev.isViewable) ? this.prev.unviewablesStart : this.index;
|
424
|
+
// Update any subsequent page to now point to the right "start"
|
425
|
+
for (const page of this.book.pagesIterator({ start: this.index + 1 })) {
|
426
|
+
if (!page.isViewable) break;
|
427
|
+
page._rawData.unviewablesStart = this._rawData.unviewablesStart;
|
428
|
+
}
|
429
|
+
}
|
430
|
+
}
|
431
|
+
|
432
|
+
get prev() {
|
433
|
+
return this.findPrev();
|
434
|
+
}
|
435
|
+
|
436
|
+
get next() {
|
437
|
+
return this.findNext();
|
438
|
+
}
|
439
|
+
|
440
|
+
/**
|
441
|
+
* @param {number} reduce
|
442
|
+
* @param {number} rotate
|
443
|
+
*/
|
444
|
+
getURI(reduce, rotate) {
|
445
|
+
return this.book.getPageURI(this.index, reduce, rotate);
|
446
|
+
}
|
447
|
+
|
448
|
+
/**
|
449
|
+
* Returns the srcset with correct URIs or void string if out of range
|
450
|
+
* @param {number} reduce
|
451
|
+
* @param {number} [rotate]
|
452
|
+
*/
|
453
|
+
getURISrcSet(reduce, rotate = 0) {
|
454
|
+
const { reduceSet } = this.book;
|
455
|
+
const initialReduce = reduceSet.floor(reduce);
|
456
|
+
// We don't need to repeat the initial reduce in the srcset
|
457
|
+
const topReduce = reduceSet.decr(initialReduce);
|
458
|
+
const reduces = [];
|
459
|
+
for (let r = topReduce; r >= 1; r = reduceSet.decr(r)) {
|
460
|
+
reduces.push(r);
|
461
|
+
}
|
462
|
+
return reduces
|
463
|
+
.map(r => `${this.getURI(r, rotate)} ${initialReduce / r}x`)
|
464
|
+
.join(', ');
|
465
|
+
}
|
466
|
+
|
467
|
+
/**
|
468
|
+
* @param {object} [arg0]
|
469
|
+
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
470
|
+
* of a series of unviewable pages instead of each page
|
471
|
+
* @return {PageModel|void}
|
472
|
+
*/
|
473
|
+
findNext({ combineConsecutiveUnviewables = false } = {}) {
|
474
|
+
return this.book
|
475
|
+
.pagesIterator({ start: this.index + 1, combineConsecutiveUnviewables })
|
476
|
+
.next().value;
|
477
|
+
}
|
478
|
+
|
479
|
+
/**
|
480
|
+
* @param {object} [arg0]
|
481
|
+
* @param {boolean} [arg0.combineConsecutiveUnviewables] Whether to only yield the first page
|
482
|
+
* of a series of unviewable pages instead of each page
|
483
|
+
* @return {PageModel|void}
|
484
|
+
*/
|
485
|
+
findPrev({ combineConsecutiveUnviewables = false } = {}) {
|
486
|
+
if (this.index == 0) return undefined;
|
487
|
+
|
488
|
+
if (combineConsecutiveUnviewables) {
|
489
|
+
if (this.isConsecutiveUnviewable) {
|
490
|
+
return this.book.getPage(this.unviewablesStart);
|
491
|
+
} else {
|
492
|
+
// Recursively goes backward through the book
|
493
|
+
// TODO make a reverse iterator to make it look identical to findNext
|
494
|
+
const prev = new PageModel(this.book, this.index - 1);
|
495
|
+
return prev.isViewable ? prev : prev.findPrev({ combineConsecutiveUnviewables });
|
496
|
+
}
|
497
|
+
} else {
|
498
|
+
return new PageModel(this.book, this.index - 1);
|
499
|
+
}
|
500
|
+
}
|
501
|
+
}
|
502
|
+
|
503
|
+
// There are a few main ways we can reference a specific page in a book:
|
504
|
+
/**
|
505
|
+
* @typedef {string} PageNumString
|
506
|
+
* Possible values: /^n?\d+$/. Example: 'n7', '18'
|
507
|
+
* Not necessarily unique
|
508
|
+
*/
|
509
|
+
/**
|
510
|
+
* @typedef {number} LeafNum
|
511
|
+
* No clue if 0 or 1 indexed or consecutive; generally from IA book info.
|
512
|
+
*/
|
513
|
+
/**
|
514
|
+
* @typedef {string} PageString
|
515
|
+
* Possible values: /^(leaf)?\d+$/ Example: 'leaf7', '18'
|
516
|
+
* If leaf-prefixed, then the number is a LeafNum. Otherwise it's a PageNumString
|
517
|
+
*/
|
518
|
+
/** @typedef {number} PageIndex 0-based index of all the pages */
|
@@ -0,0 +1,54 @@
|
|
1
|
+
/**
|
2
|
+
* Displays a console on the document for debugging devices where remote
|
3
|
+
* debugging is not feasible, and forwards all console.log's to be displayed
|
4
|
+
* on screen.
|
5
|
+
*/
|
6
|
+
export class DebugConsole {
|
7
|
+
constructor() {
|
8
|
+
/** How many times we've seen the same line in a row */
|
9
|
+
this.currentRun = 0;
|
10
|
+
}
|
11
|
+
|
12
|
+
init() {
|
13
|
+
this.$log = $(`<div id="_debugLog" style="width: 100%; height: 300px; overflow: auto" />`);
|
14
|
+
$(document.body).prepend(this.$log);
|
15
|
+
|
16
|
+
this.$form = $(`
|
17
|
+
<form>
|
18
|
+
<input style="width:100%; font-family: monospace;" id="_debugLogInput">
|
19
|
+
</form>`);
|
20
|
+
this.$log.append(this.$form);
|
21
|
+
|
22
|
+
this.$form.submit(ev => {
|
23
|
+
ev.preventDefault();
|
24
|
+
const result = eval(this.$form.find('input').val());
|
25
|
+
this.logToScreen([result]);
|
26
|
+
});
|
27
|
+
|
28
|
+
const _realLog = console.log.bind(console);
|
29
|
+
console.log = (...args) => {
|
30
|
+
_realLog(...args);
|
31
|
+
this.logToScreen(args);
|
32
|
+
};
|
33
|
+
|
34
|
+
window.onerror = (...args) => this.logToScreen(args);
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Log the provided array onto the on screen console
|
39
|
+
* @param {Array} args
|
40
|
+
*/
|
41
|
+
logToScreen(args) {
|
42
|
+
const html = args.map(JSON.stringify).join(',');
|
43
|
+
const $lastEntry = this.$log.children('.log-entry:last-child');
|
44
|
+
if ($lastEntry.find('.entry-code').html() == html) {
|
45
|
+
$lastEntry.find('.count').text(`(${++this.currentRun})`);
|
46
|
+
} else {
|
47
|
+
this.currentRun = 1;
|
48
|
+
this.$log.append($(`
|
49
|
+
<div class="log-entry">
|
50
|
+
<code class="count"></code> <code class="entry-code">${html}</code>
|
51
|
+
</div>`));
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
@@ -0,0 +1,116 @@
|
|
1
|
+
// @ts-check
|
2
|
+
/**
|
3
|
+
* Creates an image cache dictionary
|
4
|
+
* storing images in `<img>` tags so that
|
5
|
+
* BookReader can leverage browser caching
|
6
|
+
*/
|
7
|
+
/** @typedef {import("./BookModel").BookModel} BookModel */
|
8
|
+
/** @typedef {import("./BookModel").PageIndex} PageIndex */
|
9
|
+
/** @typedef {import("./ReduceSet").ReduceSet} ReduceSet */
|
10
|
+
|
11
|
+
import { Pow2ReduceSet } from "./ReduceSet";
|
12
|
+
|
13
|
+
export class ImageCache {
|
14
|
+
/**
|
15
|
+
* @param {BookModel} book
|
16
|
+
* @param {object} opts
|
17
|
+
* @param {boolean} [opts.useSrcSet]
|
18
|
+
* @param {ReduceSet} [opts.reduceSet]
|
19
|
+
*/
|
20
|
+
constructor(book, { useSrcSet = false, reduceSet = Pow2ReduceSet } = {}) {
|
21
|
+
this.book = book;
|
22
|
+
this.useSrcSet = useSrcSet;
|
23
|
+
this.reduceSet = reduceSet;
|
24
|
+
/** @type {{ [index: number]: { reduce: number, loaded: boolean }[] }} */
|
25
|
+
this.cache = {};
|
26
|
+
this.defaultScale = 8;
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Get an image
|
31
|
+
* Checks cache first if image is available & of equal/better scale,
|
32
|
+
* if not, a new image gets created
|
33
|
+
*
|
34
|
+
* @param {PageIndex} index
|
35
|
+
* @param {Number} reduce
|
36
|
+
*/
|
37
|
+
image(index, reduce) {
|
38
|
+
const cachedImages = this.cache[index] || [];
|
39
|
+
const sufficientImages = cachedImages
|
40
|
+
.filter(x => x.loaded && x.reduce <= reduce);
|
41
|
+
if (sufficientImages.length) {
|
42
|
+
// Choose the largest reduction factor that meets our needs
|
43
|
+
const bestReduce = Math.max(...sufficientImages.map(e => e.reduce));
|
44
|
+
return this._serveImageElement(index, bestReduce);
|
45
|
+
} else {
|
46
|
+
// Don't use a cache entry; i.e. a fresh fetch will be made
|
47
|
+
// for this reduce
|
48
|
+
return this._serveImageElement(index, reduce);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
/**
|
53
|
+
* Checks if an image of equal or greater quality has been loaded
|
54
|
+
* @param {PageIndex} index
|
55
|
+
* @param {Number} reduce
|
56
|
+
* @returns {Boolean}
|
57
|
+
*/
|
58
|
+
imageLoaded(index, reduce) {
|
59
|
+
const cacheImg = this.cache[index]?.find(e => e.reduce <= reduce);
|
60
|
+
return cacheImg?.loaded ?? false;
|
61
|
+
}
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Get the best image that's already loaded for the given index,
|
65
|
+
* trying to choose values less that the given reduce
|
66
|
+
* @param {PageIndex} index
|
67
|
+
* @param idealMaxReduce
|
68
|
+
* @returns {null | number}
|
69
|
+
*/
|
70
|
+
getBestLoadedReduce(index, idealMaxReduce = Infinity) {
|
71
|
+
const candidates = this.cache[index]?.filter(x => x.loaded) || [];
|
72
|
+
if (!candidates.length) return null;
|
73
|
+
|
74
|
+
const lowerResImages = candidates.filter(e => e.reduce >= idealMaxReduce);
|
75
|
+
if (lowerResImages.length) {
|
76
|
+
// Choose the highest quality loaded lower res image
|
77
|
+
return Math.min(...lowerResImages.map(e => e.reduce));
|
78
|
+
}
|
79
|
+
// Otherwise choose whatever is closest to the reduce
|
80
|
+
const higherRestImages = candidates.filter(e => e.reduce < idealMaxReduce);
|
81
|
+
return Math.max(...higherRestImages.map(e => e.reduce));
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* @private
|
86
|
+
* Generates an image element on the fly from image info in cache
|
87
|
+
*
|
88
|
+
* @param {PageIndex} index
|
89
|
+
* @param {number} reduce
|
90
|
+
* @returns {JQuery<HTMLImageElement>} with base image classes
|
91
|
+
*/
|
92
|
+
_serveImageElement(index, reduce) {
|
93
|
+
const validReduce = this.reduceSet.floor(reduce);
|
94
|
+
let cacheEntry = this.cache[index]?.find(e => e.reduce == validReduce);
|
95
|
+
if (!cacheEntry) {
|
96
|
+
cacheEntry = { reduce: validReduce, loaded: false };
|
97
|
+
const entries = this.cache[index] || (this.cache[index] = []);
|
98
|
+
entries.push(cacheEntry);
|
99
|
+
}
|
100
|
+
const page = this.book.getPage(index);
|
101
|
+
|
102
|
+
const $img = $('<img />', {
|
103
|
+
'class': 'BRpageimage',
|
104
|
+
'alt': 'Book page image',
|
105
|
+
src: page.getURI(validReduce, 0),
|
106
|
+
})
|
107
|
+
.data('reduce', validReduce);
|
108
|
+
if (this.useSrcSet) {
|
109
|
+
$img.attr('srcset', page.getURISrcSet(validReduce));
|
110
|
+
}
|
111
|
+
if (!cacheEntry.loaded) {
|
112
|
+
$img.one('load', () => cacheEntry.loaded = true);
|
113
|
+
}
|
114
|
+
return $img;
|
115
|
+
}
|
116
|
+
}
|