@internetarchive/bookreader 5.0.0-11 → 5.0.0-110

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 (380) hide show
  1. package/BookReader/474.js +2 -0
  2. package/BookReader/474.js.map +1 -0
  3. package/BookReader/BookReader.css +604 -1239
  4. package/BookReader/BookReader.js +1 -1
  5. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  6. package/BookReader/BookReader.js.map +1 -1
  7. package/BookReader/bergamot-translator-worker.js +2966 -0
  8. package/BookReader/bergamot-translator-worker.wasm +0 -0
  9. package/BookReader/hypothesis/LICENSE +50 -0
  10. package/BookReader/hypothesis/README.md +55 -0
  11. package/BookReader/hypothesis/build/boot.js +1 -0
  12. package/BookReader/hypothesis/build/manifest.json +20 -0
  13. package/BookReader/hypothesis/build/scripts/annotator.bundle.js +184 -0
  14. package/BookReader/hypothesis/build/scripts/annotator.bundle.js.map +1 -0
  15. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js +798 -0
  16. package/BookReader/hypothesis/build/scripts/sidebar.bundle.js.map +1 -0
  17. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js +711 -0
  18. package/BookReader/hypothesis/build/scripts/ui-playground.bundle.js.map +1 -0
  19. package/BookReader/hypothesis/build/styles/annotator.css +2235 -0
  20. package/BookReader/hypothesis/build/styles/annotator.css.map +1 -0
  21. package/BookReader/hypothesis/build/styles/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  22. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  23. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  24. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  25. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  26. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Bold.woff2 +0 -0
  27. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  28. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Italic.woff2 +0 -0
  29. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Main-Regular.woff2 +0 -0
  30. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  31. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Math-Italic.woff2 +0 -0
  32. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  33. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  34. package/BookReader/hypothesis/build/styles/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  35. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Script-Regular.woff2 +0 -0
  36. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  37. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  38. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  39. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  40. package/BookReader/hypothesis/build/styles/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  41. package/BookReader/hypothesis/build/styles/highlights.css +2 -0
  42. package/BookReader/hypothesis/build/styles/highlights.css.map +1 -0
  43. package/BookReader/hypothesis/build/styles/katex.min.css +2 -0
  44. package/BookReader/hypothesis/build/styles/katex.min.css.map +1 -0
  45. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css +2 -0
  46. package/BookReader/hypothesis/build/styles/pdfjs-overrides.css.map +1 -0
  47. package/BookReader/hypothesis/build/styles/sidebar.css +2731 -0
  48. package/BookReader/hypothesis/build/styles/sidebar.css.map +1 -0
  49. package/BookReader/hypothesis/build/styles/ui-playground.css +2659 -0
  50. package/BookReader/hypothesis/build/styles/ui-playground.css.map +1 -0
  51. package/BookReader/hypothesis/package.json +126 -0
  52. package/BookReader/ia-bookreader-bundle.js +1907 -0
  53. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +19 -0
  54. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  55. package/BookReader/icons/1up.svg +1 -1
  56. package/BookReader/icons/2up.svg +1 -1
  57. package/BookReader/icons/advance.svg +1 -1
  58. package/BookReader/icons/chevron-right.svg +1 -1
  59. package/BookReader/icons/close-circle-dark.svg +1 -1
  60. package/BookReader/icons/close-circle.svg +1 -1
  61. package/BookReader/icons/fullscreen.svg +1 -1
  62. package/BookReader/icons/fullscreen_exit.svg +1 -1
  63. package/BookReader/icons/hamburger.svg +1 -1
  64. package/BookReader/icons/left-arrow.svg +1 -1
  65. package/BookReader/icons/magnify-minus.svg +1 -1
  66. package/BookReader/icons/magnify-plus.svg +1 -1
  67. package/BookReader/icons/magnify.svg +1 -1
  68. package/BookReader/icons/pause.svg +1 -1
  69. package/BookReader/icons/play.svg +1 -1
  70. package/BookReader/icons/playback-speed.svg +1 -1
  71. package/BookReader/icons/read-aloud.svg +1 -1
  72. package/BookReader/icons/review.svg +1 -1
  73. package/BookReader/icons/slider-toggle.svg +1 -0
  74. package/BookReader/icons/thumbnails.svg +1 -1
  75. package/BookReader/icons/voice.svg +1 -0
  76. package/BookReader/icons/volume-full.svg +1 -1
  77. package/BookReader/images/BRicons.svg +3 -3
  78. package/BookReader/images/books_graphic.svg +1 -1
  79. package/BookReader/images/hypothesis.ico +0 -0
  80. package/BookReader/images/icon_book.svg +1 -1
  81. package/BookReader/images/icon_bookmark.svg +1 -1
  82. package/BookReader/images/icon_experiment.svg +1 -0
  83. package/BookReader/images/icon_gear.svg +1 -1
  84. package/BookReader/images/icon_hamburger.svg +1 -1
  85. package/BookReader/images/icon_home.svg +1 -1
  86. package/BookReader/images/icon_info.svg +1 -1
  87. package/BookReader/images/icon_one_page.svg +1 -1
  88. package/BookReader/images/icon_pause.svg +1 -1
  89. package/BookReader/images/icon_play.svg +1 -1
  90. package/BookReader/images/icon_playback-rate.svg +1 -1
  91. package/BookReader/images/icon_search_button.svg +1 -1
  92. package/BookReader/images/icon_share.svg +1 -1
  93. package/BookReader/images/icon_skip-ahead.svg +1 -1
  94. package/BookReader/images/icon_skip-back.svg +1 -1
  95. package/BookReader/images/icon_speaker.svg +1 -1
  96. package/BookReader/images/icon_speaker_open.svg +1 -1
  97. package/BookReader/images/icon_thumbnails.svg +1 -1
  98. package/BookReader/images/icon_toc.svg +1 -1
  99. package/BookReader/images/icon_two_pages.svg +1 -1
  100. package/BookReader/images/marker_chap-off.svg +1 -1
  101. package/BookReader/images/marker_chap-on.svg +1 -1
  102. package/BookReader/images/marker_srch-on.svg +1 -1
  103. package/BookReader/images/translate.svg +1 -0
  104. package/BookReader/images/unviewable_page.png +0 -0
  105. package/BookReader/jquery-3.js +2 -0
  106. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  107. package/BookReader/plugins/plugin.archive_analytics.js +1 -1
  108. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  109. package/BookReader/plugins/plugin.autoplay.js +1 -1
  110. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  111. package/BookReader/plugins/plugin.chapters.js +22 -1
  112. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  113. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  114. package/BookReader/plugins/plugin.experiments.js +3 -0
  115. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +1 -0
  116. package/BookReader/plugins/plugin.experiments.js.map +1 -0
  117. package/BookReader/plugins/plugin.iframe.js +1 -1
  118. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  119. package/BookReader/plugins/plugin.iiif.js +2 -0
  120. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  121. package/BookReader/plugins/plugin.resume.js +1 -1
  122. package/BookReader/plugins/plugin.resume.js.map +1 -1
  123. package/BookReader/plugins/plugin.search.js +2 -1
  124. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  125. package/BookReader/plugins/plugin.search.js.map +1 -1
  126. package/BookReader/plugins/plugin.text_selection.js +2 -1
  127. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  128. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  129. package/BookReader/plugins/plugin.translate.js +137 -0
  130. package/BookReader/plugins/plugin.translate.js.LICENSE.txt +1 -0
  131. package/BookReader/plugins/plugin.translate.js.map +1 -0
  132. package/BookReader/plugins/plugin.tts.js +1 -1
  133. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  134. package/BookReader/plugins/plugin.tts.js.map +1 -1
  135. package/BookReader/plugins/plugin.url.js +1 -1
  136. package/BookReader/plugins/plugin.url.js.map +1 -1
  137. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  138. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  139. package/BookReader/plugins/translator-worker.js +2 -0
  140. package/BookReader/plugins/translator-worker.js.map +1 -0
  141. package/BookReader/silence.mp3 +0 -0
  142. package/BookReader/translator-worker.js +475 -0
  143. package/BookReader/webcomponents-bundle.js +3 -0
  144. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  145. package/BookReader/webcomponents-bundle.js.map +1 -0
  146. package/README.md +14 -3
  147. package/jsconfig.json +19 -0
  148. package/package.json +92 -70
  149. package/src/BookReader/BookModel.js +92 -46
  150. package/src/BookReader/DragScrollable.js +233 -0
  151. package/src/BookReader/ImageCache.js +49 -16
  152. package/src/BookReader/Mode1Up.js +66 -365
  153. package/src/BookReader/Mode1UpLit.js +392 -0
  154. package/src/BookReader/Mode2Up.js +87 -1315
  155. package/src/BookReader/Mode2UpLit.js +788 -0
  156. package/src/BookReader/ModeAbstract.js +43 -0
  157. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  158. package/src/BookReader/ModeSmoothZoom.js +312 -0
  159. package/src/BookReader/ModeThumb.js +29 -15
  160. package/src/BookReader/Navbar/Navbar.js +201 -76
  161. package/src/BookReader/PageContainer.js +120 -23
  162. package/src/BookReader/ReduceSet.js +2 -2
  163. package/src/BookReader/Toolbar/Toolbar.js +18 -40
  164. package/src/BookReader/events.js +3 -3
  165. package/src/BookReader/options.js +94 -17
  166. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  167. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  168. package/src/BookReader/utils/SelectionObserver.js +45 -0
  169. package/src/BookReader/utils/classes.js +1 -1
  170. package/src/BookReader/utils.js +136 -12
  171. package/src/BookReader.js +678 -1226
  172. package/src/BookReaderPlugin.js +52 -0
  173. package/src/assets/icons/magnify-minus.svg +3 -7
  174. package/src/assets/icons/magnify-plus.svg +3 -7
  175. package/src/assets/icons/slider-toggle.svg +1 -0
  176. package/src/assets/icons/voice.svg +1 -0
  177. package/src/assets/images/hypothesis.ico +0 -0
  178. package/src/assets/images/icon_experiment.svg +1 -0
  179. package/src/assets/images/translate.svg +1 -0
  180. package/src/assets/images/unviewable_page.png +0 -0
  181. package/src/assets/silence.mp3 +0 -0
  182. package/src/css/BookReader.scss +1 -5
  183. package/src/css/_BRBookmarks.scss +1 -1
  184. package/src/css/_BRComponent.scss +1 -1
  185. package/src/css/_BRicon.scss +8 -2
  186. package/src/css/_BRmain.scss +16 -3
  187. package/src/css/_BRnav.scss +74 -70
  188. package/src/css/_BRpages.scss +171 -42
  189. package/src/css/_BRsearch.scss +69 -30
  190. package/src/css/_BRtoolbar.scss +5 -5
  191. package/src/css/_TextSelection.scss +129 -24
  192. package/src/css/_colorbox.scss +2 -2
  193. package/src/css/_controls.scss +24 -7
  194. package/src/css/_icons.scss +8 -1
  195. package/src/{BookNavigator/assets → css}/button-base.js +2 -2
  196. package/src/css/icon_checkmark.js +9 -0
  197. package/src/css/sharedStyles.js +15 -0
  198. package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
  199. package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +25 -5
  200. package/src/ia-bookreader/ia-bookreader.js +666 -0
  201. package/src/ia-bookreader/sharing.js +27 -0
  202. package/src/ia-bookreader/viewable-files.js +98 -0
  203. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
  204. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
  205. package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
  206. package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
  207. package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +43 -31
  208. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
  209. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +3 -3
  210. package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
  211. package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
  212. package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
  213. package/src/plugins/plugin.archive_analytics.js +84 -78
  214. package/src/plugins/plugin.autoplay.js +99 -104
  215. package/src/plugins/plugin.chapters.js +314 -205
  216. package/src/plugins/plugin.experiments.js +321 -0
  217. package/src/plugins/plugin.iframe.js +1 -1
  218. package/src/plugins/plugin.iiif.js +141 -0
  219. package/src/plugins/plugin.resume.js +54 -51
  220. package/src/plugins/plugin.text_selection.js +522 -219
  221. package/src/plugins/plugin.vendor-fullscreen.js +5 -5
  222. package/src/plugins/search/plugin.search.js +374 -392
  223. package/src/{BookNavigator → plugins}/search/search-provider.js +59 -27
  224. package/src/{BookNavigator → plugins}/search/search-results.js +105 -76
  225. package/src/plugins/search/utils.js +50 -0
  226. package/src/plugins/search/view.js +50 -68
  227. package/src/plugins/translate/TranslationManager.js +164 -0
  228. package/src/plugins/translate/plugin.translate.js +512 -0
  229. package/src/plugins/tts/AbstractTTSEngine.js +78 -49
  230. package/src/plugins/tts/FestivalTTSEngine.js +20 -30
  231. package/src/plugins/tts/PageChunk.js +33 -21
  232. package/src/plugins/tts/PageChunkIterator.js +11 -17
  233. package/src/plugins/tts/WebTTSEngine.js +131 -91
  234. package/src/plugins/tts/plugin.tts.js +345 -350
  235. package/src/plugins/tts/utils.js +77 -49
  236. package/src/plugins/url/UrlPlugin.js +191 -0
  237. package/src/plugins/{plugin.url.js → url/plugin.url.js} +44 -15
  238. package/src/util/TextSelectionManager.js +282 -0
  239. package/src/util/browserSniffing.js +33 -1
  240. package/src/util/cache.js +20 -0
  241. package/src/util/docCookies.js +21 -2
  242. package/src/util/lit.js +15 -0
  243. package/src/util/strings.js +1 -0
  244. package/.babelrc +0 -12
  245. package/.dependabot/config.yml +0 -6
  246. package/.eslintrc.js +0 -50
  247. package/.gitattributes +0 -2
  248. package/.github/ISSUE_TEMPLATE/bug.md +0 -32
  249. package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
  250. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
  251. package/.github/workflows/node.js.yml +0 -37
  252. package/.github/workflows/npm-publish.yml +0 -47
  253. package/.testcaferc.json +0 -5
  254. package/BookReader/bookreader-component-bundle.js +0 -1450
  255. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  256. package/BookReader/bookreader-component-bundle.js.map +0 -1
  257. package/BookReader/jquery-1.10.1.js +0 -2
  258. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  259. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  260. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  261. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  262. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  263. package/BookReaderDemo/BookReaderDemo.css +0 -41
  264. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
  265. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  266. package/BookReaderDemo/BookReaderJSSimple.js +0 -55
  267. package/BookReaderDemo/IIIFBookReader.js +0 -207
  268. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
  269. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
  270. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  271. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  272. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  273. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
  274. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
  275. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  276. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  277. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  278. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  279. package/BookReaderDemo/demo-advanced.html +0 -33
  280. package/BookReaderDemo/demo-autoplay.html +0 -38
  281. package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
  282. package/BookReaderDemo/demo-embed.html +0 -26
  283. package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
  284. package/BookReaderDemo/demo-fullscreen.html +0 -33
  285. package/BookReaderDemo/demo-iiif.html +0 -34
  286. package/BookReaderDemo/demo-iiif.js +0 -26
  287. package/BookReaderDemo/demo-internetarchive.html +0 -74
  288. package/BookReaderDemo/demo-multiple.html +0 -43
  289. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  290. package/BookReaderDemo/demo-preview-pages.html +0 -1092
  291. package/BookReaderDemo/demo-simple.html +0 -34
  292. package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
  293. package/BookReaderDemo/immersion-1up.html +0 -64
  294. package/BookReaderDemo/immersion-mode.html +0 -35
  295. package/BookReaderDemo/toggle_controls.html +0 -53
  296. package/BookReaderDemo/view_mode.html +0 -39
  297. package/BookReaderDemo/viewmode-cycle.html +0 -41
  298. package/CHANGELOG.md +0 -495
  299. package/CONTRIBUTING.md +0 -7
  300. package/codecov.yml +0 -17
  301. package/index.html +0 -31
  302. package/karma.conf.js +0 -23
  303. package/screenshot.png +0 -0
  304. package/scripts/postversion.js +0 -10
  305. package/scripts/preversion.js +0 -14
  306. package/scripts/version.js +0 -26
  307. package/src/BookNavigator/BookModel.js +0 -14
  308. package/src/BookNavigator/BookNavigator.js +0 -446
  309. package/src/BookNavigator/assets/book-loader.js +0 -27
  310. package/src/BookNavigator/assets/icon_checkmark.js +0 -6
  311. package/src/BookNavigator/assets/icon_close.js +0 -3
  312. package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
  313. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  314. package/src/BookNavigator/downloads/downloads-provider.js +0 -66
  315. package/src/BookNavigator/search/a-search-result.js +0 -55
  316. package/src/BookReader/DebugConsole.js +0 -54
  317. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  318. package/src/ItemNavigator/ItemNavigator.js +0 -376
  319. package/src/ItemNavigator/providers/sharing.js +0 -29
  320. package/src/css/_MobileNav.scss +0 -194
  321. package/src/dragscrollable-br.js +0 -261
  322. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  323. package/src/plugins/plugin.mobile_nav.js +0 -287
  324. package/tests/BookReader/BookModel.test.js +0 -312
  325. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  326. package/tests/BookReader/DebugConsole.test.js +0 -25
  327. package/tests/BookReader/ImageCache.test.js +0 -150
  328. package/tests/BookReader/Mode1Up.test.js +0 -164
  329. package/tests/BookReader/Mode2Up.test.js +0 -247
  330. package/tests/BookReader/Navbar/Navbar.test.js +0 -169
  331. package/tests/BookReader/PageContainer.test.js +0 -115
  332. package/tests/BookReader/ReduceSet.test.js +0 -38
  333. package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
  334. package/tests/BookReader/utils/classes.test.js +0 -88
  335. package/tests/BookReader/utils.test.js +0 -109
  336. package/tests/BookReader.options.test.js +0 -39
  337. package/tests/BookReader.test.js +0 -301
  338. package/tests/e2e/README.md +0 -75
  339. package/tests/e2e/autoplay.test.js +0 -13
  340. package/tests/e2e/base.test.js +0 -35
  341. package/tests/e2e/helpers/base.js +0 -263
  342. package/tests/e2e/helpers/debug.js +0 -13
  343. package/tests/e2e/helpers/desktopSearch.js +0 -72
  344. package/tests/e2e/helpers/mobileSearch.js +0 -85
  345. package/tests/e2e/helpers/mockSearch.js +0 -93
  346. package/tests/e2e/helpers/rightToLeft.js +0 -29
  347. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  348. package/tests/e2e/models/BookReader.js +0 -11
  349. package/tests/e2e/models/Navigation.js +0 -56
  350. package/tests/e2e/rightToLeft.test.js +0 -20
  351. package/tests/e2e/viewmode.test.js +0 -37
  352. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  353. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
  354. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
  355. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  356. package/tests/karma/BookNavigator/search/search-results.test.js +0 -240
  357. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  358. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  359. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  360. package/tests/plugins/plugin.autoplay.test.js +0 -52
  361. package/tests/plugins/plugin.chapters.test.js +0 -130
  362. package/tests/plugins/plugin.iframe.test.js +0 -42
  363. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  364. package/tests/plugins/plugin.resume.test.js +0 -98
  365. package/tests/plugins/plugin.text_selection.test.js +0 -203
  366. package/tests/plugins/plugin.url.test.js +0 -129
  367. package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
  368. package/tests/plugins/search/plugin.search.test.js +0 -173
  369. package/tests/plugins/search/plugin.search.view.test.js +0 -106
  370. package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
  371. package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
  372. package/tests/plugins/tts/PageChunk.test.js +0 -57
  373. package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
  374. package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
  375. package/tests/plugins/tts/utils.test.js +0 -133
  376. package/tests/util/browserSniffing.test.js +0 -56
  377. package/tests/util/docCookies.test.js +0 -15
  378. package/tests/util/strings.test.js +0 -63
  379. package/tests/utils.js +0 -42
  380. package/webpack.config.js +0 -86
