@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,2546 @@
|
|
1
|
+
/*
|
2
|
+
Copyright(c)2008-2019 Internet Archive. Software license AGPL version 3.
|
3
|
+
|
4
|
+
This file is part of BookReader.
|
5
|
+
|
6
|
+
BookReader is free software: you can redistribute it and/or modify
|
7
|
+
it under the terms of the GNU Affero General Public License as published by
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
9
|
+
(at your option) any later version.
|
10
|
+
|
11
|
+
BookReader is distributed in the hope that it will be useful,
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
GNU Affero General Public License for more details.
|
15
|
+
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
17
|
+
along with BookReader. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
The BookReader source is hosted at http://github.com/internetarchive/bookreader/
|
20
|
+
|
21
|
+
*/
|
22
|
+
// effect.js gives acces to extra easing function (e.g. easeInOutExpo)
|
23
|
+
import 'jquery-ui/ui/effect.js';
|
24
|
+
|
25
|
+
// Needed by touch-punch
|
26
|
+
import 'jquery-ui/ui/widget.js';
|
27
|
+
import 'jquery-ui/ui/widgets/mouse.js';
|
28
|
+
import 'jquery-ui-touch-punch';
|
29
|
+
|
30
|
+
import './dragscrollable-br.js';
|
31
|
+
import PACKAGE_JSON from '../package.json';
|
32
|
+
import * as utils from './BookReader/utils.js';
|
33
|
+
import { exposeOverrideable } from './BookReader/utils/classes.js';
|
34
|
+
import { Navbar, getNavPageNumHtml } from './BookReader/Navbar/Navbar.js';
|
35
|
+
import { DEFAULT_OPTIONS } from './BookReader/options.js';
|
36
|
+
/** @typedef {import('./BookReader/options.js').BookReaderOptions} BookReaderOptions */
|
37
|
+
/** @typedef {import('./BookReader/options.js').ReductionFactor} ReductionFactor */
|
38
|
+
/** @typedef {import('./BookReader/BookModel.js').PageIndex} PageIndex */
|
39
|
+
import { EVENTS } from './BookReader/events.js';
|
40
|
+
import { DebugConsole } from './BookReader/DebugConsole.js';
|
41
|
+
import {
|
42
|
+
Toolbar,
|
43
|
+
blankInfoDiv,
|
44
|
+
blankShareDiv,
|
45
|
+
createPopup,
|
46
|
+
} from './BookReader/Toolbar/Toolbar.js';
|
47
|
+
import { BookModel } from './BookReader/BookModel.js';
|
48
|
+
import { Mode1Up } from './BookReader/Mode1Up.js';
|
49
|
+
import { Mode2Up } from './BookReader/Mode2Up.js';
|
50
|
+
import { ModeThumb } from './BookReader/ModeThumb';
|
51
|
+
import { ImageCache } from './BookReader/ImageCache.js';
|
52
|
+
import { PageContainer } from './BookReader/PageContainer.js';
|
53
|
+
import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
|
54
|
+
|
55
|
+
if (location.toString().indexOf('_debugShowConsole=true') != -1) {
|
56
|
+
$(() => new DebugConsole().init());
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* BookReader
|
61
|
+
* @param {BookReaderOptions} options
|
62
|
+
* TODO document all options properties
|
63
|
+
* @constructor
|
64
|
+
*/
|
65
|
+
export default function BookReader(overrides = {}) {
|
66
|
+
const options = jQuery.extend(true, {}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
|
67
|
+
this.setup(options);
|
68
|
+
}
|
69
|
+
|
70
|
+
BookReader.version = PACKAGE_JSON.version;
|
71
|
+
|
72
|
+
// Mode constants
|
73
|
+
/** 1 page view */
|
74
|
+
BookReader.constMode1up = 1;
|
75
|
+
/** 2 pages view */
|
76
|
+
BookReader.constMode2up = 2;
|
77
|
+
/** thumbnails view */
|
78
|
+
BookReader.constModeThumb = 3;
|
79
|
+
/** image cache */
|
80
|
+
BookReader.imageCache = null;
|
81
|
+
|
82
|
+
// Animation constants
|
83
|
+
BookReader.constNavAnimationDuration = 300;
|
84
|
+
BookReader.constResizeAnimationDuration = 100;
|
85
|
+
|
86
|
+
// Names of events that can be triggered via BookReader.prototype.trigger()
|
87
|
+
BookReader.eventNames = EVENTS;
|
88
|
+
|
89
|
+
BookReader.defaultOptions = DEFAULT_OPTIONS;
|
90
|
+
|
91
|
+
/**
|
92
|
+
* @type {BookReaderOptions}
|
93
|
+
* This is here, just in case you need to absolutely override an option.
|
94
|
+
*/
|
95
|
+
BookReader.optionOverrides = {};
|
96
|
+
|
97
|
+
/**
|
98
|
+
* Setup
|
99
|
+
* It is separate from the constructor, so plugins can extend.
|
100
|
+
* @param {BookReaderOptions} options
|
101
|
+
*/
|
102
|
+
BookReader.prototype.setup = function(options) {
|
103
|
+
// Store the options used to setup bookreader
|
104
|
+
this.options = options;
|
105
|
+
|
106
|
+
/** @type {number} @deprecated some past iterations set this */
|
107
|
+
this.numLeafs = undefined;
|
108
|
+
|
109
|
+
/** Overridden by plugin.search.js */
|
110
|
+
this.enableSearch = false;
|
111
|
+
|
112
|
+
/**
|
113
|
+
* Store viewModeOrder states
|
114
|
+
* @var {boolean}
|
115
|
+
*/
|
116
|
+
this.viewModeOrder = [];
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Used to suppress fragment change for init with canonical URLs
|
120
|
+
* @var {boolean}
|
121
|
+
*/
|
122
|
+
this.suppressFragmentChange = false;
|
123
|
+
|
124
|
+
/** @type {function(): void} */
|
125
|
+
this.animationFinishedCallback = null;
|
126
|
+
|
127
|
+
// @deprecated: Instance constants. Use Class constants instead
|
128
|
+
/** 1 page view */
|
129
|
+
this.constMode1up = BookReader.constMode1up;
|
130
|
+
/** 2 pages view */
|
131
|
+
this.constMode2up = BookReader.constMode2up;
|
132
|
+
/** thumbnails view */
|
133
|
+
this.constModeThumb = BookReader.constModeThumb;
|
134
|
+
|
135
|
+
// Private properties below. Configuration should be done with options.
|
136
|
+
/** @type {number} TODO: Make private */
|
137
|
+
this.reduce = 8; /* start very small */
|
138
|
+
this.defaults = options.defaults;
|
139
|
+
this.padding = options.padding;
|
140
|
+
|
141
|
+
this.reduceSet = NAMED_REDUCE_SETS[options.reduceSet];
|
142
|
+
if (!this.reduceSet) {
|
143
|
+
console.warn(`Invalid reduceSet ${options.reduceSet}. Ignoring.`);
|
144
|
+
this.reduceSet = NAMED_REDUCE_SETS[DEFAULT_OPTIONS.reduceSet];
|
145
|
+
}
|
146
|
+
|
147
|
+
/** @type {number}
|
148
|
+
* can be 1 or 2 or 3 based on the display mode const value
|
149
|
+
*/
|
150
|
+
this.mode = null;
|
151
|
+
this.prevReadMode = null;
|
152
|
+
this.ui = options.ui;
|
153
|
+
this.uiAutoHide = options.uiAutoHide;
|
154
|
+
|
155
|
+
this.thumbWidth = 100; // will be overridden during prepareThumbnailView
|
156
|
+
this.thumbRowBuffer = options.thumbRowBuffer;
|
157
|
+
this.thumbColumns = options.thumbColumns;
|
158
|
+
this.thumbMaxLoading = options.thumbMaxLoading;
|
159
|
+
this.thumbPadding = options.thumbPadding;
|
160
|
+
this.displayedRows = [];
|
161
|
+
|
162
|
+
this.displayedIndices = [];
|
163
|
+
/** @deprecated Unused; will be remove in v5 */
|
164
|
+
this.imgs = {};
|
165
|
+
/** @deprecated No longer used; will be remove in v5 */
|
166
|
+
this.prefetchedImgs = {}; //an object with numeric keys corresponding to page index, reduce
|
167
|
+
|
168
|
+
this.animating = false;
|
169
|
+
this.flipSpeed = options.flipSpeed;
|
170
|
+
this.flipDelay = options.flipDelay;
|
171
|
+
this.twoPagePopUp = null;
|
172
|
+
this.leafEdgeTmp = null;
|
173
|
+
|
174
|
+
/**
|
175
|
+
* Represents the first displayed index
|
176
|
+
* In 2up mode it will be the left page
|
177
|
+
* In 1 up mode it is the highest page
|
178
|
+
* @property {number|null} firstIndex
|
179
|
+
*/
|
180
|
+
this.firstIndex = null;
|
181
|
+
this.lastDisplayableIndex2up = null;
|
182
|
+
this.isFullscreenActive = false;
|
183
|
+
this.lastScroll = null;
|
184
|
+
|
185
|
+
this.showLogo = options.showLogo;
|
186
|
+
this.logoURL = options.logoURL;
|
187
|
+
this.imagesBaseURL = options.imagesBaseURL;
|
188
|
+
|
189
|
+
this.reductionFactors = options.reductionFactors;
|
190
|
+
this.onePage = options.onePage;
|
191
|
+
/** @type {import('./BookReader/Mode2Up').TwoPageState} */
|
192
|
+
this.twoPage = options.twoPage;
|
193
|
+
this.onePageMinBreakpoint = options.onePageMinBreakpoint;
|
194
|
+
|
195
|
+
this.bookTitle = options.bookTitle;
|
196
|
+
this.bookUrl = options.bookUrl;
|
197
|
+
this.bookUrlText = options.bookUrlText;
|
198
|
+
this.bookUrlTitle = options.bookUrlTitle;
|
199
|
+
this.titleLeaf = options.titleLeaf;
|
200
|
+
|
201
|
+
this.metadata = options.metadata;
|
202
|
+
this.thumbnail = options.thumbnail;
|
203
|
+
this.bookUrlMoreInfo = options.bookUrlMoreInfo;
|
204
|
+
|
205
|
+
this.enableExperimentalControls = options.enableExperimentalControls;
|
206
|
+
this.el = options.el;
|
207
|
+
|
208
|
+
this.pageProgression = options.pageProgression;
|
209
|
+
this.protected = options.protected;
|
210
|
+
this.getEmbedCode = options.getEmbedCode;
|
211
|
+
this.popup = null;
|
212
|
+
|
213
|
+
// Assign the data methods
|
214
|
+
this.data = options.data;
|
215
|
+
if (options.getNumLeafs) BookReader.prototype.getNumLeafs = options.getNumLeafs;
|
216
|
+
if (options.getPageWidth) BookReader.prototype.getPageWidth = options.getPageWidth;
|
217
|
+
if (options.getPageHeight) BookReader.prototype.getPageHeight = options.getPageHeight;
|
218
|
+
if (options.getPageURI) BookReader.prototype.getPageURI = options.getPageURI;
|
219
|
+
if (options.getPageSide) BookReader.prototype.getPageSide = options.getPageSide;
|
220
|
+
if (options.getPageNum) BookReader.prototype.getPageNum = options.getPageNum;
|
221
|
+
if (options.getPageProp) BookReader.prototype.getPageProp = options.getPageProp;
|
222
|
+
if (options.getSpreadIndices) BookReader.prototype.getSpreadIndices = options.getSpreadIndices;
|
223
|
+
if (options.leafNumToIndex) BookReader.prototype.leafNumToIndex = options.leafNumToIndex;
|
224
|
+
|
225
|
+
/** @type {{[name: string]: JQuery}} */
|
226
|
+
this.refs = {};
|
227
|
+
|
228
|
+
/**
|
229
|
+
* @private (for now) Models are largely state storing classes. This might be too much
|
230
|
+
* granularity, but time will tell!
|
231
|
+
*/
|
232
|
+
this._models = {
|
233
|
+
book: new BookModel(this),
|
234
|
+
};
|
235
|
+
|
236
|
+
/**
|
237
|
+
* @private Components are 'subchunks' of bookreader functionality, usually UI related
|
238
|
+
* They should be relatively decoupled from each other/bookreader.
|
239
|
+
* Note there are no hooks right now; components just provide methods that bookreader
|
240
|
+
* calls at the correct moments.
|
241
|
+
**/
|
242
|
+
this._components = {
|
243
|
+
navbar: new Navbar(this),
|
244
|
+
toolbar: new Toolbar(this),
|
245
|
+
};
|
246
|
+
|
247
|
+
this._modes = {
|
248
|
+
mode1Up: new Mode1Up(this, this._models.book),
|
249
|
+
mode2Up: new Mode2Up(this, this._models.book),
|
250
|
+
modeThumb: new ModeThumb(this, this._models.book),
|
251
|
+
};
|
252
|
+
|
253
|
+
/** Stores classes which we want to expose (selectively) some methods as overridable */
|
254
|
+
this._overrideable = {
|
255
|
+
'_models.book': this._models.book,
|
256
|
+
'_components.navbar': this._components.navbar,
|
257
|
+
'_components.toolbar': this._components.toolbar,
|
258
|
+
'_modes.mode1Up': this._modes.mode1Up,
|
259
|
+
'_modes.mode2Up': this._modes.mode2Up,
|
260
|
+
'_modes.modeThumb': this._modes.modeThumb,
|
261
|
+
};
|
262
|
+
|
263
|
+
/** Image cache for general image fetching */
|
264
|
+
this.imageCache = new ImageCache(this._models.book, {
|
265
|
+
useSrcSet: this.options.useSrcSet,
|
266
|
+
reduceSet: this.reduceSet,
|
267
|
+
});
|
268
|
+
};
|
269
|
+
|
270
|
+
/**
|
271
|
+
* Get all the HTML Elements that are being/can be rendered.
|
272
|
+
* Includes cached elements which might be rendered again.
|
273
|
+
*/
|
274
|
+
BookReader.prototype.getActivePageContainerElements = function() {
|
275
|
+
let containerEls = Object.values(this._modes.mode2Up.pageContainers).map(pc => pc.$container[0])
|
276
|
+
.concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
|
277
|
+
if (this.mode == this.constModeThumb) {
|
278
|
+
containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
|
279
|
+
}
|
280
|
+
return containerEls;
|
281
|
+
};
|
282
|
+
|
283
|
+
/**
|
284
|
+
* Get the HTML Elements for the rendered page. Note there can be more than one, since
|
285
|
+
* (at least as of writing) different modes can maintain different caches.
|
286
|
+
* @param {PageIndex} pageIndex
|
287
|
+
*/
|
288
|
+
BookReader.prototype.getActivePageContainerElementsForIndex = function(pageIndex) {
|
289
|
+
return [
|
290
|
+
this._modes.mode2Up.pageContainers[pageIndex]?.$container?.[0],
|
291
|
+
this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]?.$container?.[0],
|
292
|
+
...(this.mode == this.constModeThumb ? this.$(`.pagediv${pageIndex}`).toArray() : [])
|
293
|
+
].filter(x => x);
|
294
|
+
};
|
295
|
+
|
296
|
+
Object.defineProperty(BookReader.prototype, 'activeMode', {
|
297
|
+
/** @return {Mode1Up | Mode2Up | ModeThumb} */
|
298
|
+
get() { return {
|
299
|
+
1: this._modes.mode1Up,
|
300
|
+
2: this._modes.mode2Up,
|
301
|
+
3: this._modes.modeThumb,
|
302
|
+
}[this.mode]; },
|
303
|
+
});
|
304
|
+
|
305
|
+
/** @deprecated unused outside Mode2Up */
|
306
|
+
Object.defineProperty(BookReader.prototype, 'leafEdgeL', {
|
307
|
+
get() { return this._modes.mode2Up.leafEdgeL; },
|
308
|
+
set(newVal) { this._modes.mode2Up.leafEdgeL = newVal; }
|
309
|
+
});
|
310
|
+
/** @deprecated unused outside Mode2Up */
|
311
|
+
Object.defineProperty(BookReader.prototype, 'leafEdgeR', {
|
312
|
+
get() { return this._modes.mode2Up.leafEdgeR; },
|
313
|
+
set(newVal) { this._modes.mode2Up.leafEdgeR = newVal; }
|
314
|
+
});
|
315
|
+
|
316
|
+
/**
|
317
|
+
* BookReader.util are static library functions
|
318
|
+
* At top of file so they can be used below
|
319
|
+
*/
|
320
|
+
BookReader.util = utils;
|
321
|
+
|
322
|
+
/**
|
323
|
+
* Helper to merge in params in to a params object.
|
324
|
+
* It normalizes "page" into the "index" field to disambiguate and prevent concflicts
|
325
|
+
* @private
|
326
|
+
*/
|
327
|
+
BookReader.prototype.extendParams = function(params, newParams) {
|
328
|
+
var modifiedNewParams = $.extend({}, newParams);
|
329
|
+
if ('undefined' != typeof(modifiedNewParams.page)) {
|
330
|
+
var pageIndex = this._models.book.parsePageString(modifiedNewParams.page);
|
331
|
+
if (!isNaN(pageIndex))
|
332
|
+
modifiedNewParams.index = pageIndex;
|
333
|
+
delete modifiedNewParams.page;
|
334
|
+
}
|
335
|
+
$.extend(params, modifiedNewParams);
|
336
|
+
};
|
337
|
+
|
338
|
+
/**
|
339
|
+
* Parses params from from various initialization contexts (url, cookie, options)
|
340
|
+
* @private
|
341
|
+
* @return {object} the parsed params
|
342
|
+
*/
|
343
|
+
BookReader.prototype.initParams = function() {
|
344
|
+
var params = {};
|
345
|
+
// Flag initializing for updateFromParams()
|
346
|
+
params.init = true;
|
347
|
+
|
348
|
+
// Flag if page given in defaults or URL (not cookie)
|
349
|
+
// Used for overriding goToFirstResult in plugin.search.js
|
350
|
+
// Note: extendParams() converts params.page to index and gets rid of page
|
351
|
+
// so check and set before extendParams()
|
352
|
+
params.pageFound = false;
|
353
|
+
|
354
|
+
// True if changing the URL
|
355
|
+
params.fragmentChange = false;
|
356
|
+
|
357
|
+
// This is ordered from lowest to highest priority
|
358
|
+
|
359
|
+
// If we have a title leaf, use that as the default instead of index 0,
|
360
|
+
// but only use as default if book has a few pages
|
361
|
+
if ('undefined' != typeof(this.titleLeaf) && this._models.book.getNumLeafs() > 2) {
|
362
|
+
params.index = this._models.book.leafNumToIndex(this.titleLeaf);
|
363
|
+
} else {
|
364
|
+
params.index = 0;
|
365
|
+
}
|
366
|
+
|
367
|
+
// this.defaults is a string passed in the url format. eg "page/1/mode/1up"
|
368
|
+
if (this.defaults) {
|
369
|
+
const defaultParams = this.paramsFromFragment(this.defaults);
|
370
|
+
if ('undefined' != typeof(defaultParams.page)) {
|
371
|
+
params.pageFound = true;
|
372
|
+
}
|
373
|
+
this.extendParams(params, defaultParams);
|
374
|
+
}
|
375
|
+
|
376
|
+
// Check for Resume plugin
|
377
|
+
if (this.options.enablePageResume) {
|
378
|
+
// Check cookies
|
379
|
+
const val = this.getResumeValue();
|
380
|
+
if (val !== null) {
|
381
|
+
// If page index different from default
|
382
|
+
if (params.index !== val) {
|
383
|
+
// Show in URL
|
384
|
+
params.fragmentChange = true;
|
385
|
+
}
|
386
|
+
params.index = val;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
|
390
|
+
// Check for URL plugin
|
391
|
+
if (this.options.enableUrlPlugin) {
|
392
|
+
// Params explicitly set in URL take precedence over all other methods
|
393
|
+
var urlParams = this.paramsFromFragment(this.urlReadFragment());
|
394
|
+
|
395
|
+
// Get params if hash fragment available with 'history' urlMode
|
396
|
+
const hasHashURL = !Object.keys(urlParams).length && this.urlReadHashFragment();
|
397
|
+
if (hasHashURL && (this.options.urlMode === 'history')) {
|
398
|
+
urlParams = this.paramsFromFragment(this.urlReadHashFragment());
|
399
|
+
}
|
400
|
+
|
401
|
+
// If there were any parameters
|
402
|
+
if (Object.keys(urlParams).length) {
|
403
|
+
if ('undefined' != typeof(urlParams.page)) {
|
404
|
+
params.pageFound = true;
|
405
|
+
}
|
406
|
+
this.extendParams(params, urlParams);
|
407
|
+
// Show in URL
|
408
|
+
params.fragmentChange = true;
|
409
|
+
}
|
410
|
+
}
|
411
|
+
|
412
|
+
// Check for Search plugin
|
413
|
+
if (this.options.enableSearch) {
|
414
|
+
// Go to first result only if no default or URL page
|
415
|
+
this.options.goToFirstResult = !params.pageFound;
|
416
|
+
|
417
|
+
// If initialSearchTerm not set
|
418
|
+
if (!this.options.initialSearchTerm) {
|
419
|
+
// Look for any term in URL
|
420
|
+
if (params.search) {
|
421
|
+
// Old style: /search/[term]
|
422
|
+
this.options.initialSearchTerm = params.search;
|
423
|
+
this.searchTerm = params.search;
|
424
|
+
} else {
|
425
|
+
// If we have a query string: q=[term]
|
426
|
+
const searchParams = new URLSearchParams(this.readQueryString());
|
427
|
+
const searchTerm = searchParams.get('q');
|
428
|
+
if (searchTerm) {
|
429
|
+
this.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
|
430
|
+
}
|
431
|
+
}
|
432
|
+
}
|
433
|
+
}
|
434
|
+
|
435
|
+
// Set for init process, return to false at end of init()
|
436
|
+
this.suppressFragmentChange = !params.fragmentChange;
|
437
|
+
|
438
|
+
return params;
|
439
|
+
};
|
440
|
+
|
441
|
+
/**
|
442
|
+
* Allow mocking of window.location.search
|
443
|
+
*/
|
444
|
+
BookReader.prototype.getLocationSearch = function () {
|
445
|
+
return window.location.search;
|
446
|
+
};
|
447
|
+
|
448
|
+
/**
|
449
|
+
* Allow mocking of window.location.hash
|
450
|
+
*/
|
451
|
+
BookReader.prototype.getLocationHash = function () {
|
452
|
+
return window.location.hash;
|
453
|
+
};
|
454
|
+
|
455
|
+
/**
|
456
|
+
* Return URL or fragment querystring
|
457
|
+
*/
|
458
|
+
BookReader.prototype.readQueryString = function() {
|
459
|
+
const queryString = this.getLocationSearch();
|
460
|
+
if (queryString) {
|
461
|
+
return queryString;
|
462
|
+
}
|
463
|
+
const hash = this.getLocationHash();
|
464
|
+
const found = hash.search(/\?\w+=/);
|
465
|
+
return found > -1 ? hash.slice(found) : '';
|
466
|
+
};
|
467
|
+
|
468
|
+
/**
|
469
|
+
* Determines the initial mode for starting if a mode is not already
|
470
|
+
* present in the params argument
|
471
|
+
* @param {object} params
|
472
|
+
* @return {number} the mode
|
473
|
+
*/
|
474
|
+
BookReader.prototype.getInitialMode = function(params) {
|
475
|
+
// Use params or browser width to set view mode
|
476
|
+
var windowWidth = $(window).width();
|
477
|
+
var nextMode;
|
478
|
+
if ('undefined' != typeof(params.mode)) {
|
479
|
+
nextMode = params.mode;
|
480
|
+
} else if (this.ui == 'full'
|
481
|
+
&& this.enableMobileNav
|
482
|
+
&& this.isFullscreenActive
|
483
|
+
&& windowWidth <= this.onePageMinBreakpoint
|
484
|
+
) {
|
485
|
+
// In full mode, we set the default based on width
|
486
|
+
nextMode = this.constMode1up;
|
487
|
+
} else {
|
488
|
+
nextMode = this.constMode2up;
|
489
|
+
}
|
490
|
+
|
491
|
+
if (!this.canSwitchToMode(nextMode)) {
|
492
|
+
nextMode = this.constMode1up;
|
493
|
+
}
|
494
|
+
return nextMode;
|
495
|
+
};
|
496
|
+
|
497
|
+
/**
|
498
|
+
* This is called by the client to initialize BookReader.
|
499
|
+
* It renders onto the DOM. It should only be called once.
|
500
|
+
*/
|
501
|
+
BookReader.prototype.init = function() {
|
502
|
+
this.init.initComplete = false;
|
503
|
+
this.pageScale = this.reduce; // preserve current reduce
|
504
|
+
|
505
|
+
var params = this.initParams();
|
506
|
+
|
507
|
+
this.firstIndex = params.index ? params.index : 0;
|
508
|
+
|
509
|
+
// Setup Navbars and other UI
|
510
|
+
this.isTouchDevice = !!('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator);
|
511
|
+
|
512
|
+
this.refs.$br = $(this.el)
|
513
|
+
.empty()
|
514
|
+
.removeClass()
|
515
|
+
.addClass("ui-" + this.ui)
|
516
|
+
.addClass("br-ui-" + this.ui)
|
517
|
+
.addClass('BookReader');
|
518
|
+
|
519
|
+
// Add a class if this is a touch enabled device
|
520
|
+
if (this.isTouchDevice) {
|
521
|
+
this.refs.$br.addClass("touch");
|
522
|
+
} else {
|
523
|
+
this.refs.$br.addClass("no-touch");
|
524
|
+
}
|
525
|
+
|
526
|
+
this.refs.$brContainer = $("<div class='BRcontainer' dir='ltr'></div>");
|
527
|
+
this.refs.$br.append(this.refs.$brContainer);
|
528
|
+
|
529
|
+
// Explicitly ensure params.mode exists for updateFromParams() below
|
530
|
+
params.mode = this.getInitialMode(params);
|
531
|
+
|
532
|
+
// Explicitly ensure this.mode exists for initNavbar() below
|
533
|
+
this.mode = params.mode;
|
534
|
+
|
535
|
+
if (this.ui == "embed" && this.options.showNavbar) {
|
536
|
+
this.initEmbedNavbar();
|
537
|
+
} else {
|
538
|
+
if (this.options.showToolbar) {
|
539
|
+
this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
|
540
|
+
}
|
541
|
+
if (this.options.showNavbar) {
|
542
|
+
this.initNavbar();
|
543
|
+
}
|
544
|
+
}
|
545
|
+
|
546
|
+
// Switch navbar controls on mobile/desktop
|
547
|
+
this.switchNavbarControls();
|
548
|
+
|
549
|
+
this.resizeBRcontainer();
|
550
|
+
this.updateFromParams(params);
|
551
|
+
this.initUIStrings();
|
552
|
+
|
553
|
+
// Bind to events
|
554
|
+
|
555
|
+
this.bindNavigationHandlers();
|
556
|
+
this.setupKeyListeners();
|
557
|
+
|
558
|
+
this.lastScroll = (new Date().getTime());
|
559
|
+
this.refs.$brContainer.bind('scroll', this, function(e) {
|
560
|
+
// Note, this scroll event fires for both user, and js generated calls
|
561
|
+
// It is functioning in some cases as the primary triggerer for rendering
|
562
|
+
e.data.lastScroll = (new Date().getTime());
|
563
|
+
if (e.data.constModeThumb == e.data.mode) {
|
564
|
+
e.data.drawLeafsThrottled();
|
565
|
+
}
|
566
|
+
});
|
567
|
+
|
568
|
+
if (this.options.autoResize) {
|
569
|
+
$(window).bind('resize', this, function(e) {
|
570
|
+
e.data.resize();
|
571
|
+
});
|
572
|
+
$(window).on("orientationchange", this, function(e) {
|
573
|
+
e.data.resize();
|
574
|
+
}.bind(this));
|
575
|
+
}
|
576
|
+
|
577
|
+
if (this.protected) {
|
578
|
+
this.$('.BRicon.share').hide();
|
579
|
+
}
|
580
|
+
|
581
|
+
// If not searching, set to allow on-going fragment changes
|
582
|
+
if (!this.options.initialSearchTerm) {
|
583
|
+
this.suppressFragmentChange = false;
|
584
|
+
}
|
585
|
+
|
586
|
+
this.init.initComplete = true;
|
587
|
+
this.trigger(BookReader.eventNames.PostInit);
|
588
|
+
|
589
|
+
// Must be called after this.init.initComplete set to true to allow
|
590
|
+
// BookReader.prototype.resize to run.
|
591
|
+
if (this.options.startFullscreen) {
|
592
|
+
this.toggleFullscreen();
|
593
|
+
}
|
594
|
+
};
|
595
|
+
|
596
|
+
/**
|
597
|
+
* @param {EVENTS} name
|
598
|
+
* @param {array | object} [props]
|
599
|
+
*/
|
600
|
+
BookReader.prototype.trigger = function(name, props = this) {
|
601
|
+
const eventName = 'BookReader:' + name;
|
602
|
+
$(document).trigger(eventName, props);
|
603
|
+
|
604
|
+
utils.polyfillCustomEvent(window);
|
605
|
+
window.dispatchEvent(new CustomEvent(eventName, {
|
606
|
+
bubbles: true,
|
607
|
+
composed: true,
|
608
|
+
detail: { props },
|
609
|
+
}));
|
610
|
+
};
|
611
|
+
|
612
|
+
BookReader.prototype.bind = function(name, callback) {
|
613
|
+
$(document).bind('BookReader:' + name, callback);
|
614
|
+
};
|
615
|
+
|
616
|
+
BookReader.prototype.unbind = function(name, callback) {
|
617
|
+
$(document).unbind('BookReader:' + name, callback);
|
618
|
+
};
|
619
|
+
|
620
|
+
/**
|
621
|
+
* Resizes based on the container width and height
|
622
|
+
*/
|
623
|
+
BookReader.prototype.resize = function() {
|
624
|
+
if (!this.init.initComplete) return;
|
625
|
+
|
626
|
+
this.resizeBRcontainer();
|
627
|
+
|
628
|
+
// Switch navbar controls on mobile/desktop
|
629
|
+
this.switchNavbarControls();
|
630
|
+
|
631
|
+
if (this.constMode1up == this.mode) {
|
632
|
+
if (this.onePage.autofit != 'none') {
|
633
|
+
this.resizePageView1up();
|
634
|
+
this.centerPageView();
|
635
|
+
} else {
|
636
|
+
this.centerPageView();
|
637
|
+
this.displayedIndices = [];
|
638
|
+
this.drawLeafsThrottled();
|
639
|
+
}
|
640
|
+
} else if (this.constModeThumb == this.mode) {
|
641
|
+
this.prepareThumbnailView();
|
642
|
+
} else {
|
643
|
+
// We only need to prepare again in autofit (size of spread changes)
|
644
|
+
if (this.twoPage.autofit) {
|
645
|
+
// most common path, esp. for archive.org books
|
646
|
+
this.prepareTwoPageView();
|
647
|
+
} else {
|
648
|
+
// used when zoomed in
|
649
|
+
// Re-center if the scrollbars have disappeared
|
650
|
+
var center = this.twoPageGetViewCenter();
|
651
|
+
var doRecenter = false;
|
652
|
+
if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
|
653
|
+
center.percentageX = 0.5;
|
654
|
+
doRecenter = true;
|
655
|
+
}
|
656
|
+
if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
|
657
|
+
center.percentageY = 0.5;
|
658
|
+
doRecenter = true;
|
659
|
+
}
|
660
|
+
if (doRecenter) {
|
661
|
+
this.twoPageCenterView(center.percentageX, center.percentageY);
|
662
|
+
}
|
663
|
+
}
|
664
|
+
}
|
665
|
+
this.trigger(BookReader.eventNames.resize);
|
666
|
+
};
|
667
|
+
|
668
|
+
/**
|
669
|
+
* Binds keyboard event listeners
|
670
|
+
*/
|
671
|
+
BookReader.prototype.setupKeyListeners = function() {
|
672
|
+
var self = this;
|
673
|
+
|
674
|
+
var KEY_PGUP = 33;
|
675
|
+
var KEY_PGDOWN = 34;
|
676
|
+
var KEY_END = 35;
|
677
|
+
var KEY_HOME = 36;
|
678
|
+
|
679
|
+
var KEY_LEFT = 37;
|
680
|
+
var KEY_UP = 38;
|
681
|
+
var KEY_RIGHT = 39;
|
682
|
+
var KEY_DOWN = 40;
|
683
|
+
// The minus(-) and equal(=) keys have different mappings for different browsers
|
684
|
+
var KEY_MINUS = 189; // Chrome
|
685
|
+
var KEY_MINUS_F = 173; // Firefox
|
686
|
+
var KEY_NUMPAD_SUBTRACT = 109;
|
687
|
+
var KEY_EQUAL = 187; // Chrome
|
688
|
+
var KEY_EQUAL_F = 61; // Firefox
|
689
|
+
var KEY_NUMPAD_ADD = 107;
|
690
|
+
|
691
|
+
// We use document here instead of window to avoid a bug in jQuery on IE7
|
692
|
+
$(document).keydown(function(e) {
|
693
|
+
|
694
|
+
// Keyboard navigation
|
695
|
+
switch (e.keyCode) {
|
696
|
+
case KEY_PGUP:
|
697
|
+
case KEY_UP:
|
698
|
+
// In 1up mode page scrolling is handled by browser
|
699
|
+
if (!utils.isInputActive() && self.constMode2up == self.mode) {
|
700
|
+
e.preventDefault();
|
701
|
+
self.prev();
|
702
|
+
}
|
703
|
+
break;
|
704
|
+
case KEY_DOWN:
|
705
|
+
case KEY_PGDOWN:
|
706
|
+
if (!utils.isInputActive() && self.constMode2up == self.mode) {
|
707
|
+
e.preventDefault();
|
708
|
+
self.next();
|
709
|
+
}
|
710
|
+
break;
|
711
|
+
case KEY_END:
|
712
|
+
if (!utils.isInputActive()) {
|
713
|
+
e.preventDefault();
|
714
|
+
self.last();
|
715
|
+
}
|
716
|
+
break;
|
717
|
+
case KEY_HOME:
|
718
|
+
if (!utils.isInputActive()) {
|
719
|
+
e.preventDefault();
|
720
|
+
self.first();
|
721
|
+
}
|
722
|
+
break;
|
723
|
+
case KEY_LEFT:
|
724
|
+
if (!utils.isInputActive() && self.constModeThumb != self.mode) {
|
725
|
+
e.preventDefault();
|
726
|
+
self.left();
|
727
|
+
}
|
728
|
+
break;
|
729
|
+
case KEY_RIGHT:
|
730
|
+
if (!utils.isInputActive() && self.constModeThumb != self.mode) {
|
731
|
+
e.preventDefault();
|
732
|
+
self.right();
|
733
|
+
}
|
734
|
+
break;
|
735
|
+
case KEY_MINUS:
|
736
|
+
case KEY_MINUS_F:
|
737
|
+
case KEY_NUMPAD_SUBTRACT:
|
738
|
+
if (!utils.isInputActive()) {
|
739
|
+
e.preventDefault();
|
740
|
+
self.zoom(-1);
|
741
|
+
}
|
742
|
+
break;
|
743
|
+
case KEY_EQUAL:
|
744
|
+
case KEY_EQUAL_F:
|
745
|
+
case KEY_NUMPAD_ADD:
|
746
|
+
if (!utils.isInputActive()) {
|
747
|
+
e.preventDefault();
|
748
|
+
self.zoom(+1);
|
749
|
+
}
|
750
|
+
break;
|
751
|
+
}
|
752
|
+
});
|
753
|
+
};
|
754
|
+
|
755
|
+
BookReader.prototype.drawLeafs = function() {
|
756
|
+
if (this.constMode1up == this.mode) {
|
757
|
+
// Not needed for Mode1Up anymore
|
758
|
+
} else if (this.constModeThumb == this.mode) {
|
759
|
+
this.drawLeafsThumbnail();
|
760
|
+
} else {
|
761
|
+
this.drawLeafsTwoPage();
|
762
|
+
}
|
763
|
+
};
|
764
|
+
|
765
|
+
/**
|
766
|
+
* @protected
|
767
|
+
* @param {PageIndex} index
|
768
|
+
*/
|
769
|
+
BookReader.prototype._createPageContainer = function(index) {
|
770
|
+
return new PageContainer(this._models.book.getPage(index, false), {
|
771
|
+
isProtected: this.protected,
|
772
|
+
imageCache: this.imageCache,
|
773
|
+
loadingImage: this.imagesBaseURL + 'loading.gif',
|
774
|
+
});
|
775
|
+
};
|
776
|
+
|
777
|
+
BookReader.prototype.bindGestures = function(jElement) {
|
778
|
+
// TODO support gesture change is only iOS. Support android.
|
779
|
+
// HACK(2017-01-20) - Momentum scrolling is causing the scroll position
|
780
|
+
// to jump after zooming in on mobile device. I am able to reproduce
|
781
|
+
// when you move the book with one finger and then add another
|
782
|
+
// finger to pinch. Gestures are aware of scroll state.
|
783
|
+
|
784
|
+
var self = this;
|
785
|
+
var numTouches = 1;
|
786
|
+
|
787
|
+
jElement.unbind('touchmove').bind('touchmove', function(e) {
|
788
|
+
if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
|
789
|
+
e.stopPropagation();
|
790
|
+
});
|
791
|
+
|
792
|
+
jElement.unbind('gesturechange').bind('gesturechange', function(e) {
|
793
|
+
e.preventDefault();
|
794
|
+
// These are two very important fixes to adjust for the scroll position
|
795
|
+
// issues described below
|
796
|
+
if (!(numTouches !== 2 || (new Date().getTime()) - self.lastScroll < 500)) {
|
797
|
+
if (e.originalEvent.scale > 1.5) {
|
798
|
+
self.zoom(1);
|
799
|
+
} else if (e.originalEvent.scale < 0.6) {
|
800
|
+
self.zoom(-1);
|
801
|
+
}
|
802
|
+
}
|
803
|
+
});
|
804
|
+
};
|
805
|
+
|
806
|
+
/** @deprecated Not used outside ModeThumb */
|
807
|
+
BookReader.prototype.drawLeafsThumbnail = ModeThumb.prototype.drawLeafs;
|
808
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'drawLeafs', 'drawLeafsThumbnail');
|
809
|
+
/** @deprecated Not used outside ModeThumb */
|
810
|
+
BookReader.prototype.lazyLoadThumbnails = ModeThumb.prototype.lazyLoadThumbnails;
|
811
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadThumbnails', 'lazyLoadThumbnails');
|
812
|
+
BookReader.prototype.lazyLoadImage = ModeThumb.prototype.lazyLoadImage;
|
813
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadImage', 'lazyLoadImage');
|
814
|
+
/** @deprecated Internal use only */
|
815
|
+
BookReader.prototype.zoomThumb = ModeThumb.prototype.zoom;
|
816
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'zoom', 'zoomThumb');
|
817
|
+
/** @deprecated Not used outside ModeThumb */
|
818
|
+
BookReader.prototype.getThumbnailWidth = ModeThumb.prototype.getThumbnailWidth;
|
819
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'getThumbnailWidth', 'getThumbnailWidth');
|
820
|
+
/** @deprecated Not used outside ModeThumb */
|
821
|
+
BookReader.prototype.prepareThumbnailView = ModeThumb.prototype.prepare;
|
822
|
+
exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'prepare', 'prepareThumbnailView');
|
823
|
+
|
824
|
+
/**
|
825
|
+
* A throttled version of drawLeafs
|
826
|
+
*/
|
827
|
+
BookReader.prototype.drawLeafsThrottled = utils.throttle(
|
828
|
+
BookReader.prototype.drawLeafs,
|
829
|
+
250 // 250 ms gives quick feedback, but doesn't eat cpu
|
830
|
+
);
|
831
|
+
|
832
|
+
/**
|
833
|
+
* @param {number} direction Pass 1 to zoom in, anything else to zoom out
|
834
|
+
*/
|
835
|
+
BookReader.prototype.zoom = function(direction) {
|
836
|
+
switch (this.mode) {
|
837
|
+
case this.constMode1up:
|
838
|
+
if (direction == 1) {
|
839
|
+
// XXX other cases
|
840
|
+
this.zoom1up('in');
|
841
|
+
} else {
|
842
|
+
this.zoom1up('out');
|
843
|
+
}
|
844
|
+
break;
|
845
|
+
case this.constMode2up:
|
846
|
+
if (direction == 1) {
|
847
|
+
// XXX other cases
|
848
|
+
this.zoom2up('in');
|
849
|
+
} else {
|
850
|
+
this.zoom2up('out');
|
851
|
+
}
|
852
|
+
break;
|
853
|
+
case this.constModeThumb:
|
854
|
+
// XXX update zoomThumb for named directions
|
855
|
+
this.zoomThumb(direction);
|
856
|
+
break;
|
857
|
+
}
|
858
|
+
|
859
|
+
this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
|
860
|
+
return;
|
861
|
+
};
|
862
|
+
|
863
|
+
/**
|
864
|
+
* Resizes the inner container to fit within the visible space to prevent
|
865
|
+
* the top toolbar and bottom navbar from clipping the visible book
|
866
|
+
*
|
867
|
+
* @param { boolean } animate - optional
|
868
|
+
* When used, BookReader will fill the main container with the book's content.
|
869
|
+
* This is primarily for 1up view - a follow up animation to the nav animation
|
870
|
+
* So resize isn't perceived sharp/jerky
|
871
|
+
*/
|
872
|
+
BookReader.prototype.resizeBRcontainer = function(animate) {
|
873
|
+
if (animate) {
|
874
|
+
this.refs.$brContainer.animate({
|
875
|
+
top: this.getToolBarHeight(),
|
876
|
+
bottom: this.getFooterHeight()
|
877
|
+
}, this.constResizeAnimationDuration, 'linear');
|
878
|
+
} else {
|
879
|
+
this.refs.$brContainer.css({
|
880
|
+
top: this.getToolBarHeight(),
|
881
|
+
bottom: this.getFooterHeight()
|
882
|
+
});
|
883
|
+
}
|
884
|
+
};
|
885
|
+
|
886
|
+
BookReader.prototype.centerPageView = function() {
|
887
|
+
var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
|
888
|
+
var clientWidth = this.refs.$brContainer.prop('clientWidth');
|
889
|
+
if (scrollWidth > clientWidth) {
|
890
|
+
this.refs.$brContainer.prop('scrollLeft', (scrollWidth - clientWidth) / 2);
|
891
|
+
}
|
892
|
+
};
|
893
|
+
|
894
|
+
/**
|
895
|
+
* Quantizes the given reduction factor to closest power of two from set from 12.5% to 200%
|
896
|
+
* @param {number} reduce
|
897
|
+
* @param {ReductionFactor[]} reductionFactors
|
898
|
+
* @return {number}
|
899
|
+
*/
|
900
|
+
BookReader.prototype.quantizeReduce = function(reduce, reductionFactors) {
|
901
|
+
let quantized = reductionFactors[0].reduce;
|
902
|
+
let distance = Math.abs(reduce - quantized);
|
903
|
+
|
904
|
+
for (let i = 1; i < reductionFactors.length; i++) {
|
905
|
+
const newDistance = Math.abs(reduce - reductionFactors[i].reduce);
|
906
|
+
if (newDistance < distance) {
|
907
|
+
distance = newDistance;
|
908
|
+
quantized = reductionFactors[i].reduce;
|
909
|
+
}
|
910
|
+
}
|
911
|
+
return quantized;
|
912
|
+
};
|
913
|
+
|
914
|
+
/**
|
915
|
+
* @param {number} currentReduce
|
916
|
+
* @param {'in' | 'out' | 'auto' | 'height' | 'width'} direction
|
917
|
+
* @param {ReductionFactor[]} reductionFactors Must be sorted
|
918
|
+
* @returns {ReductionFactor}
|
919
|
+
*/
|
920
|
+
BookReader.prototype.nextReduce = function(currentReduce, direction, reductionFactors) {
|
921
|
+
// XXX add 'closest', to replace quantize function
|
922
|
+
|
923
|
+
if (direction === 'in') {
|
924
|
+
let newReduceIndex = 0;
|
925
|
+
for (let i = 1; i < reductionFactors.length; i++) {
|
926
|
+
if (reductionFactors[i].reduce < currentReduce) {
|
927
|
+
newReduceIndex = i;
|
928
|
+
}
|
929
|
+
}
|
930
|
+
return reductionFactors[newReduceIndex];
|
931
|
+
} else if (direction === 'out') {
|
932
|
+
const lastIndex = reductionFactors.length - 1;
|
933
|
+
let newReduceIndex = lastIndex;
|
934
|
+
|
935
|
+
for (let i = lastIndex; i >= 0; i--) {
|
936
|
+
if (reductionFactors[i].reduce > currentReduce) {
|
937
|
+
newReduceIndex = i;
|
938
|
+
}
|
939
|
+
}
|
940
|
+
return reductionFactors[newReduceIndex];
|
941
|
+
} else if (direction === 'auto') {
|
942
|
+
// If an 'auto' is specified, use that
|
943
|
+
const autoMatch = reductionFactors.find(rf => rf.autofit == 'auto');
|
944
|
+
if (autoMatch) return autoMatch;
|
945
|
+
|
946
|
+
// Otherwise, choose the least reduction from height/width
|
947
|
+
const candidates = reductionFactors.filter(({autofit}) => autofit == 'height' || autofit == 'width');
|
948
|
+
let choice = null;
|
949
|
+
for (let i = 0; i < candidates.length; i++) {
|
950
|
+
if (choice === null || choice.reduce < candidates[i].reduce) {
|
951
|
+
choice = candidates[i];
|
952
|
+
}
|
953
|
+
}
|
954
|
+
if (choice) return choice;
|
955
|
+
} else if (direction === 'height' || direction === 'width') {
|
956
|
+
// Asked for specific autofit mode
|
957
|
+
const match = reductionFactors.find(rf => rf.autofit == direction);
|
958
|
+
if (match) return match;
|
959
|
+
}
|
960
|
+
|
961
|
+
return reductionFactors[0];
|
962
|
+
};
|
963
|
+
|
964
|
+
/**
|
965
|
+
* @param {ReductionFactor} a
|
966
|
+
* @param {ReductionFactor} b
|
967
|
+
*/
|
968
|
+
BookReader.prototype._reduceSort = (a, b) => a.reduce - b.reduce;
|
969
|
+
|
970
|
+
/**
|
971
|
+
* Attempts to jump to page
|
972
|
+
* @param {string}
|
973
|
+
* @return {boolean} Returns true if page could be found, false otherwise.
|
974
|
+
*/
|
975
|
+
BookReader.prototype.jumpToPage = function(pageNum) {
|
976
|
+
var pageIndex = this._models.book.parsePageString(pageNum);
|
977
|
+
|
978
|
+
if ('undefined' != typeof(pageIndex)) {
|
979
|
+
this.jumpToIndex(pageIndex);
|
980
|
+
return true;
|
981
|
+
}
|
982
|
+
|
983
|
+
// Page not found
|
984
|
+
return false;
|
985
|
+
};
|
986
|
+
|
987
|
+
/**
|
988
|
+
* Check whether the specified index is currently displayed
|
989
|
+
* @param {PageIndex} index
|
990
|
+
*/
|
991
|
+
BookReader.prototype._isIndexDisplayed = function(index) {
|
992
|
+
// One up "caches" pages +- current, so exclude those in the test.
|
993
|
+
return this.constMode1up == this.mode ? this.displayedIndices.slice(1, -1).includes(index) :
|
994
|
+
this.displayedIndices ? this.displayedIndices.includes(index) :
|
995
|
+
this.currentIndex() == index;
|
996
|
+
};
|
997
|
+
|
998
|
+
/**
|
999
|
+
* Changes the current page
|
1000
|
+
* @param {PageIndex} index
|
1001
|
+
* @param {number} [pageX]
|
1002
|
+
* @param {number} [pageY]
|
1003
|
+
* @param {boolean} [noAnimate]
|
1004
|
+
*/
|
1005
|
+
BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
|
1006
|
+
// Don't jump into specific unviewable page
|
1007
|
+
const page = this._models.book.getPage(index);
|
1008
|
+
if (!page.isViewable && page.unviewablesStart != page.index) {
|
1009
|
+
// If already in unviewable range, jump to end of that range
|
1010
|
+
const alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
|
1011
|
+
const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true }).index : page.unviewablesStart;
|
1012
|
+
return this.jumpToIndex(newIndex, pageX, pageY, noAnimate);
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
this.trigger(BookReader.eventNames.stop);
|
1016
|
+
|
1017
|
+
if (this.constMode2up == this.mode) {
|
1018
|
+
this._modes.mode2Up.jumpToIndex(index);
|
1019
|
+
} else if (this.constModeThumb == this.mode) {
|
1020
|
+
this._modes.modeThumb.jumpToIndex(index);
|
1021
|
+
} else { // 1up
|
1022
|
+
this._modes.mode1Up.jumpToIndex(index, pageX, pageY, noAnimate);
|
1023
|
+
}
|
1024
|
+
};
|
1025
|
+
|
1026
|
+
/**
|
1027
|
+
* Return mode or 1up if initial thumb
|
1028
|
+
* @param {number}
|
1029
|
+
* @see BookReader.prototype.drawLeafsThumbnail
|
1030
|
+
*/
|
1031
|
+
BookReader.prototype.getPrevReadMode = function(mode) {
|
1032
|
+
if (mode === BookReader.constMode1up || mode === BookReader.constMode2up) {
|
1033
|
+
return mode;
|
1034
|
+
} else if (this.prevReadMode === null) {
|
1035
|
+
// Initial thumb, return 1up
|
1036
|
+
return BookReader.constMode1up;
|
1037
|
+
}
|
1038
|
+
};
|
1039
|
+
|
1040
|
+
/**
|
1041
|
+
* Switches the mode (eg 1up 2up thumb)
|
1042
|
+
* @param {number}
|
1043
|
+
* @param {object} [options]
|
1044
|
+
* @param {boolean} [options.suppressFragmentChange = false]
|
1045
|
+
* @param {boolean} [options.onInit = false] - this
|
1046
|
+
*/
|
1047
|
+
BookReader.prototype.switchMode = function(
|
1048
|
+
mode,
|
1049
|
+
{
|
1050
|
+
suppressFragmentChange = false,
|
1051
|
+
init = false,
|
1052
|
+
pageFound = false
|
1053
|
+
} = {}
|
1054
|
+
) {
|
1055
|
+
// Skip checks before init() complete
|
1056
|
+
if (this.init.initComplete) {
|
1057
|
+
if (mode === this.mode) {
|
1058
|
+
return;
|
1059
|
+
}
|
1060
|
+
if (!this.canSwitchToMode(mode)) {
|
1061
|
+
return;
|
1062
|
+
}
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
this.trigger(BookReader.eventNames.stop);
|
1066
|
+
|
1067
|
+
this.prevReadMode = this.getPrevReadMode(this.mode);
|
1068
|
+
|
1069
|
+
if (this.mode != mode) {
|
1070
|
+
this.activeMode.unprepare?.();
|
1071
|
+
}
|
1072
|
+
|
1073
|
+
this.mode = mode;
|
1074
|
+
|
1075
|
+
// reinstate scale if moving from thumbnail view
|
1076
|
+
if (this.pageScale !== this.reduce) {
|
1077
|
+
this.reduce = this.pageScale;
|
1078
|
+
}
|
1079
|
+
|
1080
|
+
// $$$ TODO preserve center of view when switching between mode
|
1081
|
+
// See https://bugs.edge.launchpad.net/gnubook/+bug/416682
|
1082
|
+
|
1083
|
+
// XXX maybe better to preserve zoom in each mode
|
1084
|
+
if (this.constMode1up == mode) {
|
1085
|
+
this.prepareOnePageView();
|
1086
|
+
} else if (this.constModeThumb == mode) {
|
1087
|
+
this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
|
1088
|
+
this.prepareThumbnailView();
|
1089
|
+
} else {
|
1090
|
+
// $$$ why don't we save autofit?
|
1091
|
+
// this.twoPage.autofit = null; // Take zoom level from other mode
|
1092
|
+
// spread indices not set, so let's set them
|
1093
|
+
if (init || !pageFound) {
|
1094
|
+
this.setSpreadIndices();
|
1095
|
+
}
|
1096
|
+
|
1097
|
+
this.twoPageCalculateReductionFactors(); // this sets this.twoPage && this.reduce
|
1098
|
+
this.prepareTwoPageView();
|
1099
|
+
this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
|
1100
|
+
}
|
1101
|
+
|
1102
|
+
if (!(this.suppressFragmentChange || suppressFragmentChange)) {
|
1103
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
1104
|
+
}
|
1105
|
+
var eventName = mode + 'PageViewSelected';
|
1106
|
+
this.trigger(BookReader.eventNames[eventName]);
|
1107
|
+
|
1108
|
+
this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
|
1109
|
+
};
|
1110
|
+
|
1111
|
+
BookReader.prototype.updateBrClasses = function() {
|
1112
|
+
var modeToClass = {};
|
1113
|
+
modeToClass[this.constMode1up] = 'BRmode1up';
|
1114
|
+
modeToClass[this.constMode2up] = 'BRmode2Up';
|
1115
|
+
modeToClass[this.constModeThumb] = 'BRmodeThumb';
|
1116
|
+
|
1117
|
+
this.refs.$br
|
1118
|
+
.removeClass('BRmode1up BRmode2Up BRmodeThumb')
|
1119
|
+
.addClass(modeToClass[this.mode]);
|
1120
|
+
|
1121
|
+
if (this.isFullscreen()) {
|
1122
|
+
this.refs.$br.addClass('fullscreenActive');
|
1123
|
+
$(document.body).addClass('BRfullscreenActive');
|
1124
|
+
} else {
|
1125
|
+
this.refs.$br.removeClass('fullscreenActive');
|
1126
|
+
$(document.body).removeClass('BRfullscreenActive');
|
1127
|
+
}
|
1128
|
+
};
|
1129
|
+
|
1130
|
+
BookReader.prototype.isFullscreen = function() {
|
1131
|
+
return this.isFullscreenActive;
|
1132
|
+
};
|
1133
|
+
|
1134
|
+
/**
|
1135
|
+
* Toggles fullscreen
|
1136
|
+
* @param { boolean } bindKeyboardControls
|
1137
|
+
*/
|
1138
|
+
BookReader.prototype.toggleFullscreen = async function(bindKeyboardControls = true) {
|
1139
|
+
if (this.isFullscreen()) {
|
1140
|
+
await this.exitFullScreen();
|
1141
|
+
} else {
|
1142
|
+
await this.enterFullscreen(bindKeyboardControls);
|
1143
|
+
}
|
1144
|
+
};
|
1145
|
+
|
1146
|
+
/**
|
1147
|
+
* Enters fullscreen
|
1148
|
+
* including:
|
1149
|
+
* - animation
|
1150
|
+
* - binds keyboard controls
|
1151
|
+
* - fires custom event
|
1152
|
+
* @param { boolean } bindKeyboardControls
|
1153
|
+
*/
|
1154
|
+
BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = true) {
|
1155
|
+
const currentIndex = this.currentIndex();
|
1156
|
+
this.refs.$brContainer.css('opacity', 0);
|
1157
|
+
|
1158
|
+
if (bindKeyboardControls) {
|
1159
|
+
this._fullscreenCloseHandler = (e) => {
|
1160
|
+
if (e.keyCode === 27) this.toggleFullscreen();
|
1161
|
+
};
|
1162
|
+
$(document).keyup(this._fullscreenCloseHandler);
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
const windowWidth = $(window).width();
|
1166
|
+
if (windowWidth <= this.onePageMinBreakpoint) {
|
1167
|
+
this.switchMode(this.constMode1up);
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
this.isFullscreenActive = true;
|
1171
|
+
this.animating = true;
|
1172
|
+
await new Promise(res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res));
|
1173
|
+
this.resize();
|
1174
|
+
if (this.activeMode instanceof Mode1Up) {
|
1175
|
+
this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this._models.book.getPage(currentIndex));
|
1176
|
+
// Need the new scale to be applied before calling jumpToIndex
|
1177
|
+
await this.activeMode.mode1UpLit.requestUpdate();
|
1178
|
+
}
|
1179
|
+
this.jumpToIndex(currentIndex);
|
1180
|
+
this.animating = false;
|
1181
|
+
|
1182
|
+
this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
|
1183
|
+
this.trigger(BookReader.eventNames.fullscreenToggled);
|
1184
|
+
};
|
1185
|
+
|
1186
|
+
/**
|
1187
|
+
* Exits fullscreen
|
1188
|
+
* - toggles fullscreen
|
1189
|
+
* - binds keyboard controls
|
1190
|
+
* - fires custom event
|
1191
|
+
* @param { boolean } bindKeyboardControls
|
1192
|
+
*/
|
1193
|
+
BookReader.prototype.exitFullScreen = async function () {
|
1194
|
+
this.refs.$brContainer.css('opacity', 0);
|
1195
|
+
|
1196
|
+
$(document).unbind('keyup', this._fullscreenCloseHandler);
|
1197
|
+
|
1198
|
+
var windowWidth = $(window).width();
|
1199
|
+
|
1200
|
+
const canShow2up = this.options.controls.twoPage.visible;
|
1201
|
+
if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
|
1202
|
+
this.switchMode(this.constMode2up);
|
1203
|
+
}
|
1204
|
+
|
1205
|
+
this.isFullscreenActive = false;
|
1206
|
+
this.updateBrClasses();
|
1207
|
+
this.animating = true;
|
1208
|
+
await new Promise((res => this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', res)));
|
1209
|
+
this.resize();
|
1210
|
+
|
1211
|
+
if (this.activeMode instanceof Mode1Up) {
|
1212
|
+
this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this._models.book.getPage(this.currentIndex()));
|
1213
|
+
await this.activeMode.mode1UpLit.requestUpdate();
|
1214
|
+
}
|
1215
|
+
|
1216
|
+
this.animating = false;
|
1217
|
+
|
1218
|
+
this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
|
1219
|
+
this.trigger(BookReader.eventNames.fullscreenToggled);
|
1220
|
+
};
|
1221
|
+
|
1222
|
+
/**
|
1223
|
+
* Returns the currently active index
|
1224
|
+
* @return {number}
|
1225
|
+
* @throws
|
1226
|
+
*/
|
1227
|
+
BookReader.prototype.currentIndex = function() {
|
1228
|
+
// $$$ we should be cleaner with our idea of which index is active in 1up/2up
|
1229
|
+
if (this.mode == this.constMode1up || this.mode == this.constModeThumb) {
|
1230
|
+
return this.firstIndex; // $$$ TODO page in center of view would be better
|
1231
|
+
} else if (this.mode == this.constMode2up) {
|
1232
|
+
// Only allow indices that are actually present in book
|
1233
|
+
return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
|
1234
|
+
} else {
|
1235
|
+
throw 'currentIndex called for unimplemented mode ' + this.mode;
|
1236
|
+
}
|
1237
|
+
};
|
1238
|
+
|
1239
|
+
/**
|
1240
|
+
* Setter for this.firstIndex
|
1241
|
+
* Also triggers an event and updates the navbar slider position
|
1242
|
+
* @param {number} index
|
1243
|
+
* @param {object} [options]
|
1244
|
+
* @param {boolean} [options.suppressFragmentChange = false]
|
1245
|
+
*/
|
1246
|
+
BookReader.prototype.updateFirstIndex = function(
|
1247
|
+
index,
|
1248
|
+
{ suppressFragmentChange = false } = {}
|
1249
|
+
) {
|
1250
|
+
// If there's no change, do nothing
|
1251
|
+
if (this.firstIndex === index) return;
|
1252
|
+
|
1253
|
+
this.firstIndex = index;
|
1254
|
+
if (!(this.suppressFragmentChange || suppressFragmentChange)) {
|
1255
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
1256
|
+
}
|
1257
|
+
// If there's an initial search we stop suppressing global URL changes
|
1258
|
+
// when local suppression ends
|
1259
|
+
// This seems to correctly handle multiple calls during mode/1up
|
1260
|
+
if (this.options.initialSearchTerm && !suppressFragmentChange) {
|
1261
|
+
this.suppressFragmentChange = false;
|
1262
|
+
}
|
1263
|
+
this.trigger('pageChanged');
|
1264
|
+
this.updateNavIndexThrottled(index);
|
1265
|
+
};
|
1266
|
+
|
1267
|
+
/**
|
1268
|
+
* Flip the right page over onto the left
|
1269
|
+
*/
|
1270
|
+
BookReader.prototype.right = function() {
|
1271
|
+
if ('rl' != this.pageProgression) {
|
1272
|
+
this.next();
|
1273
|
+
} else {
|
1274
|
+
this.prev();
|
1275
|
+
}
|
1276
|
+
};
|
1277
|
+
|
1278
|
+
/**
|
1279
|
+
* Flip to the rightmost page
|
1280
|
+
*/
|
1281
|
+
BookReader.prototype.rightmost = function() {
|
1282
|
+
if ('rl' != this.pageProgression) {
|
1283
|
+
this.last();
|
1284
|
+
} else {
|
1285
|
+
this.first();
|
1286
|
+
}
|
1287
|
+
};
|
1288
|
+
|
1289
|
+
/**
|
1290
|
+
* Flip the left page over onto the right
|
1291
|
+
*/
|
1292
|
+
BookReader.prototype.left = function() {
|
1293
|
+
if ('rl' != this.pageProgression) {
|
1294
|
+
this.prev();
|
1295
|
+
} else {
|
1296
|
+
this.next();
|
1297
|
+
}
|
1298
|
+
};
|
1299
|
+
|
1300
|
+
/**
|
1301
|
+
* Flip to the leftmost page
|
1302
|
+
*/
|
1303
|
+
BookReader.prototype.leftmost = function() {
|
1304
|
+
if ('rl' != this.pageProgression) {
|
1305
|
+
this.first();
|
1306
|
+
} else {
|
1307
|
+
this.last();
|
1308
|
+
}
|
1309
|
+
};
|
1310
|
+
|
1311
|
+
BookReader.prototype.next = function() {
|
1312
|
+
if (this.constMode2up == this.mode) {
|
1313
|
+
this.trigger(BookReader.eventNames.stop);
|
1314
|
+
this.flipFwdToIndex(null);
|
1315
|
+
} else {
|
1316
|
+
if (this.firstIndex < this.lastDisplayableIndex()) {
|
1317
|
+
this.jumpToIndex(this.firstIndex + 1);
|
1318
|
+
}
|
1319
|
+
}
|
1320
|
+
};
|
1321
|
+
|
1322
|
+
BookReader.prototype.prev = function() {
|
1323
|
+
const isOnFrontPage = this.firstIndex < 1;
|
1324
|
+
if (isOnFrontPage) return;
|
1325
|
+
|
1326
|
+
if (this.constMode2up == this.mode) {
|
1327
|
+
this.trigger(BookReader.eventNames.stop);
|
1328
|
+
this.flipBackToIndex(null);
|
1329
|
+
} else {
|
1330
|
+
if (this.firstIndex >= 1) {
|
1331
|
+
this.jumpToIndex(this.firstIndex - 1);
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
};
|
1335
|
+
|
1336
|
+
BookReader.prototype.first = function() {
|
1337
|
+
this.jumpToIndex(this.firstDisplayableIndex());
|
1338
|
+
};
|
1339
|
+
|
1340
|
+
BookReader.prototype.last = function() {
|
1341
|
+
this.jumpToIndex(this.lastDisplayableIndex());
|
1342
|
+
};
|
1343
|
+
|
1344
|
+
/**
|
1345
|
+
* Scrolls down one screen view
|
1346
|
+
*/
|
1347
|
+
BookReader.prototype.scrollDown = function() {
|
1348
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
1349
|
+
if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
|
1350
|
+
// Whole pages are visible, scroll whole page only
|
1351
|
+
return this.next();
|
1352
|
+
}
|
1353
|
+
|
1354
|
+
this.refs.$brContainer.stop(true).animate(
|
1355
|
+
{ scrollTop: '+=' + this._scrollAmount() + 'px'},
|
1356
|
+
400, 'easeInOutExpo'
|
1357
|
+
);
|
1358
|
+
return true;
|
1359
|
+
} else {
|
1360
|
+
return false;
|
1361
|
+
}
|
1362
|
+
};
|
1363
|
+
|
1364
|
+
/**
|
1365
|
+
* Scrolls up one screen view
|
1366
|
+
*/
|
1367
|
+
BookReader.prototype.scrollUp = function() {
|
1368
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
1369
|
+
if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
|
1370
|
+
// Whole pages are visible, scroll whole page only
|
1371
|
+
return this.prev();
|
1372
|
+
}
|
1373
|
+
|
1374
|
+
this.refs.$brContainer.stop(true).animate(
|
1375
|
+
{ scrollTop: '-=' + this._scrollAmount() + 'px'},
|
1376
|
+
400, 'easeInOutExpo'
|
1377
|
+
);
|
1378
|
+
return true;
|
1379
|
+
} else {
|
1380
|
+
return false;
|
1381
|
+
}
|
1382
|
+
};
|
1383
|
+
|
1384
|
+
/**
|
1385
|
+
* The amount to scroll vertically in integer pixels
|
1386
|
+
*/
|
1387
|
+
BookReader.prototype._scrollAmount = function() {
|
1388
|
+
if (this.constMode1up == this.mode) {
|
1389
|
+
// Overlap by % of page size
|
1390
|
+
return parseInt(this.refs.$brContainer.prop('clientHeight') - this._models.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
|
1391
|
+
}
|
1392
|
+
|
1393
|
+
return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
|
1394
|
+
};
|
1395
|
+
|
1396
|
+
/**
|
1397
|
+
* @deprecated No longer used; will be remove in v5
|
1398
|
+
*/
|
1399
|
+
BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
|
1400
|
+
console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
|
1401
|
+
};
|
1402
|
+
|
1403
|
+
/**
|
1404
|
+
* @deprecated No longer used; will be remove in v5
|
1405
|
+
*/
|
1406
|
+
BookReader.prototype.pruneUnusedImgs = function() {
|
1407
|
+
console.warn('Call to deprecated function: BookReader.pruneUnused. No-op.');
|
1408
|
+
};
|
1409
|
+
|
1410
|
+
/************************/
|
1411
|
+
/** Mode1Up extensions **/
|
1412
|
+
/************************/
|
1413
|
+
/** @deprecated not used outside BookReader */
|
1414
|
+
BookReader.prototype.prepareOnePageView = Mode1Up.prototype.prepare;
|
1415
|
+
exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'prepare', 'prepareOnePageView');
|
1416
|
+
/** @deprecated not used outside BookReader */
|
1417
|
+
BookReader.prototype.zoom1up = Mode1Up.prototype.zoom;
|
1418
|
+
exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'zoom', 'zoom1up');
|
1419
|
+
/** @deprecated not used outside Mode1Up, BookReader */
|
1420
|
+
BookReader.prototype.resizePageView1up = Mode1Up.prototype.resizePageView;
|
1421
|
+
exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'resizePageView', 'resizePageView1up');
|
1422
|
+
/** @deprecated not used outside Mode1Up */
|
1423
|
+
BookReader.prototype.onePageCalculateViewDimensions = Mode1Up.prototype.calculateViewDimensions;
|
1424
|
+
exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateViewDimensions', 'onePageCalculateViewDimensions');
|
1425
|
+
|
1426
|
+
/************************/
|
1427
|
+
/** Mode2Up extensions **/
|
1428
|
+
/************************/
|
1429
|
+
/** @deprecated not used outside Mode2Up */
|
1430
|
+
BookReader.prototype.zoom2up = Mode2Up.prototype.zoom;
|
1431
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'zoom', 'zoom2up');
|
1432
|
+
BookReader.prototype.twoPageGetAutofitReduce = Mode2Up.prototype.getAutofitReduce;
|
1433
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getAutofitReduce', 'twoPageGetAutofitReduce');
|
1434
|
+
BookReader.prototype.flipBackToIndex = Mode2Up.prototype.flipBackToIndex;
|
1435
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipBackToIndex', 'flipBackToIndex');
|
1436
|
+
BookReader.prototype.flipFwdToIndex = Mode2Up.prototype.flipFwdToIndex;
|
1437
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipFwdToIndex', 'flipFwdToIndex');
|
1438
|
+
BookReader.prototype.setHilightCss2UP = Mode2Up.prototype.setHilightCss;
|
1439
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setHilightCss', 'setHilightCss2UP');
|
1440
|
+
/** @deprecated not used outside Mode2Up */
|
1441
|
+
BookReader.prototype.drawLeafsTwoPage = Mode2Up.prototype.drawLeafs;
|
1442
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'drawLeafs', 'drawLeafsTwoPage');
|
1443
|
+
/** @deprecated not used outside BookReader */
|
1444
|
+
BookReader.prototype.prepareTwoPageView = Mode2Up.prototype.prepareTwoPageView;
|
1445
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareTwoPageView', 'prepareTwoPageView');
|
1446
|
+
/** @deprecated not used outside Mode2Up */
|
1447
|
+
BookReader.prototype.prepareTwoPagePopUp = Mode2Up.prototype.preparePopUp;
|
1448
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'preparePopUp', 'prepareTwoPagePopUp');
|
1449
|
+
/** @deprecated not used outside BookReader, Mode2Up */
|
1450
|
+
BookReader.prototype.calculateSpreadSize = Mode2Up.prototype.calculateSpreadSize;
|
1451
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateSpreadSize', 'calculateSpreadSize');
|
1452
|
+
/** @deprecated not used outside BookReader, Mode2Up */
|
1453
|
+
BookReader.prototype.getIdealSpreadSize = Mode2Up.prototype.getIdealSpreadSize;
|
1454
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getIdealSpreadSize', 'getIdealSpreadSize');
|
1455
|
+
/** @deprecated not used outside BookReader, Mode2Up */
|
1456
|
+
BookReader.prototype.getSpreadSizeFromReduce = Mode2Up.prototype.getSpreadSizeFromReduce;
|
1457
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getSpreadSizeFromReduce', 'getSpreadSizeFromReduce');
|
1458
|
+
/** @deprecated unused */
|
1459
|
+
BookReader.prototype.twoPageIsZoomedIn = Mode2Up.prototype.isZoomedIn;
|
1460
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'isZoomedIn', 'twoPageIsZoomedIn');
|
1461
|
+
/** @deprecated not used outside BookReader */
|
1462
|
+
BookReader.prototype.twoPageCalculateReductionFactors = Mode2Up.prototype.calculateReductionFactors;
|
1463
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateReductionFactors', 'twoPageCalculateReductionFactors');
|
1464
|
+
/** @deprecated unused */
|
1465
|
+
BookReader.prototype.twoPageSetCursor = Mode2Up.prototype.setCursor;
|
1466
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setCursor', 'twoPageSetCursor');
|
1467
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1468
|
+
BookReader.prototype.flipLeftToRight = Mode2Up.prototype.flipLeftToRight;
|
1469
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipLeftToRight', 'flipLeftToRight');
|
1470
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1471
|
+
BookReader.prototype.flipRightToLeft = Mode2Up.prototype.flipRightToLeft;
|
1472
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipRightToLeft', 'flipRightToLeft');
|
1473
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1474
|
+
BookReader.prototype.prepareFlipLeftToRight = Mode2Up.prototype.prepareFlipLeftToRight;
|
1475
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipLeftToRight', 'prepareFlipLeftToRight');
|
1476
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1477
|
+
BookReader.prototype.prepareFlipRightToLeft = Mode2Up.prototype.prepareFlipRightToLeft;
|
1478
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipRightToLeft', 'prepareFlipRightToLeft');
|
1479
|
+
/** @deprecated unused outside Mode2Up */
|
1480
|
+
BookReader.prototype.getPageWidth2UP = Mode2Up.prototype.getPageWidth;
|
1481
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getPageWidth', 'getPageWidth2UP');
|
1482
|
+
/** @deprecated unused outside Mode2Up */
|
1483
|
+
BookReader.prototype.twoPageGutter = Mode2Up.prototype.gutter;
|
1484
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutter', 'twoPageGutter');
|
1485
|
+
/** @deprecated unused outside Mode2Up */
|
1486
|
+
BookReader.prototype.twoPageTop = Mode2Up.prototype.top;
|
1487
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'top', 'twoPageTop');
|
1488
|
+
/** @deprecated unused outside Mode2Up */
|
1489
|
+
BookReader.prototype.twoPageCoverWidth = Mode2Up.prototype.coverWidth;
|
1490
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'coverWidth', 'twoPageCoverWidth');
|
1491
|
+
/** @deprecated unused outside Mode2Up */
|
1492
|
+
BookReader.prototype.twoPageGetViewCenter = Mode2Up.prototype.getViewCenter;
|
1493
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getViewCenter', 'twoPageGetViewCenter');
|
1494
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1495
|
+
BookReader.prototype.twoPageCenterView = Mode2Up.prototype.centerView;
|
1496
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'centerView', 'twoPageCenterView');
|
1497
|
+
/** @deprecated unused outside Mode2Up */
|
1498
|
+
BookReader.prototype.twoPageFlipAreaHeight = Mode2Up.prototype.flipAreaHeight;
|
1499
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaHeight', 'twoPageFlipAreaHeight');
|
1500
|
+
/** @deprecated unused outside Mode2Up */
|
1501
|
+
BookReader.prototype.twoPageFlipAreaWidth = Mode2Up.prototype.flipAreaWidth;
|
1502
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaWidth', 'twoPageFlipAreaWidth');
|
1503
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1504
|
+
BookReader.prototype.twoPageFlipAreaTop = Mode2Up.prototype.flipAreaTop;
|
1505
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaTop', 'twoPageFlipAreaTop');
|
1506
|
+
/** @deprecated unused outside Mode2Up */
|
1507
|
+
BookReader.prototype.twoPageLeftFlipAreaLeft = Mode2Up.prototype.leftFlipAreaLeft;
|
1508
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leftFlipAreaLeft', 'twoPageLeftFlipAreaLeft');
|
1509
|
+
/** @deprecated unused outside Mode2Up */
|
1510
|
+
BookReader.prototype.twoPageRightFlipAreaLeft = Mode2Up.prototype.rightFlipAreaLeft;
|
1511
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'rightFlipAreaLeft', 'twoPageRightFlipAreaLeft');
|
1512
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1513
|
+
BookReader.prototype.gutterOffsetForIndex = Mode2Up.prototype.gutterOffsetForIndex;
|
1514
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutterOffsetForIndex', 'gutterOffsetForIndex');
|
1515
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1516
|
+
BookReader.prototype.leafEdgeWidth = Mode2Up.prototype.leafEdgeWidth;
|
1517
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leafEdgeWidth', 'leafEdgeWidth');
|
1518
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1519
|
+
BookReader.prototype.jumpIndexForLeftEdgePageX = Mode2Up.prototype.jumpIndexForLeftEdgePageX;
|
1520
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForLeftEdgePageX', 'jumpIndexForLeftEdgePageX');
|
1521
|
+
/** @deprecated unused outside BookReader, Mode2Up */
|
1522
|
+
BookReader.prototype.jumpIndexForRightEdgePageX = Mode2Up.prototype.jumpIndexForRightEdgePageX;
|
1523
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForRightEdgePageX', 'jumpIndexForRightEdgePageX');
|
1524
|
+
/** @deprecated unused outside Mode2Up */
|
1525
|
+
BookReader.prototype.prefetch = Mode2Up.prototype.prefetch;
|
1526
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prefetch', 'prefetch');
|
1527
|
+
/** @deprecated unused outside Mode2Up */
|
1528
|
+
BookReader.prototype.setSpreadIndices = Mode2Up.prototype.setSpreadIndices;
|
1529
|
+
exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setSpreadIndices', 'setSpreadIndices');
|
1530
|
+
/**
|
1531
|
+
* Immediately stop flip animations. Callbacks are triggered.
|
1532
|
+
*/
|
1533
|
+
BookReader.prototype.stopFlipAnimations = function() {
|
1534
|
+
this.trigger(BookReader.eventNames.stop);
|
1535
|
+
|
1536
|
+
// Stop animation, clear queue, trigger callbacks
|
1537
|
+
if (this.leafEdgeTmp) {
|
1538
|
+
$(this.leafEdgeTmp).stop(false, true);
|
1539
|
+
}
|
1540
|
+
jQuery.each(this._modes.mode2Up.pageContainers, function() {
|
1541
|
+
$(this.$container).stop(false, true);
|
1542
|
+
});
|
1543
|
+
|
1544
|
+
// And again since animations also queued in callbacks
|
1545
|
+
if (this.leafEdgeTmp) {
|
1546
|
+
$(this.leafEdgeTmp).stop(false, true);
|
1547
|
+
}
|
1548
|
+
jQuery.each(this._modes.mode2Up.pageContainers, function() {
|
1549
|
+
$(this.$container).stop(false, true);
|
1550
|
+
});
|
1551
|
+
};
|
1552
|
+
|
1553
|
+
/**
|
1554
|
+
* @template TClass extends { br: BookReader }
|
1555
|
+
* Helper method to expose a method onto BookReader from a composed class.
|
1556
|
+
* Only composed classes in BookReader._overridable can be exposed in this
|
1557
|
+
* way.
|
1558
|
+
* @param {new () => TClass} Class
|
1559
|
+
* @param {keyof BookReader['_overrideable']} classKey
|
1560
|
+
* @param {keyof TClass} method
|
1561
|
+
* @param {string} [brMethod]
|
1562
|
+
*/
|
1563
|
+
function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
|
1564
|
+
/** @type {function(TClass): BookReader} */
|
1565
|
+
const classToBr = cls => cls.br;
|
1566
|
+
/** @type {function(BookReader): TClass} */
|
1567
|
+
const brToClass = br => br._overrideable[classKey];
|
1568
|
+
exposeOverrideable(Class, method, classToBr, BookReader, brMethod, brToClass);
|
1569
|
+
}
|
1570
|
+
|
1571
|
+
|
1572
|
+
/***********************/
|
1573
|
+
/** Navbar extensions **/
|
1574
|
+
/***********************/
|
1575
|
+
BookReader.prototype.initNavbar = Navbar.prototype.init;
|
1576
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
|
1577
|
+
BookReader.prototype.switchNavbarControls = Navbar.prototype.switchNavbarControls;
|
1578
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'switchNavbarControls');
|
1579
|
+
BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButton;
|
1580
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
|
1581
|
+
BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
|
1582
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
|
1583
|
+
/** @deprecated */
|
1584
|
+
BookReader.prototype.initEmbedNavbar = Navbar.prototype.initEmbed;
|
1585
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'initEmbed', 'initEmbedNavbar');
|
1586
|
+
/** @deprecated unused */
|
1587
|
+
BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
|
1588
|
+
/** @deprecated unused outside this file */
|
1589
|
+
BookReader.prototype.updateNavPageNum = Navbar.prototype.updateNavPageNum;
|
1590
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavPageNum');
|
1591
|
+
/** @deprecated unused outside this file */
|
1592
|
+
BookReader.prototype.updateNavIndex = Navbar.prototype.updateNavIndex;
|
1593
|
+
exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavIndex');
|
1594
|
+
/** @deprecated unused outside this file */
|
1595
|
+
BookReader.prototype.updateNavIndexThrottled = utils.throttle(BookReader.prototype.updateNavIndex, 250, false);
|
1596
|
+
/** @deprecated unused */
|
1597
|
+
BookReader.prototype.updateNavIndexDebounced = utils.debounce(BookReader.prototype.updateNavIndex, 500, false);
|
1598
|
+
|
1599
|
+
|
1600
|
+
/************************/
|
1601
|
+
/** Toolbar extensions **/
|
1602
|
+
/************************/
|
1603
|
+
BookReader.prototype.buildToolbarElement = Toolbar.prototype.buildToolbarElement;
|
1604
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildToolbarElement');
|
1605
|
+
BookReader.prototype.initToolbar = Toolbar.prototype.initToolbar;
|
1606
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'initToolbar');
|
1607
|
+
BookReader.prototype.buildShareDiv = Toolbar.prototype.buildShareDiv;
|
1608
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildShareDiv');
|
1609
|
+
BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
|
1610
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
|
1611
|
+
BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
|
1612
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
|
1613
|
+
/** @deprecated zoom no longer in toolbar */
|
1614
|
+
BookReader.prototype.updateToolbarZoom = Toolbar.prototype.updateToolbarZoom;
|
1615
|
+
exposeOverrideableMethod(Toolbar, '_components.toolbar', 'updateToolbarZoom');
|
1616
|
+
/** @deprecated unused */
|
1617
|
+
BookReader.prototype.blankInfoDiv = blankInfoDiv;
|
1618
|
+
/** @deprecated unused */
|
1619
|
+
BookReader.prototype.blankShareDiv = blankShareDiv;
|
1620
|
+
/** @deprecated unused */
|
1621
|
+
BookReader.prototype.createPopup = createPopup;
|
1622
|
+
|
1623
|
+
/**
|
1624
|
+
* Bind navigation handlers
|
1625
|
+
*/
|
1626
|
+
BookReader.prototype.bindNavigationHandlers = function() {
|
1627
|
+
const self = this;
|
1628
|
+
|
1629
|
+
// Note the mobile plugin attaches itself to body, so we need to select outside
|
1630
|
+
const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
|
1631
|
+
// Map of jIcon class -> click handler
|
1632
|
+
const navigationControls = {
|
1633
|
+
book_left: () => {
|
1634
|
+
this.trigger(BookReader.eventNames.stop);
|
1635
|
+
this.left();
|
1636
|
+
},
|
1637
|
+
book_right: () => {
|
1638
|
+
this.trigger(BookReader.eventNames.stop);
|
1639
|
+
this.right();
|
1640
|
+
},
|
1641
|
+
book_up: () => {
|
1642
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
1643
|
+
this.scrollUp();
|
1644
|
+
} else {
|
1645
|
+
this.prev();
|
1646
|
+
}
|
1647
|
+
},
|
1648
|
+
book_down: () => {
|
1649
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
1650
|
+
this.scrollDown();
|
1651
|
+
} else {
|
1652
|
+
this.next();
|
1653
|
+
}
|
1654
|
+
},
|
1655
|
+
book_top: this.first.bind(this),
|
1656
|
+
book_bottom: this.last.bind(this),
|
1657
|
+
book_leftmost: this.leftmost.bind(this),
|
1658
|
+
book_rightmost: this.rightmost.bind(this),
|
1659
|
+
onepg: () => {
|
1660
|
+
this.switchMode(self.constMode1up);
|
1661
|
+
},
|
1662
|
+
thumb: () => {
|
1663
|
+
this.switchMode(self.constModeThumb);
|
1664
|
+
},
|
1665
|
+
twopg: () => {
|
1666
|
+
this.switchMode(self.constMode2up);
|
1667
|
+
},
|
1668
|
+
zoom_in: () => {
|
1669
|
+
this.trigger(BookReader.eventNames.stop);
|
1670
|
+
this.zoom(1);
|
1671
|
+
this.trigger(BookReader.eventNames.zoomIn);
|
1672
|
+
},
|
1673
|
+
zoom_out: () => {
|
1674
|
+
this.trigger(BookReader.eventNames.stop);
|
1675
|
+
this.zoom(-1);
|
1676
|
+
this.trigger(BookReader.eventNames.zoomOut);
|
1677
|
+
},
|
1678
|
+
full: () => {
|
1679
|
+
if (this.ui == 'embed') {
|
1680
|
+
var url = this.$('.BRembedreturn a').attr('href');
|
1681
|
+
window.open(url);
|
1682
|
+
} else {
|
1683
|
+
this.toggleFullscreen();
|
1684
|
+
}
|
1685
|
+
},
|
1686
|
+
};
|
1687
|
+
|
1688
|
+
jIcons.filter('.fit').bind('fit', function() {
|
1689
|
+
// XXXmang implement autofit zoom
|
1690
|
+
});
|
1691
|
+
|
1692
|
+
for (const control in navigationControls) {
|
1693
|
+
jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
|
1694
|
+
navigationControls[control]();
|
1695
|
+
return false;
|
1696
|
+
});
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
|
1700
|
+
var $brNavCntlTopEl = this.$('.BRnavCntlTop');
|
1701
|
+
|
1702
|
+
this.$('.BRnavCntl').click(
|
1703
|
+
function() {
|
1704
|
+
var promises = [];
|
1705
|
+
// TODO don't use magic constants
|
1706
|
+
// TODO move this to a function
|
1707
|
+
if ($brNavCntlBtmEl.hasClass('BRdn')) {
|
1708
|
+
if (self.refs.$BRtoolbar)
|
1709
|
+
promises.push(self.refs.$BRtoolbar.animate(
|
1710
|
+
{top: self.getToolBarHeight() * -1}
|
1711
|
+
).promise());
|
1712
|
+
promises.push(self.$('.BRfooter').animate({bottom: self.getFooterHeight() * -1}).promise());
|
1713
|
+
$brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
|
1714
|
+
$brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
|
1715
|
+
self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
|
1716
|
+
self.$('.BRnavCntl').delay(1000).animate({opacity:.75}, 1000);
|
1717
|
+
} else {
|
1718
|
+
if (self.refs.$BRtoolbar)
|
1719
|
+
promises.push(self.refs.$BRtoolbar.animate({top:0}).promise());
|
1720
|
+
promises.push(self.$('.BRfooter').animate({bottom:0}).promise());
|
1721
|
+
$brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
|
1722
|
+
$brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
|
1723
|
+
self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
|
1724
|
+
self.$('.BRvavCntl').animate({opacity:1});
|
1725
|
+
}
|
1726
|
+
$.when.apply($, promises).done(function() {
|
1727
|
+
// Only do full resize in auto mode and need to recalc. size
|
1728
|
+
if (self.mode == self.constMode2up && self.twoPage.autofit != null
|
1729
|
+
&& self.twoPage.autofit != 'none'
|
1730
|
+
) {
|
1731
|
+
self.resize();
|
1732
|
+
} else if (self.mode == self.constMode1up && self.onePage.autofit != null
|
1733
|
+
&& self.onePage.autofit != 'none') {
|
1734
|
+
self.resize();
|
1735
|
+
} else {
|
1736
|
+
// Don't do a full resize to avoid redrawing images
|
1737
|
+
self.resizeBRcontainer();
|
1738
|
+
}
|
1739
|
+
});
|
1740
|
+
}
|
1741
|
+
);
|
1742
|
+
$brNavCntlBtmEl.mouseover(function() {
|
1743
|
+
if ($(this).hasClass('BRup')) {
|
1744
|
+
self.$('.BRnavCntl').animate({opacity:1},250);
|
1745
|
+
}
|
1746
|
+
}).mouseleave(function() {
|
1747
|
+
if ($(this).hasClass('BRup')) {
|
1748
|
+
self.$('.BRnavCntl').animate({opacity:.75},250);
|
1749
|
+
}
|
1750
|
+
});
|
1751
|
+
$brNavCntlTopEl.mouseover(function() {
|
1752
|
+
if ($(this).hasClass('BRdn')) {
|
1753
|
+
self.$('.BRnavCntl').animate({opacity:1},250);
|
1754
|
+
}
|
1755
|
+
}).mouseleave(function() {
|
1756
|
+
if ($(this).hasClass('BRdn')) {
|
1757
|
+
self.$('.BRnavCntl').animate({opacity:.75},250);
|
1758
|
+
}
|
1759
|
+
});
|
1760
|
+
|
1761
|
+
this.initSwipeData();
|
1762
|
+
|
1763
|
+
$(document).off('mousemove.navigation', this.el);
|
1764
|
+
$(document).on(
|
1765
|
+
'mousemove.navigation',
|
1766
|
+
this.el,
|
1767
|
+
{ 'br': this },
|
1768
|
+
this.navigationMousemoveHandler
|
1769
|
+
);
|
1770
|
+
|
1771
|
+
$(document).off('mousedown.swipe', '.BRpageimage');
|
1772
|
+
$(document).on(
|
1773
|
+
'mousedown.swipe',
|
1774
|
+
'.BRpageimage',
|
1775
|
+
{ 'br': this },
|
1776
|
+
this.swipeMousedownHandler
|
1777
|
+
);
|
1778
|
+
|
1779
|
+
this.bindMozTouchHandlers();
|
1780
|
+
};
|
1781
|
+
|
1782
|
+
/**
|
1783
|
+
* Unbind navigation handlers
|
1784
|
+
*/
|
1785
|
+
BookReader.prototype.unbindNavigationHandlers = function() {
|
1786
|
+
$(document).off('mousemove.navigation', this.el);
|
1787
|
+
};
|
1788
|
+
|
1789
|
+
/**
|
1790
|
+
* Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
|
1791
|
+
*/
|
1792
|
+
BookReader.prototype.navigationMousemoveHandler = function(event) {
|
1793
|
+
// $$$ possibly not great to be calling this for every mousemove
|
1794
|
+
if (event.data['br'].uiAutoHide) {
|
1795
|
+
// 77px is an approximate height of the Internet Archive Top Nav
|
1796
|
+
// 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
|
1797
|
+
var navkey = $(document).height() - 75;
|
1798
|
+
if ((event.pageY < 76) || (event.pageY > navkey)) {
|
1799
|
+
// inside or near navigation elements
|
1800
|
+
event.data['br'].hideNavigation();
|
1801
|
+
} else {
|
1802
|
+
event.data['br'].showNavigation();
|
1803
|
+
}
|
1804
|
+
}
|
1805
|
+
};
|
1806
|
+
|
1807
|
+
BookReader.prototype.initSwipeData = function(clientX, clientY) {
|
1808
|
+
/*
|
1809
|
+
* Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
|
1810
|
+
*/
|
1811
|
+
this._swipe = {
|
1812
|
+
mightBeSwiping: false,
|
1813
|
+
didSwipe: false,
|
1814
|
+
mightBeDraggin: false,
|
1815
|
+
didDrag: false,
|
1816
|
+
startTime: (new Date).getTime(),
|
1817
|
+
startX: clientX,
|
1818
|
+
startY: clientY,
|
1819
|
+
lastX: clientX,
|
1820
|
+
lastY: clientY,
|
1821
|
+
deltaX: 0,
|
1822
|
+
deltaY: 0,
|
1823
|
+
deltaT: 0
|
1824
|
+
};
|
1825
|
+
};
|
1826
|
+
|
1827
|
+
BookReader.prototype.swipeMousedownHandler = function(event) {
|
1828
|
+
var self = event.data['br'];
|
1829
|
+
|
1830
|
+
// We should be the last bubble point for the page images
|
1831
|
+
// Disable image drag and select, but keep right-click
|
1832
|
+
if (event.which == 3) {
|
1833
|
+
return !self.protected;
|
1834
|
+
}
|
1835
|
+
|
1836
|
+
$(event.target).bind('mouseout.swipe',
|
1837
|
+
{ 'br': self},
|
1838
|
+
self.swipeMouseupHandler
|
1839
|
+
).bind('mouseup.swipe',
|
1840
|
+
{ 'br': self},
|
1841
|
+
self.swipeMouseupHandler
|
1842
|
+
).bind('mousemove.swipe',
|
1843
|
+
{ 'br': self },
|
1844
|
+
self.swipeMousemoveHandler
|
1845
|
+
);
|
1846
|
+
|
1847
|
+
self.initSwipeData(event.clientX, event.clientY);
|
1848
|
+
self._swipe.mightBeSwiping = true;
|
1849
|
+
self._swipe.mightBeDragging = true;
|
1850
|
+
|
1851
|
+
event.preventDefault();
|
1852
|
+
event.returnValue = false;
|
1853
|
+
event.cancelBubble = true;
|
1854
|
+
return false;
|
1855
|
+
};
|
1856
|
+
|
1857
|
+
BookReader.prototype.swipeMousemoveHandler = function(event) {
|
1858
|
+
var self = event.data['br'];
|
1859
|
+
var _swipe = self._swipe;
|
1860
|
+
if (! _swipe.mightBeSwiping) {
|
1861
|
+
return;
|
1862
|
+
}
|
1863
|
+
|
1864
|
+
// Update swipe data
|
1865
|
+
_swipe.deltaX = event.clientX - _swipe.startX;
|
1866
|
+
_swipe.deltaY = event.clientY - _swipe.startY;
|
1867
|
+
_swipe.deltaT = (new Date).getTime() - _swipe.startTime;
|
1868
|
+
|
1869
|
+
var absX = Math.abs(_swipe.deltaX);
|
1870
|
+
var absY = Math.abs(_swipe.deltaY);
|
1871
|
+
|
1872
|
+
// Minimum distance in the amount of tim to trigger the swipe
|
1873
|
+
var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
|
1874
|
+
var maxSwipeTime = 400;
|
1875
|
+
|
1876
|
+
// Check for horizontal swipe
|
1877
|
+
if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
|
1878
|
+
_swipe.mightBeSwiping = false; // only trigger once
|
1879
|
+
_swipe.didSwipe = true;
|
1880
|
+
if (self.mode == self.constMode2up) {
|
1881
|
+
if (_swipe.deltaX < 0) {
|
1882
|
+
self.right();
|
1883
|
+
} else {
|
1884
|
+
self.left();
|
1885
|
+
}
|
1886
|
+
}
|
1887
|
+
}
|
1888
|
+
|
1889
|
+
if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
|
1890
|
+
if (_swipe.mightBeDragging) {
|
1891
|
+
// Dragging
|
1892
|
+
_swipe.didDrag = true;
|
1893
|
+
self.refs.$brContainer
|
1894
|
+
.scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
|
1895
|
+
.scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
|
1896
|
+
}
|
1897
|
+
}
|
1898
|
+
_swipe.lastX = event.clientX;
|
1899
|
+
_swipe.lastY = event.clientY;
|
1900
|
+
|
1901
|
+
event.preventDefault();
|
1902
|
+
event.returnValue = false;
|
1903
|
+
event.cancelBubble = true;
|
1904
|
+
return false;
|
1905
|
+
};
|
1906
|
+
|
1907
|
+
BookReader.prototype.swipeMouseupHandler = function(event) {
|
1908
|
+
var _swipe = event.data['br']._swipe;
|
1909
|
+
_swipe.mightBeSwiping = false;
|
1910
|
+
_swipe.mightBeDragging = false;
|
1911
|
+
|
1912
|
+
$(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
|
1913
|
+
|
1914
|
+
if (_swipe.didSwipe || _swipe.didDrag) {
|
1915
|
+
// Swallow event if completed swipe gesture
|
1916
|
+
event.preventDefault();
|
1917
|
+
event.returnValue = false;
|
1918
|
+
event.cancelBubble = true;
|
1919
|
+
return false;
|
1920
|
+
}
|
1921
|
+
return true;
|
1922
|
+
};
|
1923
|
+
|
1924
|
+
BookReader.prototype.bindMozTouchHandlers = function() {
|
1925
|
+
var self = this;
|
1926
|
+
|
1927
|
+
// Currently only want touch handlers in 2up
|
1928
|
+
this.refs.$br.bind('MozTouchDown', function(event) {
|
1929
|
+
if (this.mode == self.constMode2up) {
|
1930
|
+
event.preventDefault();
|
1931
|
+
}
|
1932
|
+
})
|
1933
|
+
.bind('MozTouchMove', function(event) {
|
1934
|
+
if (this.mode == self.constMode2up) {
|
1935
|
+
event.preventDefault();
|
1936
|
+
}
|
1937
|
+
})
|
1938
|
+
.bind('MozTouchUp', function(event) {
|
1939
|
+
if (this.mode == self.constMode2up) {
|
1940
|
+
event.preventDefault();
|
1941
|
+
}
|
1942
|
+
});
|
1943
|
+
};
|
1944
|
+
|
1945
|
+
/**
|
1946
|
+
* Returns true if the navigation elements are currently visible
|
1947
|
+
* @return {boolean}
|
1948
|
+
*/
|
1949
|
+
BookReader.prototype.navigationIsVisible = function() {
|
1950
|
+
// $$$ doesn't account for transitioning states, nav must be fully visible to return true
|
1951
|
+
var toolpos = this.refs.$BRtoolbar.position();
|
1952
|
+
var tooltop = toolpos.top;
|
1953
|
+
return tooltop == 0;
|
1954
|
+
};
|
1955
|
+
|
1956
|
+
/**
|
1957
|
+
* Main controller that sets navigation into view.
|
1958
|
+
* Defaults to SHOW the navigation chrome
|
1959
|
+
*/
|
1960
|
+
BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
|
1961
|
+
var animationLength = this.constNavAnimationDuration;
|
1962
|
+
var animationType = 'linear';
|
1963
|
+
var resizePageContainer = function resizePageContainer () {
|
1964
|
+
/* main page container fills whole container */
|
1965
|
+
if (this.constMode2up !== this.mode) {
|
1966
|
+
var animate = true;
|
1967
|
+
this.resizeBRcontainer(animate);
|
1968
|
+
}
|
1969
|
+
this.trigger(BookReader.eventNames.navToggled);
|
1970
|
+
}.bind(this);
|
1971
|
+
|
1972
|
+
var toolbarHeight = 0;
|
1973
|
+
var navbarHeight = 0;
|
1974
|
+
if (hide) {
|
1975
|
+
toolbarHeight = this.getToolBarHeight() * -1;
|
1976
|
+
navbarHeight = this.getFooterHeight() * -1;
|
1977
|
+
|
1978
|
+
this.refs.$BRtoolbar.addClass('js-menu-hide');
|
1979
|
+
this.refs.$BRfooter.addClass('js-menu-hide');
|
1980
|
+
} else {
|
1981
|
+
this.refs.$BRtoolbar.removeClass('js-menu-hide');
|
1982
|
+
this.refs.$BRfooter.removeClass('js-menu-hide');
|
1983
|
+
}
|
1984
|
+
|
1985
|
+
this.refs.$BRtoolbar.animate(
|
1986
|
+
{ top: toolbarHeight },
|
1987
|
+
animationLength,
|
1988
|
+
animationType,
|
1989
|
+
resizePageContainer
|
1990
|
+
);
|
1991
|
+
this.refs.$BRfooter.animate(
|
1992
|
+
{ bottom: navbarHeight },
|
1993
|
+
animationLength,
|
1994
|
+
animationType,
|
1995
|
+
resizePageContainer
|
1996
|
+
);
|
1997
|
+
};
|
1998
|
+
/**
|
1999
|
+
* Hide navigation elements, if visible
|
2000
|
+
*/
|
2001
|
+
BookReader.prototype.hideNavigation = function() {
|
2002
|
+
// Check if navigation is showing
|
2003
|
+
if (this.navigationIsVisible()) {
|
2004
|
+
var hide = true;
|
2005
|
+
this.setNavigationView(hide);
|
2006
|
+
}
|
2007
|
+
};
|
2008
|
+
|
2009
|
+
/**
|
2010
|
+
* Show navigation elements
|
2011
|
+
*/
|
2012
|
+
BookReader.prototype.showNavigation = function() {
|
2013
|
+
// Check if navigation is hidden
|
2014
|
+
if (!this.navigationIsVisible()) {
|
2015
|
+
this.setNavigationView();
|
2016
|
+
}
|
2017
|
+
};
|
2018
|
+
|
2019
|
+
/**
|
2020
|
+
* Returns the index of the first visible page, dependent on the mode.
|
2021
|
+
* $$$ Currently we cannot display the front/back cover in 2-up and will need to update
|
2022
|
+
* this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
|
2023
|
+
* @return {number}
|
2024
|
+
*/
|
2025
|
+
BookReader.prototype.firstDisplayableIndex = function() {
|
2026
|
+
if (this.mode != this.constMode2up) {
|
2027
|
+
return 0;
|
2028
|
+
}
|
2029
|
+
|
2030
|
+
if ('rl' != this.pageProgression) {
|
2031
|
+
// LTR
|
2032
|
+
if (this._models.book.getPageSide(0) == 'L') {
|
2033
|
+
return 0;
|
2034
|
+
} else {
|
2035
|
+
return -1;
|
2036
|
+
}
|
2037
|
+
} else {
|
2038
|
+
// RTL
|
2039
|
+
if (this._models.book.getPageSide(0) == 'R') {
|
2040
|
+
return 0;
|
2041
|
+
} else {
|
2042
|
+
return -1;
|
2043
|
+
}
|
2044
|
+
}
|
2045
|
+
};
|
2046
|
+
|
2047
|
+
/**
|
2048
|
+
* Returns the index of the last visible page, dependent on the mode.
|
2049
|
+
* $$$ Currently we cannot display the front/back cover in 2-up and will need to update
|
2050
|
+
* this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
|
2051
|
+
* @return {number}
|
2052
|
+
*/
|
2053
|
+
BookReader.prototype.lastDisplayableIndex = function() {
|
2054
|
+
|
2055
|
+
var lastIndex = this._models.book.getNumLeafs() - 1;
|
2056
|
+
|
2057
|
+
if (this.mode != this.constMode2up) {
|
2058
|
+
return lastIndex;
|
2059
|
+
}
|
2060
|
+
|
2061
|
+
if ('rl' != this.pageProgression) {
|
2062
|
+
// LTR
|
2063
|
+
if (this._models.book.getPageSide(lastIndex) == 'R') {
|
2064
|
+
return lastIndex;
|
2065
|
+
} else {
|
2066
|
+
return lastIndex + 1;
|
2067
|
+
}
|
2068
|
+
} else {
|
2069
|
+
// RTL
|
2070
|
+
if (this._models.book.getPageSide(lastIndex) == 'L') {
|
2071
|
+
return lastIndex;
|
2072
|
+
} else {
|
2073
|
+
return lastIndex + 1;
|
2074
|
+
}
|
2075
|
+
}
|
2076
|
+
};
|
2077
|
+
|
2078
|
+
|
2079
|
+
/**************************/
|
2080
|
+
/** BookModel extensions **/
|
2081
|
+
/**************************/
|
2082
|
+
/** @deprecated not used outside */
|
2083
|
+
BookReader.prototype.getMedianPageSize = BookModel.prototype.getMedianPageSize;
|
2084
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getMedianPageSize');
|
2085
|
+
BookReader.prototype._getPageWidth = BookModel.prototype._getPageWidth;
|
2086
|
+
exposeOverrideableMethod(BookModel, '_models.book', '_getPageWidth');
|
2087
|
+
BookReader.prototype._getPageHeight = BookModel.prototype._getPageHeight;
|
2088
|
+
exposeOverrideableMethod(BookModel, '_models.book', '_getPageHeight');
|
2089
|
+
BookReader.prototype.getPageIndex = BookModel.prototype.getPageIndex;
|
2090
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndex');
|
2091
|
+
/** @deprecated not used outside */
|
2092
|
+
BookReader.prototype.getPageIndices = BookModel.prototype.getPageIndices;
|
2093
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndices');
|
2094
|
+
BookReader.prototype.getPageName = BookModel.prototype.getPageName;
|
2095
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageName');
|
2096
|
+
BookReader.prototype.getNumLeafs = BookModel.prototype.getNumLeafs;
|
2097
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getNumLeafs');
|
2098
|
+
BookReader.prototype.getPageWidth = BookModel.prototype.getPageWidth;
|
2099
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageWidth');
|
2100
|
+
BookReader.prototype.getPageHeight = BookModel.prototype.getPageHeight;
|
2101
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageHeight');
|
2102
|
+
BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
|
2103
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageURI');
|
2104
|
+
BookReader.prototype.getPageSide = BookModel.prototype.getPageSide;
|
2105
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageSide');
|
2106
|
+
BookReader.prototype.getPageNum = BookModel.prototype.getPageNum;
|
2107
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageNum');
|
2108
|
+
BookReader.prototype.getPageProp = BookModel.prototype.getPageProp;
|
2109
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getPageProp');
|
2110
|
+
BookReader.prototype.getSpreadIndices = BookModel.prototype.getSpreadIndices;
|
2111
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'getSpreadIndices');
|
2112
|
+
BookReader.prototype.leafNumToIndex = BookModel.prototype.leafNumToIndex;
|
2113
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'leafNumToIndex');
|
2114
|
+
BookReader.prototype.parsePageString = BookModel.prototype.parsePageString;
|
2115
|
+
exposeOverrideableMethod(BookModel, '_models.book', 'parsePageString');
|
2116
|
+
/** @deprecated unused */
|
2117
|
+
BookReader.prototype._getDataFlattened = BookModel.prototype._getDataFlattened;
|
2118
|
+
exposeOverrideableMethod(BookModel, '_models.book', '_getDataFlattened');
|
2119
|
+
/** @deprecated unused */
|
2120
|
+
BookReader.prototype._getDataProp = BookModel.prototype._getDataProp;
|
2121
|
+
exposeOverrideableMethod(BookModel, '_models.book', '_getDataProp');
|
2122
|
+
|
2123
|
+
// Parameter related functions
|
2124
|
+
|
2125
|
+
/**
|
2126
|
+
* Update from the params object
|
2127
|
+
* @param {Object}
|
2128
|
+
*/
|
2129
|
+
BookReader.prototype.updateFromParams = function(params) {
|
2130
|
+
// Set init, fragment change options for switchMode()
|
2131
|
+
const {
|
2132
|
+
mode = 0,
|
2133
|
+
init = false,
|
2134
|
+
fragmentChange = false,
|
2135
|
+
} = params;
|
2136
|
+
|
2137
|
+
if (mode) {
|
2138
|
+
this.switchMode(
|
2139
|
+
mode,
|
2140
|
+
{ init: init, suppressFragmentChange: !fragmentChange }
|
2141
|
+
);
|
2142
|
+
}
|
2143
|
+
|
2144
|
+
// $$$ process /zoom
|
2145
|
+
// We only respect page if index is not set
|
2146
|
+
if ('undefined' != typeof(params.index)) {
|
2147
|
+
if (params.index != this.currentIndex()) {
|
2148
|
+
this.jumpToIndex(params.index);
|
2149
|
+
}
|
2150
|
+
} else if ('undefined' != typeof(params.page)) {
|
2151
|
+
// $$$ this assumes page numbers are unique
|
2152
|
+
if (params.page != this._models.book.getPageNum(this.currentIndex())) {
|
2153
|
+
this.jumpToPage(params.page);
|
2154
|
+
}
|
2155
|
+
}
|
2156
|
+
|
2157
|
+
|
2158
|
+
// process /search
|
2159
|
+
// @deprecated for urlMode 'history'
|
2160
|
+
// Continues to work for urlMode 'hash'
|
2161
|
+
if (this.enableSearch && 'undefined' != typeof(params.search)) {
|
2162
|
+
if (this.searchTerm !== params.search) {
|
2163
|
+
this.$('.BRsearchInput').val(params.search);
|
2164
|
+
}
|
2165
|
+
}
|
2166
|
+
|
2167
|
+
// $$$ process /region
|
2168
|
+
// $$$ process /highlight
|
2169
|
+
|
2170
|
+
// $$$ process /theme
|
2171
|
+
if (this.enableThemesPlugin && 'undefined' != typeof(params.theme)) {
|
2172
|
+
this.updateTheme(params.theme);
|
2173
|
+
}
|
2174
|
+
};
|
2175
|
+
|
2176
|
+
/**
|
2177
|
+
* Returns true if we can switch to the requested mode
|
2178
|
+
* @param {number} mode
|
2179
|
+
* @return {boolean}
|
2180
|
+
*/
|
2181
|
+
BookReader.prototype.canSwitchToMode = function(mode) {
|
2182
|
+
if (mode == this.constMode2up || mode == this.constModeThumb) {
|
2183
|
+
// check there are enough pages to display
|
2184
|
+
// $$$ this is a workaround for the mis-feature that we can't display
|
2185
|
+
// short books in 2up mode
|
2186
|
+
if (this._models.book.getNumLeafs() < 2) {
|
2187
|
+
return false;
|
2188
|
+
}
|
2189
|
+
}
|
2190
|
+
|
2191
|
+
return true;
|
2192
|
+
};
|
2193
|
+
|
2194
|
+
|
2195
|
+
/**
|
2196
|
+
* @deprecated. Use PageModel.getURISrcSet. Slated for removal in v5.
|
2197
|
+
* Returns the srcset with correct URIs or void string if out of range
|
2198
|
+
* Also makes the reduce argument optional
|
2199
|
+
* @param {number} index
|
2200
|
+
* @param {number} [reduce]
|
2201
|
+
* @param {number} [rotate]
|
2202
|
+
* @return {string}
|
2203
|
+
*/
|
2204
|
+
BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
|
2205
|
+
const page = this._models.book.getPage(index, false);
|
2206
|
+
// Synthesize page
|
2207
|
+
if (!page) return "";
|
2208
|
+
|
2209
|
+
// reduce not passed in
|
2210
|
+
// $$$ this probably won't work for thumbnail mode
|
2211
|
+
if ('undefined' == typeof(reduce)) {
|
2212
|
+
reduce = page.height / this.twoPage.height;
|
2213
|
+
}
|
2214
|
+
|
2215
|
+
return page.getURISrcSet(reduce, rotate);
|
2216
|
+
};
|
2217
|
+
|
2218
|
+
|
2219
|
+
/**
|
2220
|
+
* Returns the page URI or transparent image if out of range
|
2221
|
+
* Also makes the reduce argument optional
|
2222
|
+
* @param {number} index
|
2223
|
+
* @param {number} [reduce]
|
2224
|
+
* @param {number} [rotate]
|
2225
|
+
* @return {string}
|
2226
|
+
*/
|
2227
|
+
BookReader.prototype._getPageURI = function(index, reduce, rotate) {
|
2228
|
+
const page = this._models.book.getPage(index, false);
|
2229
|
+
// Synthesize page
|
2230
|
+
if (!page) return this.imagesBaseURL + "transparent.png";
|
2231
|
+
|
2232
|
+
if ('undefined' == typeof(reduce)) {
|
2233
|
+
// reduce not passed in
|
2234
|
+
// $$$ this probably won't work for thumbnail mode
|
2235
|
+
reduce = page.height / this.twoPage.height;
|
2236
|
+
}
|
2237
|
+
|
2238
|
+
return page.getURI(reduce, rotate);
|
2239
|
+
};
|
2240
|
+
|
2241
|
+
/**
|
2242
|
+
* @param {string} msg
|
2243
|
+
* @param {function|undefined} onCloseCallback
|
2244
|
+
*/
|
2245
|
+
BookReader.prototype.showProgressPopup = function(msg, onCloseCallback) {
|
2246
|
+
if (this.popup) return;
|
2247
|
+
|
2248
|
+
this.popup = document.createElement("div");
|
2249
|
+
$(this.popup).prop('className', 'BRprogresspopup');
|
2250
|
+
|
2251
|
+
if (typeof(onCloseCallback) === 'function') {
|
2252
|
+
const closeButton = document.createElement('button');
|
2253
|
+
closeButton.setAttribute('title', 'close');
|
2254
|
+
closeButton.setAttribute('class', 'close-popup');
|
2255
|
+
const icon = document.createElement('span');
|
2256
|
+
icon.setAttribute('class', 'icon icon-close-dark');
|
2257
|
+
$(closeButton).append(icon);
|
2258
|
+
closeButton.addEventListener('click', () => {
|
2259
|
+
onCloseCallback();
|
2260
|
+
this.removeProgressPopup();
|
2261
|
+
});
|
2262
|
+
$(this.popup).append(closeButton);
|
2263
|
+
}
|
2264
|
+
|
2265
|
+
const bar = document.createElement("div");
|
2266
|
+
$(bar).css({
|
2267
|
+
height: '20px'
|
2268
|
+
}).prop('className', 'BRprogressbar');
|
2269
|
+
$(this.popup).append(bar);
|
2270
|
+
|
2271
|
+
if (msg) {
|
2272
|
+
const msgdiv = document.createElement("div");
|
2273
|
+
msgdiv.innerHTML = msg;
|
2274
|
+
$(this.popup).append(msgdiv);
|
2275
|
+
}
|
2276
|
+
|
2277
|
+
$(this.popup).appendTo(this.refs.$br);
|
2278
|
+
};
|
2279
|
+
|
2280
|
+
BookReader.prototype.removeProgressPopup = function() {
|
2281
|
+
$(this.popup).remove();
|
2282
|
+
this.$('.BRprogresspopup').remove();
|
2283
|
+
this.popup = null;
|
2284
|
+
};
|
2285
|
+
|
2286
|
+
/**
|
2287
|
+
* Can be overridden
|
2288
|
+
*/
|
2289
|
+
BookReader.prototype.initUIStrings = function() {
|
2290
|
+
// Navigation handlers will be bound after all UI is in place -- makes moving icons between
|
2291
|
+
// the toolbar and nav bar easier
|
2292
|
+
|
2293
|
+
// Setup tooltips -- later we could load these from a file for i18n
|
2294
|
+
var titles = {
|
2295
|
+
'.logo': 'Go to Archive.org', // $$$ update after getting OL record
|
2296
|
+
'.zoom_in': 'Zoom in',
|
2297
|
+
'.zoom_out': 'Zoom out',
|
2298
|
+
'.onepg': 'One-page view',
|
2299
|
+
'.twopg': 'Two-page view',
|
2300
|
+
'.thumb': 'Thumbnail view',
|
2301
|
+
'.print': 'Print this page',
|
2302
|
+
'.embed': 'Embed BookReader',
|
2303
|
+
'.link': 'Link to this book (and page)',
|
2304
|
+
'.bookmark': 'Bookmark this page',
|
2305
|
+
'.share': 'Share this book',
|
2306
|
+
'.info': 'About this book',
|
2307
|
+
'.full': 'Toggle fullscreen',
|
2308
|
+
'.book_left': 'Flip left',
|
2309
|
+
'.book_right': 'Flip right',
|
2310
|
+
'.book_up': 'Page up',
|
2311
|
+
'.book_down': 'Page down',
|
2312
|
+
'.play': 'Play',
|
2313
|
+
'.pause': 'Pause',
|
2314
|
+
'.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
|
2315
|
+
'.BRup': 'Show/hide nav bar',
|
2316
|
+
'.book_top': 'First page',
|
2317
|
+
'.book_bottom': 'Last page',
|
2318
|
+
'.book_leftmost': 'First page',
|
2319
|
+
'.book_rightmost': 'Last page',
|
2320
|
+
};
|
2321
|
+
if ('rl' == this.pageProgression) {
|
2322
|
+
titles['.book_leftmost'] = 'Last page';
|
2323
|
+
titles['.book_rightmost'] = 'First page';
|
2324
|
+
}
|
2325
|
+
|
2326
|
+
for (var icon in titles) {
|
2327
|
+
this.$(icon).prop('title', titles[icon]);
|
2328
|
+
}
|
2329
|
+
};
|
2330
|
+
|
2331
|
+
/**
|
2332
|
+
* Reloads images. Useful when some images might have failed.
|
2333
|
+
*/
|
2334
|
+
BookReader.prototype.reloadImages = function() {
|
2335
|
+
this.refs.$brContainer.find('img').each(function(index, elem) {
|
2336
|
+
if (!elem.complete || elem.naturalHeight === 0) {
|
2337
|
+
var src = elem.src;
|
2338
|
+
elem.src = '';
|
2339
|
+
setTimeout(function() {
|
2340
|
+
elem.src = src;
|
2341
|
+
}, 1000);
|
2342
|
+
}
|
2343
|
+
});
|
2344
|
+
};
|
2345
|
+
|
2346
|
+
/**
|
2347
|
+
* @param {boolean} ignoreDisplay - bypass the display check
|
2348
|
+
* @return {number}
|
2349
|
+
*/
|
2350
|
+
BookReader.prototype.getFooterHeight = function() {
|
2351
|
+
var $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
|
2352
|
+
if ($heightEl && this.refs.$BRfooter) {
|
2353
|
+
var outerHeight = $heightEl.outerHeight();
|
2354
|
+
var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
|
2355
|
+
if (!isNaN(outerHeight) && !isNaN(bottom)) {
|
2356
|
+
return outerHeight + bottom;
|
2357
|
+
}
|
2358
|
+
}
|
2359
|
+
return 0;
|
2360
|
+
};
|
2361
|
+
|
2362
|
+
// Basic Usage built-in Methods (can be overridden through options)
|
2363
|
+
// This implementation uses options.data value for populating BookReader
|
2364
|
+
|
2365
|
+
/**
|
2366
|
+
* Create a params object from the current parameters.
|
2367
|
+
* @return {Object}
|
2368
|
+
*/
|
2369
|
+
BookReader.prototype.paramsFromCurrent = function() {
|
2370
|
+
var params = {};
|
2371
|
+
|
2372
|
+
var index = this.currentIndex();
|
2373
|
+
var pageNum = this._models.book.getPageNum(index);
|
2374
|
+
if ((pageNum === 0) || pageNum) {
|
2375
|
+
params.page = pageNum;
|
2376
|
+
}
|
2377
|
+
|
2378
|
+
params.index = index;
|
2379
|
+
params.mode = this.mode;
|
2380
|
+
|
2381
|
+
// $$$ highlight
|
2382
|
+
// $$$ region
|
2383
|
+
|
2384
|
+
// search
|
2385
|
+
if (this.enableSearch) {
|
2386
|
+
params.search = this.searchTerm;
|
2387
|
+
}
|
2388
|
+
|
2389
|
+
return params;
|
2390
|
+
};
|
2391
|
+
|
2392
|
+
/**
|
2393
|
+
* Return an object with configuration parameters from a fragment string.
|
2394
|
+
*
|
2395
|
+
* Fragments are formatted as a URL path but may be used outside of URLs as a
|
2396
|
+
* serialization format for BookReader parameters
|
2397
|
+
*
|
2398
|
+
* @see http://openlibrary.org/dev/docs/bookurls for fragment syntax
|
2399
|
+
*
|
2400
|
+
* @param {string} fragment initial # is allowed for backwards compatibility
|
2401
|
+
* but is deprecated
|
2402
|
+
* @return {Object}
|
2403
|
+
*/
|
2404
|
+
BookReader.prototype.paramsFromFragment = function(fragment) {
|
2405
|
+
var params = {};
|
2406
|
+
|
2407
|
+
// For backwards compatibility we allow an initial # character
|
2408
|
+
// (as from window.location.hash) but don't require it
|
2409
|
+
if (fragment.substr(0, 1) == '#') {
|
2410
|
+
fragment = fragment.substr(1);
|
2411
|
+
}
|
2412
|
+
|
2413
|
+
// Simple #nn syntax
|
2414
|
+
var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
|
2415
|
+
if ( !isNaN(oldStyleLeafNum) ) {
|
2416
|
+
params.index = oldStyleLeafNum;
|
2417
|
+
|
2418
|
+
// Done processing if using old-style syntax
|
2419
|
+
return params;
|
2420
|
+
}
|
2421
|
+
|
2422
|
+
// Split into key-value pairs
|
2423
|
+
var urlArray = fragment.split('/');
|
2424
|
+
var urlHash = {};
|
2425
|
+
for (var i = 0; i < urlArray.length; i += 2) {
|
2426
|
+
urlHash[urlArray[i]] = urlArray[i + 1];
|
2427
|
+
}
|
2428
|
+
|
2429
|
+
// Mode
|
2430
|
+
if ('1up' == urlHash['mode']) {
|
2431
|
+
params.mode = this.constMode1up;
|
2432
|
+
} else if ('2up' == urlHash['mode']) {
|
2433
|
+
params.mode = this.constMode2up;
|
2434
|
+
} else if ('thumb' == urlHash['mode']) {
|
2435
|
+
params.mode = this.constModeThumb;
|
2436
|
+
}
|
2437
|
+
|
2438
|
+
// Index and page
|
2439
|
+
if ('undefined' != typeof(urlHash['page'])) {
|
2440
|
+
// page was set -- may not be int
|
2441
|
+
params.page = urlHash['page'];
|
2442
|
+
}
|
2443
|
+
|
2444
|
+
// $$$ process /region
|
2445
|
+
// $$$ process /search
|
2446
|
+
|
2447
|
+
if (urlHash['search'] != undefined) {
|
2448
|
+
params.search = utils.decodeURIComponentPlus(urlHash['search']);
|
2449
|
+
}
|
2450
|
+
|
2451
|
+
// $$$ process /highlight
|
2452
|
+
|
2453
|
+
// $$$ process /theme
|
2454
|
+
if (urlHash['theme'] != undefined) {
|
2455
|
+
params.theme = urlHash['theme'];
|
2456
|
+
}
|
2457
|
+
return params;
|
2458
|
+
};
|
2459
|
+
|
2460
|
+
/**
|
2461
|
+
* Create a fragment string from the params object.
|
2462
|
+
*
|
2463
|
+
* Fragments are formatted as a URL path but may be used outside of URLs as a
|
2464
|
+
* serialization format for BookReader parameters
|
2465
|
+
*
|
2466
|
+
* @see https://openlibrary.org/dev/docs/bookurls for fragment syntax
|
2467
|
+
*
|
2468
|
+
* @param {Object} params
|
2469
|
+
* @param {string} [urlMode]
|
2470
|
+
* @return {string}
|
2471
|
+
*/
|
2472
|
+
BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
|
2473
|
+
const separator = '/';
|
2474
|
+
const fragments = [];
|
2475
|
+
|
2476
|
+
if ('undefined' != typeof(params.page)) {
|
2477
|
+
fragments.push('page', params.page);
|
2478
|
+
} else {
|
2479
|
+
if ('undefined' != typeof(params.index)) {
|
2480
|
+
// Don't have page numbering but we do have the index
|
2481
|
+
fragments.push('page', 'n' + params.index);
|
2482
|
+
}
|
2483
|
+
}
|
2484
|
+
|
2485
|
+
// $$$ highlight
|
2486
|
+
// $$$ region
|
2487
|
+
|
2488
|
+
// mode
|
2489
|
+
if ('undefined' != typeof(params.mode)) {
|
2490
|
+
if (params.mode == this.constMode1up) {
|
2491
|
+
fragments.push('mode', '1up');
|
2492
|
+
} else if (params.mode == this.constMode2up) {
|
2493
|
+
fragments.push('mode', '2up');
|
2494
|
+
} else if (params.mode == this.constModeThumb) {
|
2495
|
+
fragments.push('mode', 'thumb');
|
2496
|
+
} else {
|
2497
|
+
throw 'fragmentFromParams called with unknown mode ' + params.mode;
|
2498
|
+
}
|
2499
|
+
}
|
2500
|
+
|
2501
|
+
// search
|
2502
|
+
if (params.search && urlMode === 'hash') {
|
2503
|
+
fragments.push('search', params.search);
|
2504
|
+
}
|
2505
|
+
|
2506
|
+
return utils.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
|
2507
|
+
};
|
2508
|
+
|
2509
|
+
/**
|
2510
|
+
* Create, update querystring from the params object
|
2511
|
+
*
|
2512
|
+
* @param {Object} params
|
2513
|
+
* @param {string} currQueryString
|
2514
|
+
* @param {string} [urlMode]
|
2515
|
+
* @return {string}
|
2516
|
+
*/
|
2517
|
+
BookReader.prototype.queryStringFromParams = function(
|
2518
|
+
params,
|
2519
|
+
currQueryString,
|
2520
|
+
urlMode = 'hash'
|
2521
|
+
) {
|
2522
|
+
const newParams = new URLSearchParams(currQueryString);
|
2523
|
+
if (params.search && urlMode === 'history') {
|
2524
|
+
newParams.set('q', params.search);
|
2525
|
+
}
|
2526
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
|
2527
|
+
// Note: This method returns the query string without the question mark.
|
2528
|
+
const result = newParams.toString();
|
2529
|
+
return result ? '?' + result : '';
|
2530
|
+
};
|
2531
|
+
|
2532
|
+
/**
|
2533
|
+
* Helper to select within instance's elements
|
2534
|
+
*/
|
2535
|
+
BookReader.prototype.$ = function(selector) {
|
2536
|
+
return this.refs.$br.find(selector);
|
2537
|
+
};
|
2538
|
+
|
2539
|
+
/**
|
2540
|
+
* Polyfill for deprecated method
|
2541
|
+
*/
|
2542
|
+
jQuery.curCSS = function(element, prop, val) {
|
2543
|
+
return jQuery(element).css(prop, val);
|
2544
|
+
};
|
2545
|
+
|
2546
|
+
window.BookReader = BookReader;
|