@internetarchive/bookreader 5.0.0-3 → 5.0.0-30-d
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 +17 -5
- package/.github/dependabot.yml +8 -0
- package/.github/workflows/node.js.yml +10 -1
- package/.husky/_/husky.sh +30 -0
- package/.testcaferc.js +10 -0
- package/BookReader/BookReader.css +75 -323
- package/BookReader/BookReader.js +32261 -2
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +15235 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/icons/close-circle-dark.svg +1 -0
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/jquery-1.10.1.js +108 -2
- package/BookReader/plugins/plugin.archive_analytics.js +170 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +163 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +333 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +72 -1
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.mobile_nav.js +332 -1
- package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
- package/BookReader/plugins/plugin.resume.js +241 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1261 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +839 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +9115 -2
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +811 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +326 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/webcomponents-bundle.js +412 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/BookReaderDemo/BookReaderDemo.css +14 -1
- package/BookReaderDemo/IADemoBr.js +107 -0
- package/BookReaderDemo/demo-advanced.html +1 -1
- package/BookReaderDemo/demo-autoplay.html +1 -0
- package/BookReaderDemo/demo-embed-iframe-src.html +1 -0
- package/BookReaderDemo/demo-fullscreen-mobile.html +1 -0
- package/BookReaderDemo/demo-fullscreen.html +1 -0
- package/BookReaderDemo/demo-iiif.html +1 -0
- package/BookReaderDemo/demo-internetarchive.html +66 -18
- package/BookReaderDemo/demo-multiple.html +1 -0
- package/BookReaderDemo/demo-preview-pages.html +1 -0
- package/BookReaderDemo/demo-simple.html +1 -0
- package/BookReaderDemo/demo-vendor-fullscreen.html +1 -0
- package/BookReaderDemo/immersion-1up.html +1 -0
- package/BookReaderDemo/immersion-mode.html +1 -0
- package/BookReaderDemo/toggle_controls.html +1 -0
- package/BookReaderDemo/view_mode.html +1 -0
- package/BookReaderDemo/viewmode-cycle.html +1 -2
- package/CHANGELOG.md +114 -0
- package/babel.config.js +18 -0
- package/index.html +3 -0
- package/jsconfig.json +19 -0
- package/package.json +45 -27
- package/src/BookNavigator/assets/button-base.js +8 -1
- package/src/BookNavigator/assets/ia-logo.js +17 -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/book-navigator.js +528 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
- package/src/BookNavigator/bookmarks/bookmark-edit.js +2 -1
- package/src/BookNavigator/bookmarks/bookmarks-list.js +1 -0
- package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +32 -11
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +88 -43
- package/src/BookNavigator/downloads/downloads-provider.js +22 -16
- package/src/BookNavigator/downloads/downloads.js +16 -23
- package/src/BookNavigator/search/a-search-result.js +1 -0
- package/src/BookNavigator/search/search-provider.js +54 -20
- package/src/BookNavigator/search/search-results.js +7 -18
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +10 -12
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +1 -0
- package/src/BookNavigator/volumes/volumes-provider.js +114 -0
- package/src/BookNavigator/volumes/volumes.js +189 -0
- package/src/BookReader/DebugConsole.js +3 -3
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/Mode1Up.js +50 -351
- package/src/BookReader/Mode1UpLit.js +434 -0
- package/src/BookReader/Mode2Up.js +94 -72
- package/src/BookReader/ModeSmoothZoom.js +177 -0
- package/src/BookReader/ModeThumb.js +16 -8
- package/src/BookReader/Navbar/Navbar.js +2 -31
- package/src/BookReader/PageContainer.js +47 -2
- package/src/BookReader/ReduceSet.js +1 -1
- package/src/BookReader/Toolbar/Toolbar.js +5 -5
- package/src/BookReader/options.js +10 -0
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils.js +68 -13
- package/src/BookReader.js +316 -232
- package/src/assets/icons/close-circle-dark.svg +1 -0
- package/src/assets/icons/voice.svg +1 -0
- package/src/css/BookReader.scss +0 -12
- package/src/css/_BRComponent.scss +1 -1
- package/src/css/_BRmain.scss +19 -24
- package/src/css/_BRnav.scss +4 -26
- package/src/css/_BRpages.scss +35 -0
- package/src/css/_BRsearch.scss +11 -215
- package/src/css/_TextSelection.scss +1 -17
- package/src/css/_controls.scss +16 -3
- package/src/css/_icons.scss +6 -0
- package/src/ia-bookreader/ia-bookreader.js +206 -0
- package/src/plugins/plugin.chapters.js +15 -18
- package/src/plugins/plugin.mobile_nav.js +11 -10
- package/src/plugins/plugin.resume.js +3 -3
- package/src/plugins/plugin.text_selection.js +17 -29
- package/src/plugins/plugin.vendor-fullscreen.js +4 -4
- package/src/plugins/search/plugin.search.js +113 -104
- package/src/plugins/search/view.js +48 -163
- package/src/plugins/tts/AbstractTTSEngine.js +7 -0
- package/src/plugins/tts/FestivalTTSEngine.js +2 -2
- package/src/plugins/tts/WebTTSEngine.js +5 -0
- package/src/plugins/tts/plugin.tts.js +67 -102
- package/src/plugins/url/UrlPlugin.js +184 -0
- package/src/plugins/url/plugin.url.js +220 -0
- package/{src → stat}/BookNavigator/BookModel.js +0 -0
- package/{src → stat}/BookNavigator/BookNavigator.js +151 -104
- package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
- package/stat/BookNavigator/assets/button-base.js +61 -0
- package/stat/BookNavigator/assets/ia-logo.js +17 -0
- package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
- package/stat/BookNavigator/assets/icon_close.js +3 -0
- package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
- package/stat/BookNavigator/assets/icon_volumes.js +11 -0
- package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
- package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
- package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
- package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
- package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
- package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
- package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
- package/stat/BookNavigator/delete-modal-actions.js +49 -0
- package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
- package/stat/BookNavigator/downloads/downloads.js +139 -0
- package/stat/BookNavigator/provider-config.js +0 -0
- package/stat/BookNavigator/search/a-search-result.js +55 -0
- package/stat/BookNavigator/search/search-provider.js +180 -0
- package/stat/BookNavigator/search/search-results.js +360 -0
- package/stat/BookNavigator/sharing.js +31 -0
- package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
- package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
- package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
- package/stat/BookNavigator/volumes/volumes.js +178 -0
- package/stat/BookReader/BookModel.js +518 -0
- package/stat/BookReader/DebugConsole.js +54 -0
- package/stat/BookReader/DragScrollable.js +233 -0
- package/stat/BookReader/ImageCache.js +116 -0
- package/stat/BookReader/Mode1Up.js +102 -0
- package/stat/BookReader/Mode1UpLit.js +434 -0
- package/stat/BookReader/Mode2Up.js +1372 -0
- package/stat/BookReader/ModeSmoothZoom.js +177 -0
- package/stat/BookReader/ModeThumb.js +344 -0
- package/stat/BookReader/Navbar/Navbar.js +310 -0
- package/stat/BookReader/PageContainer.js +120 -0
- package/stat/BookReader/ReduceSet.js +26 -0
- package/stat/BookReader/Toolbar/Toolbar.js +384 -0
- package/stat/BookReader/events.js +20 -0
- package/stat/BookReader/options.js +324 -0
- package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/stat/BookReader/utils/classes.js +36 -0
- package/stat/BookReader/utils.js +240 -0
- package/stat/BookReader.js +2550 -0
- package/{src → stat}/BookReaderComponent/BookReaderComponent.js +16 -11
- package/stat/assets/icons/1up.svg +12 -0
- package/stat/assets/icons/2up.svg +15 -0
- package/stat/assets/icons/advance.svg +26 -0
- package/stat/assets/icons/chevron-right.svg +1 -0
- package/stat/assets/icons/close-circle-dark.svg +1 -0
- package/stat/assets/icons/close-circle.svg +1 -0
- package/stat/assets/icons/fullscreen.svg +17 -0
- package/stat/assets/icons/fullscreen_exit.svg +17 -0
- package/stat/assets/icons/hamburger.svg +15 -0
- package/stat/assets/icons/left-arrow.svg +12 -0
- package/stat/assets/icons/magnify-minus.svg +16 -0
- package/stat/assets/icons/magnify-plus.svg +17 -0
- package/stat/assets/icons/magnify.svg +15 -0
- package/stat/assets/icons/pause.svg +23 -0
- package/stat/assets/icons/play.svg +22 -0
- package/stat/assets/icons/playback-speed.svg +34 -0
- package/stat/assets/icons/read-aloud.svg +22 -0
- package/stat/assets/icons/review.svg +22 -0
- package/stat/assets/icons/thumbnails.svg +17 -0
- package/stat/assets/icons/voice.svg +1 -0
- package/stat/assets/icons/volume-full.svg +22 -0
- package/stat/assets/images/BRicons.png +0 -0
- package/stat/assets/images/BRicons.svg +94 -0
- package/stat/assets/images/BRicons_ia.png +0 -0
- package/stat/assets/images/back_pages.png +0 -0
- package/stat/assets/images/book_bottom_icon.png +0 -0
- package/stat/assets/images/book_down_icon.png +0 -0
- package/stat/assets/images/book_left_icon.png +0 -0
- package/stat/assets/images/book_leftmost_icon.png +0 -0
- package/stat/assets/images/book_right_icon.png +0 -0
- package/stat/assets/images/book_rightmost_icon.png +0 -0
- package/stat/assets/images/book_top_icon.png +0 -0
- package/stat/assets/images/book_up_icon.png +0 -0
- package/stat/assets/images/books_graphic.svg +177 -0
- package/stat/assets/images/booksplit.png +0 -0
- package/stat/assets/images/control_pause_icon.png +0 -0
- package/stat/assets/images/control_play_icon.png +0 -0
- package/stat/assets/images/embed_icon.png +0 -0
- package/stat/assets/images/icon-home-ia.png +0 -0
- package/stat/assets/images/icon_OL-logo-xs.png +0 -0
- package/stat/assets/images/icon_alert-xs.png +0 -0
- package/stat/assets/images/icon_book.svg +12 -0
- package/stat/assets/images/icon_bookmark.svg +12 -0
- package/stat/assets/images/icon_close-pop.png +0 -0
- package/stat/assets/images/icon_download.png +0 -0
- package/stat/assets/images/icon_gear.svg +14 -0
- package/stat/assets/images/icon_hamburger.svg +20 -0
- package/stat/assets/images/icon_home.png +0 -0
- package/stat/assets/images/icon_home.svg +21 -0
- package/stat/assets/images/icon_home_ia.png +0 -0
- package/stat/assets/images/icon_indicator.png +0 -0
- package/stat/assets/images/icon_info.svg +11 -0
- package/stat/assets/images/icon_one_page.svg +8 -0
- package/stat/assets/images/icon_pause.svg +1 -0
- package/stat/assets/images/icon_play.svg +1 -0
- package/stat/assets/images/icon_playback-rate.svg +15 -0
- package/stat/assets/images/icon_return.png +0 -0
- package/stat/assets/images/icon_search_button.svg +8 -0
- package/stat/assets/images/icon_share.svg +9 -0
- package/stat/assets/images/icon_skip-ahead.svg +6 -0
- package/stat/assets/images/icon_skip-back.svg +13 -0
- package/stat/assets/images/icon_speaker.svg +18 -0
- package/stat/assets/images/icon_speaker_open.svg +10 -0
- package/stat/assets/images/icon_thumbnails.svg +12 -0
- package/stat/assets/images/icon_toc.svg +5 -0
- package/stat/assets/images/icon_two_pages.svg +9 -0
- package/stat/assets/images/icon_zoomer.png +0 -0
- package/stat/assets/images/loading.gif +0 -0
- package/stat/assets/images/logo_icon.png +0 -0
- package/stat/assets/images/marker_chap-off.png +0 -0
- package/stat/assets/images/marker_chap-off.svg +11 -0
- package/stat/assets/images/marker_chap-off_ia.png +0 -0
- package/stat/assets/images/marker_chap-on.png +0 -0
- package/stat/assets/images/marker_chap-on.svg +11 -0
- package/stat/assets/images/marker_srch-on.svg +11 -0
- package/stat/assets/images/marker_srchchap-off.png +0 -0
- package/stat/assets/images/marker_srchchap-on.png +0 -0
- package/stat/assets/images/nav_control-dn.png +0 -0
- package/stat/assets/images/nav_control-dn_ia.png +0 -0
- package/stat/assets/images/nav_control-up.png +0 -0
- package/stat/assets/images/nav_control-up_ia.png +0 -0
- package/stat/assets/images/nav_control.png +0 -0
- package/stat/assets/images/one_page_mode_icon.png +0 -0
- package/stat/assets/images/paper-badge.png +0 -0
- package/stat/assets/images/print_icon.png +0 -0
- package/stat/assets/images/progressbar.gif +0 -0
- package/stat/assets/images/right_edges.png +0 -0
- package/stat/assets/images/slider.png +0 -0
- package/stat/assets/images/slider_ia.png +0 -0
- package/stat/assets/images/thumbnail_mode_icon.png +0 -0
- package/stat/assets/images/transparent.png +0 -0
- package/stat/assets/images/two_page_mode_icon.png +0 -0
- package/stat/assets/images/zoom_in_icon.png +0 -0
- package/stat/assets/images/zoom_out_icon.png +0 -0
- package/stat/css/BookReader.scss +89 -0
- package/stat/css/_BRBookmarks.scss +29 -0
- package/stat/css/_BRComponent.scss +13 -0
- package/stat/css/_BRfloat.scss +197 -0
- package/stat/css/_BRicon.scss +48 -0
- package/stat/css/_BRmain.scss +251 -0
- package/stat/css/_BRnav.scss +359 -0
- package/stat/css/_BRpages.scss +139 -0
- package/stat/css/_BRsearch.scss +226 -0
- package/stat/css/_BRtoolbar.scss +84 -0
- package/stat/css/_BRvendor.scss +5 -0
- package/stat/css/_MobileNav.scss +194 -0
- package/stat/css/_TextSelection.scss +32 -0
- package/stat/css/_colorbox.scss +52 -0
- package/stat/css/_controls.scss +253 -0
- package/stat/css/_icons.scss +121 -0
- package/stat/jquery-wrapper.js +4 -0
- package/stat/plugins/plugin.archive_analytics.js +86 -0
- package/stat/plugins/plugin.autoplay.js +129 -0
- package/stat/plugins/plugin.chapters.js +248 -0
- package/stat/plugins/plugin.iframe.js +48 -0
- package/stat/plugins/plugin.mobile_nav.js +288 -0
- package/stat/plugins/plugin.resume.js +68 -0
- package/stat/plugins/plugin.text_selection.js +291 -0
- package/{src → stat}/plugins/plugin.url.js +4 -4
- package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
- package/stat/plugins/search/plugin.search.js +439 -0
- package/stat/plugins/search/view.js +439 -0
- package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
- package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
- package/stat/plugins/tts/PageChunk.js +107 -0
- package/stat/plugins/tts/PageChunkIterator.js +163 -0
- package/stat/plugins/tts/WebTTSEngine.js +357 -0
- package/stat/plugins/tts/plugin.tts.js +357 -0
- package/stat/plugins/tts/tooltip_dict.js +15 -0
- package/stat/plugins/tts/utils.js +91 -0
- package/stat/util/browserSniffing.js +30 -0
- package/stat/util/debouncer.js +26 -0
- package/stat/util/docCookies.js +67 -0
- package/stat/util/strings.js +34 -0
- package/tests/e2e/README.md +37 -0
- package/tests/e2e/autoplay.test.js +2 -2
- package/tests/e2e/base.test.js +5 -7
- package/tests/e2e/helpers/base.js +8 -3
- package/tests/e2e/helpers/debug.js +1 -1
- package/tests/e2e/helpers/desktopSearch.js +1 -1
- package/tests/e2e/helpers/mobileSearch.js +3 -3
- package/tests/e2e/helpers/params.js +17 -0
- package/tests/e2e/rightToLeft.test.js +4 -5
- package/tests/e2e/viewmode.test.js +30 -31
- package/tests/{BookReader → jest/BookReader}/BookModel.test.js +3 -3
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
- package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
- package/tests/jest/BookReader/Mode1UpLit.test.js +87 -0
- package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +5 -7
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
- package/tests/jest/BookReader/ModeThumb.test.js +71 -0
- package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
- package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +74 -2
- package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
- package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
- package/tests/jest/BookReader/utils.test.js +136 -0
- package/tests/jest/BookReader.keyboard.test.js +190 -0
- package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
- package/tests/{BookReader.test.js → jest/BookReader.test.js} +20 -4
- package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +8 -8
- package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
- package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
- package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +14 -24
- package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
- package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +12 -5
- package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +6 -6
- package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +3 -3
- package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
- package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
- package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -3
- package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
- package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
- package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
- package/tests/{util → jest/util}/docCookies.test.js +1 -1
- package/tests/{util → jest/util}/strings.test.js +1 -1
- package/tests/{utils.js → jest/utils.js} +38 -0
- package/tests/karma/BookNavigator/book-navigator.test.js +485 -0
- package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
- package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +1 -3
- package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +1 -2
- package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +67 -0
- package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
- package/tests/karma/BookNavigator/search/search-provider.test.js +123 -0
- package/tests/karma/BookNavigator/{search-results.test.js → search/search-results.test.js} +1 -4
- package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +49 -0
- package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -2
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +184 -0
- package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
- package/webpack.config.js +11 -5
- package/.babelrc +0 -12
- package/.dependabot/config.yml +0 -6
- package/.testcaferc.json +0 -5
- package/BookReader/BookReader.js.LICENSE.txt +0 -72
- package/BookReader/bookreader-component-bundle.js +0 -1450
- 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.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.menu_toggle.js +0 -2
- package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -27
- package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/ItemNavigator/ItemNavigator.js +0 -372
- package/src/ItemNavigator/providers/sharing.js +0 -29
- package/src/dragscrollable-br.js +0 -261
- package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
- package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
- package/tests/BookReader/Mode1Up.test.js +0 -164
- package/tests/BookReader/utils.test.js +0 -109
- package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/* global BookReader */
|
|
2
|
+
/**
|
|
3
|
+
* Plugin for Text to Speech in BookReader
|
|
4
|
+
*/
|
|
5
|
+
import FestivalTTSEngine from './FestivalTTSEngine.js';
|
|
6
|
+
import WebTTSEngine from './WebTTSEngine.js';
|
|
7
|
+
import { toISO6391, approximateWordCount } from './utils.js';
|
|
8
|
+
import { en as tooltips } from './tooltip_dict.js';
|
|
9
|
+
import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
|
|
10
|
+
/** @typedef {import('./PageChunk.js').default} PageChunk */
|
|
11
|
+
/** @typedef {import("./AbstractTTSEngine.js").default} AbstractTTSEngine */
|
|
12
|
+
|
|
13
|
+
// Default options for TTS
|
|
14
|
+
jQuery.extend(BookReader.defaultOptions, {
|
|
15
|
+
server: 'ia600609.us.archive.org',
|
|
16
|
+
bookPath: '',
|
|
17
|
+
enableTtsPlugin: true,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Extend the constructor to add TTS properties
|
|
21
|
+
BookReader.prototype.setup = (function (super_) {
|
|
22
|
+
return function (options) {
|
|
23
|
+
super_.call(this, options);
|
|
24
|
+
|
|
25
|
+
if (this.options.enableTtsPlugin) {
|
|
26
|
+
/** @type { {[pageIndex: number]: Array<{ l: number, r: number, t: number, b: number }>} } */
|
|
27
|
+
this._ttsBoxesByIndex = {};
|
|
28
|
+
|
|
29
|
+
let TTSEngine = WebTTSEngine.isSupported() ? WebTTSEngine :
|
|
30
|
+
FestivalTTSEngine.isSupported() ? FestivalTTSEngine :
|
|
31
|
+
null;
|
|
32
|
+
|
|
33
|
+
if (/_forceTTSEngine=(festival|web)/.test(location.toString())) {
|
|
34
|
+
const engineName = location.toString().match(/_forceTTSEngine=(festival|web)/)[1];
|
|
35
|
+
TTSEngine = { festival: FestivalTTSEngine, web: WebTTSEngine }[engineName];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (TTSEngine) {
|
|
39
|
+
/** @type {AbstractTTSEngine} */
|
|
40
|
+
this.ttsEngine = new TTSEngine({
|
|
41
|
+
server: options.server,
|
|
42
|
+
bookPath: options.bookPath,
|
|
43
|
+
bookLanguage: toISO6391(options.bookLanguage),
|
|
44
|
+
onLoadingStart: this.showProgressPopup.bind(this, 'Loading audio...'),
|
|
45
|
+
onLoadingComplete: this.removeProgressPopup.bind(this),
|
|
46
|
+
onDone: this.ttsStop.bind(this),
|
|
47
|
+
beforeChunkPlay: this.ttsBeforeChunkPlay.bind(this),
|
|
48
|
+
afterChunkPlay: this.ttsSendChunkFinishedAnalyticsEvent.bind(this),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
})(BookReader.prototype.setup);
|
|
54
|
+
|
|
55
|
+
BookReader.prototype.init = (function(super_) {
|
|
56
|
+
return function() {
|
|
57
|
+
if (this.options.enableTtsPlugin) {
|
|
58
|
+
// Bind to events
|
|
59
|
+
|
|
60
|
+
this.bind(BookReader.eventNames.PostInit, () => {
|
|
61
|
+
this.$('.BRicon.read').click(() => {
|
|
62
|
+
this.ttsToggle();
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
if (this.ttsEngine) {
|
|
66
|
+
this.ttsEngine.init();
|
|
67
|
+
if (/[?&]_autoReadAloud=show/.test(location.toString())) {
|
|
68
|
+
this.ttsStart(false); // false flag is to initiate read aloud controls
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// This is fired when the hash changes by one of the other plugins!
|
|
74
|
+
// i.e. it will fire every time the page changes -_-
|
|
75
|
+
// this.bind(BookReader.eventNames.stop, function(e, br) {
|
|
76
|
+
// this.ttsStop();
|
|
77
|
+
// }.bind(this));
|
|
78
|
+
}
|
|
79
|
+
super_.call(this);
|
|
80
|
+
};
|
|
81
|
+
})(BookReader.prototype.init);
|
|
82
|
+
|
|
83
|
+
/** @override */
|
|
84
|
+
BookReader.prototype._createPageContainer = (function (super_) {
|
|
85
|
+
return function (index) {
|
|
86
|
+
const pageContainer = super_.call(this, index);
|
|
87
|
+
if (this.options.enableTtsPlugin && pageContainer.page && index in this._ttsBoxesByIndex) {
|
|
88
|
+
const pageIndex = pageContainer.page.index;
|
|
89
|
+
renderBoxesInPageContainerLayer('ttsHiliteLayer', this._ttsBoxesByIndex[pageIndex], pageContainer.page, pageContainer.$container[0]);
|
|
90
|
+
}
|
|
91
|
+
return pageContainer;
|
|
92
|
+
};
|
|
93
|
+
})(BookReader.prototype._createPageContainer);
|
|
94
|
+
|
|
95
|
+
// Extend buildMobileDrawerElement
|
|
96
|
+
BookReader.prototype.buildMobileDrawerElement = (function (super_) {
|
|
97
|
+
return function () {
|
|
98
|
+
const $el = super_.call(this);
|
|
99
|
+
if (this.options.enableTtsPlugin && this.ttsEngine) {
|
|
100
|
+
$el.find('.BRmobileMenu__moreInfoRow').after($(`
|
|
101
|
+
<li>
|
|
102
|
+
<span>
|
|
103
|
+
<span class="DrawerIconWrapper"><img class="DrawerIcon" src="${this.imagesBaseURL}icon_speaker_open.svg" alt="info-speaker"/></span>
|
|
104
|
+
Read Aloud
|
|
105
|
+
</span>
|
|
106
|
+
<div>
|
|
107
|
+
<span class="larger">Press to toggle read aloud</span>
|
|
108
|
+
<br/>
|
|
109
|
+
<button class="BRicon read"></button>
|
|
110
|
+
</div>
|
|
111
|
+
</li>`));
|
|
112
|
+
}
|
|
113
|
+
return $el;
|
|
114
|
+
};
|
|
115
|
+
})(BookReader.prototype.buildMobileDrawerElement);
|
|
116
|
+
|
|
117
|
+
// Extend initNavbar
|
|
118
|
+
BookReader.prototype.initNavbar = (function (super_) {
|
|
119
|
+
return function () {
|
|
120
|
+
const $el = super_.call(this);
|
|
121
|
+
if (this.options.enableTtsPlugin && this.ttsEngine) {
|
|
122
|
+
this.refs.$BRReadAloudToolbar = $(`
|
|
123
|
+
<ul class="read-aloud">
|
|
124
|
+
<li>
|
|
125
|
+
<select class="playback-speed" name="playback-speed" title="${tooltips.playbackSpeed}">
|
|
126
|
+
<option value="0.25">0.25x</option>
|
|
127
|
+
<option value="0.5">0.5x</option>
|
|
128
|
+
<option value="0.75">0.75x</option>
|
|
129
|
+
<option value="1.0" selected>1.0x</option>
|
|
130
|
+
<option value="1.25">1.25x</option>
|
|
131
|
+
<option value="1.5">1.5x</option>
|
|
132
|
+
<option value="1.75">1.75x</option>
|
|
133
|
+
<option value="2">2x</option>
|
|
134
|
+
</select>
|
|
135
|
+
</li>
|
|
136
|
+
<li>
|
|
137
|
+
<button type="button" name="review" title="${tooltips.review}">
|
|
138
|
+
<div class="icon icon-review"></div>
|
|
139
|
+
</button>
|
|
140
|
+
</li>
|
|
141
|
+
<li>
|
|
142
|
+
<button type="button" name="play" title="${tooltips.play}">
|
|
143
|
+
<div class="icon icon-play"></div>
|
|
144
|
+
<div class="icon icon-pause"></div>
|
|
145
|
+
</button>
|
|
146
|
+
</li>
|
|
147
|
+
<li>
|
|
148
|
+
<button type="button" name="advance" title="${tooltips.advance}">
|
|
149
|
+
<div class="icon icon-advance"></div>
|
|
150
|
+
</button>
|
|
151
|
+
</li>
|
|
152
|
+
<li>
|
|
153
|
+
<select class="playback-voices" name="playback-voice" style="display: none" title="Change read aloud voices">
|
|
154
|
+
</select>
|
|
155
|
+
</li>
|
|
156
|
+
</ul>
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
$el.find('.BRcontrols').prepend(this.refs.$BRReadAloudToolbar);
|
|
160
|
+
|
|
161
|
+
const renderVoicesMenu = (voicesMenu) => {
|
|
162
|
+
voicesMenu.empty();
|
|
163
|
+
if (this.ttsEngine.getVoices().length > 1) {
|
|
164
|
+
voicesMenu.append(this.ttsEngine.getVoices().map(
|
|
165
|
+
voice =>
|
|
166
|
+
$(`<option value="${voice.voiceURI}">${voice.lang} - ${voice.name}</option>`)));
|
|
167
|
+
voicesMenu.val(this.ttsEngine.voice.voiceURI);
|
|
168
|
+
voicesMenu.show();
|
|
169
|
+
} else {
|
|
170
|
+
voicesMenu.hide();
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const voicesMenu = this.refs.$BRReadAloudToolbar.find('[name=playback-voice]');
|
|
174
|
+
renderVoicesMenu(voicesMenu);
|
|
175
|
+
voicesMenu.on("change", ev => this.ttsEngine.setVoice(voicesMenu.val()));
|
|
176
|
+
this.ttsEngine.events.on('pause resume start', () => this.ttsUpdateState());
|
|
177
|
+
this.ttsEngine.events.on('voiceschanged', () => renderVoicesMenu(voicesMenu));
|
|
178
|
+
this.refs.$BRReadAloudToolbar.find('[name=play]').on("click", this.ttsPlayPause.bind(this));
|
|
179
|
+
this.refs.$BRReadAloudToolbar.find('[name=advance]').on("click", this.ttsJumpForward.bind(this));
|
|
180
|
+
this.refs.$BRReadAloudToolbar.find('[name=review]').on("click", this.ttsJumpBackward.bind(this));
|
|
181
|
+
const $rateSelector = this.refs.$BRReadAloudToolbar.find('select[name="playback-speed"]');
|
|
182
|
+
$rateSelector.on("change", ev => this.ttsEngine.setPlaybackRate(parseFloat($rateSelector.val())));
|
|
183
|
+
$(`<li>
|
|
184
|
+
<button class="BRicon read js-tooltip" title="${tooltips.readAloud}">
|
|
185
|
+
<div class="icon icon-read-aloud"></div>
|
|
186
|
+
<span class="tooltip">${tooltips.readAloud}</span>
|
|
187
|
+
</button>
|
|
188
|
+
</li>`).insertBefore($el.find('.BRcontrols .BRicon.zoom_out').closest('li'));
|
|
189
|
+
}
|
|
190
|
+
return $el;
|
|
191
|
+
};
|
|
192
|
+
})(BookReader.prototype.initNavbar);
|
|
193
|
+
|
|
194
|
+
// ttsToggle()
|
|
195
|
+
//______________________________________________________________________________
|
|
196
|
+
BookReader.prototype.ttsToggle = function () {
|
|
197
|
+
if (this.autoStop) this.autoStop();
|
|
198
|
+
if (this.ttsEngine.playing) {
|
|
199
|
+
this.ttsStop();
|
|
200
|
+
} else {
|
|
201
|
+
this.ttsStart();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
// ttsStart(
|
|
206
|
+
//______________________________________________________________________________
|
|
207
|
+
BookReader.prototype.ttsStart = function (startTTSEngine = true) {
|
|
208
|
+
if (this.constModeThumb == this.mode)
|
|
209
|
+
this.switchMode(this.constMode1up);
|
|
210
|
+
|
|
211
|
+
this.refs.$BRReadAloudToolbar.addClass('visible');
|
|
212
|
+
this.$('.BRicon.read').addClass('unread active');
|
|
213
|
+
this.ttsSendAnalyticsEvent('Start');
|
|
214
|
+
if (startTTSEngine)
|
|
215
|
+
this.ttsEngine.start(this.currentIndex(), this.getNumLeafs());
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
BookReader.prototype.ttsJumpForward = function () {
|
|
219
|
+
if (this.ttsEngine.paused) {
|
|
220
|
+
this.ttsEngine.resume();
|
|
221
|
+
}
|
|
222
|
+
this.ttsEngine.jumpForward();
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
BookReader.prototype.ttsJumpBackward = function () {
|
|
226
|
+
if (this.ttsEngine.paused) {
|
|
227
|
+
this.ttsEngine.resume();
|
|
228
|
+
}
|
|
229
|
+
this.ttsEngine.jumpBackward();
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
BookReader.prototype.ttsUpdateState = function() {
|
|
233
|
+
const isPlaying = !(this.ttsEngine.paused || !this.ttsEngine.playing);
|
|
234
|
+
this.$('.read-aloud [name=play]').toggleClass('playing', isPlaying);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
BookReader.prototype.ttsPlayPause = function() {
|
|
238
|
+
if (!this.ttsEngine.playing) {
|
|
239
|
+
this.ttsToggle();
|
|
240
|
+
} else {
|
|
241
|
+
this.ttsEngine.togglePlayPause();
|
|
242
|
+
this.ttsUpdateState(this.ttsEngine.paused);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// ttsStop()
|
|
247
|
+
//______________________________________________________________________________
|
|
248
|
+
BookReader.prototype.ttsStop = function () {
|
|
249
|
+
this.refs.$BRReadAloudToolbar.removeClass('visible');
|
|
250
|
+
this.$('.BRicon.read').removeClass('unread active');
|
|
251
|
+
this.ttsSendAnalyticsEvent('Stop');
|
|
252
|
+
this.ttsEngine.stop();
|
|
253
|
+
this.ttsRemoveHilites();
|
|
254
|
+
this.removeProgressPopup();
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* @param {PageChunk} chunk
|
|
259
|
+
* @return {PromiseLike<void>} returns once the flip is done
|
|
260
|
+
*/
|
|
261
|
+
BookReader.prototype.ttsBeforeChunkPlay = async function(chunk) {
|
|
262
|
+
await this.ttsMaybeFlipToIndex(chunk.leafIndex);
|
|
263
|
+
this.ttsHighlightChunk(chunk);
|
|
264
|
+
// This appears not to work; ttsMaybeFlipToIndex causes a scroll to the top of
|
|
265
|
+
// the active page :/ Disabling cause the extra scroll just adds an odd jitter.
|
|
266
|
+
// this.ttsScrollToChunk(chunk);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* @param {PageChunk} chunk
|
|
271
|
+
*/
|
|
272
|
+
BookReader.prototype.ttsSendChunkFinishedAnalyticsEvent = function(chunk) {
|
|
273
|
+
this.ttsSendAnalyticsEvent('ChunkFinished-Words', approximateWordCount(chunk.text));
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Flip the page if the provided leaf index is not visible
|
|
278
|
+
* @param {Number} leafIndex
|
|
279
|
+
* @return {PromiseLike<void>} resolves once the flip animation has completed
|
|
280
|
+
*/
|
|
281
|
+
BookReader.prototype.ttsMaybeFlipToIndex = function (leafIndex) {
|
|
282
|
+
const in2PageMode = this.constMode2up == this.mode;
|
|
283
|
+
let resolve = null;
|
|
284
|
+
const promise = new Promise(res => resolve = res);
|
|
285
|
+
|
|
286
|
+
if (!in2PageMode) {
|
|
287
|
+
this.jumpToIndex(leafIndex);
|
|
288
|
+
resolve();
|
|
289
|
+
} else {
|
|
290
|
+
const leafVisible = leafIndex == this.twoPage.currentIndexR || leafIndex == this.twoPage.currentIndexL;
|
|
291
|
+
if (leafVisible) {
|
|
292
|
+
resolve();
|
|
293
|
+
} else {
|
|
294
|
+
this.animationFinishedCallback = resolve;
|
|
295
|
+
const mustGoNext = leafIndex > Math.max(this.twoPage.currentIndexR, this.twoPage.currentIndexL);
|
|
296
|
+
if (mustGoNext) this.next();
|
|
297
|
+
else this.prev();
|
|
298
|
+
promise.then(this.ttsMaybeFlipToIndex.bind(this, leafIndex));
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return promise;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* @param {PageChunk} chunk
|
|
307
|
+
*/
|
|
308
|
+
BookReader.prototype.ttsHighlightChunk = function(chunk) {
|
|
309
|
+
// The poorly-named variable leafIndex
|
|
310
|
+
const pageIndex = chunk.leafIndex;
|
|
311
|
+
|
|
312
|
+
this.ttsRemoveHilites();
|
|
313
|
+
|
|
314
|
+
// group by index; currently only possible to have chunks on one page :/
|
|
315
|
+
this._ttsBoxesByIndex = {
|
|
316
|
+
[pageIndex]: chunk.lineRects.map(([l, b, r, t]) => ({l, r, b, t}))
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// update any already created pages
|
|
320
|
+
for (const [pageIndexString, boxes] of Object.entries(this._ttsBoxesByIndex)) {
|
|
321
|
+
const pageIndex = parseFloat(pageIndexString);
|
|
322
|
+
const page = this._models.book.getPage(pageIndex);
|
|
323
|
+
const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
|
|
324
|
+
pageContainers.forEach(container => renderBoxesInPageContainerLayer('ttsHiliteLayer', boxes, page, container));
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* @param {PageChunk} chunk
|
|
330
|
+
*/
|
|
331
|
+
BookReader.prototype.ttsScrollToChunk = function(chunk) {
|
|
332
|
+
if (this.constMode1up != this.mode) return;
|
|
333
|
+
|
|
334
|
+
$(`.pagediv${chunk.leafIndex} .ttsHiliteLayer rect`)[0]?.scrollIntoView();
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// ttsRemoveHilites()
|
|
338
|
+
//______________________________________________________________________________
|
|
339
|
+
BookReader.prototype.ttsRemoveHilites = function () {
|
|
340
|
+
$(this.getActivePageContainerElements()).find('.ttsHiliteLayer').remove();
|
|
341
|
+
this._ttsBoxesByIndex = {};
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* @private
|
|
346
|
+
* Send an analytics event with an optional value. Also attaches the book's language.
|
|
347
|
+
* @param {string} action
|
|
348
|
+
* @param {number} [value]
|
|
349
|
+
*/
|
|
350
|
+
BookReader.prototype.ttsSendAnalyticsEvent = function(action, value) {
|
|
351
|
+
if (this.archiveAnalyticsSendEvent) {
|
|
352
|
+
const extraValues = {};
|
|
353
|
+
const mediaLanguage = this.ttsEngine.opts.bookLanguage;
|
|
354
|
+
if (mediaLanguage) extraValues.mediaLanguage = mediaLanguage;
|
|
355
|
+
this.archiveAnalyticsSendEvent('BRReadAloud', action, value, extraValues);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const en = {
|
|
2
|
+
advance: 'Advance 10 seconds',
|
|
3
|
+
play: 'Play',
|
|
4
|
+
playbackSpeed: 'Playback speed',
|
|
5
|
+
readAloud: 'Read this book aloud',
|
|
6
|
+
review: 'Review 10 seconds',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const es = {
|
|
10
|
+
advance: 'Avance 10 segundos',
|
|
11
|
+
play: 'Jugar',
|
|
12
|
+
playbackSpeed: 'Velocidad de reproducción',
|
|
13
|
+
readAloud: 'Lee este libro en voz alta',
|
|
14
|
+
review: 'Revisar 10 segundos',
|
|
15
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import langs from 'iso-language-codes/js/data.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert a EventTarget style event into a promise
|
|
5
|
+
* @param {EventTarget} target
|
|
6
|
+
* @param {string} eventType
|
|
7
|
+
* @return {Promise<Event>}
|
|
8
|
+
*/
|
|
9
|
+
export function promisifyEvent(target, eventType) {
|
|
10
|
+
return new Promise(res => {
|
|
11
|
+
const resolver = ev => {
|
|
12
|
+
target.removeEventListener(eventType, resolver);
|
|
13
|
+
res(ev);
|
|
14
|
+
};
|
|
15
|
+
target.addEventListener(eventType, resolver);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Use regex to approximate word count in a string
|
|
21
|
+
* @param {string} text
|
|
22
|
+
* @return {number}
|
|
23
|
+
*/
|
|
24
|
+
export function approximateWordCount(text) {
|
|
25
|
+
const m = text.match(/\S+/g);
|
|
26
|
+
return m ? m.length : 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Waits the provided number of ms and then resolves with a promise
|
|
31
|
+
* @param {number} ms
|
|
32
|
+
* @return {Promise}
|
|
33
|
+
*/
|
|
34
|
+
export function sleep(ms) {
|
|
35
|
+
return new Promise(res => setTimeout(res, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Checks whether the current browser is on android
|
|
40
|
+
* @param {string} [userAgent]
|
|
41
|
+
* @return {boolean}
|
|
42
|
+
*/
|
|
43
|
+
export function isAndroid(userAgent = navigator.userAgent) {
|
|
44
|
+
return /android/i.test(userAgent);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @typedef {string} ISO6391
|
|
49
|
+
* Language code in ISO 639-1 format. e.g. en, fr, zh
|
|
50
|
+
**/
|
|
51
|
+
|
|
52
|
+
/** Each lang is an array, with each index mapping to a different property */
|
|
53
|
+
const COLUMN_TO_LANG_INDEX = {
|
|
54
|
+
'Name': 0,
|
|
55
|
+
'Endonym': 1,
|
|
56
|
+
'ISO 639-1': 2,
|
|
57
|
+
'ISO 639-2/T': 3,
|
|
58
|
+
'ISO 639-2/B': 4
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @param {string} language in some format
|
|
63
|
+
* @return {ISO6391?}
|
|
64
|
+
*/
|
|
65
|
+
export function toISO6391(language) {
|
|
66
|
+
if (!language) return null;
|
|
67
|
+
language = language.toLowerCase();
|
|
68
|
+
|
|
69
|
+
return searchForISO6391(language, ['ISO 639-1']) ||
|
|
70
|
+
searchForISO6391(language, ['ISO 639-2/B']) ||
|
|
71
|
+
searchForISO6391(language, ['ISO 639-2/T', 'Endonym', 'Name']);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Searches for the given long in the given columns.
|
|
76
|
+
* @param {string} language
|
|
77
|
+
* @param {Array<keyof COLUMN_TO_LANG_INDEX>} columnsToSearch
|
|
78
|
+
* @return {ISO6391?}
|
|
79
|
+
*/
|
|
80
|
+
function searchForISO6391(language, columnsToSearch) {
|
|
81
|
+
for (let i = 0; i < langs.length; i++) {
|
|
82
|
+
for (let colI = 0; colI < columnsToSearch.length; colI++) {
|
|
83
|
+
const column = columnsToSearch[colI];
|
|
84
|
+
const columnValue = langs[i][COLUMN_TO_LANG_INDEX[column]];
|
|
85
|
+
if (columnValue.split(', ').map(x => x.toLowerCase()).indexOf(language) != -1) {
|
|
86
|
+
return langs[i][COLUMN_TO_LANG_INDEX['ISO 639-1']];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether the current browser is a Chrome/Chromium browser
|
|
4
|
+
* Code from https://stackoverflow.com/a/4565120/2317712
|
|
5
|
+
* @param {string} [userAgent]
|
|
6
|
+
* @param {string} [vendor]
|
|
7
|
+
* @return {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function isChrome(userAgent = navigator.userAgent, vendor = navigator.vendor) {
|
|
10
|
+
return /chrome/i.test(userAgent) && /google inc/i.test(vendor);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Checks whether the current browser is firefox
|
|
15
|
+
* @param {string} [userAgent]
|
|
16
|
+
* @return {boolean}
|
|
17
|
+
*/
|
|
18
|
+
export function isFirefox(userAgent = navigator.userAgent) {
|
|
19
|
+
return /firefox/i.test(userAgent);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Checks whether the current browser is safari
|
|
24
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#Browser_Name
|
|
25
|
+
* @param {string} [userAgent]
|
|
26
|
+
* @return {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export function isSafari(userAgent = navigator.userAgent) {
|
|
29
|
+
return /safari/i.test(userAgent) && !/chrome|chromium/i.test(userAgent);
|
|
30
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wait until some time has passed before executing a callback.
|
|
3
|
+
*
|
|
4
|
+
* @param {Function} callback
|
|
5
|
+
* @param {Number} threshhold - in milliseconds
|
|
6
|
+
* @param {*} context - will be bound to callback as its "this" value
|
|
7
|
+
*/
|
|
8
|
+
class Debouncer {
|
|
9
|
+
constructor(callback, threshhold = 250, context = undefined) {
|
|
10
|
+
this.callback = callback;
|
|
11
|
+
this.threshhold = threshhold;
|
|
12
|
+
this.context = context;
|
|
13
|
+
this.deferTimeout = undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
execute() {
|
|
17
|
+
clearTimeout(this.deferTimeout);
|
|
18
|
+
this.deferTimeout = setTimeout(this.executeCallback.bind(this), this.threshhold);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
executeCallback() {
|
|
22
|
+
this.callback.apply(this.context);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { Debouncer as default };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper module use to get, set and remove item from cookie
|
|
3
|
+
*
|
|
4
|
+
* See more:
|
|
5
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|
|
6
|
+
* https://developer.mozilla.org/User:fusionchess
|
|
7
|
+
* https://github.com/madmurphy/cookies.js
|
|
8
|
+
* This framework is released under the GNU Public License, version 3 or later.
|
|
9
|
+
* http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get specific key's value stored in cookie
|
|
14
|
+
*
|
|
15
|
+
* @param {string} sKey
|
|
16
|
+
*
|
|
17
|
+
* @returns {string|null}
|
|
18
|
+
*/
|
|
19
|
+
export function getItem(sKey) {
|
|
20
|
+
if (!sKey) return null;
|
|
21
|
+
|
|
22
|
+
return decodeURIComponent(
|
|
23
|
+
// eslint-disable-next-line no-useless-escape
|
|
24
|
+
document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Set specific key's value in cookie
|
|
29
|
+
*
|
|
30
|
+
* @param {string} sKey cookie name
|
|
31
|
+
* @param {string} sValue cookie value
|
|
32
|
+
* @param {string} [vEnd] expire|max-age
|
|
33
|
+
* @param {string} [sPath] path of current item
|
|
34
|
+
* @param {string} [sDomain] domain name
|
|
35
|
+
* @param {boolean} [bSecure]
|
|
36
|
+
*
|
|
37
|
+
* @returns {true}
|
|
38
|
+
*/
|
|
39
|
+
export function setItem(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
|
|
40
|
+
document.cookie = encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue)
|
|
41
|
+
+ (vEnd ? `; expires=${vEnd.toUTCString()}` : '')
|
|
42
|
+
+ (sDomain ? `; domain=${sDomain}` : '')
|
|
43
|
+
+ (sPath ? `; path=${sPath}` : '')
|
|
44
|
+
+ (bSecure ? `; secure` : '');
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* BROKEN Remove specific key's value from cookie
|
|
51
|
+
* @fixme hasItem isn't even implemented! This will always error.
|
|
52
|
+
* @param {string} sKey cookie name
|
|
53
|
+
* @param {string} [sPath] path of current item
|
|
54
|
+
* @param {string} [sDomain]
|
|
55
|
+
*
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
export function removeItem(sKey, sPath, sDomain) {
|
|
59
|
+
// eslint-disable-next-line
|
|
60
|
+
if (!hasItem(sKey)) return false;
|
|
61
|
+
|
|
62
|
+
document.cookie = encodeURIComponent(sKey) + `=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
|
|
63
|
+
+ (sDomain ? `; domain=${sDomain}` : '')
|
|
64
|
+
+ (sPath ? `; path=${sPath}` : '');
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {String} StringWithVars
|
|
3
|
+
* A template string with {{foo}} style variables
|
|
4
|
+
* Also supports filters, like {{bookPath|urlencode}} (See APPLY_FILTERS for the
|
|
5
|
+
* supported list of filters)
|
|
6
|
+
**/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {StringWithVars|String} template
|
|
10
|
+
* @param { {[varName: string]: { toString: () => string} } } vars
|
|
11
|
+
* @param { {[varName: string]: { toString: () => string} } } [overrides]
|
|
12
|
+
*/
|
|
13
|
+
export function applyVariables(template, vars, overrides = {}, possibleFilters = APPLY_FILTERS) {
|
|
14
|
+
return template?.replace(/\{\{([^}]*?)\}\}/g, ($0, $1) => {
|
|
15
|
+
if (!$1) return $0;
|
|
16
|
+
/** @type {string} */
|
|
17
|
+
const expression = $1;
|
|
18
|
+
const [varName, ...filterNames] = expression.split('|').map(x => x.trim());
|
|
19
|
+
const defined = varName in overrides || varName in vars;
|
|
20
|
+
|
|
21
|
+
// If it's not defined, don't expand it at all
|
|
22
|
+
if (!defined) return $0;
|
|
23
|
+
|
|
24
|
+
const value = varName in overrides ? overrides[varName]
|
|
25
|
+
: varName in vars ? vars[varName] : null;
|
|
26
|
+
const filters = filterNames.map(n => possibleFilters[n]);
|
|
27
|
+
return filters.reduce((acc, cur) => cur(acc), value && value.toString());
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @type { {[filterName: String]:( string => string)} } */
|
|
32
|
+
export const APPLY_FILTERS = {
|
|
33
|
+
urlencode: encodeURIComponent,
|
|
34
|
+
};
|
package/tests/e2e/README.md
CHANGED
|
@@ -48,6 +48,43 @@ To run a particular fixture, add the path to the file at the end of your argumen
|
|
|
48
48
|
|
|
49
49
|
`npm run test:e2e chrome tests/e2e/example.test.js`
|
|
50
50
|
|
|
51
|
+
### Testing netlify or archive.org
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
BASE_URL='https://lucid-poitras-9a1249.netlify.app' npx testcafe
|
|
55
|
+
BASE_URL='https://archive.org' npx testcafe
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Testing any OCAID
|
|
59
|
+
|
|
60
|
+
For OCAIDs you should pick the specific test file to run, since things like autoplay tests won't work. The main tests are in `base.test.js`.
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
OCAIDS='goody,goodytwoshoes00newyiala' npx testcafe tests/e2e/base.test.js
|
|
64
|
+
OCAIDS='goody,goodytwoshoes00newyiala' BASE_URL='https://archive.org' npx testcafe tests/e2e/base.test.js
|
|
65
|
+
|
|
66
|
+
# right to left book; note this also runs the base tests
|
|
67
|
+
OCAIDS='gendaitankashu00meijuoft' BASE_URL='https://archive.org' npx testcafe tests/e2e/rightToLeft.test.js
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Running tests with browserstack
|
|
71
|
+
|
|
72
|
+
Note these can only test a public url, so you either need to create a draft PR and use the netlify link, or use ngrok to publish your dev server's port.
|
|
73
|
+
|
|
74
|
+
Note: Windows users, there is a bug that prevents spaces in the browser field when using `npx`, so you'll need to have `testcafe` globally installed, and run it without `npx`. (See https://github.com/DevExpress/testcafe/issues/6600 ). Or, you can add browserstack browsers the `.testcaferc.js` file.
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
# Set auth; find yours at https://www.browserstack.com/accounts/settings
|
|
78
|
+
export BROWSERSTACK_USERNAME="YOUR_USERNAME"
|
|
79
|
+
export BROWSERSTACK_ACCESS_KEY="YOUR_ACCESS_KEY"
|
|
80
|
+
|
|
81
|
+
BASE_URL='https://archive.org' OCAIDS='goody,goodytwoshoes00newyiala' npx testcafe 'browserstack:iPad Pro 12.9 2018@15' tests/e2e/base.test.js
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
See a list of available browsers with `npx testcafe -b browserstack`. Note there are some browsers which appear to not work for some reason (eg `browserstack:iPad Mini 4@9.3`).
|
|
85
|
+
|
|
86
|
+
Read more about other options/etc at the browserstack docs: https://www.browserstack.com/docs/automate/selenium/getting-started/nodejs/testcafe .
|
|
87
|
+
|
|
51
88
|
## Pending (skip) tests
|
|
52
89
|
|
|
53
90
|
You can skip any tests by calling the method `.skip` on the test object rather
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ClientFunction } from 'testcafe';
|
|
2
|
+
import params from './helpers/params';
|
|
2
3
|
|
|
3
|
-
const { BASE_URL } = process.env;
|
|
4
4
|
const getLocationHref = ClientFunction(() => window.location.href.toString());
|
|
5
5
|
const FLIP_SPEED = 1000;
|
|
6
6
|
const FIRST_PAGE_DELAY = 2000;
|
|
7
7
|
|
|
8
|
-
fixture `Autoplay plugin`.page `${
|
|
8
|
+
fixture `Autoplay plugin`.page `${params.baseUrl}/BookReaderDemo/demo-autoplay.html`;
|
|
9
9
|
|
|
10
10
|
test('page auto-advances after allotted flip speed and delay', async t => {
|
|
11
11
|
await t.wait(2 * FLIP_SPEED + FIRST_PAGE_DELAY);
|