package/src/BookReader.js CHANGED
@@ -19,54 +19,77 @@ This file is part of BookReader.
19
19
  The BookReader source is hosted at http://github.com/internetarchive/bookreader/
20
20
 
21
21
  */
22
- // effect.js gives acces to extra easing function (e.g. easeInOutExpo)
23
- import 'jquery-ui/ui/effect.js';
24
-
25
22
  // Needed by touch-punch
26
23
  import 'jquery-ui/ui/widget.js';
27
24
  import 'jquery-ui/ui/widgets/mouse.js';
28
25
  import 'jquery-ui-touch-punch';
29
26
 
30
- import './dragscrollable-br.js';
31
27
  import PACKAGE_JSON from '../package.json';
32
28
  import * as utils from './BookReader/utils.js';
33
29
  import { exposeOverrideable } from './BookReader/utils/classes.js';
34
- import { Navbar, getNavPageNumHtml } from './BookReader/Navbar/Navbar.js';
35
- import { DEFAULT_OPTIONS } from './BookReader/options.js';
30
+ import { Navbar } from './BookReader/Navbar/Navbar.js';
31
+ import { DEFAULT_OPTIONS, OptionsParseError } from './BookReader/options.js';
36
32
  /** @typedef {import('./BookReader/options.js').BookReaderOptions} BookReaderOptions */
37
33
  /** @typedef {import('./BookReader/options.js').ReductionFactor} ReductionFactor */
38
34
  /** @typedef {import('./BookReader/BookModel.js').PageIndex} PageIndex */
39
35
  import { EVENTS } from './BookReader/events.js';
40
- import { DebugConsole } from './BookReader/DebugConsole.js';
41
- import {
42
- Toolbar,
43
- blankInfoDiv,
44
- blankShareDiv,
45
- createPopup,
46
- } from './BookReader/Toolbar/Toolbar.js';
36
+ import { Toolbar } from './BookReader/Toolbar/Toolbar.js';
47
37
  import { BookModel } from './BookReader/BookModel.js';
48
38
  import { Mode1Up } from './BookReader/Mode1Up.js';
49
39
  import { Mode2Up } from './BookReader/Mode2Up.js';
50
- import { ModeThumb } from './BookReader/ModeThumb';
40
+ import { ModeThumb } from './BookReader/ModeThumb.js';
51
41
  import { ImageCache } from './BookReader/ImageCache.js';
52
42
  import { PageContainer } from './BookReader/PageContainer.js';
53
- import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
54
-
55
- if (location.toString().indexOf('_debugShowConsole=true') != -1) {
56
- $(() => new DebugConsole().init());
57
- }
43
+ import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet.js';
58
44
 
59
45
  /**
60
46
  * BookReader
61
- * @param {BookReaderOptions} options
47
+ * @param {Partial<BookReaderOptions>} overrides
62
48
  * TODO document all options properties
63
49
  * @constructor
64
50
  */
