@internetarchive/bookreader 5.0.0-3 → 5.0.0-30-d

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -23,7 +23,7 @@ export default class FestivalTTSEngine extends AbstractTTSEngine {
23
23
  // $.browsers is sometimes undefined on some Android browsers :/
24
24
  // Likely related to when $.browser was moved to npm
25
25
  /** @type {'mp3' | 'ogg'} format of audio to get */
26
- this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3';
26
+ this.audioFormat = $.browser?.mozilla ? 'ogg' : 'mp3'; //eslint-disable-line no-jquery/no-browser
27
27
  }
28
28
 
29
29
  /** @override */
@@ -107,7 +107,7 @@ class FestivalTTSSound {
107
107
  this.sound = null;
108
108
  this.rate = 1;
109
109
  /** @type {function} calling this resolves the "play" promise */
110
- this._finishResolver = null
110
+ this._finishResolver = null;
111
111
  }
112
112
 
113
113
  get loaded() {
@@ -296,6 +296,11 @@ export class WebTTSSound {
296
296
  this.reload();
297
297
  }
298
298
 
299
+ /** @param {SpeechSynthesisVoice} voice */
300
+ setVoice(voice) {
301
+ this.voice = voice;
302
+ this.reload();
303
+ }
299
304
  /**
300
305
  * @private
301
306
  * Chrome has a bug where it only plays 15 seconds of TTS and then
@@ -6,8 +6,9 @@ import FestivalTTSEngine from './FestivalTTSEngine.js';
6
6
  import WebTTSEngine from './WebTTSEngine.js';
7
7
  import { toISO6391, approximateWordCount } from './utils.js';
8
8
  import { en as tooltips } from './tooltip_dict.js';
9
- /** @typedef {import('./PageChunk.js')} PageChunk */
10
- /** @typedef {import("./AbstractTTSEngine.js")} AbstractTTSEngine */
9
+ import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
10
+ /** @typedef {import('./PageChunk.js').default} PageChunk */
11
+ /** @typedef {import("./AbstractTTSEngine.js").default} AbstractTTSEngine */
11
12
 
12
13
  // Default options for TTS
13
14
  jQuery.extend(BookReader.defaultOptions, {
@@ -22,7 +23,9 @@ BookReader.prototype.setup = (function (super_) {
22
23
  super_.call(this, options);
23
24
 
24
25
  if (this.options.enableTtsPlugin) {
25
- this.ttsHilites = [];
26
+ /** @type { {[pageIndex: number]: Array<{ l: number, r: number, t: number, b: number }>} } */
27
+ this._ttsBoxesByIndex = {};
28
+
26
29
  let TTSEngine = WebTTSEngine.isSupported() ? WebTTSEngine :
27
30
  FestivalTTSEngine.isSupported() ? FestivalTTSEngine :
28
31
  null;
@@ -54,17 +57,6 @@ BookReader.prototype.init = (function(super_) {
54
57
  if (this.options.enableTtsPlugin) {
55
58
  // Bind to events
56
59
 
57
- // TODO move this to BookReader.js or something
58
- this.bind(BookReader.eventNames.fragmentChange, () => {
59
- if (this.mode == this.constMode2up) {
60
- // clear highlights if they're no longer valid for this page
61
- const visibleIndices = [this.twoPage.currentIndexL, this.twoPage.currentIndexR];
62
- const visibleSelector = visibleIndices.map(i => `.BRReadAloudHilite.Leaf-${i}`).join(', ');
63
- $(this.ttsHilites).filter(visibleSelector).show();
64
- $(this.ttsHilites).not(visibleSelector).hide();
65
- }
66
- });
67
-
68
60
  this.bind(BookReader.eventNames.PostInit, () => {
69
61
  this.$('.BRicon.read').click(() => {
70
62
  this.ttsToggle();
@@ -88,6 +80,17 @@ BookReader.prototype.init = (function(super_) {
88
80
  };
89
81
  })(BookReader.prototype.init);
90
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);
91
94
 
92
95
  // Extend buildMobileDrawerElement
93
96
  BookReader.prototype.buildMobileDrawerElement = (function (super_) {
@@ -146,15 +149,37 @@ BookReader.prototype.initNavbar = (function (super_) {
146
149
  <div class="icon icon-advance"></div>
147
150
  </button>
148
151
  </li>
152
+ <li>
153
+ <select class="playback-voices" name="playback-voice" style="display: none" title="Change read aloud voices">
154
+ </select>
155
+ </li>
149
156
  </ul>
150
157
  `);
158
+
151
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()));
152
176
  this.ttsEngine.events.on('pause resume start', () => this.ttsUpdateState());
153
- this.refs.$BRReadAloudToolbar.find('[name=play]').click(this.ttsPlayPause.bind(this));
154
- this.refs.$BRReadAloudToolbar.find('[name=advance]').click(this.ttsJumpForward.bind(this));
155
- this.refs.$BRReadAloudToolbar.find('[name=review]').click(this.ttsJumpBackward.bind(this));
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));
156
181
  const $rateSelector = this.refs.$BRReadAloudToolbar.find('select[name="playback-speed"]');
157
- $rateSelector.change(ev => this.ttsEngine.setPlaybackRate(parseFloat($rateSelector.val())));
182
+ $rateSelector.on("change", ev => this.ttsEngine.setPlaybackRate(parseFloat($rateSelector.val())));
158
183
  $(`<li>
159
184
  <button class="BRicon read js-tooltip" title="${tooltips.readAloud}">
160
185
  <div class="icon icon-read-aloud"></div>
@@ -233,12 +258,12 @@ BookReader.prototype.ttsStop = function () {
233
258
  * @param {PageChunk} chunk
234
259
  * @return {PromiseLike<void>} returns once the flip is done
235
260
  */
236
- BookReader.prototype.ttsBeforeChunkPlay = function(chunk) {
237
- return this.ttsMaybeFlipToIndex(chunk.leafIndex)
238
- .then(() => {
239
- this.ttsHighlightChunk(chunk);
240
- this.ttsScrollToChunk(chunk);
241
- });
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);
242
267
  };
243
268
 
244
269
  /**
@@ -275,18 +300,28 @@ BookReader.prototype.ttsMaybeFlipToIndex = function (leafIndex) {
275
300
  }
276
301
 
277
302
  return promise;
278
- }
303
+ };
279
304
 
280
305
  /**
281
306
  * @param {PageChunk} chunk
282
307
  */
283
308
  BookReader.prototype.ttsHighlightChunk = function(chunk) {
309
+ // The poorly-named variable leafIndex
310
+ const pageIndex = chunk.leafIndex;
311
+
284
312
  this.ttsRemoveHilites();
285
313
 
286
- if (this.constMode2up == this.mode) {
287
- this.ttsHilite2UP(chunk);
288
- } else {
289
- this.ttsHilite1UP(chunk);
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));
290
325
  }
291
326
  };
292
327
 
@@ -296,84 +331,14 @@ BookReader.prototype.ttsHighlightChunk = function(chunk) {
296
331
  BookReader.prototype.ttsScrollToChunk = function(chunk) {
297
332
  if (this.constMode1up != this.mode) return;
298
333
 
299
- let leafTop = 0;
300
- let h;
301
- let i;
302
- for (i = 0; i < chunk.leafIndex; i++) {
303
- h = parseInt(this._getPageHeight(i) / this.reduce);
304
- leafTop += h + this.padding;
305
- }
306
-
307
- const chunkTop = chunk.lineRects[0][3]; //coords are in l,b,r,t order
308
- const chunkBot = chunk.lineRects[chunk.lineRects.length - 1][1];
309
-
310
- const topOfFirstChunk = leafTop + chunkTop / this.reduce;
311
- const botOfLastChunk = leafTop + chunkBot / this.reduce;
312
-
313
- if (window?.soundManager?.debugMode) console.log('leafTop = ' + leafTop + ' topOfFirstChunk = ' + topOfFirstChunk + ' botOfLastChunk = ' + botOfLastChunk);
314
-
315
- const containerTop = this.refs.$brContainer.prop('scrollTop');
316
- const containerBot = containerTop + this.refs.$brContainer.height();
317
- if (window?.soundManager?.debugMode) console.log('containerTop = ' + containerTop + ' containerBot = ' + containerBot);
318
-
319
- if ((topOfFirstChunk < containerTop) || (botOfLastChunk > containerBot)) {
320
- this.refs.$brContainer.stop(true).animate({scrollTop: topOfFirstChunk},'fast');
321
- }
322
- };
323
-
324
- /**
325
- * @param {PageChunk} chunk
326
- */
327
- BookReader.prototype.ttsHilite1UP = function(chunk) {
328
- for (let i = 0; i < chunk.lineRects.length; i++) {
329
- //each rect is an array of l,b,r,t coords (djvu.xml ordering...)
330
- const l = chunk.lineRects[i][0];
331
- const b = chunk.lineRects[i][1];
332
- const r = chunk.lineRects[i][2];
333
- const t = chunk.lineRects[i][3];
334
-
335
- const div = document.createElement('div');
336
- this.ttsHilites.push(div);
337
- $(div).prop('className', 'BookReaderSearchHilite').appendTo(
338
- this.$('.pagediv' + chunk.leafIndex)
339
- );
340
-
341
- $(div).css({
342
- width: (r - l) / this.reduce + 'px',
343
- height: (b - t) / this.reduce + 'px',
344
- left: l / this.reduce + 'px',
345
- top: t / this.reduce + 'px'
346
- });
347
- }
348
-
349
- };
350
-
351
- /**
352
- * @param {PageChunk} chunk
353
- */
354
- BookReader.prototype.ttsHilite2UP = function (chunk) {
355
- for (let i = 0; i < chunk.lineRects.length; i++) {
356
- //each rect is an array of l,b,r,t coords (djvu.xml ordering...)
357
- const l = chunk.lineRects[i][0];
358
- const b = chunk.lineRects[i][1];
359
- const r = chunk.lineRects[i][2];
360
- const t = chunk.lineRects[i][3];
361
-
362
- const div = document.createElement('div');
363
- this.ttsHilites.push(div);
364
- $(div)
365
- .prop('className', 'BookReaderSearchHilite BRReadAloudHilite Leaf-' + chunk.leafIndex)
366
- .css('zIndex', 3)
367
- .appendTo(this.refs.$brTwoPageView);
368
- this.setHilightCss2UP(div, chunk.leafIndex, l, r, t, b);
369
- }
334
+ $(`.pagediv${chunk.leafIndex} .ttsHiliteLayer rect`)[0]?.scrollIntoView();
370
335
  };
371
336
 
372
337
  // ttsRemoveHilites()
373
338
  //______________________________________________________________________________
374
339
  BookReader.prototype.ttsRemoveHilites = function () {
375
- $(this.ttsHilites).remove();
376
- this.ttsHilites = [];
340
+ $(this.getActivePageContainerElements()).find('.ttsHiliteLayer').remove();
341
+ this._ttsBoxesByIndex = {};
377
342
  };
378
343
 
379
344
  /**
@@ -0,0 +1,184 @@
1
+ export class UrlPlugin {
2
+ constructor(options = {}) {
3
+ this.bookReaderOptions = options;
4
+
5
+ // the canonical order of elements is important in the path and query string
6
+ this.urlSchema = [
7
+ { name: 'page', position: 'path', default: 'n0' },
8
+ { name: 'mode', position: 'path', default: '2up' },
9
+ { name: 'search', position: 'path', deprecated_for: 'q' },
10
+ { name: 'q', position: 'query_param' },
11
+ { name: 'sort', position: 'query_param' },
12
+ { name: 'view', position: 'query_param' },
13
+ { name: 'admin', position: 'query_param' },
14
+ ];
15
+
16
+ this.urlState = {};
17
+ this.urlMode = this.bookReaderOptions.urlMode || 'hash';
18
+ this.urlHistoryBasePath = this.bookReaderOptions.urlHistoryBasePath || '/';
19
+ this.urlLocationPollId = null;
20
+ this.oldLocationHash = null;
21
+ this.oldUserHash = null;
22
+ }
23
+
24
+ /**
25
+ * Parse JSON object URL state to string format
26
+ * Arrange path names in an order that it is positioned on the urlSchema
27
+ * @param {Object} urlState
28
+ * @returns {string}
29
+ */
30
+ urlStateToUrlString(urlState) {
31
+ const searchParams = new URLSearchParams();
32
+ const pathParams = {};
33
+
34
+ Object.keys(urlState).forEach(key => {
35
+ let schema = this.urlSchema.find(schema => schema.name === key);
36
+ if (schema?.deprecated_for) {
37
+ schema = this.urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);
38
+ }
39
+ if (schema?.position == 'path') {
40
+ pathParams[schema?.name] = urlState[key];
41
+ } else {
42
+ searchParams.append(schema?.name || key, urlState[key]);
43
+ }
44
+ });
45
+
46
+ const strPathParams = this.urlSchema
47
+ .filter(s => s.position == 'path')
48
+ .map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')
49
+ .join('/');
50
+
51
+ // replace consecutive slashes with a single slash + remove trailing slashes
52
+ const strStrippedTrailingSlash = `${strPathParams.replace(/\/+/g, '/').replace(/\/+$/, '')}`;
53
+ const concatenatedPath = `${strStrippedTrailingSlash}?${searchParams.toString()}`;
54
+ return searchParams.toString() ? concatenatedPath : `${strStrippedTrailingSlash}`;
55
+ }
56
+
57
+ /**
58
+ * Parse string URL and add it in the current urlState
59
+ * Example:
60
+ * /page/n7/mode/2up => {page: 'n7', mode: '2up'}
61
+ * /page/n7/mode/2up/search/hello => {page: 'n7', mode: '2up', q: 'hello'}
62
+ * @param {string} urlString
63
+ * @returns {object}
64
+ */
65
+ urlStringToUrlState(urlString) {
66
+ const urlState = {};
67
+
68
+ // Fetch searchParams from given {str}
69
+ // Note: whole URL path is needed for URL parsing
70
+ const urlPath = new URL(urlString, 'http://example.com');
71
+ const urlSearchParamsObj = Object.fromEntries(urlPath.searchParams.entries());
72
+ const splitUrlMatches = urlPath.pathname.match(/[^\\/]+\/[^\\/]+/g);
73
+ const urlStrSplitSlashObj = splitUrlMatches ? Object.fromEntries(splitUrlMatches.map(x => x.split('/'))) : {};
74
+
75
+ const doesKeyExists = (_object, _key) => {
76
+ return Object.keys(_object).some(value => value == _key);
77
+ };
78
+
79
+ // Add path objects to urlState
80
+ this.urlSchema
81
+ .filter(schema => schema.position == 'path')
82
+ .forEach(schema => {
83
+ const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);
84
+ const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;
85
+
86
+ if (hasDeprecatedKey) {
87
+ urlState[schema.deprecated_for] = urlStrSplitSlashObj[schema.name];
88
+ return;
89
+ }
90
+
91
+ if (hasPropertyKey) {
92
+ urlState[schema.name] = urlStrSplitSlashObj[schema.name];
93
+ return;
94
+ }
95
+ });
96
+
97
+ // Add searchParams to urlState
98
+ Object.entries(urlSearchParamsObj).forEach(([key, value]) => {
99
+ urlState[key] = value;
100
+ });
101
+
102
+ return urlState;
103
+ }
104
+
105
+ /**
106
+ * Add or update key-value to the urlState
107
+ * @param {string} key
108
+ * @param {string} val
109
+ */
110
+ setUrlParam(key, value) {
111
+ this.urlState[key] = value;
112
+
113
+ this.pushToAddressBar();
114
+ }
115
+
116
+ /**
117
+ * Delete key-value to the urlState
118
+ * @param {string} key
119
+ */
120
+ removeUrlParam(key) {
121
+ delete this.urlState[key];
122
+
123
+ this.pushToAddressBar();
124
+ }
125
+
126
+ /**
127
+ * Get key-value from the urlState
128
+ * @param {string} key
129
+ * @return {string}
130
+ */
131
+ getUrlParam(key) {
132
+ return this.urlState[key];
133
+ }
134
+
135
+ /**
136
+ * Push URL params to addressbar
137
+ */
138
+ pushToAddressBar() {
139
+ const urlStrPath = this.urlStateToUrlString(this.urlState);
140
+ const concatenatedPath = urlStrPath !== '/' ? urlStrPath : '';
141
+ if (this.urlMode == 'history') {
142
+ if (window.history && window.history.replaceState) {
143
+ const newUrlPath = `${this.urlHistoryBasePath}${concatenatedPath}`;
144
+ window.history.replaceState({}, null, newUrlPath);
145
+ }
146
+ } else {
147
+ window.location.replace('#' + concatenatedPath);
148
+ }
149
+ this.oldLocationHash = urlStrPath;
150
+ }
151
+
152
+ /**
153
+ * Get the url and check if it has changed
154
+ * If it was changeed, update the urlState
155
+ */
156
+ listenForHashChanges() {
157
+ this.oldLocationHash = window.location.hash.substr(1);
158
+ if (this.urlLocationPollId) {
159
+ clearInterval(this.urlLocationPollId);
160
+ this.urlLocationPollId = null;
161
+ }
162
+
163
+ // check if the URL changes
164
+ const updateHash = () => {
165
+ const newFragment = window.location.hash.substr(1);
166
+ const hasFragmentChange = newFragment != this.oldLocationHash;
167
+
168
+ if (!hasFragmentChange) { return; }
169
+
170
+ this.urlState = this.urlStringToUrlState(newFragment);
171
+ };
172
+ this.urlLocationPollId = setInterval(updateHash, 500);
173
+ }
174
+
175
+ /**
176
+ * Will read either the hash or URL and return the bookreader fragment
177
+ */
178
+ pullFromAddressBar (location = window.location) {
179
+ const path = this.urlMode === 'history'
180
+ ? (location.pathname.substr(this.urlHistoryBasePath.length) + location.search)
181
+ : location.hash.substr(1);
182
+ this.urlState = this.urlStringToUrlState(path);
183
+ }
184
+ }
@@ -0,0 +1,220 @@
1
+ /* global BookReader */
2
+
3
+ import { UrlPlugin } from "./UrlPlugin";
4
+
5
+ /**
6
+ * Plugin for URL management in BookReader
7
+ * Note read more about the url "fragment" here:
8
+ * https://openlibrary.org/dev/docs/bookurls
9
+ */
10
+
11
+ jQuery.extend(BookReader.defaultOptions, {
12
+ enableUrlPlugin: true,
13
+ bookId: '',
14
+ /** @type {string} Defaults can be a urlFragment string */
15
+ defaults: null,
16
+ updateWindowTitle: false,
17
+
18
+ /** @type {'history' | 'hash'} */
19
+ urlMode: 'hash',
20
+
21
+ /**
22
+ * When using 'history' mode, this part of the URL is kept constant
23
+ * @example /details/plato/
24
+ */
25
+ urlHistoryBasePath: '/',
26
+
27
+ /** Only these params will be reflected onto the URL */
28
+ urlTrackedParams: ['page', 'search', 'mode', 'region', 'highlight', 'view'],
29
+
30
+ /** If true, don't update the URL when `page == n0 (eg "/page/n0")` */
31
+ urlTrackIndex0: false,
32
+ });
33
+
34
+ /** @override */
35
+ BookReader.prototype.setup = (function(super_) {
36
+ return function(options) {
37
+ super_.call(this, options);
38
+
39
+ this.bookId = options.bookId;
40
+ this.defaults = options.defaults;
41
+
42
+ this.locationPollId = null;
43
+ this.oldLocationHash = null;
44
+ this.oldUserHash = null;
45
+ };
46
+ })(BookReader.prototype.setup);
47
+
48
+ /** @override */
49
+ BookReader.prototype.init = (function(super_) {
50
+ return function() {
51
+
52
+ if (this.options.enableUrlPlugin) {
53
+ this.bind(BookReader.eventNames.PostInit, () => {
54
+ const { updateWindowTitle, urlMode } = this.options;
55
+ if (updateWindowTitle) {
56
+ document.title = this.shortTitle(this.bookTitle, 50);
57
+ }
58
+ if (urlMode === 'hash') {
59
+ this.urlStartLocationPolling();
60
+ }
61
+ });
62
+
63
+ this.bind(BookReader.eventNames.fragmentChange,
64
+ this.urlUpdateFragment.bind(this)
65
+ );
66
+ }
67
+ super_.call(this);
68
+ };
69
+ })(BookReader.prototype.init);
70
+
71
+ /**
72
+ * Returns a shortened version of the title with the maximum number of characters
73
+ * @param {number} maximumCharacters
74
+ * @return {string}
75
+ */
76
+ BookReader.prototype.shortTitle = function(maximumCharacters) {
77
+ if (this.bookTitle.length < maximumCharacters) {
78
+ return this.bookTitle;
79
+ }
80
+
81
+ const title = `${this.bookTitle.substr(0, maximumCharacters - 3)}...`;
82
+ return title;
83
+ };
84
+
85
+ /**
86
+ * Starts polling of window.location to see hash fragment changes
87
+ */
88
+ BookReader.prototype.urlStartLocationPolling = function() {
89
+ this.oldLocationHash = this.urlReadFragment();
90
+
91
+ if (this.locationPollId) {
92
+ clearInterval(this.locationPollId);
93
+ this.locationPollId = null;
94
+ }
95
+
96
+ const updateHash = () => {
97
+ const newFragment = this.urlReadFragment();
98
+ const hasFragmentChange = (newFragment != this.oldLocationHash) && (newFragment != this.oldUserHash);
99
+
100
+ if (!hasFragmentChange) { return; }
101
+
102
+ const params = this.paramsFromFragment(newFragment);
103
+
104
+ const updateParams = () => this.updateFromParams(params);
105
+
106
+ this.trigger(BookReader.eventNames.stop);
107
+ if (this.animating) {
108
+ // Queue change if animating
109
+ if (this.autoStop) this.autoStop();
110
+ this.animationFinishedCallback = updateParams;
111
+ } else {
112
+ // update immediately
113
+ updateParams();
114
+ }
115
+ this.oldUserHash = newFragment;
116
+ };
117
+
118
+ this.locationPollId = setInterval(updateHash, 500);
119
+ };
120
+
121
+ /**
122
+ * Update URL from the current parameters.
123
+ * Call this instead of manually using window.location.replace
124
+ */
125
+ BookReader.prototype.urlUpdateFragment = function() {
126
+ const allParams = this.paramsFromCurrent();
127
+ const { urlMode, urlTrackIndex0, urlTrackedParams } = this.options;
128
+
129
+ if (!urlTrackIndex0
130
+ && (typeof(allParams.index) !== 'undefined')
131
+ && allParams.index === 0) {
132
+ delete allParams.index;
133
+ delete allParams.page;
134
+ }
135
+
136
+ const params = urlTrackedParams.reduce((validParams, paramName) => {
137
+ if (paramName in allParams) {
138
+ validParams[paramName] = allParams[paramName];
139
+ }
140
+ return validParams;
141
+ }, {});
142
+
143
+ const newFragment = this.fragmentFromParams(params, urlMode);
144
+ const currFragment = this.urlReadFragment();
145
+ const currQueryString = this.getLocationSearch();
146
+ const newQueryString = this.queryStringFromParams(params, currQueryString, urlMode);
147
+ if (currFragment === newFragment && currQueryString === newQueryString) {
148
+ return;
149
+ }
150
+
151
+ if (urlMode === 'history') {
152
+ if (window.history && window.history.replaceState) {
153
+ const baseWithoutSlash = this.options.urlHistoryBasePath.replace(/\/+$/, '');
154
+ const newFragmentWithSlash = newFragment === '' ? '' : `/${newFragment}`;
155
+
156
+ const newUrlPath = `${baseWithoutSlash}${newFragmentWithSlash}${newQueryString}`;
157
+ window.history.replaceState({}, null, newUrlPath);
158
+ this.oldLocationHash = newFragment + newQueryString;
159
+
160
+ }
161
+ } else {
162
+ const newQueryStringSearch = this.urlParamsFiltersOnlySearch(this.readQueryString());
163
+ window.location.replace('#' + newFragment + newQueryStringSearch);
164
+ this.oldLocationHash = newFragment + newQueryStringSearch;
165
+
166
+ }
167
+ };
168
+
169
+ /**
170
+ * @private
171
+ * Filtering query parameters to select only book search param (?q=foo)
172
+ This needs to be updated/URL system modified if future query params are to be added
173
+ * @param {string} url
174
+ * @return {string}
175
+ * */
176
+ BookReader.prototype.urlParamsFiltersOnlySearch = function(url) {
177
+ const params = new URLSearchParams(url);
178
+ return params.has('q') ? `?${new URLSearchParams({ q: params.get('q') })}` : '';
179
+ };
180
+
181
+
182
+ /**
183
+ * Will read either the hash or URL and return the bookreader fragment
184
+ * @return {string}
185
+ */
186
+ BookReader.prototype.urlReadFragment = function() {
187
+ const { urlMode, urlHistoryBasePath } = this.options;
188
+ if (urlMode === 'history') {
189
+ return window.location.pathname.substr(urlHistoryBasePath.length);
190
+ } else {
191
+ return window.location.hash.substr(1);
192
+ }
193
+ };
194
+
195
+ /**
196
+ * Will read the hash return the bookreader fragment
197
+ * @return {string}
198
+ */
199
+ BookReader.prototype.urlReadHashFragment = function() {
200
+ return window.location.hash.substr(1);
201
+ };
202
+ export class BookreaderUrlPlugin extends BookReader {
203
+ init() {
204
+ if (this.options.enableUrlPlugin) {
205
+ this.urlPlugin = new UrlPlugin(this.options);
206
+ this.bind(BookReader.eventNames.PostInit, () => {
207
+ const { urlMode } = this.options;
208
+
209
+ if (urlMode === 'hash') {
210
+ this.urlPlugin.listenForHashChanges();
211
+ }
212
+ });
213
+ }
214
+
215
+ super.init();
216
+ }
217
+ }
218
+
219
+ window.BookReader = BookreaderUrlPlugin;
220
+ export default BookreaderUrlPlugin;
File without changes