@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.
Files changed (391) hide show
  1. package/.eslintrc.js +17 -5
  2. package/.github/dependabot.yml +8 -0
  3. package/.github/workflows/node.js.yml +10 -1
  4. package/.husky/_/husky.sh +30 -0
  5. package/.testcaferc.js +10 -0
  6. package/BookReader/BookReader.css +75 -323
  7. package/BookReader/BookReader.js +32261 -2
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +15235 -0
  10. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  11. package/BookReader/icons/close-circle-dark.svg +1 -0
  12. package/BookReader/icons/voice.svg +1 -0
  13. package/BookReader/jquery-1.10.1.js +108 -2
  14. package/BookReader/plugins/plugin.archive_analytics.js +170 -1
  15. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  16. package/BookReader/plugins/plugin.autoplay.js +163 -1
  17. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  18. package/BookReader/plugins/plugin.chapters.js +333 -1
  19. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  20. package/BookReader/plugins/plugin.iframe.js +72 -1
  21. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  22. package/BookReader/plugins/plugin.mobile_nav.js +332 -1
  23. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  24. package/BookReader/plugins/plugin.resume.js +241 -1
  25. package/BookReader/plugins/plugin.resume.js.map +1 -1
  26. package/BookReader/plugins/plugin.search.js +1261 -1
  27. package/BookReader/plugins/plugin.search.js.map +1 -1
  28. package/BookReader/plugins/plugin.text_selection.js +839 -1
  29. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  30. package/BookReader/plugins/plugin.tts.js +9115 -2
  31. package/BookReader/plugins/plugin.tts.js.map +1 -1
  32. package/BookReader/plugins/plugin.url.js +811 -1
  33. package/BookReader/plugins/plugin.url.js.map +1 -1
  34. package/BookReader/plugins/plugin.vendor-fullscreen.js +326 -1
  35. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  36. package/BookReader/webcomponents-bundle.js +412 -0
  37. package/BookReader/webcomponents-bundle.js.map +1 -0
  38. package/BookReaderDemo/BookReaderDemo.css +14 -1
  39. package/BookReaderDemo/IADemoBr.js +107 -0
  40. package/BookReaderDemo/demo-advanced.html +1 -1
  41. package/BookReaderDemo/demo-autoplay.html +1 -0
  42. package/BookReaderDemo/demo-embed-iframe-src.html +1 -0
  43. package/BookReaderDemo/demo-fullscreen-mobile.html +1 -0
  44. package/BookReaderDemo/demo-fullscreen.html +1 -0
  45. package/BookReaderDemo/demo-iiif.html +1 -0
  46. package/BookReaderDemo/demo-internetarchive.html +66 -18
  47. package/BookReaderDemo/demo-multiple.html +1 -0
  48. package/BookReaderDemo/demo-preview-pages.html +1 -0
  49. package/BookReaderDemo/demo-simple.html +1 -0
  50. package/BookReaderDemo/demo-vendor-fullscreen.html +1 -0
  51. package/BookReaderDemo/immersion-1up.html +1 -0
  52. package/BookReaderDemo/immersion-mode.html +1 -0
  53. package/BookReaderDemo/toggle_controls.html +1 -0
  54. package/BookReaderDemo/view_mode.html +1 -0
  55. package/BookReaderDemo/viewmode-cycle.html +1 -2
  56. package/CHANGELOG.md +114 -0
  57. package/babel.config.js +18 -0
  58. package/index.html +3 -0
  59. package/jsconfig.json +19 -0
  60. package/package.json +45 -27
  61. package/src/BookNavigator/assets/button-base.js +8 -1
  62. package/src/BookNavigator/assets/ia-logo.js +17 -0
  63. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  64. package/src/BookNavigator/assets/icon_sort_desc.js +5 -0
  65. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  66. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  67. package/src/BookNavigator/book-navigator.js +528 -0
  68. package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
  69. package/src/BookNavigator/bookmarks/bookmark-edit.js +2 -1
  70. package/src/BookNavigator/bookmarks/bookmarks-list.js +1 -0
  71. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
  72. package/src/BookNavigator/bookmarks/bookmarks-provider.js +32 -11
  73. package/src/BookNavigator/bookmarks/ia-bookmarks.js +88 -43
  74. package/src/BookNavigator/downloads/downloads-provider.js +22 -16
  75. package/src/BookNavigator/downloads/downloads.js +16 -23
  76. package/src/BookNavigator/search/a-search-result.js +1 -0
  77. package/src/BookNavigator/search/search-provider.js +54 -20
  78. package/src/BookNavigator/search/search-results.js +7 -18
  79. package/src/BookNavigator/sharing.js +27 -0
  80. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +10 -12
  81. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +1 -0
  82. package/src/BookNavigator/volumes/volumes-provider.js +114 -0
  83. package/src/BookNavigator/volumes/volumes.js +189 -0
  84. package/src/BookReader/DebugConsole.js +3 -3
  85. package/src/BookReader/DragScrollable.js +233 -0
  86. package/src/BookReader/Mode1Up.js +50 -351
  87. package/src/BookReader/Mode1UpLit.js +434 -0
  88. package/src/BookReader/Mode2Up.js +94 -72
  89. package/src/BookReader/ModeSmoothZoom.js +177 -0
  90. package/src/BookReader/ModeThumb.js +16 -8
  91. package/src/BookReader/Navbar/Navbar.js +2 -31
  92. package/src/BookReader/PageContainer.js +47 -2
  93. package/src/BookReader/ReduceSet.js +1 -1
  94. package/src/BookReader/Toolbar/Toolbar.js +5 -5
  95. package/src/BookReader/options.js +10 -0
  96. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  97. package/src/BookReader/utils.js +68 -13
  98. package/src/BookReader.js +316 -232
  99. package/src/assets/icons/close-circle-dark.svg +1 -0
  100. package/src/assets/icons/voice.svg +1 -0
  101. package/src/css/BookReader.scss +0 -12
  102. package/src/css/_BRComponent.scss +1 -1
  103. package/src/css/_BRmain.scss +19 -24
  104. package/src/css/_BRnav.scss +4 -26
  105. package/src/css/_BRpages.scss +35 -0
  106. package/src/css/_BRsearch.scss +11 -215
  107. package/src/css/_TextSelection.scss +1 -17
  108. package/src/css/_controls.scss +16 -3
  109. package/src/css/_icons.scss +6 -0
  110. package/src/ia-bookreader/ia-bookreader.js +206 -0
  111. package/src/plugins/plugin.chapters.js +15 -18
  112. package/src/plugins/plugin.mobile_nav.js +11 -10
  113. package/src/plugins/plugin.resume.js +3 -3
  114. package/src/plugins/plugin.text_selection.js +17 -29
  115. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  116. package/src/plugins/search/plugin.search.js +113 -104
  117. package/src/plugins/search/view.js +48 -163
  118. package/src/plugins/tts/AbstractTTSEngine.js +7 -0
  119. package/src/plugins/tts/FestivalTTSEngine.js +2 -2
  120. package/src/plugins/tts/WebTTSEngine.js +5 -0
  121. package/src/plugins/tts/plugin.tts.js +67 -102
  122. package/src/plugins/url/UrlPlugin.js +184 -0
  123. package/src/plugins/url/plugin.url.js +220 -0
  124. package/{src → stat}/BookNavigator/BookModel.js +0 -0
  125. package/{src → stat}/BookNavigator/BookNavigator.js +151 -104
  126. package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
  127. package/stat/BookNavigator/assets/button-base.js +61 -0
  128. package/stat/BookNavigator/assets/ia-logo.js +17 -0
  129. package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
  130. package/stat/BookNavigator/assets/icon_close.js +3 -0
  131. package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
  132. package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
  133. package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
  134. package/stat/BookNavigator/assets/icon_volumes.js +11 -0
  135. package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
  136. package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
  137. package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
  138. package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
  139. package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
  140. package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
  141. package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
  142. package/stat/BookNavigator/delete-modal-actions.js +49 -0
  143. package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
  144. package/stat/BookNavigator/downloads/downloads.js +139 -0
  145. package/stat/BookNavigator/provider-config.js +0 -0
  146. package/stat/BookNavigator/search/a-search-result.js +55 -0
  147. package/stat/BookNavigator/search/search-provider.js +180 -0
  148. package/stat/BookNavigator/search/search-results.js +360 -0
  149. package/stat/BookNavigator/sharing.js +31 -0
  150. package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
  151. package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
  152. package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
  153. package/stat/BookNavigator/volumes/volumes.js +178 -0
  154. package/stat/BookReader/BookModel.js +518 -0
  155. package/stat/BookReader/DebugConsole.js +54 -0
  156. package/stat/BookReader/DragScrollable.js +233 -0
  157. package/stat/BookReader/ImageCache.js +116 -0
  158. package/stat/BookReader/Mode1Up.js +102 -0
  159. package/stat/BookReader/Mode1UpLit.js +434 -0
  160. package/stat/BookReader/Mode2Up.js +1372 -0
  161. package/stat/BookReader/ModeSmoothZoom.js +177 -0
  162. package/stat/BookReader/ModeThumb.js +344 -0
  163. package/stat/BookReader/Navbar/Navbar.js +310 -0
  164. package/stat/BookReader/PageContainer.js +120 -0
  165. package/stat/BookReader/ReduceSet.js +26 -0
  166. package/stat/BookReader/Toolbar/Toolbar.js +384 -0
  167. package/stat/BookReader/events.js +20 -0
  168. package/stat/BookReader/options.js +324 -0
  169. package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  170. package/stat/BookReader/utils/classes.js +36 -0
  171. package/stat/BookReader/utils.js +240 -0
  172. package/stat/BookReader.js +2550 -0
  173. package/{src → stat}/BookReaderComponent/BookReaderComponent.js +16 -11
  174. package/stat/assets/icons/1up.svg +12 -0
  175. package/stat/assets/icons/2up.svg +15 -0
  176. package/stat/assets/icons/advance.svg +26 -0
  177. package/stat/assets/icons/chevron-right.svg +1 -0
  178. package/stat/assets/icons/close-circle-dark.svg +1 -0
  179. package/stat/assets/icons/close-circle.svg +1 -0
  180. package/stat/assets/icons/fullscreen.svg +17 -0
  181. package/stat/assets/icons/fullscreen_exit.svg +17 -0
  182. package/stat/assets/icons/hamburger.svg +15 -0
  183. package/stat/assets/icons/left-arrow.svg +12 -0
  184. package/stat/assets/icons/magnify-minus.svg +16 -0
  185. package/stat/assets/icons/magnify-plus.svg +17 -0
  186. package/stat/assets/icons/magnify.svg +15 -0
  187. package/stat/assets/icons/pause.svg +23 -0
  188. package/stat/assets/icons/play.svg +22 -0
  189. package/stat/assets/icons/playback-speed.svg +34 -0
  190. package/stat/assets/icons/read-aloud.svg +22 -0
  191. package/stat/assets/icons/review.svg +22 -0
  192. package/stat/assets/icons/thumbnails.svg +17 -0
  193. package/stat/assets/icons/voice.svg +1 -0
  194. package/stat/assets/icons/volume-full.svg +22 -0
  195. package/stat/assets/images/BRicons.png +0 -0
  196. package/stat/assets/images/BRicons.svg +94 -0
  197. package/stat/assets/images/BRicons_ia.png +0 -0
  198. package/stat/assets/images/back_pages.png +0 -0
  199. package/stat/assets/images/book_bottom_icon.png +0 -0
  200. package/stat/assets/images/book_down_icon.png +0 -0
  201. package/stat/assets/images/book_left_icon.png +0 -0
  202. package/stat/assets/images/book_leftmost_icon.png +0 -0
  203. package/stat/assets/images/book_right_icon.png +0 -0
  204. package/stat/assets/images/book_rightmost_icon.png +0 -0
  205. package/stat/assets/images/book_top_icon.png +0 -0
  206. package/stat/assets/images/book_up_icon.png +0 -0
  207. package/stat/assets/images/books_graphic.svg +177 -0
  208. package/stat/assets/images/booksplit.png +0 -0
  209. package/stat/assets/images/control_pause_icon.png +0 -0
  210. package/stat/assets/images/control_play_icon.png +0 -0
  211. package/stat/assets/images/embed_icon.png +0 -0
  212. package/stat/assets/images/icon-home-ia.png +0 -0
  213. package/stat/assets/images/icon_OL-logo-xs.png +0 -0
  214. package/stat/assets/images/icon_alert-xs.png +0 -0
  215. package/stat/assets/images/icon_book.svg +12 -0
  216. package/stat/assets/images/icon_bookmark.svg +12 -0
  217. package/stat/assets/images/icon_close-pop.png +0 -0
  218. package/stat/assets/images/icon_download.png +0 -0
  219. package/stat/assets/images/icon_gear.svg +14 -0
  220. package/stat/assets/images/icon_hamburger.svg +20 -0
  221. package/stat/assets/images/icon_home.png +0 -0
  222. package/stat/assets/images/icon_home.svg +21 -0
  223. package/stat/assets/images/icon_home_ia.png +0 -0
  224. package/stat/assets/images/icon_indicator.png +0 -0
  225. package/stat/assets/images/icon_info.svg +11 -0
  226. package/stat/assets/images/icon_one_page.svg +8 -0
  227. package/stat/assets/images/icon_pause.svg +1 -0
  228. package/stat/assets/images/icon_play.svg +1 -0
  229. package/stat/assets/images/icon_playback-rate.svg +15 -0
  230. package/stat/assets/images/icon_return.png +0 -0
  231. package/stat/assets/images/icon_search_button.svg +8 -0
  232. package/stat/assets/images/icon_share.svg +9 -0
  233. package/stat/assets/images/icon_skip-ahead.svg +6 -0
  234. package/stat/assets/images/icon_skip-back.svg +13 -0
  235. package/stat/assets/images/icon_speaker.svg +18 -0
  236. package/stat/assets/images/icon_speaker_open.svg +10 -0
  237. package/stat/assets/images/icon_thumbnails.svg +12 -0
  238. package/stat/assets/images/icon_toc.svg +5 -0
  239. package/stat/assets/images/icon_two_pages.svg +9 -0
  240. package/stat/assets/images/icon_zoomer.png +0 -0
  241. package/stat/assets/images/loading.gif +0 -0
  242. package/stat/assets/images/logo_icon.png +0 -0
  243. package/stat/assets/images/marker_chap-off.png +0 -0
  244. package/stat/assets/images/marker_chap-off.svg +11 -0
  245. package/stat/assets/images/marker_chap-off_ia.png +0 -0
  246. package/stat/assets/images/marker_chap-on.png +0 -0
  247. package/stat/assets/images/marker_chap-on.svg +11 -0
  248. package/stat/assets/images/marker_srch-on.svg +11 -0
  249. package/stat/assets/images/marker_srchchap-off.png +0 -0
  250. package/stat/assets/images/marker_srchchap-on.png +0 -0
  251. package/stat/assets/images/nav_control-dn.png +0 -0
  252. package/stat/assets/images/nav_control-dn_ia.png +0 -0
  253. package/stat/assets/images/nav_control-up.png +0 -0
  254. package/stat/assets/images/nav_control-up_ia.png +0 -0
  255. package/stat/assets/images/nav_control.png +0 -0
  256. package/stat/assets/images/one_page_mode_icon.png +0 -0
  257. package/stat/assets/images/paper-badge.png +0 -0
  258. package/stat/assets/images/print_icon.png +0 -0
  259. package/stat/assets/images/progressbar.gif +0 -0
  260. package/stat/assets/images/right_edges.png +0 -0
  261. package/stat/assets/images/slider.png +0 -0
  262. package/stat/assets/images/slider_ia.png +0 -0
  263. package/stat/assets/images/thumbnail_mode_icon.png +0 -0
  264. package/stat/assets/images/transparent.png +0 -0
  265. package/stat/assets/images/two_page_mode_icon.png +0 -0
  266. package/stat/assets/images/zoom_in_icon.png +0 -0
  267. package/stat/assets/images/zoom_out_icon.png +0 -0
  268. package/stat/css/BookReader.scss +89 -0
  269. package/stat/css/_BRBookmarks.scss +29 -0
  270. package/stat/css/_BRComponent.scss +13 -0
  271. package/stat/css/_BRfloat.scss +197 -0
  272. package/stat/css/_BRicon.scss +48 -0
  273. package/stat/css/_BRmain.scss +251 -0
  274. package/stat/css/_BRnav.scss +359 -0
  275. package/stat/css/_BRpages.scss +139 -0
  276. package/stat/css/_BRsearch.scss +226 -0
  277. package/stat/css/_BRtoolbar.scss +84 -0
  278. package/stat/css/_BRvendor.scss +5 -0
  279. package/stat/css/_MobileNav.scss +194 -0
  280. package/stat/css/_TextSelection.scss +32 -0
  281. package/stat/css/_colorbox.scss +52 -0
  282. package/stat/css/_controls.scss +253 -0
  283. package/stat/css/_icons.scss +121 -0
  284. package/stat/jquery-wrapper.js +4 -0
  285. package/stat/plugins/plugin.archive_analytics.js +86 -0
  286. package/stat/plugins/plugin.autoplay.js +129 -0
  287. package/stat/plugins/plugin.chapters.js +248 -0
  288. package/stat/plugins/plugin.iframe.js +48 -0
  289. package/stat/plugins/plugin.mobile_nav.js +288 -0
  290. package/stat/plugins/plugin.resume.js +68 -0
  291. package/stat/plugins/plugin.text_selection.js +291 -0
  292. package/{src → stat}/plugins/plugin.url.js +4 -4
  293. package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
  294. package/stat/plugins/search/plugin.search.js +439 -0
  295. package/stat/plugins/search/view.js +439 -0
  296. package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
  297. package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
  298. package/stat/plugins/tts/PageChunk.js +107 -0
  299. package/stat/plugins/tts/PageChunkIterator.js +163 -0
  300. package/stat/plugins/tts/WebTTSEngine.js +357 -0
  301. package/stat/plugins/tts/plugin.tts.js +357 -0
  302. package/stat/plugins/tts/tooltip_dict.js +15 -0
  303. package/stat/plugins/tts/utils.js +91 -0
  304. package/stat/util/browserSniffing.js +30 -0
  305. package/stat/util/debouncer.js +26 -0
  306. package/stat/util/docCookies.js +67 -0
  307. package/stat/util/strings.js +34 -0
  308. package/tests/e2e/README.md +37 -0
  309. package/tests/e2e/autoplay.test.js +2 -2
  310. package/tests/e2e/base.test.js +5 -7
  311. package/tests/e2e/helpers/base.js +8 -3
  312. package/tests/e2e/helpers/debug.js +1 -1
  313. package/tests/e2e/helpers/desktopSearch.js +1 -1
  314. package/tests/e2e/helpers/mobileSearch.js +3 -3
  315. package/tests/e2e/helpers/params.js +17 -0
  316. package/tests/e2e/rightToLeft.test.js +4 -5
  317. package/tests/e2e/viewmode.test.js +30 -31
  318. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +3 -3
  319. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  320. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  321. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  322. package/tests/jest/BookReader/Mode1UpLit.test.js +87 -0
  323. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +5 -7
  324. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  325. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  326. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  327. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +74 -2
  328. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  329. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  330. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  331. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  332. package/tests/jest/BookReader/utils.test.js +136 -0
  333. package/tests/jest/BookReader.keyboard.test.js +190 -0
  334. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  335. package/tests/{BookReader.test.js → jest/BookReader.test.js} +20 -4
  336. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  337. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +2 -2
  338. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +8 -8
  339. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  340. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  341. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  342. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +14 -24
  343. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  344. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +12 -5
  345. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +6 -6
  346. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +3 -3
  347. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  348. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  349. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  350. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  351. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -3
  352. package/tests/jest/plugins/url/UrlPlugin.test.js +175 -0
  353. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  354. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  355. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  356. package/tests/{util → jest/util}/strings.test.js +1 -1
  357. package/tests/{utils.js → jest/utils.js} +38 -0
  358. package/tests/karma/BookNavigator/book-navigator.test.js +485 -0
  359. package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
  360. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +1 -3
  361. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +1 -2
  362. package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +67 -0
  363. package/tests/karma/BookNavigator/downloads/downloads.test.js +54 -0
  364. package/tests/karma/BookNavigator/search/search-provider.test.js +123 -0
  365. package/tests/karma/BookNavigator/{search-results.test.js → search/search-results.test.js} +1 -4
  366. package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +49 -0
  367. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -2
  368. package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +184 -0
  369. package/tests/karma/BookNavigator/volumes/volumes.test.js +98 -0
  370. package/webpack.config.js +11 -5
  371. package/.babelrc +0 -12
  372. package/.dependabot/config.yml +0 -6
  373. package/.testcaferc.json +0 -5
  374. package/BookReader/BookReader.js.LICENSE.txt +0 -72
  375. package/BookReader/bookreader-component-bundle.js +0 -1450
  376. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  377. package/BookReader/bookreader-component-bundle.js.map +0 -1
  378. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  379. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  380. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  381. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -27
  382. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  383. package/src/BookNavigator/assets/book-loader.js +0 -27
  384. package/src/ItemNavigator/ItemNavigator.js +0 -372
  385. package/src/ItemNavigator/providers/sharing.js +0 -29
  386. package/src/dragscrollable-br.js +0 -261
  387. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  388. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  389. package/tests/BookReader/Mode1Up.test.js +0 -164
  390. package/tests/BookReader/utils.test.js +0 -109
  391. 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
+ };
@@ -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 `${BASE_URL}demo-autoplay.html`;
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);