@internetarchive/bookreader 5.0.0-12-fixsorting-8 → 5.0.0-120
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/BookReader/474.js +2 -0
- package/BookReader/474.js.map +1 -0
- package/BookReader/BookReader.css +670 -1190
- package/BookReader/bergamot-translator-worker.js +2966 -0
- package/BookReader/bergamot-translator-worker.wasm +0 -0
- package/BookReader/hypothesis/LICENSE +50 -0
- package/BookReader/hypothesis/README.md +55 -0
- package/BookReader/hypothesis/build/boot-template.js +1 -0
- package/BookReader/hypothesis/build/boot.js +1 -0
- package/BookReader/hypothesis/build/manifest.json +20 -0
- package/BookReader/hypothesis/build/scripts/annotator.bundle.js +199 -0
- package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +811 -0
- package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +437 -0
- package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
- package/BookReader/hypothesis/build/styles/annotator.css +4 -0
- package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/BookReader/hypothesis/build/styles/highlights.css +2 -0
- package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
- package/BookReader/hypothesis/build/styles/katex.min.css +3 -0
- package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
- package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +14 -0
- package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
- package/BookReader/hypothesis/build/styles/sidebar.css +4 -0
- package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
- package/BookReader/hypothesis/build/styles/ui-playground.css +4 -0
- package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
- package/BookReader/hypothesis/package.json +126 -0
- package/BookReader/ia-bookreader.js +1886 -0
- package/BookReader/{BookReader.js.LICENSE.txt → ia-bookreader.js.LICENSE.txt} +26 -20
- package/BookReader/ia-bookreader.js.map +1 -0
- package/BookReader/icons/1up.svg +1 -12
- package/BookReader/icons/2up.svg +1 -15
- package/BookReader/icons/advance.svg +3 -26
- package/BookReader/icons/chevron-right.svg +1 -1
- package/BookReader/icons/close-circle-dark.svg +1 -1
- package/BookReader/icons/close-circle.svg +1 -1
- package/BookReader/icons/fullscreen.svg +1 -17
- package/BookReader/icons/fullscreen_exit.svg +1 -17
- package/BookReader/icons/hamburger.svg +1 -15
- package/BookReader/icons/left-arrow.svg +1 -12
- package/BookReader/icons/magnify-minus.svg +1 -16
- package/BookReader/icons/magnify-plus.svg +1 -17
- package/BookReader/icons/magnify.svg +1 -15
- package/BookReader/icons/pause.svg +1 -23
- package/BookReader/icons/play.svg +1 -22
- package/BookReader/icons/playback-speed.svg +1 -34
- package/BookReader/icons/read-aloud.svg +1 -22
- package/BookReader/icons/review.svg +3 -22
- package/BookReader/icons/slider-toggle.svg +1 -0
- package/BookReader/icons/thumbnails.svg +1 -17
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/icons/volume-full.svg +1 -22
- package/BookReader/images/BRicons.svg +5 -94
- package/BookReader/images/books_graphic.svg +1 -177
- package/BookReader/images/hypothesis.ico +0 -0
- package/BookReader/images/icon_book.svg +1 -12
- package/BookReader/images/icon_bookmark.svg +1 -12
- package/BookReader/images/icon_experiment.svg +1 -0
- package/BookReader/images/icon_gear.svg +1 -14
- package/BookReader/images/icon_hamburger.svg +1 -20
- package/BookReader/images/icon_home.svg +1 -21
- package/BookReader/images/icon_info.svg +1 -11
- package/BookReader/images/icon_one_page.svg +1 -8
- package/BookReader/images/icon_pause.svg +1 -1
- package/BookReader/images/icon_play.svg +1 -1
- package/BookReader/images/icon_playback-rate.svg +1 -15
- package/BookReader/images/icon_search_button.svg +1 -8
- package/BookReader/images/icon_share.svg +1 -9
- package/BookReader/images/icon_skip-ahead.svg +1 -6
- package/BookReader/images/icon_skip-back.svg +2 -13
- package/BookReader/images/icon_speaker.svg +1 -18
- package/BookReader/images/icon_speaker_open.svg +1 -10
- package/BookReader/images/icon_thumbnails.svg +1 -12
- package/BookReader/images/icon_toc.svg +1 -5
- package/BookReader/images/icon_two_pages.svg +1 -9
- package/BookReader/images/marker_chap-off.svg +1 -11
- package/BookReader/images/marker_chap-on.svg +1 -11
- package/BookReader/images/marker_srch-on.svg +1 -11
- package/BookReader/images/translate.svg +1 -0
- package/BookReader/images/unviewable_page.png +0 -0
- package/BookReader/jquery-3.js +2 -0
- package/BookReader/jquery-3.js.LICENSE.txt +24 -0
- package/BookReader/plugins/plugin.archive_analytics.js +1 -172
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -165
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +19 -301
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.experiments.js +3 -0
- package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.experiments.js.map +1 -0
- package/BookReader/plugins/plugin.iframe.js +1 -74
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +2 -0
- package/BookReader/plugins/plugin.iiif.js.map +1 -0
- package/BookReader/plugins/plugin.resume.js +1 -368
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +2 -1385
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +2 -1080
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.translate.js +3 -0
- package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.translate.js.map +1 -0
- package/BookReader/plugins/plugin.tts.js +2 -9193
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -269
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/plugins/translator-worker.js +2 -0
- package/BookReader/plugins/translator-worker.js.map +1 -0
- package/BookReader/silence.mp3 +0 -0
- package/BookReader/translator-worker.js +475 -0
- package/BookReader/webcomponents-bundle.js +3 -0
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/README.md +17 -4
- package/dist/esm/ia-bookreader.js +3 -0
- package/dist/esm/ia-bookreader.js.LICENSE.txt +17 -0
- package/dist/esm/ia-bookreader.js.map +1 -0
- package/dist/esm/plugins/plugin.archive_analytics.js +2 -0
- package/dist/esm/plugins/plugin.archive_analytics.js.map +1 -0
- package/dist/esm/plugins/plugin.autoplay.js +2 -0
- package/dist/esm/plugins/plugin.autoplay.js.map +1 -0
- package/dist/esm/plugins/plugin.chapters.js +2 -0
- package/dist/esm/plugins/plugin.chapters.js.map +1 -0
- package/dist/esm/plugins/plugin.experiments.js +2 -0
- package/dist/esm/plugins/plugin.experiments.js.map +1 -0
- package/dist/esm/plugins/plugin.iframe.js +2 -0
- package/dist/esm/plugins/plugin.iframe.js.map +1 -0
- package/dist/esm/plugins/plugin.iiif.js +2 -0
- package/dist/esm/plugins/plugin.iiif.js.map +1 -0
- package/dist/esm/plugins/plugin.resume.js +2 -0
- package/dist/esm/plugins/plugin.resume.js.map +1 -0
- package/dist/esm/plugins/plugin.search.js +2 -0
- package/dist/esm/plugins/plugin.search.js.map +1 -0
- package/dist/esm/plugins/plugin.text_selection.js +2 -0
- package/dist/esm/plugins/plugin.text_selection.js.map +1 -0
- package/dist/esm/plugins/plugin.translate.js +2 -0
- package/dist/esm/plugins/plugin.translate.js.map +1 -0
- package/dist/esm/plugins/plugin.tts.js +2 -0
- package/dist/esm/plugins/plugin.tts.js.map +1 -0
- package/dist/esm/plugins/plugin.url.js +2 -0
- package/dist/esm/plugins/plugin.url.js.map +1 -0
- package/dist/esm/plugins/plugin.vendor-fullscreen.js +2 -0
- package/dist/esm/plugins/plugin.vendor-fullscreen.js.map +1 -0
- package/jsconfig.json +18 -0
- package/package.json +113 -71
- package/src/BookReader/BookModel.js +92 -46
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/ImageCache.js +49 -16
- package/src/BookReader/Mode1Up.js +66 -365
- package/src/BookReader/Mode1UpLit.js +392 -0
- package/src/BookReader/Mode2Up.js +87 -1315
- package/src/BookReader/Mode2UpLit.js +786 -0
- package/src/BookReader/ModeAbstract.js +43 -0
- package/src/BookReader/ModeCoordinateSpace.js +29 -0
- package/src/BookReader/ModeSmoothZoom.js +312 -0
- package/src/BookReader/ModeThumb.js +29 -15
- package/src/BookReader/Navbar/Navbar.js +204 -84
- package/src/BookReader/PageContainer.js +120 -23
- package/src/BookReader/ReduceSet.js +2 -2
- package/src/BookReader/Toolbar/Toolbar.js +18 -40
- package/src/BookReader/events.js +3 -3
- package/src/BookReader/options.js +105 -18
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/BookReader/utils/SelectionObserver.js +72 -0
- package/src/BookReader/utils/classes.js +1 -1
- package/src/BookReader/utils.js +136 -12
- package/src/BookReader.js +732 -1231
- package/src/BookReaderPlugin.js +52 -0
- package/src/assets/icons/magnify-minus.svg +3 -7
- package/src/assets/icons/magnify-plus.svg +3 -7
- package/src/assets/icons/slider-toggle.svg +1 -0
- package/src/assets/icons/voice.svg +1 -0
- package/src/assets/images/hypothesis.ico +0 -0
- package/src/assets/images/icon_experiment.svg +1 -0
- package/src/assets/images/translate.svg +1 -0
- package/src/assets/images/unviewable_page.png +0 -0
- package/src/assets/silence.mp3 +0 -0
- package/src/css/BookReader.scss +1 -5
- package/src/css/_BRBookmarks.scss +1 -1
- package/src/css/_BRComponent.scss +1 -1
- package/src/css/_BRicon.scss +8 -2
- package/src/css/_BRmain.scss +16 -3
- package/src/css/_BRnav.scss +74 -70
- package/src/css/_BRpages.scss +174 -42
- package/src/css/_BRsearch.scss +69 -30
- package/src/css/_BRtoolbar.scss +5 -5
- package/src/css/_TextSelection.scss +249 -25
- package/src/css/_colorbox.scss +2 -2
- package/src/css/_controls.scss +24 -7
- package/src/css/_icons.scss +8 -1
- package/src/{BookNavigator/assets → css}/button-base.js +2 -2
- package/src/css/icon_checkmark.js +9 -0
- package/src/css/sharedStyles.js +15 -0
- package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
- package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +25 -5
- package/src/ia-bookreader/ia-bookreader.js +686 -0
- package/src/ia-bookreader/sharing.js +27 -0
- package/src/ia-bookreader/viewable-files.js +98 -0
- package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
- package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
- package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
- package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
- package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +43 -31
- package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
- package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +3 -3
- package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
- package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
- package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
- package/src/plugins/plugin.archive_analytics.js +84 -78
- package/src/plugins/plugin.autoplay.js +99 -104
- package/src/plugins/plugin.chapters.js +323 -205
- package/src/plugins/plugin.experiments.js +375 -0
- package/src/plugins/plugin.iframe.js +36 -37
- package/src/plugins/plugin.iiif.js +147 -0
- package/src/plugins/plugin.resume.js +54 -51
- package/src/plugins/plugin.text_selection.js +464 -219
- package/src/plugins/plugin.vendor-fullscreen.js +5 -5
- package/src/plugins/search/plugin.search.js +347 -397
- package/src/{BookNavigator → plugins}/search/search-provider.js +59 -27
- package/src/{BookNavigator → plugins}/search/search-results.js +105 -76
- package/src/plugins/search/utils.js +50 -0
- package/src/plugins/search/view.js +51 -69
- package/src/plugins/translate/TranslationManager.js +164 -0
- package/src/plugins/translate/plugin.translate.js +559 -0
- package/src/plugins/tts/AbstractTTSEngine.js +78 -49
- package/src/plugins/tts/FestivalTTSEngine.js +20 -30
- package/src/plugins/tts/PageChunk.js +117 -23
- package/src/plugins/tts/PageChunkIterator.js +11 -17
- package/src/plugins/tts/WebTTSEngine.js +132 -92
- package/src/plugins/tts/plugin.tts.js +341 -351
- package/src/plugins/tts/utils.js +76 -67
- package/src/plugins/url/UrlPlugin.js +213 -0
- package/src/plugins/{plugin.url.js → url/plugin.url.js} +54 -18
- package/src/util/TextSelectionManager.js +1464 -0
- package/src/util/browserSniffing.js +42 -1
- package/src/util/cache.js +20 -0
- package/src/util/docCookies.js +21 -2
- package/src/util/dom.js +88 -0
- package/src/util/generators.js +89 -0
- package/src/util/lit.js +15 -0
- package/src/util/strings.js +11 -0
- package/.babelrc +0 -12
- package/.dependabot/config.yml +0 -6
- package/.eslintrc.js +0 -50
- package/.gitattributes +0 -2
- package/.github/ISSUE_TEMPLATE/bug.md +0 -32
- package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
- package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
- package/.github/workflows/node.js.yml +0 -37
- package/.github/workflows/npm-publish.yml +0 -47
- package/.testcaferc.json +0 -5
- package/BookReader/BookReader.js +0 -21613
- package/BookReader/BookReader.js.map +0 -1
- package/BookReader/bookreader-component-bundle.js +0 -14497
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
- package/BookReader/bookreader-component-bundle.js.map +0 -1
- package/BookReader/jquery-1.10.1.js +0 -108
- package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.menu_toggle.js +0 -369
- package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
- package/BookReader/plugins/plugin.mobile_nav.js +0 -335
- package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
- package/BookReaderDemo/BookReaderDemo.css +0 -41
- package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
- package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
- package/BookReaderDemo/BookReaderJSSimple.js +0 -55
- package/BookReaderDemo/IIIFBookReader.js +0 -207
- package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
- package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
- 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 +0 -1
- package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
- 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 +0 -7178
- package/BookReaderDemo/demo-advanced.html +0 -33
- package/BookReaderDemo/demo-autoplay.html +0 -38
- package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
- package/BookReaderDemo/demo-embed.html +0 -26
- package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
- package/BookReaderDemo/demo-fullscreen.html +0 -33
- package/BookReaderDemo/demo-iiif.html +0 -34
- package/BookReaderDemo/demo-iiif.js +0 -26
- package/BookReaderDemo/demo-internetarchive.html +0 -74
- package/BookReaderDemo/demo-multiple.html +0 -43
- package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
- package/BookReaderDemo/demo-preview-pages.html +0 -1092
- package/BookReaderDemo/demo-simple.html +0 -34
- package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
- package/BookReaderDemo/immersion-1up.html +0 -64
- package/BookReaderDemo/immersion-mode.html +0 -35
- package/BookReaderDemo/toggle_controls.html +0 -53
- package/BookReaderDemo/view_mode.html +0 -39
- package/BookReaderDemo/viewmode-cycle.html +0 -41
- package/CHANGELOG.md +0 -499
- package/CONTRIBUTING.md +0 -7
- package/codecov.yml +0 -17
- package/index.html +0 -31
- package/karma.conf.js +0 -23
- package/screenshot.png +0 -0
- package/scripts/postversion.js +0 -10
- package/scripts/preversion.js +0 -14
- package/scripts/version.js +0 -26
- package/src/BookNavigator/BookModel.js +0 -14
- package/src/BookNavigator/BookNavigator.js +0 -459
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/BookNavigator/assets/icon_checkmark.js +0 -6
- package/src/BookNavigator/assets/icon_close.js +0 -3
- package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
- package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
- package/src/BookNavigator/assets/icon_sort_neutral.js +0 -5
- package/src/BookNavigator/assets/icon_volumes.js +0 -11
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
- package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
- package/src/BookNavigator/downloads/downloads-provider.js +0 -66
- package/src/BookNavigator/search/a-search-result.js +0 -55
- package/src/BookNavigator/volumes/volumes-provider.js +0 -83
- package/src/BookNavigator/volumes/volumes.js +0 -178
- package/src/BookReader/DebugConsole.js +0 -54
- package/src/BookReaderComponent/BookReaderComponent.js +0 -112
- package/src/ItemNavigator/ItemNavigator.js +0 -376
- package/src/ItemNavigator/providers/sharing.js +0 -33
- package/src/css/_MobileNav.scss +0 -194
- package/src/dragscrollable-br.js +0 -261
- package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
- package/src/plugins/plugin.mobile_nav.js +0 -287
- package/tests/BookReader/BookModel.test.js +0 -312
- package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
- package/tests/BookReader/DebugConsole.test.js +0 -25
- package/tests/BookReader/ImageCache.test.js +0 -150
- package/tests/BookReader/Mode1Up.test.js +0 -164
- package/tests/BookReader/Mode2Up.test.js +0 -247
- package/tests/BookReader/Navbar/Navbar.test.js +0 -169
- package/tests/BookReader/PageContainer.test.js +0 -115
- package/tests/BookReader/ReduceSet.test.js +0 -38
- package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
- package/tests/BookReader/utils/classes.test.js +0 -88
- package/tests/BookReader/utils.test.js +0 -109
- package/tests/BookReader.options.test.js +0 -39
- package/tests/BookReader.test.js +0 -301
- package/tests/e2e/README.md +0 -75
- package/tests/e2e/autoplay.test.js +0 -13
- package/tests/e2e/base.test.js +0 -35
- package/tests/e2e/helpers/base.js +0 -263
- package/tests/e2e/helpers/debug.js +0 -13
- package/tests/e2e/helpers/desktopSearch.js +0 -72
- package/tests/e2e/helpers/mobileSearch.js +0 -85
- package/tests/e2e/helpers/mockSearch.js +0 -93
- package/tests/e2e/helpers/rightToLeft.js +0 -29
- package/tests/e2e/ia-production/ia-prod-base.js +0 -17
- package/tests/e2e/models/BookReader.js +0 -11
- package/tests/e2e/models/Navigation.js +0 -56
- package/tests/e2e/rightToLeft.test.js +0 -20
- package/tests/e2e/viewmode.test.js +0 -37
- package/tests/karma/BookNavigator/book-navigator.test.js +0 -139
- package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
- package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
- package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
- package/tests/karma/BookNavigator/search/search-results.test.js +0 -240
- package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +0 -40
- package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +0 -141
- package/tests/karma/BookNavigator/volumes/volumes.test.js +0 -98
- package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
- package/tests/plugins/plugin.archive_analytics.test.js +0 -23
- package/tests/plugins/plugin.autoplay.test.js +0 -52
- package/tests/plugins/plugin.chapters.test.js +0 -130
- package/tests/plugins/plugin.iframe.test.js +0 -42
- package/tests/plugins/plugin.mobile_nav.test.js +0 -66
- package/tests/plugins/plugin.resume.test.js +0 -98
- package/tests/plugins/plugin.text_selection.test.js +0 -203
- package/tests/plugins/plugin.url.test.js +0 -129
- package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
- package/tests/plugins/search/plugin.search.test.js +0 -173
- package/tests/plugins/search/plugin.search.view.test.js +0 -106
- package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
- package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
- package/tests/plugins/tts/PageChunk.test.js +0 -57
- package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
- package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
- package/tests/plugins/tts/utils.test.js +0 -133
- package/tests/util/browserSniffing.test.js +0 -56
- package/tests/util/docCookies.test.js +0 -15
- package/tests/util/strings.test.js +0 -63
- package/tests/utils.js +0 -42
- package/webpack.config.js +0 -86
|
@@ -1,79 +1,154 @@
|
|
|
1
1
|
//@ts-check
|
|
2
|
-
import {
|
|
2
|
+
import { createDIVPageLayer } from '../BookReader/PageContainer.js';
|
|
3
|
+
import { BookReaderPlugin } from '../BookReaderPlugin.js';
|
|
3
4
|
import { applyVariables } from '../util/strings.js';
|
|
5
|
+
import { Cache } from '../util/cache.js';
|
|
6
|
+
import { toISO6391 } from './tts/utils.js';
|
|
7
|
+
import { BookReaderTextFragment, renderHighlight, TextSelectionManager } from '../util/TextSelectionManager.js';
|
|
8
|
+
import { genMap, lookAroundWindow, zip } from '../util/generators.js';
|
|
9
|
+
import textSelectionCss from '../css/_TextSelection.scss';
|
|
4
10
|
/** @typedef {import('../util/strings.js').StringWithVars} StringWithVars */
|
|
11
|
+
/** @typedef {import('../BookReader/PageContainer.js').PageContainer} PageContainer */
|
|
5
12
|
|
|
6
13
|
const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
|
|
7
14
|
|
|
8
|
-
export const DEFAULT_OPTIONS = {
|
|
9
|
-
enabled: true,
|
|
10
|
-
/** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
|
|
11
|
-
fullDjvuXmlUrl: null,
|
|
12
|
-
/** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
|
|
13
|
-
singlePageDjvuXmlUrl: null,
|
|
14
|
-
};
|
|
15
|
-
/** @typedef {typeof DEFAULT_OPTIONS} TextSelectionPluginOptions */
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
export class TextSelectionPlugin extends BookReaderPlugin {
|
|
17
|
+
options = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
/** @type {StringWithVars} The URL to fetch the entire DJVU xml. Supports options.vars */
|
|
20
|
+
fullDjvuXmlUrl: null,
|
|
21
|
+
/** @type {StringWithVars} The URL to fetch a single page of the DJVU xml. Supports options.vars. Also has {{pageIndex}} */
|
|
22
|
+
singlePageDjvuXmlUrl: null,
|
|
23
|
+
/** Whether to fetch the XML as a jsonp */
|
|
24
|
+
jsonp: false,
|
|
25
|
+
/** Mox words that can be selected when the text layer is protected */
|
|
26
|
+
maxProtectedWords: 200,
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
/**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
|
|
30
|
+
djvuPagesPromise = null;
|
|
31
|
+
|
|
32
|
+
/** @type {Cache<{index: number, response: any}>} */
|
|
33
|
+
pageTextCache = new Cache();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Sometimes there are too many words on a page, and the browser becomes near
|
|
37
|
+
* unusable. For now don't render text layer for pages with too many words.
|
|
38
|
+
*/
|
|
39
|
+
maxWordRendered = 2500;
|
|
40
|
+
|
|
41
|
+
_jumpedToHighlight = false;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Isolated document/layout used to performantly measure OCR text-layer
|
|
45
|
+
* elements.
|
|
46
|
+
* @type {Document}
|
|
47
|
+
*/
|
|
48
|
+
_measurementDocument;
|
|
49
|
+
|
|
27
50
|
/**
|
|
28
|
-
* @param {
|
|
51
|
+
* @param {import('../BookReader.js').default} br
|
|
29
52
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
53
|
+
constructor(br) {
|
|
54
|
+
super(br);
|
|
55
|
+
// In the future this should be in the ocr file
|
|
56
|
+
// since a book being right to left doesn't mean the ocr is right to left. But for
|
|
57
|
+
// now we do make that assumption.
|
|
58
|
+
/** Whether the book is right-to-left */
|
|
59
|
+
this.rtl = this.br.pageProgression === 'rl';
|
|
60
|
+
this.textSelectionManager = new TextSelectionManager('.BRtextLayer', this.br, {selectionElement: ['.BRwordElement', '.BRspace', 'mark']}, this.options.maxProtectedWords);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** @override */
|
|
64
|
+
init() {
|
|
65
|
+
if (!this.options.enabled) return;
|
|
66
|
+
|
|
67
|
+
// Setup measurement iframe for OCR
|
|
68
|
+
const measurementIframe = document.createElement('iframe');
|
|
69
|
+
measurementIframe.setAttribute('aria-hidden', 'true');
|
|
70
|
+
measurementIframe.tabIndex = -1;
|
|
71
|
+
measurementIframe.style.cssText = 'position:fixed; top:-99999px; left:-99999px; width:2000px; height:4000px; border:0; visibility:hidden;';
|
|
72
|
+
document.body.appendChild(measurementIframe);
|
|
73
|
+
this._measurementDocument = measurementIframe.contentDocument;
|
|
74
|
+
// Injects _TextSelection.scss so measurements match the real
|
|
75
|
+
// rendering
|
|
76
|
+
const style = this._measurementDocument.createElement('style');
|
|
77
|
+
style.textContent = textSelectionCss;
|
|
78
|
+
this._measurementDocument.head.appendChild(style);
|
|
79
|
+
|
|
80
|
+
this.br.on('pageVisible', (_, {pageContainerEl}) => {
|
|
81
|
+
const textLayer = pageContainerEl.querySelector('.BRtextLayer');
|
|
82
|
+
if (textLayer) {
|
|
83
|
+
this.br.trigger('textLayerVisible', {pageContainerEl, textLayer});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
this.loadData();
|
|
88
|
+
this.textSelectionManager.init();
|
|
89
|
+
|
|
90
|
+
// Init text fragment
|
|
91
|
+
const textParam = new URLSearchParams(location.search).get('text');
|
|
92
|
+
if (textParam) {
|
|
93
|
+
// save on global BR
|
|
94
|
+
this.br.targetTextFragment = BookReaderTextFragment.fromString(textParam, this.br.book, this.br.firstIndex);
|
|
95
|
+
this.initTextFragment();
|
|
33
96
|
}
|
|
34
|
-
this.entries.push(entry);
|
|
35
97
|
}
|
|
36
|
-
}
|
|
37
98
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
this.options = options;
|
|
42
|
-
this.optionVariables = optionVariables;
|
|
43
|
-
/**@type {PromiseLike<JQuery<HTMLElement>|undefined>} */
|
|
44
|
-
this.djvuPagesPromise = null;
|
|
45
|
-
// Using text elements instead of tspans for words because Firefox does not allow svg tspan stretch.
|
|
46
|
-
// Tspans are necessary on Chrome because they prevent newline character after every word when copying
|
|
47
|
-
this.svgParagraphElement = "text";
|
|
48
|
-
this.svgWordElement = "tspan";
|
|
49
|
-
this.insertNewlines = avoidTspans;
|
|
50
|
-
// Safari has a bug where `pointer-events` doesn't work on `<tspans>`. So
|
|
51
|
-
// there we will set `pointer-events: all` on the paragraph element. We don't
|
|
52
|
-
// do this everywhere, because it's a worse experience. Thanks Safari :/
|
|
53
|
-
this.pointerEventsOnParagraph = pointerEventsOnParagraph;
|
|
54
|
-
if (avoidTspans) {
|
|
55
|
-
this.svgParagraphElement = "g";
|
|
56
|
-
this.svgWordElement = "text";
|
|
99
|
+
async initTextFragment() {
|
|
100
|
+
if (this.br.options.verifyTextFragment && !await this.br.options.verifyTextFragment(this.br.targetTextFragment)) {
|
|
101
|
+
return;
|
|
57
102
|
}
|
|
58
103
|
|
|
59
|
-
|
|
60
|
-
this.
|
|
104
|
+
const targetTextFragment = this.br.targetTextFragment;
|
|
105
|
+
this.br.on('textLayerVisible', async (_, {pageContainerEl, textLayer}) => {
|
|
106
|
+
const pageIndex = targetTextFragment.pageIndex;
|
|
107
|
+
const hasTargetText = pageIndex === parseFloat(pageContainerEl.getAttribute('data-index'));
|
|
108
|
+
if (hasTargetText) {
|
|
109
|
+
const markEls = renderHighlight(textLayer, targetTextFragment, 'BRhighlight--target-text');
|
|
110
|
+
// Only jump once; presumably on first page load.
|
|
111
|
+
if (!this._jumpedToHighlight) {
|
|
112
|
+
this.br.scrollIntoView(markEls[0], {behavior: 'smooth', block: 'center'});
|
|
113
|
+
this._jumpedToHighlight = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
61
117
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
118
|
+
// Now jump to the page and open a slot if necessary
|
|
119
|
+
const page = this.br.book.getPage(this.br.targetTextFragment.pageIndex);
|
|
120
|
+
if (!page.isViewable) {
|
|
121
|
+
await this.br.requestPage(page);
|
|
122
|
+
this.br.jumpToIndex(page.index, { ariaLive: true, allowUnviewable: true });
|
|
123
|
+
}
|
|
67
124
|
}
|
|
68
125
|
|
|
69
|
-
|
|
126
|
+
/**
|
|
127
|
+
* @override
|
|
128
|
+
* @param {PageContainer} pageContainer
|
|
129
|
+
* @returns {PageContainer}
|
|
130
|
+
*/
|
|
131
|
+
_configurePageContainer(pageContainer) {
|
|
132
|
+
// Disable if thumb mode; it's too janky
|
|
133
|
+
// .page can be null for "pre-cover" region
|
|
134
|
+
if (this.options.enabled && this.br.mode !== this.br.constModeThumb && pageContainer.page?.isViewable) {
|
|
135
|
+
this.createTextLayer(pageContainer);
|
|
136
|
+
}
|
|
137
|
+
return pageContainer;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
loadData() {
|
|
70
141
|
// Only fetch the full djvu xml if the single page url isn't there
|
|
71
142
|
if (this.options.singlePageDjvuXmlUrl) return;
|
|
72
143
|
this.djvuPagesPromise = $.ajax({
|
|
73
144
|
type: "GET",
|
|
74
|
-
url: applyVariables(this.options.fullDjvuXmlUrl, this.
|
|
75
|
-
dataType: "html",
|
|
76
|
-
|
|
145
|
+
url: applyVariables(this.options.fullDjvuXmlUrl, this.br.options.vars),
|
|
146
|
+
dataType: this.options.jsonp ? "jsonp" : "html",
|
|
147
|
+
cache: true,
|
|
148
|
+
xhrFields: {
|
|
149
|
+
withCredentials: this.br.protected,
|
|
150
|
+
},
|
|
151
|
+
error: (e) => undefined,
|
|
77
152
|
}).then((res) => {
|
|
78
153
|
try {
|
|
79
154
|
const xmlMap = $.parseXML(res);
|
|
@@ -94,21 +169,24 @@ export class TextSelectionPlugin {
|
|
|
94
169
|
if (cachedEntry) {
|
|
95
170
|
return cachedEntry.response;
|
|
96
171
|
}
|
|
97
|
-
|
|
172
|
+
const res = await $.ajax({
|
|
98
173
|
type: "GET",
|
|
99
|
-
url: applyVariables(this.options.singlePageDjvuXmlUrl, this.
|
|
100
|
-
dataType: "html",
|
|
174
|
+
url: applyVariables(this.options.singlePageDjvuXmlUrl, this.br.options.vars, { pageIndex: index }),
|
|
175
|
+
dataType: this.options.jsonp ? "jsonp" : "html",
|
|
176
|
+
cache: true,
|
|
177
|
+
xhrFields: {
|
|
178
|
+
withCredentials: this.br.protected,
|
|
179
|
+
},
|
|
101
180
|
error: (e) => undefined,
|
|
102
|
-
}).then((res) => {
|
|
103
|
-
try {
|
|
104
|
-
const xmlDoc = $.parseXML(res);
|
|
105
|
-
const result = xmlDoc && $(xmlDoc).find("OBJECT")[0];
|
|
106
|
-
this.pageTextCache.add({ index, response: result });
|
|
107
|
-
return result;
|
|
108
|
-
} catch (e) {
|
|
109
|
-
return undefined;
|
|
110
|
-
}
|
|
111
181
|
});
|
|
182
|
+
try {
|
|
183
|
+
const xmlDoc = $.parseXML(res);
|
|
184
|
+
const result = xmlDoc && $(xmlDoc).find("OBJECT")[0];
|
|
185
|
+
this.pageTextCache.add({ index, response: result });
|
|
186
|
+
return result;
|
|
187
|
+
} catch (e) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
112
190
|
} else {
|
|
113
191
|
const XMLpagesArr = await this.djvuPagesPromise;
|
|
114
192
|
if (XMLpagesArr) return XMLpagesArr[index];
|
|
@@ -116,80 +194,25 @@ export class TextSelectionPlugin {
|
|
|
116
194
|
}
|
|
117
195
|
|
|
118
196
|
/**
|
|
119
|
-
*
|
|
120
|
-
* @param {JQuery} $container
|
|
121
|
-
*/
|
|
122
|
-
interceptCopy($container) {
|
|
123
|
-
$container[0].addEventListener('copy', (event) => {
|
|
124
|
-
const selection = document.getSelection();
|
|
125
|
-
event.clipboardData.setData('text/plain', selection.toString());
|
|
126
|
-
event.preventDefault();
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Applies mouse events when in default mode
|
|
132
|
-
* @param {SVGElement} svg
|
|
133
|
-
*/
|
|
134
|
-
defaultMode(svg) {
|
|
135
|
-
svg.classList.remove("selectingSVG");
|
|
136
|
-
$(svg).on("mousedown.textSelectPluginHandler", (event) => {
|
|
137
|
-
if (!$(event.target).is(".BRwordElement")) return;
|
|
138
|
-
event.stopPropagation();
|
|
139
|
-
svg.classList.add("selectingSVG");
|
|
140
|
-
$(svg).one("mouseup.textSelectPluginHandler", (event) => {
|
|
141
|
-
if (window.getSelection().toString() != "") {
|
|
142
|
-
event.stopPropagation();
|
|
143
|
-
$(svg).off(".textSelectPluginHandler");
|
|
144
|
-
this.textSelectingMode(svg);
|
|
145
|
-
}
|
|
146
|
-
else svg.classList.remove("selectingSVG");
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Applies mouse events when in textSelecting mode
|
|
153
|
-
* @param {SVGElement} svg
|
|
154
|
-
*/
|
|
155
|
-
textSelectingMode(svg) {
|
|
156
|
-
$(svg).on('mousedown.textSelectPluginHandler', (event) => {
|
|
157
|
-
if (!$(event.target).is(".BRwordElement")) {
|
|
158
|
-
if (window.getSelection().toString() != "") window.getSelection().removeAllRanges();
|
|
159
|
-
}
|
|
160
|
-
event.stopPropagation();
|
|
161
|
-
});
|
|
162
|
-
$(svg).on('mouseup.textSelectPluginHandler', (event) => {
|
|
163
|
-
event.stopPropagation();
|
|
164
|
-
if (window.getSelection().toString() == "") {
|
|
165
|
-
$(svg).off(".textSelectPluginHandler");
|
|
166
|
-
this.defaultMode(svg); }
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Initializes text selection modes if there is an svg on the page
|
|
172
|
-
* @param {JQuery} $container
|
|
197
|
+
* @param {PageContainer} pageContainer
|
|
173
198
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const $
|
|
177
|
-
|
|
178
|
-
$
|
|
179
|
-
this.interceptCopy($container);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* @param {number} pageIndex
|
|
184
|
-
* @param {JQuery} $container
|
|
185
|
-
*/
|
|
186
|
-
async createTextLayer(pageIndex, $container) {
|
|
187
|
-
const $svgLayers = $container.find('.textSelectionSVG');
|
|
188
|
-
if ($svgLayers.length) return;
|
|
199
|
+
async createTextLayer(pageContainer) {
|
|
200
|
+
const pageIndex = pageContainer.page.index;
|
|
201
|
+
const $container = pageContainer.$container;
|
|
202
|
+
const $textLayers = $container.find('.BRtextLayer');
|
|
203
|
+
if ($textLayers.length) return;
|
|
189
204
|
const XMLpage = await this.getPageText(pageIndex);
|
|
190
205
|
if (!XMLpage) return;
|
|
191
|
-
|
|
192
|
-
|
|
206
|
+
// Seeing some 0 left and 0 top coordinates in OCR, remove it entirely to prevent odd rendering
|
|
207
|
+
// eg https://archive.org/details/illustratedbooko00robe/page/n11/mode/2up
|
|
208
|
+
$(XMLpage).find("WORD").filter((_, ele) => {
|
|
209
|
+
const [left, , , top] = ele.getAttribute('coords').split(",").map(parseFloat);
|
|
210
|
+
if (left == 0 && top == 0) {
|
|
211
|
+
console.error("Found invalid ocr word coordinates");
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
}).remove();
|
|
215
|
+
recursivelyAddCoords(XMLpage);
|
|
193
216
|
|
|
194
217
|
const totalWords = $(XMLpage).find("WORD").length;
|
|
195
218
|
if (totalWords > this.maxWordRendered) {
|
|
@@ -197,107 +220,329 @@ export class TextSelectionPlugin {
|
|
|
197
220
|
return;
|
|
198
221
|
}
|
|
199
222
|
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
"
|
|
210
|
-
|
|
211
|
-
|
|
223
|
+
const textLayer = createDIVPageLayer(pageContainer.page, 'BRtextLayer');
|
|
224
|
+
// Have to wait to make sure the page container is actually rendered,
|
|
225
|
+
// otherwise width/height are unset after a mode change.
|
|
226
|
+
await Promise.resolve();
|
|
227
|
+
const ratioW = parseFloat(pageContainer.$container[0].style.width) / pageContainer.page.width;
|
|
228
|
+
const ratioH = parseFloat(pageContainer.$container[0].style.height) / pageContainer.page.height;
|
|
229
|
+
textLayer.style.transform = `scale(${ratioW}, ${ratioH})`;
|
|
230
|
+
const bookLangCode = toISO6391(this.br.options.bookLanguage);
|
|
231
|
+
if (bookLangCode) {
|
|
232
|
+
textLayer.setAttribute("lang", bookLangCode);
|
|
233
|
+
}
|
|
234
|
+
textLayer.setAttribute("dir", this.rtl ? "rtl" : "ltr");
|
|
235
|
+
|
|
236
|
+
const ocrParagraphs = $(XMLpage).find("PARAGRAPH[coords]").toArray();
|
|
237
|
+
const paragEls = ocrParagraphs.map(p => {
|
|
238
|
+
const el = this.renderParagraph(p);
|
|
239
|
+
textLayer.appendChild(el);
|
|
240
|
+
return el;
|
|
212
241
|
});
|
|
213
242
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
243
|
+
// Fix up paragraph positions
|
|
244
|
+
const paragraphRects = determineRealRects(textLayer, '.BRparagraphElement', this._measurementDocument);
|
|
245
|
+
let yAdded = 0;
|
|
246
|
+
for (const [ocrParagraph, paragEl] of zip(ocrParagraphs, paragEls)) {
|
|
247
|
+
const ocrParagBounds = $(ocrParagraph).attr("coords").split(",").map(parseFloat);
|
|
248
|
+
const realRect = paragraphRects.get(paragEl);
|
|
249
|
+
const [ocrLeft, , ocrRight, ocrTop] = ocrParagBounds;
|
|
250
|
+
const newStartMargin = this.rtl ? (realRect.right - ocrRight) : (ocrLeft - realRect.left);
|
|
251
|
+
const newTop = ocrTop - (realRect.top + yAdded);
|
|
223
252
|
|
|
224
|
-
|
|
253
|
+
paragEl.style[this.rtl ? 'marginRight' : 'marginLeft'] = `${newStartMargin}px`;
|
|
254
|
+
paragEl.style.marginTop = `${newTop}px`;
|
|
255
|
+
yAdded += newTop;
|
|
256
|
+
textLayer.appendChild(paragEl);
|
|
257
|
+
textLayer.appendChild(document.createTextNode('\n'));
|
|
258
|
+
}
|
|
259
|
+
$container.append(textLayer);
|
|
260
|
+
this.textSelectionManager.stopPageFlip($container);
|
|
261
|
+
this.br.trigger('textLayerRendered', {
|
|
262
|
+
pageIndex,
|
|
263
|
+
pageContainer,
|
|
264
|
+
});
|
|
225
265
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
266
|
+
// Check if page is visible
|
|
267
|
+
if ($container.hasClass('BRpage-visible')) {
|
|
268
|
+
this.br.trigger('textLayerVisible', {pageContainerEl: $container[0], textLayer});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {HTMLElement} ocrParagraph
|
|
274
|
+
* @returns {HTMLParagraphElement}
|
|
275
|
+
*/
|
|
276
|
+
renderParagraph(ocrParagraph) {
|
|
277
|
+
const paragEl = document.createElement('p');
|
|
278
|
+
paragEl.classList.add('BRparagraphElement');
|
|
279
|
+
if (ocrParagraph.getAttribute("x-role")) {
|
|
280
|
+
paragEl.classList.add('ocr-role-header-footer');
|
|
281
|
+
paragEl.ariaHidden = "true";
|
|
282
|
+
}
|
|
283
|
+
const [paragLeft, paragBottom, paragRight, paragTop] = $(ocrParagraph).attr("coords").split(",").map(parseFloat);
|
|
284
|
+
const wordHeightArr = [];
|
|
285
|
+
const lines = $(ocrParagraph).find("LINE[coords]").toArray();
|
|
286
|
+
if (!lines.length) return paragEl;
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
for (const [prevLine, line, nextLine] of lookAroundWindow(genMap(lines, augmentLine))) {
|
|
290
|
+
const isLastLineOfParagraph = line.ocrElement == lines[lines.length - 1];
|
|
291
|
+
const lineEl = document.createElement('span');
|
|
292
|
+
lineEl.classList.add('BRlineElement');
|
|
293
|
+
|
|
294
|
+
for (const [wordIndex, currWord] of line.words.entries()) {
|
|
295
|
+
const [, bottom, right, top] = $(currWord).attr("coords").split(',').map(parseFloat);
|
|
231
296
|
const wordHeight = bottom - top;
|
|
232
297
|
wordHeightArr.push(wordHeight);
|
|
233
298
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
paragSvg.appendChild(wordTspan);
|
|
242
|
-
|
|
243
|
-
// Adding spaces after words except at the end of the paragraph
|
|
244
|
-
// TODO: assumes left-to-right text
|
|
245
|
-
if (i < words.length - 1) {
|
|
246
|
-
const nextWord = words[i + 1];
|
|
247
|
-
// eslint-disable-next-line no-unused-vars
|
|
248
|
-
const [leftNext, bottomNext, rightNext, topNext] = $(nextWord).attr("coords").split(',').map(parseFloat);
|
|
249
|
-
const spaceTspan = document.createElementNS("http://www.w3.org/2000/svg", this.svgWordElement);
|
|
250
|
-
spaceTspan.setAttribute("class", "BRwordElement");
|
|
251
|
-
spaceTspan.setAttribute("x", right.toString());
|
|
252
|
-
spaceTspan.setAttribute("y", bottom.toString());
|
|
253
|
-
if ((leftNext - right) > 0) spaceTspan.setAttribute("textLength", (leftNext - right).toString());
|
|
254
|
-
spaceTspan.setAttribute("lengthAdjust", "spacingAndGlyphs");
|
|
255
|
-
spaceTspan.textContent = " ";
|
|
256
|
-
paragSvg.appendChild(spaceTspan);
|
|
299
|
+
if (wordIndex == 0 && prevLine?.lastWord.textContent.trim().endsWith('-')) {
|
|
300
|
+
// ideally prefer the next line to determine the left position,
|
|
301
|
+
// since the previous line could be the first line of the paragraph
|
|
302
|
+
// and hence have an incorrectly indented first word.
|
|
303
|
+
// E.g. https://archive.org/details/driitaleofdaring00bachuoft/page/360/mode/2up
|
|
304
|
+
const [newLeft, , , ] = $((nextLine || prevLine).firstWord).attr("coords").split(',').map(parseFloat);
|
|
305
|
+
$(currWord).attr("coords", `${newLeft},${bottom},${right},${top}`);
|
|
257
306
|
}
|
|
258
307
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
308
|
+
const wordEl = document.createElement('span');
|
|
309
|
+
wordEl.setAttribute("class", "BRwordElement");
|
|
310
|
+
wordEl.textContent = currWord.textContent.trim();
|
|
311
|
+
|
|
312
|
+
if (wordIndex > 0) {
|
|
313
|
+
const space = document.createElement('span');
|
|
314
|
+
space.classList.add('BRspace');
|
|
315
|
+
space.textContent = ' ';
|
|
316
|
+
// Hack to make screen readers (eg NVDA) read spaces correctly;
|
|
317
|
+
// otherwise they ignore elements with just whitespace.
|
|
318
|
+
space.setAttribute('aria-label', '\u00A0');
|
|
319
|
+
lineEl.append(space);
|
|
320
|
+
|
|
321
|
+
// Edge ignores empty elements (like BRspace), so add another
|
|
322
|
+
// space to ensure Edge's ReadAloud works correctly.
|
|
323
|
+
lineEl.appendChild(document.createTextNode(' '));
|
|
262
324
|
}
|
|
325
|
+
|
|
326
|
+
lineEl.appendChild(wordEl);
|
|
263
327
|
}
|
|
264
328
|
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
329
|
+
const hasHyphen = line.lastWord.textContent.trim().endsWith('-');
|
|
330
|
+
const lastWordEl = lineEl.children[lineEl.children.length - 1];
|
|
331
|
+
if (hasHyphen && !isLastLineOfParagraph) {
|
|
332
|
+
lastWordEl.textContent = lastWordEl.textContent.trim().slice(0, -1);
|
|
333
|
+
lastWordEl.classList.add('BRwordElement--hyphen');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
paragEl.appendChild(lineEl);
|
|
337
|
+
if (!isLastLineOfParagraph && !hasHyphen) {
|
|
338
|
+
// Edge does not correctly have spaces between the lines.
|
|
339
|
+
paragEl.appendChild(document.createTextNode(' '));
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
wordHeightArr.sort((a, b) => a - b);
|
|
344
|
+
const paragWordHeight = wordHeightArr[Math.floor(wordHeightArr.length * 0.85)] + 4;
|
|
345
|
+
paragEl.style.left = `${paragLeft}px`;
|
|
346
|
+
paragEl.style.top = `${paragTop}px`;
|
|
347
|
+
paragEl.style.width = `${paragRight - paragLeft}px`;
|
|
348
|
+
paragEl.style.height = `${paragBottom - paragTop}px`;
|
|
349
|
+
paragEl.style.fontSize = `${paragWordHeight}px`;
|
|
350
|
+
|
|
351
|
+
// Fix up sizes - stretch/crush words as necessary using letter spacing
|
|
352
|
+
let wordRects = determineRealRects(paragEl, '.BRwordElement', this._measurementDocument);
|
|
353
|
+
const ocrWords = $(ocrParagraph).find("WORD").toArray();
|
|
354
|
+
const wordEls = paragEl.querySelectorAll('.BRwordElement');
|
|
355
|
+
for (const [ocrWord, wordEl] of zip(ocrWords, wordEls)) {
|
|
356
|
+
const realRect = wordRects.get(wordEl);
|
|
357
|
+
const [left, , right ] = $(ocrWord).attr("coords").split(',').map(parseFloat);
|
|
358
|
+
let ocrWidth = right - left;
|
|
359
|
+
// Some books (eg theworksofplato01platiala) have a space _inside_ the <WORD>
|
|
360
|
+
// element. That makes it impossible to determine the correct positining
|
|
361
|
+
// of everything, but to avoid the BRspace's being width 0, which makes selection
|
|
362
|
+
// janky on Chrome Android, assume the space is the same width as one of the
|
|
363
|
+
// letters.
|
|
364
|
+
if (ocrWord.textContent.endsWith(' ')) {
|
|
365
|
+
ocrWidth = ocrWidth * (ocrWord.textContent.length - 1) / ocrWord.textContent.length;
|
|
366
|
+
}
|
|
367
|
+
const diff = ocrWidth - realRect.width;
|
|
368
|
+
wordEl.style.letterSpacing = `${diff / (ocrWord.textContent.length - 1)}px`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Stretch/crush lines as necessary using line spacing
|
|
372
|
+
// Recompute rects after letter spacing
|
|
373
|
+
wordRects = determineRealRects(paragEl, '.BRwordElement', this._measurementDocument);
|
|
374
|
+
const spaceRects = determineRealRects(paragEl, '.BRspace', this._measurementDocument);
|
|
375
|
+
|
|
376
|
+
const ocrLines = $(ocrParagraph).find("LINE[coords]").toArray();
|
|
377
|
+
const lineEls = Array.from(paragEl.querySelectorAll('.BRlineElement'));
|
|
378
|
+
|
|
379
|
+
let ySoFar = paragTop;
|
|
380
|
+
for (const [ocrLine, lineEl] of zip(ocrLines, lineEls)) {
|
|
381
|
+
// shift words using marginLeft to align with the correct x position
|
|
382
|
+
const words = $(ocrLine).find("WORD").toArray();
|
|
383
|
+
// const ocrLineLeft = Math.min(...words.map(w => parseFloat($(w).attr("coords").split(',')[0])));
|
|
384
|
+
let xSoFar = this.rtl ? paragRight : paragLeft;
|
|
385
|
+
for (const [ocrWord, wordEl] of zip(words, lineEl.querySelectorAll('.BRwordElement'))) {
|
|
386
|
+
// start of line, need to compute the offset relative to the OCR words
|
|
387
|
+
const wordRect = wordRects.get(wordEl);
|
|
388
|
+
const [ocrLeft, , ocrRight ] = $(ocrWord).attr("coords").split(',').map(parseFloat);
|
|
389
|
+
const diff = (this.rtl ? -(ocrRight - xSoFar) : ocrLeft - xSoFar);
|
|
390
|
+
|
|
391
|
+
if (wordEl.previousElementSibling) {
|
|
392
|
+
const space = wordEl.previousElementSibling;
|
|
393
|
+
space.style.letterSpacing = `${diff - spaceRects.get(space).width}px`;
|
|
394
|
+
} else {
|
|
395
|
+
wordEl.style[this.rtl ? 'paddingRight' : 'paddingLeft'] = `${diff}px`;
|
|
396
|
+
}
|
|
397
|
+
if (this.rtl) xSoFar -= diff + wordRect.width;
|
|
398
|
+
else xSoFar += diff + wordRect.width;
|
|
399
|
+
}
|
|
400
|
+
// And also fix y position
|
|
401
|
+
const ocrLineTop = Math.min(...words.map(w => parseFloat($(w).attr("coords").split(',')[3])));
|
|
402
|
+
const diff = ocrLineTop - ySoFar;
|
|
403
|
+
if (lineEl.previousElementSibling) {
|
|
404
|
+
lineEl.previousElementSibling.style.lineHeight = `${diff}px`;
|
|
405
|
+
ySoFar += diff;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// The last line will have a line height subtracting from the paragraph height
|
|
410
|
+
lineEls[lineEls.length - 1].style.lineHeight = `${paragBottom - ySoFar}px`;
|
|
411
|
+
|
|
412
|
+
// Edge does not include a newline for some reason when copying/pasting the <p> els
|
|
413
|
+
paragEl.appendChild(document.createElement('br'));
|
|
414
|
+
return paragEl;
|
|
271
415
|
}
|
|
272
416
|
}
|
|
273
417
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
418
|
+
BookReader?.registerPlugin('textSelection', TextSelectionPlugin);
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* @param {HTMLElement} parentEl
|
|
423
|
+
* @param {string} selector
|
|
424
|
+
* @param {Document} measurementDocument Isolated document to measure within
|
|
425
|
+
* (see TextSelectionPlugin#_measurementDocument for why).
|
|
426
|
+
* @returns {Map<Element, Rect>}
|
|
427
|
+
*/
|
|
428
|
+
function determineRealRects(parentEl, selector, measurementDocument) {
|
|
429
|
+
const initals = {
|
|
430
|
+
position: parentEl.style.position,
|
|
431
|
+
visibility: parentEl.style.visibility,
|
|
432
|
+
top: parentEl.style.top,
|
|
433
|
+
left: parentEl.style.left,
|
|
434
|
+
transform: parentEl.style.transform,
|
|
435
|
+
};
|
|
436
|
+
parentEl.style.position = 'absolute';
|
|
437
|
+
parentEl.style.visibility = 'hidden';
|
|
438
|
+
parentEl.style.top = '0';
|
|
439
|
+
parentEl.style.left = '0';
|
|
440
|
+
parentEl.style.transform = 'none';
|
|
441
|
+
measurementDocument.body.appendChild(parentEl);
|
|
442
|
+
const rects = new Map(
|
|
443
|
+
Array.from(parentEl.querySelectorAll(selector))
|
|
444
|
+
.map(wordEl => {
|
|
445
|
+
const origRect = wordEl.getBoundingClientRect();
|
|
446
|
+
return [wordEl, new Rect(
|
|
447
|
+
origRect.left + measurementDocument.defaultView.scrollX,
|
|
448
|
+
origRect.top + measurementDocument.defaultView.scrollY,
|
|
449
|
+
origRect.width,
|
|
450
|
+
origRect.height,
|
|
451
|
+
)];
|
|
452
|
+
}),
|
|
453
|
+
);
|
|
454
|
+
measurementDocument.body.removeChild(parentEl);
|
|
455
|
+
Object.assign(parentEl.style, initals);
|
|
456
|
+
// Need to restore the document to the main window document
|
|
457
|
+
document.adoptNode(parentEl);
|
|
458
|
+
return rects;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* @param {HTMLElement} line
|
|
463
|
+
*/
|
|
464
|
+
function augmentLine(line) {
|
|
465
|
+
const words = $(line).find("WORD").toArray();
|
|
466
|
+
return {
|
|
467
|
+
ocrElement: line,
|
|
468
|
+
words,
|
|
469
|
+
firstWord: words[0],
|
|
470
|
+
lastWord: words[words.length - 1],
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* [left, bottom, right, top]
|
|
476
|
+
* @param {Array<[number, number, number, number]>} bounds
|
|
477
|
+
* @returns {[number, number, number, number]}
|
|
478
|
+
*/
|
|
479
|
+
function determineBounds(bounds) {
|
|
480
|
+
let leftMost = Infinity;
|
|
481
|
+
let bottomMost = -Infinity;
|
|
482
|
+
let rightMost = -Infinity;
|
|
483
|
+
let topMost = Infinity;
|
|
484
|
+
|
|
485
|
+
for (const [left, bottom, right, top] of bounds) {
|
|
486
|
+
leftMost = Math.min(leftMost, left);
|
|
487
|
+
bottomMost = Math.max(bottomMost, bottom);
|
|
488
|
+
rightMost = Math.max(rightMost, right);
|
|
489
|
+
topMost = Math.min(topMost, top);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return [leftMost, bottomMost, rightMost, topMost];
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Recursively traverses the XML tree and adds coords
|
|
497
|
+
* which are the bounding box of all child coords
|
|
498
|
+
* @param {Element} xmlEl
|
|
499
|
+
*/
|
|
500
|
+
function recursivelyAddCoords(xmlEl) {
|
|
501
|
+
if ($(xmlEl).attr('coords') || !xmlEl.children) {
|
|
502
|
+
return;
|
|
285
503
|
}
|
|
286
504
|
|
|
505
|
+
const children = $(xmlEl).children().toArray();
|
|
506
|
+
if (children.length === 0) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
for (const child of children) {
|
|
511
|
+
recursivelyAddCoords(child);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const childCoords = [];
|
|
515
|
+
|
|
516
|
+
for (const child of children) {
|
|
517
|
+
if (!$(child).attr('coords')) continue;
|
|
518
|
+
childCoords.push($(child).attr('coords').split(',').map(parseFloat));
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const boundingCoords = determineBounds(childCoords);
|
|
522
|
+
if (Math.abs(boundingCoords[0]) != Infinity) {
|
|
523
|
+
$(xmlEl).attr('coords', boundingCoords.join(','));
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Basically a polyfill for the native DOMRect class
|
|
529
|
+
*/
|
|
530
|
+
class Rect {
|
|
287
531
|
/**
|
|
288
|
-
* @param {number}
|
|
532
|
+
* @param {number} x
|
|
533
|
+
* @param {number} y
|
|
534
|
+
* @param {number} width
|
|
535
|
+
* @param {number} height
|
|
289
536
|
*/
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const lastPageIndex = this.getNumLeafs() - 1;
|
|
296
|
-
if (this.mode !== this.constModeThumb && (index >= 0 && index <= lastPageIndex)) {
|
|
297
|
-
this.textSelectionPlugin?.createTextLayer(index, pageContainer.$container);
|
|
298
|
-
}
|
|
299
|
-
return pageContainer;
|
|
537
|
+
constructor(x, y, width, height) {
|
|
538
|
+
this.x = x;
|
|
539
|
+
this.y = y;
|
|
540
|
+
this.width = width;
|
|
541
|
+
this.height = height;
|
|
300
542
|
}
|
|
543
|
+
|
|
544
|
+
get right() { return this.x + this.width; }
|
|
545
|
+
get bottom() { return this.y + this.height; }
|
|
546
|
+
get top() { return this.y; }
|
|
547
|
+
get left() { return this.x; }
|
|
301
548
|
}
|
|
302
|
-
window.BookReader = BookreaderWithTextSelection;
|
|
303
|
-
export default BookreaderWithTextSelection;
|