@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,153 @@
|
|
1
|
+
import sinon from 'sinon';
|
2
|
+
import { afterEventLoop } from '../../utils.js';
|
3
|
+
import AbstractTTSEngine from '../../../src/plugins/tts/AbstractTTSEngine.js';
|
4
|
+
import PageChunkIterator from '../../../src/plugins/tts/PageChunkIterator.js';
|
5
|
+
/** @typedef {import('../../../src/plugins/tts/AbstractTTSEngine.js').TTSEngineOptions} TTSEngineOptions */
|
6
|
+
|
7
|
+
// Skipping because it's flaky. Fix in #672
|
8
|
+
describe.skip('AbstractTTSEngine', () => {
|
9
|
+
test('stops playing once done', () => {
|
10
|
+
class DummyEngine extends AbstractTTSEngine {
|
11
|
+
getVoices() { return []; }
|
12
|
+
}
|
13
|
+
const d = new DummyEngine(DUMMY_TTS_ENGINE_OPTS);
|
14
|
+
d._chunkIterator = { next: sinon.stub().resolves(PageChunkIterator.AT_END) };
|
15
|
+
const stopStub = sinon.stub(d, 'stop');
|
16
|
+
expect(stopStub.callCount).toBe(0);
|
17
|
+
d.step();
|
18
|
+
return afterEventLoop()
|
19
|
+
.then(() => expect(stopStub.callCount).toBe(1));
|
20
|
+
});
|
21
|
+
});
|
22
|
+
|
23
|
+
for (const dummyVoice of [dummyVoice, dummyVoiceUnderscores]) {
|
24
|
+
describe(`getBestBookVoice with BCP47 ${dummyVoice == dummyVoiceUnderscores ? '+' : '-'} underscores`, () => {
|
25
|
+
const { getBestBookVoice } = AbstractTTSEngine;
|
26
|
+
|
27
|
+
test('undefined if no voices', () => {
|
28
|
+
expect(getBestBookVoice([], 'en', [])).toBe(undefined);
|
29
|
+
});
|
30
|
+
|
31
|
+
test('returns first voice if no matching', () => {
|
32
|
+
const enVoice = dummyVoice({lang: "en-US"});
|
33
|
+
expect(getBestBookVoice([enVoice], 'fr', [])).toBe(enVoice);
|
34
|
+
});
|
35
|
+
|
36
|
+
test('choose first matching voice', () => {
|
37
|
+
const voices = [
|
38
|
+
dummyVoice({lang: "en-GB"}),
|
39
|
+
dummyVoice({lang: "en-US"}),
|
40
|
+
];
|
41
|
+
expect(getBestBookVoice(voices, 'en', [])).toBe(voices[0]);
|
42
|
+
});
|
43
|
+
|
44
|
+
test('choose first matching default voice', () => {
|
45
|
+
const voices = [
|
46
|
+
dummyVoice({lang: "en-GB"}),
|
47
|
+
dummyVoice({lang: "en-US", default: true}),
|
48
|
+
];
|
49
|
+
expect(getBestBookVoice(voices, 'en', [])).toBe(voices[1]);
|
50
|
+
});
|
51
|
+
|
52
|
+
test('does not choose default if better language match exists', () => {
|
53
|
+
const voices = [
|
54
|
+
dummyVoice({lang: "en-US", default: true}),
|
55
|
+
dummyVoice({lang: "fr-FR"}),
|
56
|
+
];
|
57
|
+
expect(getBestBookVoice(voices, 'fr', [])).toBe(voices[1]);
|
58
|
+
});
|
59
|
+
|
60
|
+
test('choose users dialect if present', () => {
|
61
|
+
const voices = [
|
62
|
+
dummyVoice({lang: "en-GB"}),
|
63
|
+
dummyVoice({lang: "en-CA"}),
|
64
|
+
dummyVoice({lang: "en-US"}),
|
65
|
+
];
|
66
|
+
expect(getBestBookVoice(voices, 'en', ['en-CA', 'en'])).toBe(voices[1]);
|
67
|
+
});
|
68
|
+
|
69
|
+
test('choose users dialect even if not default', () => {
|
70
|
+
const voices = [
|
71
|
+
dummyVoice({lang: "en-US", default: true}),
|
72
|
+
dummyVoice({lang: "en-GB"}),
|
73
|
+
dummyVoice({lang: "en-CA"}),
|
74
|
+
];
|
75
|
+
expect(getBestBookVoice(voices, 'en', ['en-CA', 'en'])).toBe(voices[2]);
|
76
|
+
});
|
77
|
+
|
78
|
+
test('choose language even if dialect does not match', () => {
|
79
|
+
const voices = [
|
80
|
+
dummyVoice({lang: "en-GB"}),
|
81
|
+
];
|
82
|
+
expect(getBestBookVoice(voices, 'en', ['en-CA'])).toBe(voices[0]);
|
83
|
+
});
|
84
|
+
|
85
|
+
test('real world example', () => {
|
86
|
+
// Chrome 77 @ Windows 10
|
87
|
+
const voices = [
|
88
|
+
{ default: true, lang: "en-US", name: "Microsoft David Desktop - English (United States)", localService: true, voiceURI: "Microsoft David Desktop - English (United States)" },
|
89
|
+
{ default: false, lang: "de-DE", name: "Microsoft Hedda Desktop - German", localService: true, voiceURI: "Microsoft Hedda Desktop - German" },
|
90
|
+
{ default: false, lang: "en-US", name: "Microsoft Zira Desktop - English (United States)", localService: true, voiceURI: "Microsoft Zira Desktop - English (United States)" },
|
91
|
+
{ default: false, lang: "de-DE", name: "Google Deutsch", localService: false, voiceURI: "Google Deutsch" },
|
92
|
+
{ default: false, lang: "en-US", name: "Google US English", localService: false, voiceURI: "Google US English" },
|
93
|
+
{ default: false, lang: "en-GB", name: "Google UK English Female", localService: false, voiceURI: "Google UK English Female" },
|
94
|
+
{ default: false, lang: "en-GB", name: "Google UK English Male", localService: false, voiceURI: "Google UK English Male" },
|
95
|
+
{ default: false, lang: "es-ES", name: "Google español", localService: false, voiceURI: "Google español" },
|
96
|
+
{ default: false, lang: "es-US", name: "Google español de Estados Unidos", localService: false, voiceURI: "Google español de Estados Unidos" },
|
97
|
+
{ default: false, lang: "fr-FR", name: "Google français", localService: false, voiceURI: "Google français" },
|
98
|
+
{ default: false, lang: "hi-IN", name: "Google हिन्दी", localService: false, voiceURI: "Google हिन्दी" },
|
99
|
+
{ default: false, lang: "id-ID", name: "Google Bahasa Indonesia", localService: false, voiceURI: "Google Bahasa Indonesia" },
|
100
|
+
{ default: false, lang: "it-IT", name: "Google italiano", localService: false, voiceURI: "Google italiano" },
|
101
|
+
{ default: false, lang: "ja-JP", name: "Google 日本語", localService: false, voiceURI: "Google 日本語" },
|
102
|
+
{ default: false, lang: "ko-KR", name: "Google 한국의", localService: false, voiceURI: "Google 한국의" },
|
103
|
+
{ default: false, lang: "nl-NL", name: "Google Nederlands", localService: false, voiceURI: "Google Nederlands" },
|
104
|
+
{ default: false, lang: "pl-PL", name: "Google polski", localService: false, voiceURI: "Google polski" },
|
105
|
+
{ default: false, lang: "pt-BR", name: "Google português do Brasil", localService: false, voiceURI: "Google português do Brasil" },
|
106
|
+
{ default: false, lang: "ru-RU", name: "Google русский", localService: false, voiceURI: "Google русский" },
|
107
|
+
{ default: false, lang: "zh-CN", name: "Google 普通话(中国大陆)", localService: false, voiceURI: "Google 普通话(中国大陆)" },
|
108
|
+
{ default: false, lang: "zh-HK", name: "Google 粤語(香港)", localService: false, voiceURI: "Google 粤語(香港)" },
|
109
|
+
{ default: false, lang: "zh-TW", name: "Google 國語(臺灣)", localService: false, voiceURI: "Google 國語(臺灣)" }
|
110
|
+
];
|
111
|
+
|
112
|
+
expect(getBestBookVoice(voices, 'en', ['en-CA', 'en'])).toBe(voices[0]);
|
113
|
+
});
|
114
|
+
});
|
115
|
+
}
|
116
|
+
|
117
|
+
/** @type {TTSEngineOptions} */
|
118
|
+
export const DUMMY_TTS_ENGINE_OPTS = {
|
119
|
+
server: 'blah',
|
120
|
+
bookPath: 'blah',
|
121
|
+
bookLanguage: 'blah',
|
122
|
+
onLoadingStart() {},
|
123
|
+
onLoadingComplete() {},
|
124
|
+
onDone() {},
|
125
|
+
beforeChunkPlay() { return Promise.resolve(); },
|
126
|
+
afterChunkPlay() {},
|
127
|
+
};
|
128
|
+
|
129
|
+
/**
|
130
|
+
* @param {SpeechSynthesisVoice}
|
131
|
+
* @return {SpeechSynthesisVoice}
|
132
|
+
**/
|
133
|
+
function dummyVoice(overrides) {
|
134
|
+
return Object.assign({
|
135
|
+
default: false,
|
136
|
+
lang: "en-US",
|
137
|
+
name: "Microsoft David",
|
138
|
+
localService: false,
|
139
|
+
voiceURI: "",
|
140
|
+
}, overrides);
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Construct a voice-like object using underscores instead of hyphenes
|
145
|
+
* (Like Chrome/Android)
|
146
|
+
* @param {SpeechSynthesisVoice}
|
147
|
+
* @return {SpeechSynthesisVoice}
|
148
|
+
**/
|
149
|
+
function dummyVoiceUnderscores(overrides) {
|
150
|
+
const voice = dummyVoice(overrides);
|
151
|
+
voice.lang = voice.lang.replace('-', '_');
|
152
|
+
return voice;
|
153
|
+
}
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import FestivalTTSEngine from '../../../src/plugins/tts/FestivalTTSEngine.js';
|
2
|
+
import sinon from 'sinon';
|
3
|
+
import { afterEventLoop } from '../../utils.js';
|
4
|
+
import { DUMMY_TTS_ENGINE_OPTS } from './AbstractTTSEngine.test.js';
|
5
|
+
import PageChunk from '../../../src/plugins/tts/PageChunk.js';
|
6
|
+
import PageChunkIterator from '../../../src/plugins/tts/PageChunkIterator.js';
|
7
|
+
/** @typedef {import('../../../src/plugins/tts/AbstractTTSEngine.js').TTSEngineOptions} TTSEngineOptions */
|
8
|
+
|
9
|
+
describe('iOSCaptureUserIntentHack', () => {
|
10
|
+
test('synchronously calls createSound/play to capture user intent', () => {
|
11
|
+
const mockSound = createMockSound();
|
12
|
+
const sm = {
|
13
|
+
supported: sinon.stub().returns(true),
|
14
|
+
createSound: sinon.stub().returns(mockSound),
|
15
|
+
};
|
16
|
+
window.soundManager = sm;
|
17
|
+
const engine = new FestivalTTSEngine(DUMMY_TTS_ENGINE_OPTS);
|
18
|
+
engine.iOSCaptureUserIntentHack();
|
19
|
+
|
20
|
+
expect(sm.createSound.callCount).toBe(1);
|
21
|
+
expect(mockSound.play.callCount).toBe(1);
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
describe('misc', () => {
|
26
|
+
test('starts playing even if onload not called until play starts', async () => {
|
27
|
+
const sm = {
|
28
|
+
supported: sinon.stub().returns(true),
|
29
|
+
createSound: sinon.stub().returns(createMockSound()),
|
30
|
+
stopAll: sinon.stub(),
|
31
|
+
};
|
32
|
+
window.soundManager = sm;
|
33
|
+
const engine = new FestivalTTSEngine(DUMMY_TTS_ENGINE_OPTS);
|
34
|
+
sinon.stub(engine, 'playSound').returns(new Promise(() => {}));
|
35
|
+
sinon.stub(engine, 'stop');
|
36
|
+
sinon.stub(PageChunkIterator.prototype, '_fetchPageChunks').resolves([dummyPageChunk()]);
|
37
|
+
engine.start(0, 5);
|
38
|
+
|
39
|
+
// because things happen in callbacks, need to run code at end of the JS event loop
|
40
|
+
await afterEventLoop();
|
41
|
+
expect(sm.createSound.callCount).toBe(1);
|
42
|
+
expect(PageChunkIterator.prototype._fetchPageChunks.callCount).toBeGreaterThanOrEqual(1);
|
43
|
+
expect(engine.playSound.callCount).toBe(1);
|
44
|
+
});
|
45
|
+
});
|
46
|
+
|
47
|
+
/** @returns {SMSound} */
|
48
|
+
function createMockSound() {
|
49
|
+
return {
|
50
|
+
load: sinon.stub(),
|
51
|
+
play: sinon.stub(),
|
52
|
+
destruct: sinon.stub(),
|
53
|
+
};
|
54
|
+
}
|
55
|
+
|
56
|
+
/** @return {PageChunk} */
|
57
|
+
function dummyPageChunk() {
|
58
|
+
return new PageChunk(0, 0, 'Once upon a time', []);
|
59
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
import PageChunk from '../../../src/plugins/tts/PageChunk.js';
|
2
|
+
|
3
|
+
describe('_fixChunkRects', () => {
|
4
|
+
const { _fixChunkRects } = PageChunk;
|
5
|
+
|
6
|
+
test('Handles empty array', () => {
|
7
|
+
const rects = [];
|
8
|
+
expect(_fixChunkRects(rects)).toBe(rects);
|
9
|
+
expect(rects).toEqual([]);
|
10
|
+
});
|
11
|
+
|
12
|
+
test('Does not modify normal values values normal', () => {
|
13
|
+
const rects = [[100,100,500,80], [200,200,500,180], [300,300,500,280]];
|
14
|
+
expect(_fixChunkRects(rects)).toBe(rects);
|
15
|
+
expect(rects).toEqual([[100,100,500,80], [200,200,500,180], [300,300,500,280]]);
|
16
|
+
});
|
17
|
+
|
18
|
+
test('Fixes outlier values in first rect', () => {
|
19
|
+
const rects = [[100,2300,2300,0], [200,200,200,180], [300,300,200,280]];
|
20
|
+
expect(_fixChunkRects(rects)).toBe(rects);
|
21
|
+
expect(rects).toEqual([[100,2300,200,2280], [200,200,200,180], [300,300,200,280]]);
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
describe('_fromTextWrapperResponse', () => {
|
26
|
+
const { _fromTextWrapperResponse } = PageChunk;
|
27
|
+
|
28
|
+
test('Handles empty array', () => {
|
29
|
+
expect(_fromTextWrapperResponse(0, [])).toEqual([]);
|
30
|
+
});
|
31
|
+
|
32
|
+
test('Basic test', () => {
|
33
|
+
const chunks = _fromTextWrapperResponse(0, [['Line', [0,100,100,0]]]);
|
34
|
+
expect(chunks).toEqual([new PageChunk(0, 0, 'Line', [[0,100,100,0]])]);
|
35
|
+
});
|
36
|
+
});
|
37
|
+
|
38
|
+
describe('_removeDanglingHyphens', () => {
|
39
|
+
const { _removeDanglingHyphens } = PageChunk;
|
40
|
+
|
41
|
+
test('No change to empty string', () => {
|
42
|
+
expect(_removeDanglingHyphens('')).toEqual('');
|
43
|
+
});
|
44
|
+
|
45
|
+
test('No change when no hyphens string', () => {
|
46
|
+
expect(_removeDanglingHyphens('Hello world!')).toEqual('Hello world!');
|
47
|
+
});
|
48
|
+
|
49
|
+
test('No change to hyphens mid-word', () => {
|
50
|
+
expect(_removeDanglingHyphens('It is mid-word!')).toEqual('It is mid-word!');
|
51
|
+
});
|
52
|
+
|
53
|
+
test('Removes hyphens followed by spaces', () => {
|
54
|
+
expect(_removeDanglingHyphens('It is not mid- word!')).toEqual('It is not midword!');
|
55
|
+
expect(_removeDanglingHyphens('mid- word fid- word')).toEqual('midword fidword');
|
56
|
+
});
|
57
|
+
});
|
@@ -0,0 +1,179 @@
|
|
1
|
+
import sinon from 'sinon';
|
2
|
+
import PageChunkIterator from "../../../src/plugins/tts/PageChunkIterator";
|
3
|
+
import PageChunk from '../../../src/plugins/tts/PageChunk';
|
4
|
+
|
5
|
+
describe('Buffers pages', () => {
|
6
|
+
test('Does not error if no room for reverse buffer', async () => {
|
7
|
+
const iterator = new PageChunkIterator(100, 0, {pageBufferSize: 5});
|
8
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
9
|
+
await iterator.next();
|
10
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(6);
|
11
|
+
expect(Object.keys(iterator._bufferedPages).length).toBe(6);
|
12
|
+
});
|
13
|
+
|
14
|
+
test('Does not error if no room for forward buffer', async () => {
|
15
|
+
const iterator = new PageChunkIterator(100, 99, {pageBufferSize: 5});
|
16
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
17
|
+
await iterator.next();
|
18
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(6);
|
19
|
+
expect(Object.keys(iterator._bufferedPages).length).toBe(6);
|
20
|
+
});
|
21
|
+
|
22
|
+
test('Fewer pages than buffer size', async () => {
|
23
|
+
const iterator = new PageChunkIterator(1, 0, {pageBufferSize: 5});
|
24
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
25
|
+
await iterator.next();
|
26
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(1);
|
27
|
+
expect(Object.keys(iterator._bufferedPages).length).toBe(1);
|
28
|
+
});
|
29
|
+
|
30
|
+
test('Buffers data before/after', async () => {
|
31
|
+
const iterator = new PageChunkIterator(100, 50, {pageBufferSize: 5});
|
32
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
33
|
+
await iterator.next();
|
34
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(11);
|
35
|
+
expect(Object.keys(iterator._bufferedPages).length).toBe(11);
|
36
|
+
});
|
37
|
+
|
38
|
+
test('Buffer size does not grow indefinitely', async () => {
|
39
|
+
const iterator = new PageChunkIterator(100, 50, {pageBufferSize: 5});
|
40
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
41
|
+
for (let i = 0; i < 5; i++) await iterator.next();
|
42
|
+
expect(Object.keys(iterator._bufferedPages).length).toBe(11);
|
43
|
+
});
|
44
|
+
|
45
|
+
test('Does not make a new request if buffered', async () => {
|
46
|
+
const iterator = new PageChunkIterator(10, 7, {pageBufferSize: 5});
|
47
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
48
|
+
|
49
|
+
await iterator.next();
|
50
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(8);
|
51
|
+
|
52
|
+
await iterator.next();
|
53
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(8);
|
54
|
+
});
|
55
|
+
});
|
56
|
+
|
57
|
+
describe('Iterates pages', () => {
|
58
|
+
test('Moves between chunks before moving between pages', async () => {
|
59
|
+
const iterator = new PageChunkIterator(100, 50, {pageBufferSize: 5});
|
60
|
+
const chunks = [
|
61
|
+
dummyPageChunk(),
|
62
|
+
dummyPageChunk(),
|
63
|
+
dummyPageChunk(),
|
64
|
+
];
|
65
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves(chunks);
|
66
|
+
|
67
|
+
for (let i = 0; i < 4; i++) {
|
68
|
+
expect(iterator._cursor.page).toBe(50);
|
69
|
+
expect(iterator._cursor.chunk).toBe(i);
|
70
|
+
await iterator.next();
|
71
|
+
}
|
72
|
+
|
73
|
+
expect(iterator._cursor.page).toBe(51);
|
74
|
+
expect(iterator._cursor.chunk).toBe(1);
|
75
|
+
});
|
76
|
+
|
77
|
+
test('Fires AT_END when done', async () => {
|
78
|
+
const iterator = new PageChunkIterator(1, 0, {pageBufferSize: 5});
|
79
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
80
|
+
|
81
|
+
expect((await iterator.next()) instanceof PageChunk).toBe(true);
|
82
|
+
expect(await iterator.next()).toBe(PageChunkIterator.AT_END);
|
83
|
+
});
|
84
|
+
|
85
|
+
test('Fires AT_END reaching past end', async () => {
|
86
|
+
const iterator = new PageChunkIterator(1, 0, {pageBufferSize: 5});
|
87
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([dummyPageChunk()]);
|
88
|
+
|
89
|
+
expect((await iterator.next()) instanceof PageChunk).toBe(true);
|
90
|
+
expect(iterator._cursor.page).toBe(0);
|
91
|
+
expect(iterator._cursor.chunk).toBe(1);
|
92
|
+
|
93
|
+
for (let i = 0; i < 4; i++) {
|
94
|
+
expect(await iterator.next()).toBe(PageChunkIterator.AT_END);
|
95
|
+
expect(iterator._cursor.page).toBe(1);
|
96
|
+
expect(iterator._cursor.chunk).toBe(0);
|
97
|
+
}
|
98
|
+
});
|
99
|
+
|
100
|
+
test('Moves backwards between chunks/pages', async () => {
|
101
|
+
const iterator = new PageChunkIterator(100, 50, {pageBufferSize: 5});
|
102
|
+
const chunks = [
|
103
|
+
dummyPageChunk(),
|
104
|
+
dummyPageChunk(),
|
105
|
+
dummyPageChunk(),
|
106
|
+
];
|
107
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves(chunks);
|
108
|
+
expect(iterator._cursor.page).toBe(50);
|
109
|
+
expect(iterator._cursor.chunk).toBe(0);
|
110
|
+
|
111
|
+
for (let i = 2; i >= 0; i--) {
|
112
|
+
await iterator.decrement();
|
113
|
+
expect(iterator._cursor.page).toBe(49);
|
114
|
+
expect(iterator._cursor.chunk).toBe(i);
|
115
|
+
}
|
116
|
+
|
117
|
+
await iterator.decrement();
|
118
|
+
expect(iterator._cursor.page).toBe(48);
|
119
|
+
expect(iterator._cursor.chunk).toBe(2);
|
120
|
+
});
|
121
|
+
|
122
|
+
test('Moving backwards at start refires start chunk', async () => {
|
123
|
+
const iterator = new PageChunkIterator(100, 0, {pageBufferSize: 5});
|
124
|
+
const chunks = [
|
125
|
+
dummyPageChunk(),
|
126
|
+
dummyPageChunk(),
|
127
|
+
dummyPageChunk(),
|
128
|
+
];
|
129
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves(chunks);
|
130
|
+
|
131
|
+
for (let i = 0; i < 4; i++) {
|
132
|
+
expect(iterator._cursor.page).toBe(0);
|
133
|
+
expect(iterator._cursor.chunk).toBe(0);
|
134
|
+
await iterator.decrement();
|
135
|
+
}
|
136
|
+
});
|
137
|
+
|
138
|
+
test('Empty book', async () => {
|
139
|
+
const iterator = new PageChunkIterator(100, 0, {pageBufferSize: 5});
|
140
|
+
sinon.stub(iterator, '_fetchPageChunksDirect').resolves([]);
|
141
|
+
const result = await iterator.next();
|
142
|
+
expect(iterator._fetchPageChunksDirect.callCount).toBe(100);
|
143
|
+
expect(result).toBe(PageChunkIterator.AT_END);
|
144
|
+
});
|
145
|
+
|
146
|
+
test('Moving forward through empty pages', async () => {
|
147
|
+
const iterator = new PageChunkIterator(10, 0, {pageBufferSize: 5});
|
148
|
+
const nonEmptyPages = {
|
149
|
+
0: [dummyPageChunk()],
|
150
|
+
5: [dummyPageChunk()],
|
151
|
+
};
|
152
|
+
sinon.stub(iterator, '_fetchPageChunksDirect')
|
153
|
+
.callsFake(async i => nonEmptyPages[i] || []);
|
154
|
+
|
155
|
+
const result0 = await iterator.next();
|
156
|
+
expect(result0).toBe(nonEmptyPages[0][0]);
|
157
|
+
|
158
|
+
const result1 = await iterator.next();
|
159
|
+
expect(result1).toBe(nonEmptyPages[5][0]);
|
160
|
+
});
|
161
|
+
|
162
|
+
test('Moving backward through empty pages', async () => {
|
163
|
+
const iterator = new PageChunkIterator(10, 5, {pageBufferSize: 5});
|
164
|
+
const nonEmptyPages = {
|
165
|
+
0: [dummyPageChunk()],
|
166
|
+
5: [dummyPageChunk()],
|
167
|
+
};
|
168
|
+
sinon.stub(iterator, '_fetchPageChunksDirect')
|
169
|
+
.callsFake(async i => nonEmptyPages[i] || []);
|
170
|
+
|
171
|
+
await iterator.decrement();
|
172
|
+
const result0 = await iterator.next();
|
173
|
+
expect(result0).toBe(nonEmptyPages[0][0]);
|
174
|
+
});
|
175
|
+
});
|
176
|
+
|
177
|
+
function dummyPageChunk() {
|
178
|
+
return new PageChunk(0, 0, "Line", []);
|
179
|
+
}
|
@@ -0,0 +1,126 @@
|
|
1
|
+
import sinon from 'sinon';
|
2
|
+
import { WebTTSSound } from '../../../src/plugins/tts/WebTTSEngine.js';
|
3
|
+
import { afterEventLoop, eventTargetMixin } from '../../utils.js';
|
4
|
+
|
5
|
+
beforeEach(() => {
|
6
|
+
window.speechSynthesis = {
|
7
|
+
cancel: sinon.stub(),
|
8
|
+
speak: sinon.stub(),
|
9
|
+
pause: sinon.stub(),
|
10
|
+
resume: sinon.stub(),
|
11
|
+
...eventTargetMixin(),
|
12
|
+
};
|
13
|
+
window.SpeechSynthesisUtterance = function (text) {
|
14
|
+
this.text = text;
|
15
|
+
Object.assign(this, eventTargetMixin());
|
16
|
+
};
|
17
|
+
});
|
18
|
+
|
19
|
+
afterEach(() => {
|
20
|
+
delete window.speechSynthesis;
|
21
|
+
delete window.SpeechSynthesisUtterance;
|
22
|
+
});
|
23
|
+
|
24
|
+
|
25
|
+
describe('WebTTSSound', () => {
|
26
|
+
describe('setPlaybackRate', () => {
|
27
|
+
test('works if not playing', () => {
|
28
|
+
const sound = new WebTTSSound('hello world');
|
29
|
+
sound.load();
|
30
|
+
expect(sound.rate).toBe(1);
|
31
|
+
sound.setPlaybackRate(2);
|
32
|
+
expect(sound.rate).toBe(2);
|
33
|
+
});
|
34
|
+
|
35
|
+
test('reloading does not resolve the original promise', async () => {
|
36
|
+
const sound = new WebTTSSound('hello world');
|
37
|
+
sound.load();
|
38
|
+
const finishSpy = sinon.spy();
|
39
|
+
sound.play().then(finishSpy);
|
40
|
+
sound.reload();
|
41
|
+
sound.utterance.dispatchEvent('pause', {});
|
42
|
+
await afterEventLoop();
|
43
|
+
sound.utterance.dispatchEvent('end', {});
|
44
|
+
await afterEventLoop();
|
45
|
+
expect(finishSpy.callCount).toBe(0);
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
describe('_chromePausingBugFix', () => {
|
50
|
+
test('if speech less than 15s, nothing special', async () => {
|
51
|
+
const clock = sinon.useFakeTimers();
|
52
|
+
const sound = new WebTTSSound('hello world foo bar');
|
53
|
+
sound.load();
|
54
|
+
sound.play();
|
55
|
+
sound._chromePausingBugFix();
|
56
|
+
clock.tick(10000);
|
57
|
+
sound.utterance.dispatchEvent('end', {});
|
58
|
+
clock.restore();
|
59
|
+
await afterEventLoop();
|
60
|
+
expect(speechSynthesis.pause.callCount).toBe(0);
|
61
|
+
});
|
62
|
+
|
63
|
+
test('if speech greater than 15s, pause called', async () => {
|
64
|
+
const clock = sinon.useFakeTimers();
|
65
|
+
const sound = new WebTTSSound('foo bah');
|
66
|
+
sound.load();
|
67
|
+
sound.play();
|
68
|
+
sound._chromePausingBugFix();
|
69
|
+
clock.tick(20000);
|
70
|
+
clock.restore();
|
71
|
+
|
72
|
+
await afterEventLoop();
|
73
|
+
expect(speechSynthesis.pause.callCount).toBe(1);
|
74
|
+
});
|
75
|
+
|
76
|
+
test('on pause reloads if timed out', async () => {
|
77
|
+
const clock = sinon.useFakeTimers();
|
78
|
+
const sound = new WebTTSSound('foo bah');
|
79
|
+
sound.load();
|
80
|
+
sound.play();
|
81
|
+
sound._chromePausingBugFix();
|
82
|
+
sound.pause();
|
83
|
+
clock.tick(2000);
|
84
|
+
clock.restore();
|
85
|
+
|
86
|
+
await afterEventLoop();
|
87
|
+
expect(speechSynthesis.cancel.callCount).toBe(1);
|
88
|
+
});
|
89
|
+
});
|
90
|
+
|
91
|
+
test('fire pause if browser does not do it', async () => {
|
92
|
+
const clock = sinon.useFakeTimers();
|
93
|
+
const languageGetter = jest.spyOn(window.navigator, 'userAgent', 'get');
|
94
|
+
languageGetter.mockReturnValue('firefox android');
|
95
|
+
const sound = new WebTTSSound('foo bah');
|
96
|
+
sound.load();
|
97
|
+
sound.play();
|
98
|
+
const dispatchSpy = sinon.spy(sound.utterance, 'dispatchEvent');
|
99
|
+
sound.pause();
|
100
|
+
clock.tick(1000);
|
101
|
+
clock.restore();
|
102
|
+
|
103
|
+
await afterEventLoop();
|
104
|
+
expect(dispatchSpy.callCount).toBe(1);
|
105
|
+
expect(dispatchSpy.args[0][0].type).toBe('pause');
|
106
|
+
});
|
107
|
+
|
108
|
+
test('fire resume if browser does not do it', async () => {
|
109
|
+
const clock = sinon.useFakeTimers();
|
110
|
+
const languageGetter = jest.spyOn(window.navigator, 'userAgent', 'get');
|
111
|
+
languageGetter.mockReturnValue('firefox android');
|
112
|
+
const sound = new WebTTSSound('foo bah');
|
113
|
+
sound.load();
|
114
|
+
sound.play();
|
115
|
+
sound.started = true;
|
116
|
+
sound.paused = true;
|
117
|
+
const dispatchSpy = sinon.spy(sound.utterance, 'dispatchEvent');
|
118
|
+
sound.resume();
|
119
|
+
clock.tick(1000);
|
120
|
+
clock.restore();
|
121
|
+
|
122
|
+
await afterEventLoop();
|
123
|
+
expect(dispatchSpy.callCount).toBe(1);
|
124
|
+
expect(dispatchSpy.args[0][0].type).toBe('resume');
|
125
|
+
});
|
126
|
+
});
|