65
51
  export default function BookReader(overrides = {}) {
66
- const options = jQuery.extend(true, {}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
52
+ const options = BookReader.extendOptions({}, BookReader.defaultOptions, overrides, BookReader.optionOverrides);
67
53
  this.setup(options);
68
54
  }
69
55
 
56
+ /**
57
+ * Extend the default options for BookReader
58
+ * Accepts any number of option objects and merges them in order.
59
+ *
60
+ * Does a shallow merge of everything except plugin options. These are individually merged.
61
+ * @param {...Partial<BookReaderOptions>} newOptions
62
+ */
63
+ BookReader.extendOptions = function(...newOptions) {
64
+ if (newOptions.length <= 1) {
65
+ return newOptions[0];
66
+ }
67
+ /** @type {Array<keyof BookReaderOptions>} */
68
+ const shallowOverrides = ['onePage', 'twoPage', 'vars'];
69
+ /** @type {Array<keyof BookReaderOptions>} */
70
+ const mapOverrides = ['plugins', 'controls'];
71
+
72
+ const result = newOptions.shift();
73
+ for (const opts of newOptions) {
74
+ for (const [key, value] of Object.entries(opts)) {
75
+ if (shallowOverrides.includes(key)) {
76
+ result[key] ||= {};
77
+ result[key] = Object.assign(result[key], value);
78
+ } else if (mapOverrides.includes(key)) {
79
+ result[key] ||= {};
80
+ for (const [name, mapValue] of Object.entries(value)) {
81
+ result[key][name] ||= {};
82
+ Object.assign(result[key][name], mapValue);
83
+ }
84
+ } else {
85
+ result[key] = value;
86
+ }
87
+ }
88
+ }
89
+
90
+ return result;
91
+ };
92
+
70
93
  BookReader.version = PACKAGE_JSON.version;
71
94
 
72
95
  // Mode constants
@@ -76,6 +99,41 @@ BookReader.constMode1up = 1;
76
99
  BookReader.constMode2up = 2;
77
100
  /** thumbnails view */
78
101
  BookReader.constModeThumb = 3;
102
+
103
+ // Although this can actualy have any BookReaderPlugin subclass as value, we
104
+ // hardcode the known plugins here for type checking
105
+ BookReader.PLUGINS = {
106
+ /** @type {typeof import('./plugins/plugin.archive_analytics.js').ArchiveAnalyticsPlugin | null}*/
107
+ archiveAnalytics: null,
108
+ /** @type {typeof import('./plugins/plugin.autoplay.js').AutoplayPlugin | null}*/
109
+ autoplay: null,
110
+ /** @type {typeof import('./plugins/plugin.chapters.js').ChaptersPlugin | null}*/
111
+ chapters: null,
112
+ /** @type {typeof import('./plugins/plugin.experiments.js').ExperimentsPlugin | null}*/
113
+ experiments: null,
114
+ /** @type {typeof import('./plugins/plugin.resume.js').ResumePlugin | null}*/
115
+ resume: null,
116
+ /** @type {typeof import('./plugins/search/plugin.search.js').SearchPlugin | null}*/
117
+ search: null,
118
+ /** @type {typeof import('./plugins/plugin.text_selection.js').TextSelectionPlugin | null}*/
119
+ textSelection: null,
120
+ /** @type {typeof import('./plugins/translate/plugin.translate.js').TranslatePlugin | null}*/
121
+ translate: null,
122
+ /** @type {typeof import('./plugins/tts/plugin.tts.js').TtsPlugin | null}*/
123
+ tts: null,
124
+ };
125
+
126
+ /**
127
+ * @param {string} pluginName
128
+ * @param {new (...args: any[]) => import('./BookReaderPlugin.js').BookReaderPlugin} plugin
129
+ */
130
+ BookReader.registerPlugin = function(pluginName, plugin) {
131
+ if (BookReader.PLUGINS[pluginName]) {
132
+ console.warn(`Plugin ${pluginName} already registered. Overwriting.`);
133
+ }
134
+ BookReader.PLUGINS[pluginName] = plugin;
135
+ };
136
+
79
137
  /** image cache */
80
138
  BookReader.imageCache = null;
81
139
 
@@ -103,12 +161,63 @@ BookReader.prototype.setup = function(options) {
103
161
  // Store the options used to setup bookreader
104
162
  this.options = options;
105
163
 
164
+ /** @type {import('@/src/ia-bookreader/ia-bookreader.js').IaBookReader} */
165
+ this.shell;
166
+
167
+ // Base server used by some api calls
168
+ /** @deprecated */
169
+ this.bookId = options.bookId;
170
+ /** @deprecated */
171
+ this.server = options.server;
172
+ /** @deprecated */
173
+ this.subPrefix = options.subPrefix;
174
+ /** @deprecated */
175
+ this.bookPath = options.bookPath;
176
+
177
+ // Construct the usual plugins first to get type hints
178
+ this.plugins = {
179
+ archiveAnalytics: BookReader.PLUGINS.archiveAnalytics ? new BookReader.PLUGINS.archiveAnalytics(this) : null,
180
+ autoplay: BookReader.PLUGINS.autoplay ? new BookReader.PLUGINS.autoplay(this) : null,
181
+ chapters: BookReader.PLUGINS.chapters ? new BookReader.PLUGINS.chapters(this) : null,
182
+ experiments: BookReader.PLUGINS.experiments ? new BookReader.PLUGINS.experiments(this) : null,
183
+ search: BookReader.PLUGINS.search ? new BookReader.PLUGINS.search(this) : null,
184
+ resume: BookReader.PLUGINS.resume ? new BookReader.PLUGINS.resume(this) : null,
185
+ textSelection: BookReader.PLUGINS.textSelection ? new BookReader.PLUGINS.textSelection(this) : null,
186
+ translate: BookReader.PLUGINS.translate ? new BookReader.PLUGINS.translate(this) : null,
187
+ tts: BookReader.PLUGINS.tts ? new BookReader.PLUGINS.tts(this) : null,
188
+ };
189
+
190
+ // Delete anything that's null
191
+ for (const [pluginName, plugin] of Object.entries(this.plugins)) {
192
+ if (!plugin) delete this.plugins[pluginName];
193
+ }
194
+
195
+ // Now construct the rest of the plugins
196
+ for (const [pluginName, PluginClass] of Object.entries(BookReader.PLUGINS)) {
197
+ if (this.plugins[pluginName] || !PluginClass) continue;
198
+ this.plugins[pluginName] = new PluginClass(this);
199
+ }
200
+
201
+ // And call setup on them
202
+ for (const [pluginName, plugin] of Object.entries(this.plugins)) {
203
+ try {
204
+ plugin.setup(this.options.plugins?.[pluginName] ?? {});
205
+ // Write the options back; this way the plugin is the source of truth,
206
+ // and BR just contains a reference to it.
207
+ this.options.plugins[pluginName] = plugin.options;
208
+ } catch (e) {
209
+ console.error(`Error setting up plugin ${pluginName}`, e);
210
+ }
211
+ }
212
+
213
+ if (this.plugins.search?.options.enabled) {
214
+ // Expose the search method for convenience / backward compat
215
+ this.search = this.plugins.search.search.bind(this.plugins.search);
216
+ }
217
+
106
218
  /** @type {number} @deprecated some past iterations set this */
107
219
  this.numLeafs = undefined;
108
220
 
109
- /** Overridden by plugin.search.js */
110
- this.enableSearch = false;
111
-
112
221
  /**
113
222
  * Store viewModeOrder states
114
223
  * @var {boolean}
@@ -152,24 +261,16 @@ BookReader.prototype.setup = function(options) {
152
261
  this.ui = options.ui;
153
262
  this.uiAutoHide = options.uiAutoHide;
154
263
 
155
- this.thumbWidth = 100; // will be overridden during prepareThumbnailView
264
+ this.thumbWidth = 100; // will be overridden during this._modes.modeThumb.prepare();
156
265
  this.thumbRowBuffer = options.thumbRowBuffer;
157
266
  this.thumbColumns = options.thumbColumns;
158
267
  this.thumbMaxLoading = options.thumbMaxLoading;
159
268
  this.thumbPadding = options.thumbPadding;
160
269
  this.displayedRows = [];
161
-
162
270
  this.displayedIndices = [];
163
- /** @deprecated Unused; will be remove in v5 */
164
- this.imgs = {};
165
- /** @deprecated No longer used; will be remove in v5 */
166
- this.prefetchedImgs = {}; //an object with numeric keys corresponding to page index, reduce
167
271
 
168
272
  this.animating = false;
169
- this.flipSpeed = options.flipSpeed;
170
- this.flipDelay = options.flipDelay;
171
- this.twoPagePopUp = null;
172
- this.leafEdgeTmp = null;
273
+ this.flipSpeed = utils.parseAnimationSpeed(options.flipSpeed) || 400;
173
274
 
174
275
  /**
175
276
  * Represents the first displayed index
@@ -178,8 +279,7 @@ BookReader.prototype.setup = function(options) {
178
279
  * @property {number|null} firstIndex
179
280
  */
180
281
  this.firstIndex = null;
181
- this.lastDisplayableIndex2up = null;
182
- this.isFullscreenActive = false;
282
+ this.isFullscreenActive = options.startFullscreen || false;
183
283
  this.lastScroll = null;
184
284
 
185
285
  this.showLogo = options.showLogo;
@@ -212,26 +312,22 @@ BookReader.prototype.setup = function(options) {
212
312
 
213
313
  // Assign the data methods
214
314
  this.data = options.data;
215
- if (options.getNumLeafs) BookReader.prototype.getNumLeafs = options.getNumLeafs;
216
- if (options.getPageWidth) BookReader.prototype.getPageWidth = options.getPageWidth;
217
- if (options.getPageHeight) BookReader.prototype.getPageHeight = options.getPageHeight;
218
- if (options.getPageURI) BookReader.prototype.getPageURI = options.getPageURI;
219
- if (options.getPageSide) BookReader.prototype.getPageSide = options.getPageSide;
220
- if (options.getPageNum) BookReader.prototype.getPageNum = options.getPageNum;
221
- if (options.getPageProp) BookReader.prototype.getPageProp = options.getPageProp;
222
- if (options.getSpreadIndices) BookReader.prototype.getSpreadIndices = options.getSpreadIndices;
223
- if (options.leafNumToIndex) BookReader.prototype.leafNumToIndex = options.leafNumToIndex;
224
315
 
225
316
  /** @type {{[name: string]: JQuery}} */
226
317
  this.refs = {};
227
318
 
228
- /**
229
- * @private (for now) Models are largely state storing classes. This might be too much
230
- * granularity, but time will tell!
231
- */
232
- this._models = {
233
- book: new BookModel(this),
234
- };
319
+ /** The book being displayed in BookReader*/
320
+ this.book = new BookModel(this);
321
+
322
+ if (options.getNumLeafs) this.book.getNumLeafs = options.getNumLeafs.bind(this);
323
+ if (options.getPageWidth) this.book.getPageWidth = options.getPageWidth.bind(this);
324
+ if (options.getPageHeight) this.book.getPageHeight = options.getPageHeight.bind(this);
325
+ if (options.getPageURI) this.book.getPageURI = options.getPageURI.bind(this);
326
+ if (options.getPageSide) this.book.getPageSide = options.getPageSide.bind(this);
327
+ if (options.getPageNum) this.book.getPageNum = options.getPageNum.bind(this);
328
+ if (options.getPageProp) this.book.getPageProp = options.getPageProp.bind(this);
329
+ if (options.getSpreadIndices) this.book.getSpreadIndices = options.getSpreadIndices.bind(this);
330
+ if (options.leafNumToIndex) this.book.leafNumToIndex = options.leafNumToIndex.bind(this);
235
331
 
236
332
  /**
237
333
  * @private Components are 'subchunks' of bookreader functionality, usually UI related
@@ -245,14 +341,14 @@ BookReader.prototype.setup = function(options) {
245
341
  };
246
342
 
247
343
  this._modes = {
248
- mode1Up: new Mode1Up(this, this._models.book),
249
- mode2Up: new Mode2Up(this, this._models.book),
250
- modeThumb: new ModeThumb(this, this._models.book),
344
+ mode1Up: new Mode1Up(this, this.book),
345
+ mode2Up: new Mode2Up(this, this.book),
346
+ modeThumb: new ModeThumb(this, this.book),
251
347
  };
252
348
 
253
349
  /** Stores classes which we want to expose (selectively) some methods as overridable */
254
350
  this._overrideable = {
255
- '_models.book': this._models.book,
351
+ 'book': this.book,
256
352
  '_components.navbar': this._components.navbar,
257
353
  '_components.toolbar': this._components.toolbar,
258
354
  '_modes.mode1Up': this._modes.mode1Up,
@@ -261,21 +357,74 @@ BookReader.prototype.setup = function(options) {
261
357
  };
262
358
 
263
359
  /** Image cache for general image fetching */
264
- this.imageCache = new ImageCache(this._models.book, {
360
+ this.imageCache = new ImageCache(this.book, {
265
361
  useSrcSet: this.options.useSrcSet,
266
362
  reduceSet: this.reduceSet,
363
+ renderPageURI: options.renderPageURI.bind(this),
267
364
  });
365
+
366
+ /**
367
+ * Flag if BookReader has "focus" for keyboard shortcuts
368
+ * Initially true, set to false when:
369
+ * - BookReader scrolled out of view
370
+ * Set to true when:
371
+ * - BookReader scrolled into view
372
+ */
373
+ this.hasKeyFocus = true;
268
374
  };
269
375
 
270
- /** @deprecated unused outside Mode2Up */
271
- Object.defineProperty(BookReader.prototype, 'leafEdgeL', {
272
- get() { return this._modes.mode2Up.leafEdgeL; },
273
- set(newVal) { this._modes.mode2Up.leafEdgeL = newVal; }
274
- });
275
- /** @deprecated unused outside Mode2Up */
276
- Object.defineProperty(BookReader.prototype, 'leafEdgeR', {
277
- get() { return this._modes.mode2Up.leafEdgeR; },
278
- set(newVal) { this._modes.mode2Up.leafEdgeR = newVal; }
376
+ BookReader.prototype.initializePlugin = function(pluginName) {
377
+ const plugin = new BookReader.PLUGINS[pluginName](this);
378
+ this.plugins[pluginName] = plugin;
379
+ try {
380
+ plugin.setup(this.options.plugins?.[pluginName] ?? {});
381
+ this.options.plugins[pluginName] = plugin.options;
382
+ } catch (e) {
383
+ console.error(`Error setting up plugin ${pluginName} outside of regular cycle`, e);
384
+ throw e;
385
+ }
386
+
387
+ try {
388
+ plugin.init();
389
+ } catch (e) {
390
+ console.error(`Error initializing plugin ${pluginName} outside of regular cycle`, e);
391
+ throw e;
392
+ }
393
+ };
394
+ /**
395
+ * Get all the HTML Elements that are being/can be rendered.
396
+ * Includes cached elements which might be rendered again.
397
+ */
398
+ BookReader.prototype.getActivePageContainerElements = function() {
399
+ let containerEls = Object.values(this._modes.mode2Up.mode2UpLit.pageContainerCache).map(pc => pc.$container[0])
400
+ .concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
401
+ if (this.mode == this.constModeThumb) {
402
+ containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
403
+ }
404
+ return containerEls;
405
+ };
406
+
407
+ /**
408
+ * Get the HTML Elements for the rendered page. Note there can be more than one, since
409
+ * (at least as of writing) different modes can maintain different caches.
410
+ * @param {PageIndex} pageIndex
411
+ * @returns {HTMLElement[]}
412
+ */
413
+ BookReader.prototype.getActivePageContainerElementsForIndex = function(pageIndex) {
414
+ return [
415
+ this._modes.mode2Up.mode2UpLit.pageContainerCache[pageIndex]?.$container?.[0],
416
+ this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]?.$container?.[0],
417
+ ...(this.mode == this.constModeThumb ? this.$(`.pagediv${pageIndex}`).toArray() : []),
418
+ ].filter(x => x);
419
+ };
420
+
421
+ Object.defineProperty(BookReader.prototype, 'activeMode', {
422
+ /** @return {Mode1Up | Mode2Up | ModeThumb} */
423
+ get() { return {
424
+ 1: this._modes.mode1Up,
425
+ 2: this._modes.mode2Up,
426
+ 3: this._modes.modeThumb,
427
+ }[this.mode]; },
279
428
  });
280
429
 
281
430
  /**
@@ -290,9 +439,9 @@ BookReader.util = utils;
290
439
  * @private
291
440
  */
292
441
  BookReader.prototype.extendParams = function(params, newParams) {
293
- var modifiedNewParams = $.extend({}, newParams);
442
+ const modifiedNewParams = $.extend({}, newParams);
294
443
  if ('undefined' != typeof(modifiedNewParams.page)) {
295
- var pageIndex = this._models.book.parsePageString(modifiedNewParams.page);
444
+ const pageIndex = this.book.parsePageString(modifiedNewParams.page);
296
445
  if (!isNaN(pageIndex))
297
446
  modifiedNewParams.index = pageIndex;
298
447
  delete modifiedNewParams.page;
@@ -306,7 +455,7 @@ BookReader.prototype.extendParams = function(params, newParams) {
306
455
  * @return {object} the parsed params
307
456
  */
308
457
  BookReader.prototype.initParams = function() {
309
- var params = {};
458
+ const params = {};
310
459
  // Flag initializing for updateFromParams()
311
460
  params.init = true;
312
461
 
@@ -323,8 +472,8 @@ BookReader.prototype.initParams = function() {
323
472
 
324
473
  // If we have a title leaf, use that as the default instead of index 0,
325
474
  // but only use as default if book has a few pages
326
- if ('undefined' != typeof(this.titleLeaf) && this._models.book.getNumLeafs() > 2) {
327
- params.index = this._models.book.leafNumToIndex(this.titleLeaf);
475
+ if ('undefined' != typeof(this.titleLeaf) && this.book.getNumLeafs() > 2) {
476
+ params.index = this.book.leafNumToIndex(this.titleLeaf);
328
477
  } else {
329
478
  params.index = 0;
330
479
  }
@@ -339,9 +488,9 @@ BookReader.prototype.initParams = function() {
339
488
  }
340
489
 
341
490
  // Check for Resume plugin
342
- if (this.options.enablePageResume) {
491
+ if (this.plugins.resume?.options.enabled) {
343
492
  // Check cookies
344
- const val = this.getResumeValue();
493
+ const val = this.plugins.resume.getResumeValue();
345
494
  if (val !== null) {
346
495
  // If page index different from default
347
496
  if (params.index !== val) {
@@ -355,7 +504,7 @@ BookReader.prototype.initParams = function() {
355
504
  // Check for URL plugin
356
505
  if (this.options.enableUrlPlugin) {
357
506
  // Params explicitly set in URL take precedence over all other methods
358
- var urlParams = this.paramsFromFragment(this.urlReadFragment());
507
+ let urlParams = this.paramsFromFragment(this.urlReadFragment());
359
508
 
360
509
  // Get params if hash fragment available with 'history' urlMode
361
510
  const hasHashURL = !Object.keys(urlParams).length && this.urlReadHashFragment();
@@ -375,23 +524,24 @@ BookReader.prototype.initParams = function() {
375
524
  }
376
525
 
377
526
  // Check for Search plugin
378
- if (this.options.enableSearch) {
527
+ if (this.plugins.search?.options.enabled) {
528
+ const sp = this.plugins.search;
379
529
  // Go to first result only if no default or URL page
380
- this.options.goToFirstResult = !params.pageFound;
530
+ sp.options.goToFirstResult = !params.pageFound;
381
531
 
382
532
  // If initialSearchTerm not set
383
- if (!this.options.initialSearchTerm) {
533
+ if (!sp.initialSearchTerm) {
384
534
  // Look for any term in URL
385
535
  if (params.search) {
386
536
  // Old style: /search/[term]
387
- this.options.initialSearchTerm = params.search;
388
- this.searchTerm = params.search;
537
+ sp.options.initialSearchTerm = params.search;
538
+ sp.searchTerm = params.search;
389
539
  } else {
390
540
  // If we have a query string: q=[term]
391
541
  const searchParams = new URLSearchParams(this.readQueryString());
392
542
  const searchTerm = searchParams.get('q');
393
543
  if (searchTerm) {
394
- this.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
544
+ sp.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
395
545
  }
396
546
  }
397
547
  }
@@ -434,31 +584,57 @@ BookReader.prototype.readQueryString = function() {
434
584
  * Determines the initial mode for starting if a mode is not already
435
585
  * present in the params argument
436
586
  * @param {object} params
437
- * @return {number} the mode
587
+ * @return {1 | 2 | 3} the initial mode
438
588
  */
439
589
  BookReader.prototype.getInitialMode = function(params) {
440
- // Use params or browser width to set view mode
441
- var windowWidth = $(window).width();
442
- var nextMode;
443
- if ('undefined' != typeof(params.mode)) {
444
- nextMode = params.mode;
445
- } else if (this.ui == 'full'
446
- && this.enableMobileNav
447
- && this.isFullscreenActive
448
- && windowWidth <= this.onePageMinBreakpoint
449
- ) {
450
- // In full mode, we set the default based on width
451
- nextMode = this.constMode1up;
590
+ // if mobile breakpoint, we always show this.constMode1up mode
591
+ const windowWidth = $(window).width();
592
+ const isMobile = windowWidth && windowWidth <= this.onePageMinBreakpoint;
593
+
594
+ let initialMode;
595
+ if (params.mode) {
596
+ initialMode = params.mode;
597
+ } else if (isMobile) {
598
+ initialMode = this.constMode1up;
452
599
  } else {
453
- nextMode = this.constMode2up;
600
+ initialMode = this.constMode2up;
454
601
  }
455
602
 
456
- if (!this.canSwitchToMode(nextMode)) {
457
- nextMode = this.constMode1up;
603
+ if (!this.canSwitchToMode(initialMode)) {
604
+ initialMode = this.constMode1up;
458
605
  }
459
- return nextMode;
606
+
607
+ // override defaults mode via `options.defaults` metadata
608
+ if (this.options.defaults) {
609
+ try {
610
+ initialMode = _modeStringToNumber(this.options.defaults);
611
+ } catch (e) {
612
+ // Can ignore this error
613
+ }
614
+ }
615
+
616
+ return initialMode;
460
617
  };
461
618
 
619
+ /**
620
+ * Converts a mode string to a the mode numeric constant
621
+ * @param {'mode/1up'|'mode/2up'|'mode/thumb'} modeString
622
+ * @return {1 | 2 | 3}
623
+ */
624
+ export function _modeStringToNumber(modeString) {
625
+ const MAPPING = {
626
+ 'mode/1up': 1,
627
+ 'mode/2up': 2,
628
+ 'mode/thumb': 3,
629
+ };
630
+
631
+ if (!(modeString in MAPPING)) {
632
+ throw new OptionsParseError(`Invalid mode string: ${modeString}`);
633
+ }
634
+
635
+ return MAPPING[modeString];
636
+ }
637
+
462
638
  /**
463
639
  * This is called by the client to initialize BookReader.
464
640
  * It renders onto the DOM. It should only be called once.
@@ -467,7 +643,7 @@ BookReader.prototype.init = function() {
467
643
  this.init.initComplete = false;
468
644
  this.pageScale = this.reduce; // preserve current reduce
469
645
 
470
- var params = this.initParams();
646
+ const params = this.initParams();
471
647
 
472
648
  this.firstIndex = params.index ? params.index : 0;
473
649
 
@@ -497,41 +673,45 @@ BookReader.prototype.init = function() {
497
673
  // Explicitly ensure this.mode exists for initNavbar() below
498
674
  this.mode = params.mode;
499
675
 
500
- if (this.ui == "embed" && this.options.showNavbar) {
501
- this.initEmbedNavbar();
502
- } else {
503
- if (this.options.showToolbar) {
504
- this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
505
- }
506
- if (this.options.showNavbar) {
507
- this.initNavbar();
676
+ // Display Navigation
677
+ if (this.options.showToolbar) {
678
+ this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
679
+ }
680
+ if (this.options.showNavbar) { // default navigation
681
+ const $navBar = this.initNavbar();
682
+
683
+ // extend navbar with plugins
684
+ for (const plugin of Object.values(this.plugins)) {
685
+ plugin.extendNavBar($navBar);
508
686
  }
509
687
  }
510
688
 
511
689
  // Switch navbar controls on mobile/desktop
512
- this.switchNavbarControls();
690
+ this._components.navbar.switchNavbarControls();
513
691
 
514
692
  this.resizeBRcontainer();
515
693
  this.updateFromParams(params);
516
694
  this.initUIStrings();
517
695
 
518
696
  // Bind to events
519
-
520
- this.bindNavigationHandlers();
697
+ this._components.navbar.bindControlClickHandlers();
698
+ for (const plugin of Object.values(this.plugins)) {
699
+ plugin._bindNavigationHandlers();
700
+ }
521
701
  this.setupKeyListeners();
522
702
 
523
703
  this.lastScroll = (new Date().getTime());
524
- this.refs.$brContainer.bind('scroll', this, function(e) {
704
+ this.refs.$brContainer.on('scroll', this, function(e) {
525
705
  // Note, this scroll event fires for both user, and js generated calls
526
706
  // It is functioning in some cases as the primary triggerer for rendering
527
707
  e.data.lastScroll = (new Date().getTime());
528
- if (e.data.constMode2up != e.data.mode) {
708
+ if (e.data.constModeThumb == e.data.mode) {
529
709
  e.data.drawLeafsThrottled();
530
710
  }
531
711
  });
532
712
 
533
713
  if (this.options.autoResize) {
534
- $(window).bind('resize', this, function(e) {
714
+ $(window).on('resize', this, function(e) {
535
715
  e.data.resize();
536
716
  });
537
717
  $(window).on("orientationchange", this, function(e) {
@@ -544,18 +724,30 @@ BookReader.prototype.init = function() {
544
724
  }
545
725
 
546
726
  // If not searching, set to allow on-going fragment changes
547
- if (!this.options.initialSearchTerm) {
727
+ if (!this.plugins.search?.options.initialSearchTerm) {
548
728
  this.suppressFragmentChange = false;
549
729
  }
550
730
 
731
+ if (this.options.startFullscreen) {
732
+ this.enterFullscreen(true);
733
+ }
734
+
735
+ // Init plugins
736
+ for (const [pluginName, plugin] of Object.entries(this.plugins)) {
737
+ try {
738
+ plugin.init();
739
+ }
740
+ catch (e) {
741
+ console.error(`Error initializing plugin ${pluginName}`, e);
742
+ }
743
+ }
744
+
551
745
  this.init.initComplete = true;
552
746
  this.trigger(BookReader.eventNames.PostInit);
553
747
 
554
748
  // Must be called after this.init.initComplete set to true to allow
555
749
  // BookReader.prototype.resize to run.
556
- if (this.options.startFullscreen) {
557
- this.toggleFullscreen();
558
- }
750
+
559
751
  };
560
752
 
561
753
  /**
@@ -564,7 +756,6 @@ BookReader.prototype.init = function() {
564
756
  */
565
757
  BookReader.prototype.trigger = function(name, props = this) {
566
758
  const eventName = 'BookReader:' + name;
567
- $(document).trigger(eventName, props);
568
759
 
569
760
  utils.polyfillCustomEvent(window);
570
761
  window.dispatchEvent(new CustomEvent(eventName, {
@@ -572,14 +763,29 @@ BookReader.prototype.trigger = function(name, props = this) {
572
763
  composed: true,
573
764
  detail: { props },
574
765
  }));
766
+ $(document).trigger(eventName, props);
767
+ };
768
+
769
+ BookReader.prototype.on = function(name, callback) {
770
+ $(document).on('BookReader:' + name, callback);
771
+ };
772
+
773
+ BookReader.prototype.off = function(name, callback) {
774
+ $(document).off('BookReader:' + name, callback);
575
775
  };
576
776
 
777
+ /**
778
+ * @deprecated Use .on and .off instead
779
+ */
577
780
  BookReader.prototype.bind = function(name, callback) {
578
- $(document).bind('BookReader:' + name, callback);
781
+ return this.on(name, callback);
579
782
  };
580
783
 
784
+ /**
785
+ * @deprecated Use .on and .off instead
786
+ */
581
787
  BookReader.prototype.unbind = function(name, callback) {
582
- $(document).unbind('BookReader:' + name, callback);
788
+ return this.off(name, callback);
583
789
  };
584
790
 
585
791
  /**
@@ -591,135 +797,143 @@ BookReader.prototype.resize = function() {
591
797
  this.resizeBRcontainer();
592
798
 
593
799
  // Switch navbar controls on mobile/desktop
594
- this.switchNavbarControls();
800
+ this._components.navbar.switchNavbarControls();
595
801
 
596
802
  if (this.constMode1up == this.mode) {
597
803
  if (this.onePage.autofit != 'none') {
598
- this.resizePageView1up();
804
+ this._modes.mode1Up.resizePageView();
599
805
  this.centerPageView();
600
- if (this.enableSearch) this.updateSearchHilites(); //deletes highlights but does not call remove()
601
806
  } else {
602
807
  this.centerPageView();
603
808
  this.displayedIndices = [];
604
- if (this.enableSearch) this.updateSearchHilites(); //deletes highlights but does not call remove()
605
809
  this.drawLeafsThrottled();
606
810
  }
607
811
  } else if (this.constModeThumb == this.mode) {
608
- this.prepareThumbnailView();
812
+ this._modes.modeThumb.prepare();
609
813
  } else {
610
- // We only need to prepare again in autofit (size of spread changes)
611
- if (this.twoPage.autofit) {
612
- // most common path, esp. for archive.org books
613
- this.prepareTwoPageView();
614
- } else {
615
- // used when zoomed in
616
- // Re-center if the scrollbars have disappeared
617
- var center = this.twoPageGetViewCenter();
618
- var doRecenter = false;
619
- if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
620
- center.percentageX = 0.5;
621
- doRecenter = true;
622
- }
623
- if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
624
- center.percentageY = 0.5;
625
- doRecenter = true;
626
- }
627
- if (doRecenter) {
628
- this.twoPageCenterView(center.percentageX, center.percentageY);
629
- }
630
- }
814
+ this._modes.mode2Up.resizePageView();
631
815
  }
632
816
  this.trigger(BookReader.eventNames.resize);
633
817
  };
634
818
 
635
819
  /**
636
- * Binds keyboard event listeners
820
+ * Binds keyboard and keyboard focus event listeners
637
821
  */
638
- BookReader.prototype.setupKeyListeners = function() {
639
- var self = this;
640
-
641
- var KEY_PGUP = 33;
642
- var KEY_PGDOWN = 34;
643
- var KEY_END = 35;
644
- var KEY_HOME = 36;
645
-
646
- var KEY_LEFT = 37;
647
- var KEY_UP = 38;
648
- var KEY_RIGHT = 39;
649
- var KEY_DOWN = 40;
650
- // The minus(-) and equal(=) keys have different mappings for different browsers
651
- var KEY_MINUS = 189; // Chrome
652
- var KEY_MINUS_F = 173; // Firefox
653
- var KEY_NUMPAD_SUBTRACT = 109;
654
- var KEY_EQUAL = 187; // Chrome
655
- var KEY_EQUAL_F = 61; // Firefox
656
- var KEY_NUMPAD_ADD = 107;
657
-
658
- // We use document here instead of window to avoid a bug in jQuery on IE7
659
- $(document).keydown(function(e) {
660
-
661
- // Keyboard navigation
662
- if (!self.keyboardNavigationIsDisabled(e)) {
663
- switch (e.keyCode) {
664
- case KEY_PGUP:
665
- case KEY_UP:
666
- // In 1up mode page scrolling is handled by browser
667
- if (self.constMode2up == self.mode) {
668
- e.preventDefault();
669
- self.prev();
670
- }
671
- break;
672
- case KEY_DOWN:
673
- case KEY_PGDOWN:
674
- if (self.constMode2up == self.mode) {
675
- e.preventDefault();
676
- self.next();
822
+ BookReader.prototype.setupKeyListeners = function () {
823
+
824
+ // Keyboard focus by BookReader in viewport
825
+ //
826
+ // Intersection observer and callback sets BookReader keyboard
827
+ // "focus" flag off when the BookReader is not in the viewport.
828
+ if (window.IntersectionObserver) {
829
+ const observer = new IntersectionObserver((entries) => {
830
+ entries.forEach((entry) => {
831
+ if (entry.intersectionRatio === 0) {
832
+ this.hasKeyFocus = false;
833
+ } else {
834
+ this.hasKeyFocus = true;
677
835
  }
678
- break;
679
- case KEY_END:
836
+ });
837
+ }, {
838
+ root: null,
839
+ rootMargin: '0px',
840
+ threshold: [0, 0.05, 1],
841
+ });
842
+ observer.observe(this.refs.$br[0]);
843
+ }
844
+
845
+ // Keyboard listeners
846
+ document.addEventListener('keydown', (e) => {
847
+
848
+ // Ignore if BookReader "focus" flag not set
849
+ if (!this.hasKeyFocus) {
850
+ return;
851
+ }
852
+
853
+ // Ignore if modifiers are active.
854
+ if (e.getModifierState('Control') ||
855
+ e.getModifierState('Alt') ||
856
+ e.getModifierState('Meta') ||
857
+ e.getModifierState('Win') /* hack for IE */) {
858
+ return;
859
+ }
860
+
861
+ // Ignore in input elements
862
+ if (utils.isInputActive()) {
863
+ return;
864
+ }
865
+
866
+ // KeyboardEvent code values:
867
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
868
+ switch (e.key) {
869
+
870
+ // Page navigation
871
+ case "Home":
872
+ e.preventDefault();
873
+ this.first();
874
+ break;
875
+ case "End":
876
+ e.preventDefault();
877
+ this.last();
878
+ break;
879
+ case "PageDown":
880
+ // In 1up and thumb mode page scrolling handled by browser
881
+ if (this.constMode2up === this.mode) {
680
882
  e.preventDefault();
681
- self.last();
682
- break;
683
- case KEY_HOME:
883
+ this.next();
884
+ }
885
+ break;
886
+ case "PageUp":
887
+ // In 1up and thumb mode page scrolling handled by browser
888
+ if (this.constMode2up === this.mode) {
684
889
  e.preventDefault();
685
- self.first();
686
- break;
687
- case KEY_LEFT:
688
- if (self.constModeThumb != self.mode) {
689
- e.preventDefault();
690
- self.left();
691
- }
692
- break;
693
- case KEY_RIGHT:
694
- if (self.constModeThumb != self.mode) {
695
- e.preventDefault();
696
- self.right();
697
- }
698
- break;
699
- case KEY_MINUS:
700
- case KEY_MINUS_F:
701
- case KEY_NUMPAD_SUBTRACT:
890
+ this.prev();
891
+ }
892
+ break;
893
+ case "ArrowLeft":
894
+ case "Left": // hack for IE and old Gecko
895
+ // No y-scrolling in thumb mode
896
+ if (this.constModeThumb != this.mode) {
702
897
  e.preventDefault();
703
- self.zoom(-1);
704
- break;
705
- case KEY_EQUAL:
706
- case KEY_EQUAL_F:
707
- case KEY_NUMPAD_ADD:
898
+ this.left();
899
+ }
900
+ break;
901
+ case "ArrowRight":
902
+ case "Right": // hack for IE and old Gecko
903
+ // No y-scrolling in thumb mode
904
+ if (this.constModeThumb != this.mode) {
708
905
  e.preventDefault();
709
- self.zoom(+1);
710
- break;
906
+ this.right();
711
907
  }
908
+ break;
909
+ // Zoom
910
+ case '-':
911
+ case 'Subtract':
912
+ e.preventDefault();
913
+ this.zoom(-1);
914
+ break;
915
+ case '+':
916
+ case '=':
917
+ case 'Add':
918
+ e.preventDefault();
919
+ this.zoom(1);
920
+ break;
921
+ // Fullscreen
922
+ case 'F':
923
+ case 'f':
924
+ e.preventDefault();
925
+ this.toggleFullscreen();
926
+ break;
712
927
  }
713
928
  });
714
929
  };
715
930
 
716
931
  BookReader.prototype.drawLeafs = function() {
717
932
  if (this.constMode1up == this.mode) {
718
- this.drawLeafsOnePage();
719
- } else if (this.constModeThumb == this.mode) {
720
- this.drawLeafsThumbnail();
933
+ // Not needed for Mode1Up anymore
934
+ return;
721
935
  } else {
722
- this.drawLeafsTwoPage();
936
+ this.activeMode.drawLeafs();
723
937
  }
724
938
  };
725
939
 
@@ -728,11 +942,17 @@ BookReader.prototype.drawLeafs = function() {
728
942
  * @param {PageIndex} index
729
943
  */
730
944
  BookReader.prototype._createPageContainer = function(index) {
731
- return new PageContainer(this._models.book.getPage(index, false), {
945
+ const pageContainer = new PageContainer(this.book.getPage(index, false), {
732
946
  isProtected: this.protected,
733
947
  imageCache: this.imageCache,
734
- loadingImage: this.imagesBaseURL + 'loading.gif',
735
948
  });
949
+
950
+ // Call plugin handlers
951
+ for (const plugin of Object.values(this.plugins)) {
952
+ plugin._configurePageContainer(pageContainer);
953
+ }
954
+
955
+ return pageContainer;
736
956
  };
737
957
 
738
958
  BookReader.prototype.bindGestures = function(jElement) {
@@ -742,8 +962,8 @@ BookReader.prototype.bindGestures = function(jElement) {
742
962
  // when you move the book with one finger and then add another
743
963
  // finger to pinch. Gestures are aware of scroll state.
744
964
 
745
- var self = this;
746
- var numTouches = 1;
965
+ const self = this;
966
+ let numTouches = 1;
747
967
 
748
968
  jElement.unbind('touchmove').bind('touchmove', function(e) {
749
969
  if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
@@ -764,60 +984,25 @@ BookReader.prototype.bindGestures = function(jElement) {
764
984
  });
765
985
  };
766
986
 
767
- /** @deprecated Not used outside ModeThumb */
768
- BookReader.prototype.drawLeafsThumbnail = ModeThumb.prototype.drawLeafs;
769
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'drawLeafs', 'drawLeafsThumbnail');
770
- /** @deprecated Not used outside ModeThumb */
771
- BookReader.prototype.lazyLoadThumbnails = ModeThumb.prototype.lazyLoadThumbnails;
772
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadThumbnails', 'lazyLoadThumbnails');
773
- BookReader.prototype.lazyLoadImage = ModeThumb.prototype.lazyLoadImage;
774
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'lazyLoadImage', 'lazyLoadImage');
775
- /** @deprecated Internal use only */
776
- BookReader.prototype.zoomThumb = ModeThumb.prototype.zoom;
777
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'zoom', 'zoomThumb');
778
- /** @deprecated Not used outside ModeThumb */
779
- BookReader.prototype.getThumbnailWidth = ModeThumb.prototype.getThumbnailWidth;
780
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'getThumbnailWidth', 'getThumbnailWidth');
781
- /** @deprecated Not used outside ModeThumb */
782
- BookReader.prototype.prepareThumbnailView = ModeThumb.prototype.prepare;
783
- exposeOverrideableMethod(ModeThumb, '_modes.modeThumb', 'prepare', 'prepareThumbnailView');
784
-
785
987
  /**
786
988
  * A throttled version of drawLeafs
787
989
  */
788
990
  BookReader.prototype.drawLeafsThrottled = utils.throttle(
789
991
  BookReader.prototype.drawLeafs,
790
- 250 // 250 ms gives quick feedback, but doesn't eat cpu
992
+ 250, // 250 ms gives quick feedback, but doesn't eat cpu
791
993
  );
792
994
 
793
995
  /**
794
996
  * @param {number} direction Pass 1 to zoom in, anything else to zoom out
795
997
  */
796
998
  BookReader.prototype.zoom = function(direction) {
797
- switch (this.mode) {
798
- case this.constMode1up:
799
- if (direction == 1) {
800
- // XXX other cases
801
- this.zoom1up('in');
802
- } else {
803
- this.zoom1up('out');
804
- }
805
- break;
806
- case this.constMode2up:
807
- if (direction == 1) {
808
- // XXX other cases
809
- this.zoom2up('in');
810
- } else {
811
- this.zoom2up('out');
812
- }
813
- break;
814
- case this.constModeThumb:
815
- // XXX update zoomThumb for named directions
816
- this.zoomThumb(direction);
817
- break;
999
+ if (direction == 1) {
1000
+ this.activeMode.zoom('in');
1001
+ } else {
1002
+ this.activeMode.zoom('out');
818
1003
  }
819
-
820
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1004
+ this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1005
+ this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
821
1006
  return;
822
1007
  };
823
1008
 
@@ -834,19 +1019,19 @@ BookReader.prototype.resizeBRcontainer = function(animate) {
834
1019
  if (animate) {
835
1020
  this.refs.$brContainer.animate({
836
1021
  top: this.getToolBarHeight(),
837
- bottom: this.getFooterHeight()
1022
+ bottom: this.getFooterHeight(),
838
1023
  }, this.constResizeAnimationDuration, 'linear');
839
1024
  } else {
840
1025
  this.refs.$brContainer.css({
841
1026
  top: this.getToolBarHeight(),
842
- bottom: this.getFooterHeight()
1027
+ bottom: this.getFooterHeight(),
843
1028
  });
844
1029
  }
845
1030
  };
846
1031
 
847
1032
  BookReader.prototype.centerPageView = function() {
848
- var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
849
- var clientWidth = this.refs.$brContainer.prop('clientWidth');
1033
+ const scrollWidth = this.refs.$brContainer.prop('scrollWidth');
1034
+ const clientWidth = this.refs.$brContainer.prop('clientWidth');
850
1035
  if (scrollWidth > clientWidth) {
851
1036
  this.refs.$brContainer.prop('scrollLeft', (scrollWidth - clientWidth) / 2);
852
1037
  }
@@ -930,14 +1115,16 @@ BookReader.prototype._reduceSort = (a, b) => a.reduce - b.reduce;
930
1115
 
931
1116
  /**
932
1117
  * Attempts to jump to page
933
- * @param {string}
1118
+ * @param {string} pageNum
1119
+ * @param {object} options
1120
+ * @param {boolean} [options.ariaLive = false]
934
1121
  * @return {boolean} Returns true if page could be found, false otherwise.
935
1122
  */
936
- BookReader.prototype.jumpToPage = function(pageNum) {
937
- var pageIndex = this._models.book.parsePageString(pageNum);
1123
+ BookReader.prototype.jumpToPage = function(pageNum, {ariaLive = false} = {}) {
1124
+ const pageIndex = this.book.parsePageString(pageNum);
938
1125
 
939
1126
  if ('undefined' != typeof(pageIndex)) {
940
- this.jumpToIndex(pageIndex);
1127
+ this.jumpToIndex(pageIndex, { ariaLive });
941
1128
  return true;
942
1129
  }
943
1130
 
@@ -950,44 +1137,47 @@ BookReader.prototype.jumpToPage = function(pageNum) {
950
1137
  * @param {PageIndex} index
951
1138
  */
952
1139
  BookReader.prototype._isIndexDisplayed = function(index) {
953
- // One up "caches" pages +- current, so exclude those in the test.
954
- return this.constMode1up == this.mode ? this.displayedIndices.slice(1, -1).includes(index) :
955
- this.displayedIndices ? this.displayedIndices.includes(index) :
956
- this.currentIndex() == index;
1140
+ return this.displayedIndices ? this.displayedIndices.includes(index) :
1141
+ this.currentIndex() == index;
957
1142
  };
958
1143
 
959
1144
  /**
960
1145
  * Changes the current page
961
- * @param {PageIndex} index
962
- * @param {number} [pageX]
963
- * @param {number} [pageY]
964
- * @param {boolean} [noAnimate]
965
- */
966
- BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
1146
+ * @param {PageIndex | 'left' | 'right' | 'next' | 'prev'} indexOrDirection
1147
+ * @param {object} options
1148
+ * @param {number} [options.pageX] Position on page ; not implemented
1149
+ * @param {number} [options.pageY] Position on page ; not implemented
1150
+ * @param {boolean} [options.noAnimate]
1151
+ * @param {number | 'fast' | 'slow'} [options.flipSpeed]
1152
+ * @param {boolean} [options.ariaLive]
1153
+ * @param {boolean} [options.triggerStop] - whether to trigger the stop event; default true; maybe deprecated?
1154
+ */
1155
+ BookReader.prototype.jumpToIndex = function(indexOrDirection, {pageX = 0, pageY = 0, noAnimate = false, flipSpeed = null, ariaLive = false, triggerStop = true} = {}) {
1156
+ const page = this.activeMode.parsePageSpecifier(indexOrDirection);
1157
+ flipSpeed = utils.parseAnimationSpeed(flipSpeed) || this.flipSpeed;
1158
+ if (!page || page.index == this.currentIndex()) {
1159
+ return;
1160
+ }
1161
+
967
1162
  // Don't jump into specific unviewable page
968
- const page = this._models.book.getPage(index);
969
1163
  if (!page.isViewable && page.unviewablesStart != page.index) {
970
1164
  // If already in unviewable range, jump to end of that range
971
1165
  const alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
972
- const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true }).index : page.unviewablesStart;
973
- return this.jumpToIndex(newIndex, pageX, pageY, noAnimate);
974
- }
975
-
976
- this.trigger(BookReader.eventNames.stop);
977
-
978
- if (this.constMode2up == this.mode) {
979
- this._modes.mode2Up.jumpToIndex(index);
980
- } else if (this.constModeThumb == this.mode) {
981
- this._modes.modeThumb.jumpToIndex(index);
982
- } else { // 1up
983
- this._modes.mode1Up.jumpToIndex(index, pageX, pageY, noAnimate);
1166
+ const newIndex = alreadyInPreview ? page.findNext({ combineConsecutiveUnviewables: true })?.index : page.unviewablesStart;
1167
+ // Rare, but a book can end on an unviewable page, so this could be undefined
1168
+ if (typeof newIndex !== 'undefined') {
1169
+ this.jumpToIndex(newIndex, { pageX, pageY, noAnimate, flipSpeed, ariaLive });
1170
+ }
1171
+ } else {
1172
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1173
+ this.trigger(BookReader.eventNames.beforePageChanged, { index: page.index, ariaLive });
1174
+ this.activeMode.jumpToIndex(page.index, { pageX, pageY, noAnimate, flipSpeed, ariaLive });
984
1175
  }
985
1176
  };
986
1177
 
987
1178
  /**
988
1179
  * Return mode or 1up if initial thumb
989
1180
  * @param {number}
990
- * @see BookReader.prototype.drawLeafsThumbnail
991
1181
  */
992
1182
  BookReader.prototype.getPrevReadMode = function(mode) {
993
1183
  if (mode === BookReader.constMode1up || mode === BookReader.constMode2up) {
@@ -1000,7 +1190,7 @@ BookReader.prototype.getPrevReadMode = function(mode) {
1000
1190
 
1001
1191
  /**
1002
1192
  * Switches the mode (eg 1up 2up thumb)
1003
- * @param {number}
1193
+ * @param {number|'1up' | '2up' | 'thumb'}
1004
1194
  * @param {object} [options]
1005
1195
  * @param {boolean} [options.suppressFragmentChange = false]
1006
1196
  * @param {boolean} [options.onInit = false] - this
@@ -1010,9 +1200,21 @@ BookReader.prototype.switchMode = function(
1010
1200
  {
1011
1201
  suppressFragmentChange = false,
1012
1202
  init = false,
1013
- pageFound = false
1014
- } = {}
1203
+ pageFound = false,
1204
+ } = {},
1015
1205
  ) {
1206
+ if (typeof mode === 'string') {
1207
+ mode = {
1208
+ '1up': this.constMode1up,
1209
+ '2up': this.constMode2up,
1210
+ 'thumb': this.constModeThumb,
1211
+ }[mode];
1212
+ }
1213
+
1214
+ if (!mode) {
1215
+ throw new Error(`Invalid mode: ${mode}`);
1216
+ }
1217
+
1016
1218
  // Skip checks before init() complete
1017
1219
  if (this.init.initComplete) {
1018
1220
  if (mode === this.mode) {
@@ -1024,10 +1226,13 @@ BookReader.prototype.switchMode = function(
1024
1226
  }
1025
1227
 
1026
1228
  this.trigger(BookReader.eventNames.stop);
1027
- if (this.enableSearch) this.removeSearchHilites();
1028
1229
 
1029
1230
  this.prevReadMode = this.getPrevReadMode(this.mode);
1030
1231
 
1232
+ if (this.mode != mode) {
1233
+ this.activeMode.unprepare?.();
1234
+ }
1235
+
1031
1236
  this.mode = mode;
1032
1237
 
1033
1238
  // reinstate scale if moving from thumbnail view
@@ -1040,42 +1245,32 @@ BookReader.prototype.switchMode = function(
1040
1245
 
1041
1246
  // XXX maybe better to preserve zoom in each mode
1042
1247
  if (this.constMode1up == mode) {
1043
- this.onePageCalculateReductionFactors();
1044
- this.reduce = this.quantizeReduce(this.reduce, this.onePage.reductionFactors);
1045
- this.prepareOnePageView();
1248
+ this._modes.mode1Up.prepare();
1046
1249
  } else if (this.constModeThumb == mode) {
1047
1250
  this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
1048
- this.prepareThumbnailView();
1251
+ this._modes.modeThumb.prepare();
1049
1252
  } else {
1050
- // $$$ why don't we save autofit?
1051
- // this.twoPage.autofit = null; // Take zoom level from other mode
1052
- // spread indices not set, so let's set them
1053
- if (init || !pageFound) {
1054
- this.setSpreadIndices();
1055
- }
1056
-
1057
- this.twoPageCalculateReductionFactors(); // this sets this.twoPage && this.reduce
1058
- this.prepareTwoPageView();
1059
- this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
1253
+ this._modes.mode2Up.prepare();
1060
1254
  }
1061
1255
 
1062
1256
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1063
1257
  this.trigger(BookReader.eventNames.fragmentChange);
1064
1258
  }
1065
- var eventName = mode + 'PageViewSelected';
1259
+ const eventName = mode + 'PageViewSelected';
1066
1260
  this.trigger(BookReader.eventNames[eventName]);
1067
1261
 
1068
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1262
+ this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1263
+ this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1069
1264
  };
1070
1265
 
1071
1266
  BookReader.prototype.updateBrClasses = function() {
1072
- var modeToClass = {};
1267
+ const modeToClass = {};
1073
1268
  modeToClass[this.constMode1up] = 'BRmode1up';
1074
- modeToClass[this.constMode2up] = 'BRmode2Up';
1269
+ modeToClass[this.constMode2up] = 'BRmode2up';
1075
1270
  modeToClass[this.constModeThumb] = 'BRmodeThumb';
1076
1271
 
1077
1272
  this.refs.$br
1078
- .removeClass('BRmode1up BRmode2Up BRmodeThumb')
1273
+ .removeClass('BRmode1up BRmode2up BRmodeThumb')
1079
1274
  .addClass(modeToClass[this.mode]);
1080
1275
 
1081
1276
  if (this.isFullscreen()) {
@@ -1095,31 +1290,30 @@ BookReader.prototype.isFullscreen = function() {
1095
1290
  * Toggles fullscreen
1096
1291
  * @param { boolean } bindKeyboardControls
1097
1292
  */
1098
- BookReader.prototype.toggleFullscreen = function(bindKeyboardControls = true) {
1293
+ BookReader.prototype.toggleFullscreen = async function(bindKeyboardControls = true) {
1099
1294
  if (this.isFullscreen()) {
1100
- this.exitFullScreen();
1295
+ await this.exitFullScreen();
1101
1296
  } else {
1102
- this.enterFullscreen(bindKeyboardControls);
1297
+ await this.enterFullscreen(bindKeyboardControls);
1103
1298
  }
1104
1299
  };
1105
1300
 
1106
1301
  /**
1107
1302
  * Enters fullscreen
1108
1303
  * including:
1109
- * - animation
1110
1304
  * - binds keyboard controls
1111
1305
  * - fires custom event
1112
1306
  * @param { boolean } bindKeyboardControls
1113
1307
  */
1114
- BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1308
+ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = true) {
1309
+ this.refs.$br.addClass('BRfullscreenAnimation');
1115
1310
  const currentIndex = this.currentIndex();
1116
- this.refs.$brContainer.css('opacity', 0);
1117
1311
 
1118
1312
  if (bindKeyboardControls) {
1119
1313
  this._fullscreenCloseHandler = (e) => {
1120
1314
  if (e.keyCode === 27) this.toggleFullscreen();
1121
1315
  };
1122
- $(document).keyup(this._fullscreenCloseHandler);
1316
+ $(document).on("keyup", this._fullscreenCloseHandler);
1123
1317
  }
1124
1318
 
1125
1319
  const windowWidth = $(window).width();
@@ -1128,15 +1322,34 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1128
1322
  }
1129
1323
 
1130
1324
  this.isFullscreenActive = true;
1131
- this.animating = true;
1132
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear',() => {
1133
- this.resize();
1134
- this.jumpToIndex(currentIndex);
1135
- this.animating = false;
1136
- });
1137
1325
 
1138
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1326
+ // Change tooltip of fullscreen button
1327
+ this.$('.BRnav .BRicon.full').attr('title', 'Exit fullscreen');
1328
+ this.$('.BRnav .BRicon.full .BRtooltip').text('Exit fullscreen');
1329
+
1330
+ // prioritize class updates so CSS can propagate
1331
+ this.updateBrClasses();
1332
+ if (this.activeMode instanceof Mode1Up) {
1333
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(currentIndex));
1334
+ // Need the new scale to be applied before calling jumpToIndex
1335
+ this.activeMode.mode1UpLit.requestUpdate();
1336
+ await this.activeMode.mode1UpLit.updateComplete;
1337
+ }
1338
+ this.jumpToIndex(currentIndex);
1339
+
1340
+ this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1341
+ this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1342
+ // Add "?view=theater"
1343
+ this.trigger(BookReader.eventNames.fragmentChange);
1344
+ // trigger event here, so that animations,
1345
+ // class updates happen before book-nav relays to web components
1139
1346
  this.trigger(BookReader.eventNames.fullscreenToggled);
1347
+
1348
+ // resize book after all events & css updates
1349
+ await new Promise(resolve => setTimeout(resolve, 0));
1350
+
1351
+ this.resize();
1352
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1140
1353
  };
1141
1354
 
1142
1355
  /**
@@ -1146,27 +1359,51 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1146
1359
  * - fires custom event
1147
1360
  * @param { boolean } bindKeyboardControls
1148
1361
  */
1149
- BookReader.prototype.exitFullScreen = function() {
1150
- this.refs.$brContainer.css('opacity', 0);
1151
-
1152
- $(document).unbind('keyup', this._fullscreenCloseHandler);
1153
-
1154
- var windowWidth = $(window).width();
1362
+ BookReader.prototype.exitFullScreen = async function () {
1363
+ this.refs.$br.addClass('BRfullscreenAnimation');
1364
+ $(document).off('keyup', this._fullscreenCloseHandler);
1155
1365
 
1366
+ const windowWidth = $(window).width();
1156
1367
  const canShow2up = this.options.controls.twoPage.visible;
1157
1368
  if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1158
1369
  this.switchMode(this.constMode2up);
1159
1370
  }
1160
1371
 
1161
1372
  this.isFullscreenActive = false;
1162
- this.updateBrClasses();
1163
- this.animating = true;
1164
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', () => {
1165
- this.resize();
1166
- this.animating = false;
1167
- });
1168
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1373
+
1374
+ this.$('.BRnav .BRicon.full').attr('title', 'Go fullscreen');
1375
+ this.$('.BRnav .BRicon.full .BRtooltip').text('Go fullscreen');
1376
+
1377
+ // Trigger fullscreen event immediately
1378
+ // so that book-nav can relay to web components
1169
1379
  this.trigger(BookReader.eventNames.fullscreenToggled);
1380
+
1381
+ this.updateBrClasses();
1382
+ await new Promise(resolve => setTimeout(resolve, 0));
1383
+ this.resize();
1384
+
1385
+ if (this.activeMode instanceof Mode1Up) {
1386
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(this.currentIndex()));
1387
+ this.activeMode.mode1UpLit.requestUpdate();
1388
+ await this.activeMode.mode1UpLit.updateComplete;
1389
+ }
1390
+
1391
+ this.plugins.textSelection?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1392
+ this.plugins.translate?.textSelectionManager.stopPageFlip(this.refs.$brContainer);
1393
+ // Remove "?view=theater"
1394
+ this.trigger(BookReader.eventNames.fragmentChange);
1395
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1396
+ };
1397
+
1398
+ /**
1399
+ * Toggles the mobile slider and page controls
1400
+ */
1401
+ BookReader.prototype.toggleSlider = function () {
1402
+ const toggleButton = this.refs.$BRnav.find('.toggle_slider');
1403
+ const mobileControls = this.refs.$br.find('.BRnavMobile');
1404
+
1405
+ toggleButton.toggleClass('active');
1406
+ mobileControls.toggleClass('docked');
1170
1407
  };
1171
1408
 
1172
1409
  /**
@@ -1180,7 +1417,7 @@ BookReader.prototype.currentIndex = function() {
1180
1417
  return this.firstIndex; // $$$ TODO page in center of view would be better
1181
1418
  } else if (this.mode == this.constMode2up) {
1182
1419
  // Only allow indices that are actually present in book
1183
- return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
1420
+ return utils.clamp(this.firstIndex, 0, this.book.getNumLeafs() - 1);
1184
1421
  } else {
1185
1422
  throw 'currentIndex called for unimplemented mode ' + this.mode;
1186
1423
  }
@@ -1195,13 +1432,11 @@ BookReader.prototype.currentIndex = function() {
1195
1432
  */
1196
1433
  BookReader.prototype.updateFirstIndex = function(
1197
1434
  index,
1198
- { suppressFragmentChange = false } = {}
1435
+ { suppressFragmentChange = false } = {},
1199
1436
  ) {
1200
- // Called multiple times when defaults contains "mode/1up",
1201
- // including after init(). Skip fragment change if no index change
1202
- if (this.firstIndex === index) {
1203
- suppressFragmentChange = true;
1204
- }
1437
+ // If there's no change, do nothing
1438
+ if (this.firstIndex === index) return;
1439
+
1205
1440
  this.firstIndex = index;
1206
1441
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1207
1442
  this.trigger(BookReader.eventNames.fragmentChange);
@@ -1209,22 +1444,21 @@ BookReader.prototype.updateFirstIndex = function(
1209
1444
  // If there's an initial search we stop suppressing global URL changes
1210
1445
  // when local suppression ends
1211
1446
  // This seems to correctly handle multiple calls during mode/1up
1212
- if (this.options.initialSearchTerm && !suppressFragmentChange) {
1447
+ if (this.plugins.search?.options.initialSearchTerm && !suppressFragmentChange) {
1213
1448
  this.suppressFragmentChange = false;
1214
1449
  }
1215
- this.trigger('pageChanged');
1216
- this.updateNavIndexThrottled(index);
1450
+
1451
+ this.trigger(BookReader.eventNames.pageChanged);
1452
+
1453
+ // event to know if user is actively reading
1454
+ this.trigger(BookReader.eventNames.userAction);
1217
1455
  };
1218
1456
 
1219
1457
  /**
1220
1458
  * Flip the right page over onto the left
1221
1459
  */
1222
- BookReader.prototype.right = function() {
1223
- if ('rl' != this.pageProgression) {
1224
- this.next();
1225
- } else {
1226
- this.prev();
1227
- }
1460
+ BookReader.prototype.right = function({ ariaLive = true, triggerStop = true } = {}) {
1461
+ this.jumpToIndex('right', { ariaLive, triggerStop });
1228
1462
  };
1229
1463
 
1230
1464
  /**
@@ -1241,12 +1475,8 @@ BookReader.prototype.rightmost = function() {
1241
1475
  /**
1242
1476
  * Flip the left page over onto the right
1243
1477
  */
1244
- BookReader.prototype.left = function() {
1245
- if ('rl' != this.pageProgression) {
1246
- this.prev();
1247
- } else {
1248
- this.next();
1249
- }
1478
+ BookReader.prototype.left = function({ ariaLive = true, triggerStop = true } = {}) {
1479
+ this.jumpToIndex('left', { ariaLive, triggerStop });
1250
1480
  };
1251
1481
 
1252
1482
  /**
@@ -1260,283 +1490,42 @@ BookReader.prototype.leftmost = function() {
1260
1490
  }
1261
1491
  };
1262
1492
 
1263
- BookReader.prototype.next = function() {
1264
- if (this.constMode2up == this.mode) {
1265
- this.trigger(BookReader.eventNames.stop);
1266
- this.flipFwdToIndex(null);
1267
- } else {
1268
- if (this.firstIndex < this.lastDisplayableIndex()) {
1269
- this.jumpToIndex(this.firstIndex + 1);
1270
- }
1271
- }
1272
- };
1273
-
1274
- BookReader.prototype.prev = function() {
1275
- const isOnFrontPage = this.firstIndex < 1;
1276
- if (isOnFrontPage) return;
1277
-
1278
- if (this.constMode2up == this.mode) {
1279
- this.trigger(BookReader.eventNames.stop);
1280
- this.flipBackToIndex(null);
1281
- } else {
1282
- if (this.firstIndex >= 1) {
1283
- this.jumpToIndex(this.firstIndex - 1);
1284
- }
1285
- }
1286
- };
1287
-
1288
- BookReader.prototype.first = function() {
1289
- this.jumpToIndex(this.firstDisplayableIndex());
1290
- };
1291
-
1292
- BookReader.prototype.last = function() {
1293
- this.jumpToIndex(this.lastDisplayableIndex());
1294
- };
1295
-
1296
1493
  /**
1297
- * Scrolls down one screen view
1494
+ * @param {object} options
1495
+ * @param {boolean} [options.triggerStop = true]
1496
+ * @param {number | 'fast' | 'slow'} [options.flipSpeed]
1497
+ * @param {boolean} [options.ariaLive = true]
1298
1498
  */
1299
- BookReader.prototype.scrollDown = function() {
1300
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1301
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1302
- // Whole pages are visible, scroll whole page only
1303
- return this.next();
1304
- }
1305
-
1306
- this.refs.$brContainer.stop(true).animate(
1307
- { scrollTop: '+=' + this._scrollAmount() + 'px'},
1308
- 400, 'easeInOutExpo'
1309
- );
1310
- return true;
1311
- } else {
1312
- return false;
1313
- }
1499
+ BookReader.prototype.next = function({
1500
+ triggerStop = true,
1501
+ flipSpeed = null,
1502
+ ariaLive = true,
1503
+ } = {}) {
1504
+ this.jumpToIndex('next', { ariaLive, flipSpeed, triggerStop });
1314
1505
  };
1315
1506
 
1316
1507
  /**
1317
- * Scrolls up one screen view
1508
+ * @param {object} options
1509
+ * @param {boolean} [options.triggerStop = true]
1510
+ * @param {number | 'fast' | 'slow'} [options.flipSpeed]
1511
+ * @param {boolean} [options.ariaLive = true]
1318
1512
  */
1319
- BookReader.prototype.scrollUp = function() {
1320
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1321
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1322
- // Whole pages are visible, scroll whole page only
1323
- return this.prev();
1324
- }
1325
-
1326
- this.refs.$brContainer.stop(true).animate(
1327
- { scrollTop: '-=' + this._scrollAmount() + 'px'},
1328
- 400, 'easeInOutExpo'
1329
- );
1330
- return true;
1331
- } else {
1332
- return false;
1333
- }
1513
+ BookReader.prototype.prev = function({
1514
+ triggerStop = true,
1515
+ flipSpeed = null,
1516
+ ariaLive = true,
1517
+ } = {}) {
1518
+ this.jumpToIndex('prev', { ariaLive, flipSpeed, triggerStop });
1334
1519
  };
1335
1520
 
1336
- /**
1337
- * The amount to scroll vertically in integer pixels
1338
- */
1339
- BookReader.prototype._scrollAmount = function() {
1340
- if (this.constMode1up == this.mode) {
1341
- // Overlap by % of page size
1342
- return parseInt(this.refs.$brContainer.prop('clientHeight') - this._models.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
1343
- }
1344
-
1345
- return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
1521
+ BookReader.prototype.first = function({ ariaLive = true, triggerStop = false } = {}) {
1522
+ this.jumpToIndex(0, { ariaLive, triggerStop });
1346
1523
  };
1347
1524
 
1348
- /**
1349
- * @deprecated No longer used; will be remove in v5
1350
- */
1351
- BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
1352
- console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
1525
+ BookReader.prototype.last = function({ ariaLive = true, triggerStop = false } = {}) {
1526
+ this.jumpToIndex(this.book.getNumLeafs() - 1, { ariaLive, triggerStop });
1353
1527
  };
1354
1528
 
1355
- /**
1356
- * @deprecated No longer used; will be remove in v5
1357
- */
1358
- BookReader.prototype.pruneUnusedImgs = function() {
1359
- console.warn('Call to deprecated function: BookReader.pruneUnused. No-op.');
1360
- };
1361
-
1362
- /************************/
1363
- /** Mode1Up extensions **/
1364
- /************************/
1365
- /** @deprecated not used outside BookReader */
1366
- BookReader.prototype.prepareOnePageView = Mode1Up.prototype.prepare;
1367
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'prepare', 'prepareOnePageView');
1368
- /** @deprecated not used outside BookReader */
1369
- BookReader.prototype.drawLeafsOnePage = Mode1Up.prototype.drawLeafs;
1370
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'drawLeafs', 'drawLeafsOnePage');
1371
- /** @deprecated not used outside BookReader */
1372
- BookReader.prototype.zoom1up = Mode1Up.prototype.zoom;
1373
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'zoom', 'zoom1up');
1374
- /** @deprecated not used outside Mode1Up */
1375
- BookReader.prototype.onePageGetAutofitWidth = Mode1Up.prototype.getAutofitWidth;
1376
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitWidth', 'onePageGetAutofitWidth');
1377
- /** @deprecated not used outside Mode1Up, BookReader */
1378
- BookReader.prototype.onePageGetAutofitHeight = Mode1Up.prototype.getAutofitHeight;
1379
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitHeight', 'onePageGetAutofitHeight');
1380
- /** @deprecated not used outside Mode1Up, BookReader */
1381
- BookReader.prototype.onePageGetPageTop = Mode1Up.prototype.getPageTop;
1382
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getPageTop', 'onePageGetPageTop');
1383
- /** @deprecated not used outside Mode1Up, BookReader */
1384
- BookReader.prototype.onePageCalculateReductionFactors = Mode1Up.prototype.calculateReductionFactors;
1385
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateReductionFactors', 'onePageCalculateReductionFactors');
1386
- /** @deprecated not used outside Mode1Up, BookReader */
1387
- BookReader.prototype.resizePageView1up = Mode1Up.prototype.resizePageView;
1388
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'resizePageView', 'resizePageView1up');
1389
- /** @deprecated not used outside Mode1Up */
1390
- BookReader.prototype.onePageCalculateViewDimensions = Mode1Up.prototype.calculateViewDimensions;
1391
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateViewDimensions', 'onePageCalculateViewDimensions');
1392
- /** @deprecated not used outside Mode1Up */
1393
- BookReader.prototype.centerX1up = Mode1Up.prototype.centerX;
1394
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerX', 'centerX1up');
1395
- /** @deprecated not used outside Mode1Up */
1396
- BookReader.prototype.centerY1up = Mode1Up.prototype.centerY;
1397
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerY', 'centerY1up');
1398
-
1399
- /************************/
1400
- /** Mode2Up extensions **/
1401
- /************************/
1402
- /** @deprecated not used outside Mode2Up */
1403
- BookReader.prototype.zoom2up = Mode2Up.prototype.zoom;
1404
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'zoom', 'zoom2up');
1405
- BookReader.prototype.twoPageGetAutofitReduce = Mode2Up.prototype.getAutofitReduce;
1406
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getAutofitReduce', 'twoPageGetAutofitReduce');
1407
- BookReader.prototype.flipBackToIndex = Mode2Up.prototype.flipBackToIndex;
1408
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipBackToIndex', 'flipBackToIndex');
1409
- BookReader.prototype.flipFwdToIndex = Mode2Up.prototype.flipFwdToIndex;
1410
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipFwdToIndex', 'flipFwdToIndex');
1411
- BookReader.prototype.setHilightCss2UP = Mode2Up.prototype.setHilightCss;
1412
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setHilightCss', 'setHilightCss2UP');
1413
- /** @deprecated not used outside Mode2Up */
1414
- BookReader.prototype.setClickHandler2UP = Mode2Up.prototype.setClickHandler;
1415
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setClickHandler', 'setClickHandler2UP');
1416
- /** @deprecated not used outside Mode2Up */
1417
- BookReader.prototype.drawLeafsTwoPage = Mode2Up.prototype.drawLeafs;
1418
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'drawLeafs', 'drawLeafsTwoPage');
1419
- /** @deprecated not used outside BookReader */
1420
- BookReader.prototype.prepareTwoPageView = Mode2Up.prototype.prepareTwoPageView;
1421
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareTwoPageView', 'prepareTwoPageView');
1422
- /** @deprecated not used outside Mode2Up */
1423
- BookReader.prototype.prepareTwoPagePopUp = Mode2Up.prototype.preparePopUp;
1424
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'preparePopUp', 'prepareTwoPagePopUp');
1425
- /** @deprecated not used outside BookReader, Mode2Up */
1426
- BookReader.prototype.calculateSpreadSize = Mode2Up.prototype.calculateSpreadSize;
1427
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateSpreadSize', 'calculateSpreadSize');
1428
- /** @deprecated not used outside BookReader, Mode2Up */
1429
- BookReader.prototype.getIdealSpreadSize = Mode2Up.prototype.getIdealSpreadSize;
1430
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getIdealSpreadSize', 'getIdealSpreadSize');
1431
- /** @deprecated not used outside BookReader, Mode2Up */
1432
- BookReader.prototype.getSpreadSizeFromReduce = Mode2Up.prototype.getSpreadSizeFromReduce;
1433
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getSpreadSizeFromReduce', 'getSpreadSizeFromReduce');
1434
- /** @deprecated unused */
1435
- BookReader.prototype.twoPageIsZoomedIn = Mode2Up.prototype.isZoomedIn;
1436
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'isZoomedIn', 'twoPageIsZoomedIn');
1437
- /** @deprecated not used outside BookReader */
1438
- BookReader.prototype.twoPageCalculateReductionFactors = Mode2Up.prototype.calculateReductionFactors;
1439
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateReductionFactors', 'twoPageCalculateReductionFactors');
1440
- /** @deprecated unused */
1441
- BookReader.prototype.twoPageSetCursor = Mode2Up.prototype.setCursor;
1442
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setCursor', 'twoPageSetCursor');
1443
- /** @deprecated unused outside BookReader, Mode2Up */
1444
- BookReader.prototype.flipLeftToRight = Mode2Up.prototype.flipLeftToRight;
1445
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipLeftToRight', 'flipLeftToRight');
1446
- /** @deprecated unused outside BookReader, Mode2Up */
1447
- BookReader.prototype.flipRightToLeft = Mode2Up.prototype.flipRightToLeft;
1448
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipRightToLeft', 'flipRightToLeft');
1449
- /** @deprecated unused outside Mode2Up */
1450
- BookReader.prototype.setMouseHandlers2UP = Mode2Up.prototype.setMouseHandlers;
1451
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setMouseHandlers', 'setMouseHandlers2UP');
1452
- /** @deprecated unused outside BookReader, Mode2Up */
1453
- BookReader.prototype.prepareFlipLeftToRight = Mode2Up.prototype.prepareFlipLeftToRight;
1454
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipLeftToRight', 'prepareFlipLeftToRight');
1455
- /** @deprecated unused outside BookReader, Mode2Up */
1456
- BookReader.prototype.prepareFlipRightToLeft = Mode2Up.prototype.prepareFlipRightToLeft;
1457
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipRightToLeft', 'prepareFlipRightToLeft');
1458
- /** @deprecated unused outside Mode2Up */
1459
- BookReader.prototype.getPageWidth2UP = Mode2Up.prototype.getPageWidth;
1460
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getPageWidth', 'getPageWidth2UP');
1461
- /** @deprecated unused outside Mode2Up */
1462
- BookReader.prototype.twoPageGutter = Mode2Up.prototype.gutter;
1463
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutter', 'twoPageGutter');
1464
- /** @deprecated unused outside Mode2Up */
1465
- BookReader.prototype.twoPageTop = Mode2Up.prototype.top;
1466
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'top', 'twoPageTop');
1467
- /** @deprecated unused outside Mode2Up */
1468
- BookReader.prototype.twoPageCoverWidth = Mode2Up.prototype.coverWidth;
1469
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'coverWidth', 'twoPageCoverWidth');
1470
- /** @deprecated unused outside Mode2Up */
1471
- BookReader.prototype.twoPageGetViewCenter = Mode2Up.prototype.getViewCenter;
1472
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getViewCenter', 'twoPageGetViewCenter');
1473
- /** @deprecated unused outside BookReader, Mode2Up */
1474
- BookReader.prototype.twoPageCenterView = Mode2Up.prototype.centerView;
1475
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'centerView', 'twoPageCenterView');
1476
- /** @deprecated unused outside Mode2Up */
1477
- BookReader.prototype.twoPageFlipAreaHeight = Mode2Up.prototype.flipAreaHeight;
1478
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaHeight', 'twoPageFlipAreaHeight');
1479
- /** @deprecated unused outside Mode2Up */
1480
- BookReader.prototype.twoPageFlipAreaWidth = Mode2Up.prototype.flipAreaWidth;
1481
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaWidth', 'twoPageFlipAreaWidth');
1482
- /** @deprecated unused outside BookReader, Mode2Up */
1483
- BookReader.prototype.twoPageFlipAreaTop = Mode2Up.prototype.flipAreaTop;
1484
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaTop', 'twoPageFlipAreaTop');
1485
- /** @deprecated unused outside Mode2Up */
1486
- BookReader.prototype.twoPageLeftFlipAreaLeft = Mode2Up.prototype.leftFlipAreaLeft;
1487
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leftFlipAreaLeft', 'twoPageLeftFlipAreaLeft');
1488
- /** @deprecated unused outside Mode2Up */
1489
- BookReader.prototype.twoPageRightFlipAreaLeft = Mode2Up.prototype.rightFlipAreaLeft;
1490
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'rightFlipAreaLeft', 'twoPageRightFlipAreaLeft');
1491
- /** @deprecated unused outside BookReader, Mode2Up */
1492
- BookReader.prototype.gutterOffsetForIndex = Mode2Up.prototype.gutterOffsetForIndex;
1493
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutterOffsetForIndex', 'gutterOffsetForIndex');
1494
- /** @deprecated unused outside BookReader, Mode2Up */
1495
- BookReader.prototype.leafEdgeWidth = Mode2Up.prototype.leafEdgeWidth;
1496
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leafEdgeWidth', 'leafEdgeWidth');
1497
- /** @deprecated unused outside BookReader, Mode2Up */
1498
- BookReader.prototype.jumpIndexForLeftEdgePageX = Mode2Up.prototype.jumpIndexForLeftEdgePageX;
1499
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForLeftEdgePageX', 'jumpIndexForLeftEdgePageX');
1500
- /** @deprecated unused outside BookReader, Mode2Up */
1501
- BookReader.prototype.jumpIndexForRightEdgePageX = Mode2Up.prototype.jumpIndexForRightEdgePageX;
1502
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForRightEdgePageX', 'jumpIndexForRightEdgePageX');
1503
- /** @deprecated unused outside Mode2Up */
1504
- BookReader.prototype.prefetch = Mode2Up.prototype.prefetch;
1505
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prefetch', 'prefetch');
1506
- /** @deprecated unused outside Mode2Up */
1507
- BookReader.prototype.setSpreadIndices = Mode2Up.prototype.setSpreadIndices;
1508
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setSpreadIndices', 'setSpreadIndices');
1509
- /**
1510
- * Immediately stop flip animations. Callbacks are triggered.
1511
- */
1512
- BookReader.prototype.stopFlipAnimations = function() {
1513
- this.trigger(BookReader.eventNames.stop);
1514
-
1515
- // Stop animation, clear queue, trigger callbacks
1516
- if (this.leafEdgeTmp) {
1517
- $(this.leafEdgeTmp).stop(false, true);
1518
- }
1519
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1520
- $(this.$container).stop(false, true);
1521
- });
1522
-
1523
- // And again since animations also queued in callbacks
1524
- if (this.leafEdgeTmp) {
1525
- $(this.leafEdgeTmp).stop(false, true);
1526
- }
1527
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1528
- $(this.$container).stop(false, true);
1529
- });
1530
- };
1531
-
1532
- /**
1533
- * Returns true if keyboard navigation should be disabled for the event
1534
- * @param {Event}
1535
- * @return {boolean}
1536
- */
1537
- BookReader.prototype.keyboardNavigationIsDisabled = function(event) {
1538
- return event.target.tagName == "INPUT";
1539
- };
1540
1529
 
1541
1530
  /**
1542
1531
  * @template TClass extends { br: BookReader }
@@ -1560,30 +1549,9 @@ function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
1560
1549
  /***********************/
1561
1550
  /** Navbar extensions **/
1562
1551
  /***********************/
1552
+ /** This cannot be removed yet because plugin.tts.js overrides it */
1563
1553
  BookReader.prototype.initNavbar = Navbar.prototype.init;
1564
1554
  exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
1565
- BookReader.prototype.switchNavbarControls = Navbar.prototype.switchNavbarControls;
1566
- exposeOverrideableMethod(Navbar, '_components.navbar', 'switchNavbarControls');
1567
- BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButton;
1568
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
1569
- BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
1570
- exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
1571
- /** @deprecated */
1572
- BookReader.prototype.initEmbedNavbar = Navbar.prototype.initEmbed;
1573
- exposeOverrideableMethod(Navbar, '_components.navbar', 'initEmbed', 'initEmbedNavbar');
1574
- /** @deprecated unused */
1575
- BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
1576
- /** @deprecated unused outside this file */
1577
- BookReader.prototype.updateNavPageNum = Navbar.prototype.updateNavPageNum;
1578
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavPageNum');
1579
- /** @deprecated unused outside this file */
1580
- BookReader.prototype.updateNavIndex = Navbar.prototype.updateNavIndex;
1581
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavIndex');
1582
- /** @deprecated unused outside this file */
1583
- BookReader.prototype.updateNavIndexThrottled = utils.throttle(BookReader.prototype.updateNavIndex, 250, false);
1584
- /** @deprecated unused */
1585
- BookReader.prototype.updateNavIndexDebounced = utils.debounce(BookReader.prototype.updateNavIndex, 500, false);
1586
-
1587
1555
 
1588
1556
  /************************/
1589
1557
  /** Toolbar extensions **/
@@ -1598,515 +1566,15 @@ BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1598
1566
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1599
1567
  BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1600
1568
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
1601
- /** @deprecated zoom no longer in toolbar */
1602
- BookReader.prototype.updateToolbarZoom = Toolbar.prototype.updateToolbarZoom;
1603
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'updateToolbarZoom');
1604
- /** @deprecated unused */
1605
- BookReader.prototype.blankInfoDiv = blankInfoDiv;
1606
- /** @deprecated unused */
1607
- BookReader.prototype.blankShareDiv = blankShareDiv;
1608
- /** @deprecated unused */
1609
- BookReader.prototype.createPopup = createPopup;
1610
-
1611
- /**
1612
- * Bind navigation handlers
1613
- */
1614
- BookReader.prototype.bindNavigationHandlers = function() {
1615
- const self = this;
1616
-
1617
- // Note the mobile plugin attaches itself to body, so we need to select outside
1618
- const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
1619
- // Map of jIcon class -> click handler
1620
- const navigationControls = {
1621
- book_left: () => {
1622
- this.trigger(BookReader.eventNames.stop);
1623
- this.left();
1624
- },
1625
- book_right: () => {
1626
- this.trigger(BookReader.eventNames.stop);
1627
- this.right();
1628
- },
1629
- book_up: () => {
1630
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1631
- this.scrollUp();
1632
- } else {
1633
- this.prev();
1634
- }
1635
- },
1636
- book_down: () => {
1637
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1638
- this.scrollDown();
1639
- } else {
1640
- this.next();
1641
- }
1642
- },
1643
- book_top: this.first.bind(this),
1644
- book_bottom: this.last.bind(this),
1645
- book_leftmost: this.leftmost.bind(this),
1646
- book_rightmost: this.rightmost.bind(this),
1647
- onepg: () => {
1648
- this.switchMode(self.constMode1up);
1649
- },
1650
- thumb: () => {
1651
- this.switchMode(self.constModeThumb);
1652
- },
1653
- twopg: () => {
1654
- this.switchMode(self.constMode2up);
1655
- },
1656
- zoom_in: () => {
1657
- this.trigger(BookReader.eventNames.stop);
1658
- this.zoom(1);
1659
- this.trigger(BookReader.eventNames.zoomIn);
1660
- },
1661
- zoom_out: () => {
1662
- this.trigger(BookReader.eventNames.stop);
1663
- this.zoom(-1);
1664
- this.trigger(BookReader.eventNames.zoomOut);
1665
- },
1666
- full: () => {
1667
- if (this.ui == 'embed') {
1668
- var url = this.$('.BRembedreturn a').attr('href');
1669
- window.open(url);
1670
- } else {
1671
- this.toggleFullscreen();
1672
- }
1673
- },
1674
- };
1675
-
1676
- jIcons.filter('.fit').bind('fit', function() {
1677
- // XXXmang implement autofit zoom
1678
- });
1679
-
1680
- for (const control in navigationControls) {
1681
- jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1682
- navigationControls[control]();
1683
- return false;
1684
- });
1685
- }
1686
-
1687
- var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1688
- var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1689
-
1690
- this.$('.BRnavCntl').click(
1691
- function() {
1692
- var promises = [];
1693
- // TODO don't use magic constants
1694
- // TODO move this to a function
1695
- if ($brNavCntlBtmEl.hasClass('BRdn')) {
1696
- if (self.refs.$BRtoolbar)
1697
- promises.push(self.refs.$BRtoolbar.animate(
1698
- {top: self.getToolBarHeight() * -1}
1699
- ).promise());
1700
- promises.push(self.$('.BRfooter').animate({bottom: self.getFooterHeight() * -1}).promise());
1701
- $brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
1702
- $brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
1703
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
1704
- self.$('.BRnavCntl').delay(1000).animate({opacity:.75}, 1000);
1705
- } else {
1706
- if (self.refs.$BRtoolbar)
1707
- promises.push(self.refs.$BRtoolbar.animate({top:0}).promise());
1708
- promises.push(self.$('.BRfooter').animate({bottom:0}).promise());
1709
- $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1710
- $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1711
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
1712
- self.$('.BRvavCntl').animate({opacity:1});
1713
- }
1714
- $.when.apply($, promises).done(function() {
1715
- // Only do full resize in auto mode and need to recalc. size
1716
- if (self.mode == self.constMode2up && self.twoPage.autofit != null
1717
- && self.twoPage.autofit != 'none'
1718
- ) {
1719
- self.resize();
1720
- } else if (self.mode == self.constMode1up && self.onePage.autofit != null
1721
- && self.onePage.autofit != 'none') {
1722
- self.resize();
1723
- } else {
1724
- // Don't do a full resize to avoid redrawing images
1725
- self.resizeBRcontainer();
1726
- }
1727
- });
1728
- }
1729
- );
1730
- $brNavCntlBtmEl.mouseover(function() {
1731
- if ($(this).hasClass('BRup')) {
1732
- self.$('.BRnavCntl').animate({opacity:1},250);
1733
- }
1734
- }).mouseleave(function() {
1735
- if ($(this).hasClass('BRup')) {
1736
- self.$('.BRnavCntl').animate({opacity:.75},250);
1737
- }
1738
- });
1739
- $brNavCntlTopEl.mouseover(function() {
1740
- if ($(this).hasClass('BRdn')) {
1741
- self.$('.BRnavCntl').animate({opacity:1},250);
1742
- }
1743
- }).mouseleave(function() {
1744
- if ($(this).hasClass('BRdn')) {
1745
- self.$('.BRnavCntl').animate({opacity:.75},250);
1746
- }
1747
- });
1748
-
1749
- this.initSwipeData();
1750
-
1751
- $(document).off('mousemove.navigation', this.el);
1752
- $(document).on(
1753
- 'mousemove.navigation',
1754
- this.el,
1755
- { 'br': this },
1756
- this.navigationMousemoveHandler
1757
- );
1758
-
1759
- $(document).off('mousedown.swipe', '.BRpageimage');
1760
- $(document).on(
1761
- 'mousedown.swipe',
1762
- '.BRpageimage',
1763
- { 'br': this },
1764
- this.swipeMousedownHandler
1765
- );
1766
-
1767
- this.bindMozTouchHandlers();
1768
- };
1769
-
1770
- /**
1771
- * Unbind navigation handlers
1772
- */
1773
- BookReader.prototype.unbindNavigationHandlers = function() {
1774
- $(document).off('mousemove.navigation', this.el);
1775
- };
1776
-
1777
- /**
1778
- * Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
1779
- */
1780
- BookReader.prototype.navigationMousemoveHandler = function(event) {
1781
- // $$$ possibly not great to be calling this for every mousemove
1782
- if (event.data['br'].uiAutoHide) {
1783
- // 77px is an approximate height of the Internet Archive Top Nav
1784
- // 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
1785
- var navkey = $(document).height() - 75;
1786
- if ((event.pageY < 76) || (event.pageY > navkey)) {
1787
- // inside or near navigation elements
1788
- event.data['br'].hideNavigation();
1789
- } else {
1790
- event.data['br'].showNavigation();
1791
- }
1792
- }
1793
- };
1794
-
1795
- BookReader.prototype.initSwipeData = function(clientX, clientY) {
1796
- /*
1797
- * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
1798
- */
1799
- this._swipe = {
1800
- mightBeSwiping: false,
1801
- didSwipe: false,
1802
- mightBeDraggin: false,
1803
- didDrag: false,
1804
- startTime: (new Date).getTime(),
1805
- startX: clientX,
1806
- startY: clientY,
1807
- lastX: clientX,
1808
- lastY: clientY,
1809
- deltaX: 0,
1810
- deltaY: 0,
1811
- deltaT: 0
1812
- };
1813
- };
1814
-
1815
- BookReader.prototype.swipeMousedownHandler = function(event) {
1816
- var self = event.data['br'];
1817
-
1818
- // We should be the last bubble point for the page images
1819
- // Disable image drag and select, but keep right-click
1820
- if (event.which == 3) {
1821
- return !self.protected;
1822
- }
1823
-
1824
- $(event.target).bind('mouseout.swipe',
1825
- { 'br': self},
1826
- self.swipeMouseupHandler
1827
- ).bind('mouseup.swipe',
1828
- { 'br': self},
1829
- self.swipeMouseupHandler
1830
- ).bind('mousemove.swipe',
1831
- { 'br': self },
1832
- self.swipeMousemoveHandler
1833
- );
1834
-
1835
- self.initSwipeData(event.clientX, event.clientY);
1836
- self._swipe.mightBeSwiping = true;
1837
- self._swipe.mightBeDragging = true;
1838
-
1839
- event.preventDefault();
1840
- event.returnValue = false;
1841
- event.cancelBubble = true;
1842
- return false;
1843
- };
1844
-
1845
- BookReader.prototype.swipeMousemoveHandler = function(event) {
1846
- var self = event.data['br'];
1847
- var _swipe = self._swipe;
1848
- if (! _swipe.mightBeSwiping) {
1849
- return;
1850
- }
1851
-
1852
- // Update swipe data
1853
- _swipe.deltaX = event.clientX - _swipe.startX;
1854
- _swipe.deltaY = event.clientY - _swipe.startY;
1855
- _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
1856
-
1857
- var absX = Math.abs(_swipe.deltaX);
1858
- var absY = Math.abs(_swipe.deltaY);
1859
-
1860
- // Minimum distance in the amount of tim to trigger the swipe
1861
- var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
1862
- var maxSwipeTime = 400;
1863
-
1864
- // Check for horizontal swipe
1865
- if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
1866
- _swipe.mightBeSwiping = false; // only trigger once
1867
- _swipe.didSwipe = true;
1868
- if (self.mode == self.constMode2up) {
1869
- if (_swipe.deltaX < 0) {
1870
- self.right();
1871
- } else {
1872
- self.left();
1873
- }
1874
- }
1875
- }
1876
-
1877
- if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
1878
- if (_swipe.mightBeDragging) {
1879
- // Dragging
1880
- _swipe.didDrag = true;
1881
- self.refs.$brContainer
1882
- .scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
1883
- .scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
1884
- }
1885
- }
1886
- _swipe.lastX = event.clientX;
1887
- _swipe.lastY = event.clientY;
1888
-
1889
- event.preventDefault();
1890
- event.returnValue = false;
1891
- event.cancelBubble = true;
1892
- return false;
1893
- };
1894
-
1895
- BookReader.prototype.swipeMouseupHandler = function(event) {
1896
- var _swipe = event.data['br']._swipe;
1897
- _swipe.mightBeSwiping = false;
1898
- _swipe.mightBeDragging = false;
1899
-
1900
- $(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
1901
-
1902
- if (_swipe.didSwipe || _swipe.didDrag) {
1903
- // Swallow event if completed swipe gesture
1904
- event.preventDefault();
1905
- event.returnValue = false;
1906
- event.cancelBubble = true;
1907
- return false;
1908
- }
1909
- return true;
1910
- };
1911
-
1912
- BookReader.prototype.bindMozTouchHandlers = function() {
1913
- var self = this;
1914
-
1915
- // Currently only want touch handlers in 2up
1916
- this.refs.$br.bind('MozTouchDown', function(event) {
1917
- if (this.mode == self.constMode2up) {
1918
- event.preventDefault();
1919
- }
1920
- })
1921
- .bind('MozTouchMove', function(event) {
1922
- if (this.mode == self.constMode2up) {
1923
- event.preventDefault();
1924
- }
1925
- })
1926
- .bind('MozTouchUp', function(event) {
1927
- if (this.mode == self.constMode2up) {
1928
- event.preventDefault();
1929
- }
1930
- });
1931
- };
1932
-
1933
- /**
1934
- * Returns true if the navigation elements are currently visible
1935
- * @return {boolean}
1936
- */
1937
- BookReader.prototype.navigationIsVisible = function() {
1938
- // $$$ doesn't account for transitioning states, nav must be fully visible to return true
1939
- var toolpos = this.refs.$BRtoolbar.position();
1940
- var tooltop = toolpos.top;
1941
- return tooltop == 0;
1942
- };
1943
-
1944
- /**
1945
- * Main controller that sets navigation into view.
1946
- * Defaults to SHOW the navigation chrome
1947
- */
1948
- BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
1949
- var animationLength = this.constNavAnimationDuration;
1950
- var animationType = 'linear';
1951
- var resizePageContainer = function resizePageContainer () {
1952
- /* main page container fills whole container */
1953
- if (this.constMode2up !== this.mode) {
1954
- var animate = true;
1955
- this.resizeBRcontainer(animate);
1956
- }
1957
- this.trigger(BookReader.eventNames.navToggled);
1958
- }.bind(this);
1959
-
1960
- var toolbarHeight = 0;
1961
- var navbarHeight = 0;
1962
- if (hide) {
1963
- toolbarHeight = this.getToolBarHeight() * -1;
1964
- navbarHeight = this.getFooterHeight() * -1;
1965
-
1966
- this.refs.$BRtoolbar.addClass('js-menu-hide');
1967
- this.refs.$BRfooter.addClass('js-menu-hide');
1968
- } else {
1969
- this.refs.$BRtoolbar.removeClass('js-menu-hide');
1970
- this.refs.$BRfooter.removeClass('js-menu-hide');
1971
- }
1972
-
1973
- this.refs.$BRtoolbar.animate(
1974
- { top: toolbarHeight },
1975
- animationLength,
1976
- animationType,
1977
- resizePageContainer
1978
- );
1979
- this.refs.$BRfooter.animate(
1980
- { bottom: navbarHeight },
1981
- animationLength,
1982
- animationType,
1983
- resizePageContainer
1984
- );
1985
- };
1986
- /**
1987
- * Hide navigation elements, if visible
1988
- */
1989
- BookReader.prototype.hideNavigation = function() {
1990
- // Check if navigation is showing
1991
- if (this.navigationIsVisible()) {
1992
- var hide = true;
1993
- this.setNavigationView(hide);
1994
- }
1995
- };
1996
-
1997
- /**
1998
- * Show navigation elements
1999
- */
2000
- BookReader.prototype.showNavigation = function() {
2001
- // Check if navigation is hidden
2002
- if (!this.navigationIsVisible()) {
2003
- this.setNavigationView();
2004
- }
2005
- };
2006
-
2007
- /**
2008
- * Returns the index of the first visible page, dependent on the mode.
2009
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2010
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2011
- * @return {number}
2012
- */
2013
- BookReader.prototype.firstDisplayableIndex = function() {
2014
- if (this.mode != this.constMode2up) {
2015
- return 0;
2016
- }
2017
-
2018
- if ('rl' != this.pageProgression) {
2019
- // LTR
2020
- if (this._models.book.getPageSide(0) == 'L') {
2021
- return 0;
2022
- } else {
2023
- return -1;
2024
- }
2025
- } else {
2026
- // RTL
2027
- if (this._models.book.getPageSide(0) == 'R') {
2028
- return 0;
2029
- } else {
2030
- return -1;
2031
- }
2032
- }
2033
- };
2034
-
2035
- /**
2036
- * Returns the index of the last visible page, dependent on the mode.
2037
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2038
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2039
- * @return {number}
2040
- */
2041
- BookReader.prototype.lastDisplayableIndex = function() {
2042
-
2043
- var lastIndex = this._models.book.getNumLeafs() - 1;
2044
-
2045
- if (this.mode != this.constMode2up) {
2046
- return lastIndex;
2047
- }
2048
-
2049
- if ('rl' != this.pageProgression) {
2050
- // LTR
2051
- if (this._models.book.getPageSide(lastIndex) == 'R') {
2052
- return lastIndex;
2053
- } else {
2054
- return lastIndex + 1;
2055
- }
2056
- } else {
2057
- // RTL
2058
- if (this._models.book.getPageSide(lastIndex) == 'L') {
2059
- return lastIndex;
2060
- } else {
2061
- return lastIndex + 1;
2062
- }
2063
- }
2064
- };
2065
-
2066
1569
 
2067
1570
  /**************************/
2068
1571
  /** BookModel extensions **/
2069
1572
  /**************************/
2070
- /** @deprecated not used outside */
2071
- BookReader.prototype.getMedianPageSize = BookModel.prototype.getMedianPageSize;
2072
- exposeOverrideableMethod(BookModel, '_models.book', 'getMedianPageSize');
2073
- BookReader.prototype._getPageWidth = BookModel.prototype._getPageWidth;
2074
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageWidth');
2075
- BookReader.prototype._getPageHeight = BookModel.prototype._getPageHeight;
2076
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageHeight');
2077
- BookReader.prototype.getPageIndex = BookModel.prototype.getPageIndex;
2078
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndex');
2079
- /** @deprecated not used outside */
2080
- BookReader.prototype.getPageIndices = BookModel.prototype.getPageIndices;
2081
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndices');
2082
- BookReader.prototype.getPageName = BookModel.prototype.getPageName;
2083
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageName');
2084
- BookReader.prototype.getNumLeafs = BookModel.prototype.getNumLeafs;
2085
- exposeOverrideableMethod(BookModel, '_models.book', 'getNumLeafs');
2086
- BookReader.prototype.getPageWidth = BookModel.prototype.getPageWidth;
2087
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageWidth');
2088
- BookReader.prototype.getPageHeight = BookModel.prototype.getPageHeight;
2089
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageHeight');
1573
+ // Must modify petabox extension, which expects this on the prototype
1574
+ // before removing.
2090
1575
  BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
2091
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageURI');
2092
- BookReader.prototype.getPageSide = BookModel.prototype.getPageSide;
2093
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageSide');
2094
- BookReader.prototype.getPageNum = BookModel.prototype.getPageNum;
2095
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageNum');
2096
- BookReader.prototype.getPageProp = BookModel.prototype.getPageProp;
2097
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageProp');
2098
- BookReader.prototype.getSpreadIndices = BookModel.prototype.getSpreadIndices;
2099
- exposeOverrideableMethod(BookModel, '_models.book', 'getSpreadIndices');
2100
- BookReader.prototype.leafNumToIndex = BookModel.prototype.leafNumToIndex;
2101
- exposeOverrideableMethod(BookModel, '_models.book', 'leafNumToIndex');
2102
- BookReader.prototype.parsePageString = BookModel.prototype.parsePageString;
2103
- exposeOverrideableMethod(BookModel, '_models.book', 'parsePageString');
2104
- /** @deprecated unused */
2105
- BookReader.prototype._getDataFlattened = BookModel.prototype._getDataFlattened;
2106
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataFlattened');
2107
- /** @deprecated unused */
2108
- BookReader.prototype._getDataProp = BookModel.prototype._getDataProp;
2109
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataProp');
1576
+ exposeOverrideableMethod(BookModel, 'book', 'getPageURI');
1577
+
2110
1578
 
2111
1579
  // Parameter related functions
2112
1580
 
@@ -2125,7 +1593,7 @@ BookReader.prototype.updateFromParams = function(params) {
2125
1593
  if (mode) {
2126
1594
  this.switchMode(
2127
1595
  mode,
2128
- { init: init, suppressFragmentChange: !fragmentChange }
1596
+ { init: init, suppressFragmentChange: !fragmentChange },
2129
1597
  );
2130
1598
  }
2131
1599
 
@@ -2137,7 +1605,7 @@ BookReader.prototype.updateFromParams = function(params) {
2137
1605
  }
2138
1606
  } else if ('undefined' != typeof(params.page)) {
2139
1607
  // $$$ this assumes page numbers are unique
2140
- if (params.page != this._models.book.getPageNum(this.currentIndex())) {
1608
+ if (params.page != this.book.getPageNum(this.currentIndex())) {
2141
1609
  this.jumpToPage(params.page);
2142
1610
  }
2143
1611
  }
@@ -2146,8 +1614,8 @@ BookReader.prototype.updateFromParams = function(params) {
2146
1614
  // process /search
2147
1615
  // @deprecated for urlMode 'history'
2148
1616
  // Continues to work for urlMode 'hash'
2149
- if (this.enableSearch && 'undefined' != typeof(params.search)) {
2150
- if (this.searchTerm !== params.search) {
1617
+ if (this.plugins.search?.enabled && 'undefined' != typeof(params.search)) {
1618
+ if (this.plugins.search.searchTerm !== params.search) {
2151
1619
  this.$('.BRsearchInput').val(params.search);
2152
1620
  }
2153
1621
  }
@@ -2171,7 +1639,7 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2171
1639
  // check there are enough pages to display
2172
1640
  // $$$ this is a workaround for the mis-feature that we can't display
2173
1641
  // short books in 2up mode
2174
- if (this._models.book.getNumLeafs() < 2) {
1642
+ if (this.book.getNumLeafs() < 2) {
2175
1643
  return false;
2176
1644
  }
2177
1645
  }
@@ -2179,31 +1647,6 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2179
1647
  return true;
2180
1648
  };
2181
1649
 
2182
-
2183
- /**
2184
- * @deprecated. Use PageModel.getURISrcSet. Slated for removal in v5.
2185
- * Returns the srcset with correct URIs or void string if out of range
2186
- * Also makes the reduce argument optional
2187
- * @param {number} index
2188
- * @param {number} [reduce]
2189
- * @param {number} [rotate]
2190
- * @return {string}
2191
- */
2192
- BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2193
- const page = this._models.book.getPage(index, false);
2194
- // Synthesize page
2195
- if (!page) return "";
2196
-
2197
- // reduce not passed in
2198
- // $$$ this probably won't work for thumbnail mode
2199
- if ('undefined' == typeof(reduce)) {
2200
- reduce = page.height / this.twoPage.height;
2201
- }
2202
-
2203
- return page.getURISrcSet(reduce, rotate);
2204
- };
2205
-
2206
-
2207
1650
  /**
2208
1651
  * Returns the page URI or transparent image if out of range
2209
1652
  * Also makes the reduce argument optional
@@ -2213,7 +1656,7 @@ BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2213
1656
  * @return {string}
2214
1657
  */
2215
1658
  BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2216
- const page = this._models.book.getPage(index, false);
1659
+ const page = this.book.getPage(index, false);
2217
1660
  // Synthesize page
2218
1661
  if (!page) return this.imagesBaseURL + "transparent.png";
2219
1662
 
@@ -2252,7 +1695,7 @@ BookReader.prototype.showProgressPopup = function(msg, onCloseCallback) {
2252
1695
 
2253
1696
  const bar = document.createElement("div");
2254
1697
  $(bar).css({
2255
- height: '20px'
1698
+ height: '20px',
2256
1699
  }).prop('className', 'BRprogressbar');
2257
1700
  $(this.popup).append(bar);
2258
1701
 
@@ -2279,7 +1722,7 @@ BookReader.prototype.initUIStrings = function() {
2279
1722
  // the toolbar and nav bar easier
2280
1723
 
2281
1724
  // Setup tooltips -- later we could load these from a file for i18n
2282
- var titles = {
1725
+ const titles = {
2283
1726
  '.logo': 'Go to Archive.org', // $$$ update after getting OL record
2284
1727
  '.zoom_in': 'Zoom in',
2285
1728
  '.zoom_out': 'Zoom out',
@@ -2292,15 +1735,12 @@ BookReader.prototype.initUIStrings = function() {
2292
1735
  '.bookmark': 'Bookmark this page',
2293
1736
  '.share': 'Share this book',
2294
1737
  '.info': 'About this book',
2295
- '.full': 'Toggle fullscreen',
1738
+ '.full': 'Go fullscreen',
1739
+ '.toggle_slider': 'Toggle page controls',
2296
1740
  '.book_left': 'Flip left',
2297
1741
  '.book_right': 'Flip right',
2298
- '.book_up': 'Page up',
2299
- '.book_down': 'Page down',
2300
1742
  '.play': 'Play',
2301
1743
  '.pause': 'Pause',
2302
- '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
2303
- '.BRup': 'Show/hide nav bar',
2304
1744
  '.book_top': 'First page',
2305
1745
  '.book_bottom': 'Last page',
2306
1746
  '.book_leftmost': 'First page',
@@ -2311,7 +1751,7 @@ BookReader.prototype.initUIStrings = function() {
2311
1751
  titles['.book_rightmost'] = 'First page';
2312
1752
  }
2313
1753
 
2314
- for (var icon in titles) {
1754
+ for (const icon in titles) {
2315
1755
  this.$(icon).prop('title', titles[icon]);
2316
1756
  }
2317
1757
  };
@@ -2322,7 +1762,7 @@ BookReader.prototype.initUIStrings = function() {
2322
1762
  BookReader.prototype.reloadImages = function() {
2323
1763
  this.refs.$brContainer.find('img').each(function(index, elem) {
2324
1764
  if (!elem.complete || elem.naturalHeight === 0) {
2325
- var src = elem.src;
1765
+ const src = elem.src;
2326
1766
  elem.src = '';
2327
1767
  setTimeout(function() {
2328
1768
  elem.src = src;
@@ -2336,10 +1776,10 @@ BookReader.prototype.reloadImages = function() {
2336
1776
  * @return {number}
2337
1777
  */
2338
1778
  BookReader.prototype.getFooterHeight = function() {
2339
- var $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
1779
+ const $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
2340
1780
  if ($heightEl && this.refs.$BRfooter) {
2341
- var outerHeight = $heightEl.outerHeight();
2342
- var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
1781
+ const outerHeight = $heightEl.outerHeight();
1782
+ const bottom = parseInt(this.refs.$BRfooter.css('bottom'));
2343
1783
  if (!isNaN(outerHeight) && !isNaN(bottom)) {
2344
1784
  return outerHeight + bottom;
2345
1785
  }
@@ -2355,10 +1795,11 @@ BookReader.prototype.getFooterHeight = function() {
2355
1795
  * @return {Object}
2356
1796
  */
2357
1797
  BookReader.prototype.paramsFromCurrent = function() {
2358
- var params = {};
1798
+ const params = {};
2359
1799
 
2360
- var index = this.currentIndex();
2361
- var pageNum = this._models.book.getPageNum(index);
1800
+ // Path params
1801
+ const index = this.currentIndex();
1802
+ const pageNum = this.book.getPageNum(index);
2362
1803
  if ((pageNum === 0) || pageNum) {
2363
1804
  params.page = pageNum;
2364
1805
  }
@@ -2366,12 +1807,19 @@ BookReader.prototype.paramsFromCurrent = function() {
2366
1807
  params.index = index;
2367
1808
  params.mode = this.mode;
2368
1809
 
1810
+ // Unused params
2369
1811
  // $$$ highlight
2370
1812
  // $$$ region
2371
1813
 
2372
- // search
2373
- if (this.enableSearch) {
2374
- params.search = this.searchTerm;
1814
+ // Querystring params
1815
+ // View
1816
+ const fullscreenView = 'theater';
1817
+ if (this.isFullscreenActive) {
1818
+ params.view = fullscreenView;
1819
+ }
1820
+ // Search
1821
+ if (this.plugins.search?.enabled) {
1822
+ params.search = this.plugins.search.searchTerm;
2375
1823
  }
2376
1824
 
2377
1825
  return params;
@@ -2390,7 +1838,7 @@ BookReader.prototype.paramsFromCurrent = function() {
2390
1838
  * @return {Object}
2391
1839
  */
2392
1840
  BookReader.prototype.paramsFromFragment = function(fragment) {
2393
- var params = {};
1841
+ const params = {};
2394
1842
 
2395
1843
  // For backwards compatibility we allow an initial # character
2396
1844
  // (as from window.location.hash) but don't require it
@@ -2399,7 +1847,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2399
1847
  }
2400
1848
 
2401
1849
  // Simple #nn syntax
2402
- var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
1850
+ const oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
2403
1851
  if ( !isNaN(oldStyleLeafNum) ) {
2404
1852
  params.index = oldStyleLeafNum;
2405
1853
 
@@ -2408,9 +1856,9 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2408
1856
  }
2409
1857
 
2410
1858
  // Split into key-value pairs
2411
- var urlArray = fragment.split('/');
2412
- var urlHash = {};
2413
- for (var i = 0; i < urlArray.length; i += 2) {
1859
+ const urlArray = fragment.split('/');
1860
+ const urlHash = {};
1861
+ for (let i = 0; i < urlArray.length; i += 2) {
2414
1862
  urlHash[urlArray[i]] = urlArray[i + 1];
2415
1863
  }
2416
1864
 
@@ -2426,7 +1874,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2426
1874
  // Index and page
2427
1875
  if ('undefined' != typeof(urlHash['page'])) {
2428
1876
  // page was set -- may not be int
2429
- params.page = urlHash['page'];
1877
+ params.page = decodeURIComponent(urlHash['page']);
2430
1878
  }
2431
1879
 
2432
1880
  // $$$ process /region
@@ -2458,11 +1906,10 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2458
1906
  * @return {string}
2459
1907
  */
2460
1908
  BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2461
- const separator = '/';
2462
1909
  const fragments = [];
2463
1910
 
2464
1911
  if ('undefined' != typeof(params.page)) {
2465
- fragments.push('page', params.page);
1912
+ fragments.push('page', encodeURIComponent(params.page));
2466
1913
  } else {
2467
1914
  if ('undefined' != typeof(params.index)) {
2468
1915
  // Don't have page numbering but we do have the index
@@ -2488,15 +1935,18 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2488
1935
 
2489
1936
  // search
2490
1937
  if (params.search && urlMode === 'hash') {
2491
- fragments.push('search', params.search);
1938
+ fragments.push('search', utils.encodeURIComponentPlus(params.search));
2492
1939
  }
2493
1940
 
2494
- return utils.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
1941
+ return fragments.join('/');
2495
1942
  };
2496
1943
 
2497
1944
  /**
2498
1945
  * Create, update querystring from the params object
2499
1946
  *
1947
+ * Handles:
1948
+ * view=
1949
+ * q=
2500
1950
  * @param {Object} params
2501
1951
  * @param {string} currQueryString
2502
1952
  * @param {string} [urlMode]
@@ -2505,9 +1955,18 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2505
1955
  BookReader.prototype.queryStringFromParams = function(
2506
1956
  params,
2507
1957
  currQueryString,
2508
- urlMode = 'hash'
1958
+ urlMode = 'hash',
2509
1959
  ) {
2510
1960
  const newParams = new URLSearchParams(currQueryString);
1961
+
1962
+ if (params.view) {
1963
+ // Set ?view=theater when fullscreen
1964
+ newParams.set('view', params.view);
1965
+ } else {
1966
+ // Remove
1967
+ newParams.delete('view');
1968
+ }
1969
+
2511
1970
  if (params.search && urlMode === 'history') {
2512
1971
  newParams.set('q', params.search);
2513
1972
  }
@@ -2524,11 +1983,4 @@ BookReader.prototype.$ = function(selector) {
2524
1983
  return this.refs.$br.find(selector);
2525
1984
  };
2526
1985
 
2527
- /**
2528
- * Polyfill for deprecated method
2529
- */
2530
- jQuery.curCSS = function(element, prop, val) {
2531
- return jQuery(element).css(prop, val);
2532
- };
2533
-
2534
1986
  window.BookReader = BookReader;