@internetarchive/bookreader 5.0.0-11-multiple-files → 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 (389) hide show
  1. package/BookReader/474.js +2 -0
  2. package/BookReader/474.js.map +1 -0
  3. package/BookReader/BookReader.css +616 -1467
  4. package/BookReader/BookReader.js +2 -21564
  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 -12
  56. package/BookReader/icons/2up.svg +1 -15
  57. package/BookReader/icons/advance.svg +3 -26
  58. package/BookReader/icons/chevron-right.svg +1 -1
  59. package/BookReader/icons/close-circle-dark.svg +1 -0
  60. package/BookReader/icons/close-circle.svg +1 -1
  61. package/BookReader/icons/fullscreen.svg +1 -17
  62. package/BookReader/icons/fullscreen_exit.svg +1 -17
  63. package/BookReader/icons/hamburger.svg +1 -15
  64. package/BookReader/icons/left-arrow.svg +1 -12
  65. package/BookReader/icons/magnify-minus.svg +1 -16
  66. package/BookReader/icons/magnify-plus.svg +1 -17
  67. package/BookReader/icons/magnify.svg +1 -15
  68. package/BookReader/icons/pause.svg +1 -23
  69. package/BookReader/icons/play.svg +1 -22
  70. package/BookReader/icons/playback-speed.svg +1 -34
  71. package/BookReader/icons/read-aloud.svg +1 -22
  72. package/BookReader/icons/review.svg +3 -22
  73. package/BookReader/icons/slider-toggle.svg +1 -0
  74. package/BookReader/icons/thumbnails.svg +1 -17
  75. package/BookReader/icons/voice.svg +1 -0
  76. package/BookReader/icons/volume-full.svg +1 -22
  77. package/BookReader/images/BRicons.svg +5 -94
  78. package/BookReader/images/books_graphic.svg +1 -177
  79. package/BookReader/images/hypothesis.ico +0 -0
  80. package/BookReader/images/icon_book.svg +1 -12
  81. package/BookReader/images/icon_bookmark.svg +1 -12
  82. package/BookReader/images/icon_experiment.svg +1 -0
  83. package/BookReader/images/icon_gear.svg +1 -14
  84. package/BookReader/images/icon_hamburger.svg +1 -20
  85. package/BookReader/images/icon_home.svg +1 -21
  86. package/BookReader/images/icon_info.svg +1 -11
  87. package/BookReader/images/icon_one_page.svg +1 -8
  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 -15
  91. package/BookReader/images/icon_search_button.svg +1 -8
  92. package/BookReader/images/icon_share.svg +1 -9
  93. package/BookReader/images/icon_skip-ahead.svg +1 -6
  94. package/BookReader/images/icon_skip-back.svg +2 -13
  95. package/BookReader/images/icon_speaker.svg +1 -18
  96. package/BookReader/images/icon_speaker_open.svg +1 -10
  97. package/BookReader/images/icon_thumbnails.svg +1 -12
  98. package/BookReader/images/icon_toc.svg +1 -5
  99. package/BookReader/images/icon_two_pages.svg +1 -9
  100. package/BookReader/images/marker_chap-off.svg +1 -11
  101. package/BookReader/images/marker_chap-on.svg +1 -11
  102. package/BookReader/images/marker_srch-on.svg +1 -11
  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 -172
  108. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  109. package/BookReader/plugins/plugin.autoplay.js +1 -165
  110. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  111. package/BookReader/plugins/plugin.chapters.js +19 -301
  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 -74
  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 -368
  122. package/BookReader/plugins/plugin.resume.js.map +1 -1
  123. package/BookReader/plugins/plugin.search.js +2 -1420
  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 -1080
  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 +2 -9193
  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 -269
  136. package/BookReader/plugins/plugin.url.js.map +1 -1
  137. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  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 -356
  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 +30 -16
  160. package/src/BookReader/Navbar/Navbar.js +201 -76
  161. package/src/BookReader/PageContainer.js +120 -23
  162. package/src/BookReader/ReduceSet.js +3 -3
  163. package/src/BookReader/Toolbar/Toolbar.js +19 -41
  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 +141 -13
  171. package/src/BookReader.js +711 -1241
  172. package/src/BookReaderPlugin.js +52 -0
  173. package/src/assets/icons/close-circle-dark.svg +1 -0
  174. package/src/assets/icons/magnify-minus.svg +3 -7
  175. package/src/assets/icons/magnify-plus.svg +3 -7
  176. package/src/assets/icons/slider-toggle.svg +1 -0
  177. package/src/assets/icons/voice.svg +1 -0
  178. package/src/assets/images/hypothesis.ico +0 -0
  179. package/src/assets/images/icon_experiment.svg +1 -0
  180. package/src/assets/images/translate.svg +1 -0
  181. package/src/assets/images/unviewable_page.png +0 -0
  182. package/src/assets/silence.mp3 +0 -0
  183. package/src/css/BookReader.scss +1 -17
  184. package/src/css/_BRBookmarks.scss +1 -1
  185. package/src/css/_BRComponent.scss +1 -1
  186. package/src/css/_BRicon.scss +8 -2
  187. package/src/css/_BRmain.scss +33 -27
  188. package/src/css/_BRnav.scss +74 -70
  189. package/src/css/_BRpages.scss +171 -42
  190. package/src/css/_BRsearch.scss +69 -235
  191. package/src/css/_BRtoolbar.scss +5 -5
  192. package/src/css/_TextSelection.scss +129 -24
  193. package/src/css/_colorbox.scss +2 -2
  194. package/src/css/_controls.scss +24 -7
  195. package/src/css/_icons.scss +14 -1
  196. package/src/{BookNavigator/assets → css}/button-base.js +10 -3
  197. package/src/css/icon_checkmark.js +9 -0
  198. package/src/css/sharedStyles.js +15 -0
  199. package/src/ia-bookreader/downloads/downloads-provider.js +81 -0
  200. package/src/{BookNavigator → ia-bookreader}/downloads/downloads.js +29 -25
  201. package/src/ia-bookreader/ia-bookreader.js +666 -0
  202. package/src/ia-bookreader/sharing.js +27 -0
  203. package/src/ia-bookreader/viewable-files.js +98 -0
  204. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments-provider.js +17 -17
  205. package/src/{BookNavigator → ia-bookreader}/visual-adjustments/visual-adjustments.js +75 -67
  206. package/src/{BookNavigator → plugins}/bookmarks/bookmark-button.js +4 -3
  207. package/src/{BookNavigator/assets → plugins/bookmarks}/bookmark-colors.js +1 -1
  208. package/src/{BookNavigator → plugins}/bookmarks/bookmark-edit.js +44 -32
  209. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-list.js +48 -49
  210. package/src/{BookNavigator → plugins}/bookmarks/bookmarks-loginCTA.js +5 -10
  211. package/src/plugins/bookmarks/bookmarks-provider.js +63 -0
  212. package/src/{BookNavigator → plugins/bookmarks}/delete-modal-actions.js +1 -1
  213. package/src/{BookNavigator → plugins}/bookmarks/ia-bookmarks.js +117 -68
  214. package/src/plugins/plugin.archive_analytics.js +84 -78
  215. package/src/plugins/plugin.autoplay.js +99 -104
  216. package/src/plugins/plugin.chapters.js +310 -201
  217. package/src/plugins/plugin.experiments.js +321 -0
  218. package/src/plugins/plugin.iframe.js +1 -1
  219. package/src/plugins/plugin.iiif.js +141 -0
  220. package/src/plugins/plugin.resume.js +53 -50
  221. package/src/plugins/plugin.text_selection.js +522 -219
  222. package/src/plugins/plugin.vendor-fullscreen.js +7 -7
  223. package/src/plugins/search/plugin.search.js +380 -360
  224. package/src/{BookNavigator → plugins}/search/search-provider.js +94 -32
  225. package/src/{BookNavigator → plugins}/search/search-results.js +108 -90
  226. package/src/plugins/search/utils.js +50 -0
  227. package/src/plugins/search/view.js +79 -210
  228. package/src/plugins/translate/TranslationManager.js +164 -0
  229. package/src/plugins/translate/plugin.translate.js +512 -0
  230. package/src/plugins/tts/AbstractTTSEngine.js +78 -49
  231. package/src/plugins/tts/FestivalTTSEngine.js +21 -31
  232. package/src/plugins/tts/PageChunk.js +33 -21
  233. package/src/plugins/tts/PageChunkIterator.js +11 -17
  234. package/src/plugins/tts/WebTTSEngine.js +131 -91
  235. package/src/plugins/tts/plugin.tts.js +345 -350
  236. package/src/plugins/tts/utils.js +77 -49
  237. package/src/plugins/url/UrlPlugin.js +191 -0
  238. package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
  239. package/src/util/TextSelectionManager.js +282 -0
  240. package/src/util/browserSniffing.js +33 -1
  241. package/src/util/cache.js +20 -0
  242. package/src/util/docCookies.js +21 -2
  243. package/src/util/lit.js +15 -0
  244. package/src/util/strings.js +1 -0
  245. package/.babelrc +0 -12
  246. package/.dependabot/config.yml +0 -6
  247. package/.eslintrc.js +0 -49
  248. package/.gitattributes +0 -2
  249. package/.github/ISSUE_TEMPLATE/bug.md +0 -32
  250. package/.github/ISSUE_TEMPLATE/feature-request.md +0 -30
  251. package/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md +0 -15
  252. package/.github/workflows/node.js.yml +0 -28
  253. package/.github/workflows/npm-publish.yml +0 -47
  254. package/.testcaferc.json +0 -5
  255. package/BookReader/bookreader-component-bundle.js +0 -14330
  256. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  257. package/BookReader/bookreader-component-bundle.js.map +0 -1
  258. package/BookReader/icons/sort-ascending.svg +0 -1
  259. package/BookReader/icons/sort-descending.svg +0 -1
  260. package/BookReader/jquery-1.10.1.js +0 -108
  261. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  262. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  263. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  264. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  265. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  266. package/BookReaderDemo/BookReaderDemo.css +0 -41
  267. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -115
  268. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  269. package/BookReaderDemo/BookReaderJSSimple.js +0 -55
  270. package/BookReaderDemo/IIIFBookReader.js +0 -207
  271. package/BookReaderDemo/assets/v5/Bookreader-logo-cool-grad.svg +0 -1
  272. package/BookReaderDemo/assets/v5/Bookreader-logo-flat.svg +0 -1
  273. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-cool-grad.png +0 -0
  274. package/BookReaderDemo/assets/v5/Bookreader-logo-hex-flat.png +0 -0
  275. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.png +0 -0
  276. package/BookReaderDemo/assets/v5/Bookreader-logo-lines.svg +0 -1
  277. package/BookReaderDemo/assets/v5/Bookreader-logo-warm.svg +0 -1
  278. package/BookReaderDemo/assets/v5/bookreader-logo-renders@1x.png +0 -0
  279. package/BookReaderDemo/assets/v5/bookreader-logo-renders@2x.png +0 -0
  280. package/BookReaderDemo/assets/v5/bookreader-v5-screenshot.png +0 -0
  281. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  282. package/BookReaderDemo/demo-advanced.html +0 -33
  283. package/BookReaderDemo/demo-autoplay.html +0 -38
  284. package/BookReaderDemo/demo-embed-iframe-src.html +0 -84
  285. package/BookReaderDemo/demo-embed.html +0 -26
  286. package/BookReaderDemo/demo-fullscreen-mobile.html +0 -36
  287. package/BookReaderDemo/demo-fullscreen.html +0 -33
  288. package/BookReaderDemo/demo-iiif.html +0 -34
  289. package/BookReaderDemo/demo-iiif.js +0 -26
  290. package/BookReaderDemo/demo-internetarchive.html +0 -74
  291. package/BookReaderDemo/demo-multiple.html +0 -43
  292. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  293. package/BookReaderDemo/demo-preview-pages.html +0 -1092
  294. package/BookReaderDemo/demo-simple.html +0 -34
  295. package/BookReaderDemo/demo-vendor-fullscreen.html +0 -36
  296. package/BookReaderDemo/immersion-1up.html +0 -64
  297. package/BookReaderDemo/immersion-mode.html +0 -35
  298. package/BookReaderDemo/toggle_controls.html +0 -53
  299. package/BookReaderDemo/view_mode.html +0 -39
  300. package/BookReaderDemo/viewmode-cycle.html +0 -41
  301. package/CHANGELOG.md +0 -476
  302. package/CONTRIBUTING.md +0 -7
  303. package/codecov.yml +0 -17
  304. package/index.html +0 -31
  305. package/karma.conf.js +0 -23
  306. package/screenshot.png +0 -0
  307. package/scripts/postversion.js +0 -10
  308. package/scripts/preversion.js +0 -14
  309. package/scripts/version.js +0 -26
  310. package/src/BookNavigator/BookModel.js +0 -14
  311. package/src/BookNavigator/BookNavigator.js +0 -451
  312. package/src/BookNavigator/assets/book-loader.js +0 -27
  313. package/src/BookNavigator/assets/icon_checkmark.js +0 -6
  314. package/src/BookNavigator/assets/icon_close.js +0 -3
  315. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  316. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  317. package/src/BookNavigator/bookmarks/bookmarks-provider.js +0 -53
  318. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  319. package/src/BookNavigator/downloads/downloads-provider.js +0 -66
  320. package/src/BookNavigator/search/a-search-result.js +0 -55
  321. package/src/BookNavigator/volumes/volumes-provider.js +0 -75
  322. package/src/BookNavigator/volumes/volumes.js +0 -161
  323. package/src/BookReader/DebugConsole.js +0 -54
  324. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  325. package/src/ItemNavigator/ItemNavigator.js +0 -372
  326. package/src/ItemNavigator/providers/sharing.js +0 -29
  327. package/src/assets/icons/sort-ascending.svg +0 -1
  328. package/src/assets/icons/sort-descending.svg +0 -1
  329. package/src/css/_MobileNav.scss +0 -194
  330. package/src/dragscrollable-br.js +0 -261
  331. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  332. package/src/plugins/plugin.mobile_nav.js +0 -287
  333. package/tests/BookReader/BookModel.test.js +0 -312
  334. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  335. package/tests/BookReader/DebugConsole.test.js +0 -25
  336. package/tests/BookReader/ImageCache.test.js +0 -150
  337. package/tests/BookReader/Mode1Up.test.js +0 -164
  338. package/tests/BookReader/Mode2Up.test.js +0 -247
  339. package/tests/BookReader/Navbar/Navbar.test.js +0 -169
  340. package/tests/BookReader/PageContainer.test.js +0 -115
  341. package/tests/BookReader/ReduceSet.test.js +0 -38
  342. package/tests/BookReader/Toolbar/Toolbar.test.js +0 -26
  343. package/tests/BookReader/utils/classes.test.js +0 -88
  344. package/tests/BookReader/utils.test.js +0 -109
  345. package/tests/BookReader.options.test.js +0 -39
  346. package/tests/BookReader.test.js +0 -301
  347. package/tests/e2e/README.md +0 -75
  348. package/tests/e2e/autoplay.test.js +0 -13
  349. package/tests/e2e/base.test.js +0 -35
  350. package/tests/e2e/helpers/base.js +0 -258
  351. package/tests/e2e/helpers/debug.js +0 -13
  352. package/tests/e2e/helpers/desktopSearch.js +0 -72
  353. package/tests/e2e/helpers/mobileSearch.js +0 -85
  354. package/tests/e2e/helpers/mockSearch.js +0 -93
  355. package/tests/e2e/helpers/rightToLeft.js +0 -29
  356. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  357. package/tests/e2e/models/BookReader.js +0 -11
  358. package/tests/e2e/models/Navigation.js +0 -56
  359. package/tests/e2e/rightToLeft.test.js +0 -20
  360. package/tests/e2e/viewmode.test.js +0 -37
  361. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  362. package/tests/karma/BookNavigator/bookmarks/bookmark-edit.test.js +0 -133
  363. package/tests/karma/BookNavigator/bookmarks/bookmarks-list.test.js +0 -222
  364. package/tests/karma/BookNavigator/search-results.test.js +0 -240
  365. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  366. package/tests/karma/BookNavigator/volumes.test.js +0 -133
  367. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  368. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  369. package/tests/plugins/plugin.autoplay.test.js +0 -52
  370. package/tests/plugins/plugin.chapters.test.js +0 -130
  371. package/tests/plugins/plugin.iframe.test.js +0 -42
  372. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  373. package/tests/plugins/plugin.resume.test.js +0 -98
  374. package/tests/plugins/plugin.text_selection.test.js +0 -203
  375. package/tests/plugins/plugin.url.test.js +0 -129
  376. package/tests/plugins/plugin.vendor-fullscreen.test.js +0 -65
  377. package/tests/plugins/search/plugin.search.test.js +0 -166
  378. package/tests/plugins/search/plugin.search.view.test.js +0 -106
  379. package/tests/plugins/tts/AbstractTTSEngine.test.js +0 -153
  380. package/tests/plugins/tts/FestivalTTSEngine.test.js +0 -59
  381. package/tests/plugins/tts/PageChunk.test.js +0 -57
  382. package/tests/plugins/tts/PageChunkIterator.test.js +0 -179
  383. package/tests/plugins/tts/WebTTSEngine.test.js +0 -126
  384. package/tests/plugins/tts/utils.test.js +0 -133
  385. package/tests/util/browserSniffing.test.js +0 -56
  386. package/tests/util/docCookies.test.js +0 -15
  387. package/tests/util/strings.test.js +0 -63
  388. package/tests/utils.js +0 -42
  389. 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,15 +439,15 @@ 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;
299
448
  }
300
449
  $.extend(params, modifiedNewParams);
301
- }
450
+ };
302
451
 
303
452
  /**
304
453
  * Parses params from from various initialization contexts (url, cookie, options)
@@ -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.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
- const searchTerm = searchParams.get('q')
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
  }
@@ -401,21 +551,21 @@ BookReader.prototype.initParams = function() {
401
551
  this.suppressFragmentChange = !params.fragmentChange;
402
552
 
403
553
  return params;
404
- }
554
+ };
405
555
 
406
556
  /**
407
557
  * Allow mocking of window.location.search
408
558
  */
409
559
  BookReader.prototype.getLocationSearch = function () {
410
560
  return window.location.search;
411
- }
561
+ };
412
562
 
413
563
  /**
414
564
  * Allow mocking of window.location.hash
415
565
  */
416
566
  BookReader.prototype.getLocationHash = function () {
417
567
  return window.location.hash;
418
- }
568
+ };
419
569
 
420
570
  /**
421
571
  * Return URL or fragment querystring
@@ -428,37 +578,63 @@ BookReader.prototype.readQueryString = function() {
428
578
  const hash = this.getLocationHash();
429
579
  const found = hash.search(/\?\w+=/);
430
580
  return found > -1 ? hash.slice(found) : '';
431
- }
581
+ };
432
582
 
433
583
  /**
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,19 +724,31 @@ 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
- }
559
- }
750
+
751
+ };
560
752
 
561
753
  /**
562
754
  * @param {EVENTS} name
@@ -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;
957
- }
1140
+ return this.displayedIndices ? this.displayedIndices.includes(index) :
1141
+ this.currentIndex() == index;
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) {
@@ -996,11 +1186,11 @@ BookReader.prototype.getPrevReadMode = function(mode) {
996
1186
  // Initial thumb, return 1up
997
1187
  return BookReader.constMode1up;
998
1188
  }
999
- }
1189
+ };
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();
@@ -1129,13 +1323,33 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1129
1323
 
1130
1324
  this.isFullscreenActive = true;
1131
1325
 
1132
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear',() => {
1133
- this.resize();
1134
- this.jumpToIndex(currentIndex);
1135
- });
1326
+ // Change tooltip of fullscreen button
1327
+ this.$('.BRnav .BRicon.full').attr('title', 'Exit fullscreen');
1328
+ this.$('.BRnav .BRicon.full .BRtooltip').text('Exit fullscreen');
1136
1329
 
1137
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
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
1138
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');
1139
1353
  };
1140
1354
 
1141
1355
  /**
@@ -1145,26 +1359,51 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1145
1359
  * - fires custom event
1146
1360
  * @param { boolean } bindKeyboardControls
1147
1361
  */
1148
- BookReader.prototype.exitFullScreen = function() {
1149
- this.refs.$brContainer.css('opacity', 0);
1150
-
1151
- $(document).unbind('keyup', this._fullscreenCloseHandler);
1152
-
1153
- var windowWidth = $(window).width();
1362
+ BookReader.prototype.exitFullScreen = async function () {
1363
+ this.refs.$br.addClass('BRfullscreenAnimation');
1364
+ $(document).off('keyup', this._fullscreenCloseHandler);
1154
1365
 
1366
+ const windowWidth = $(window).width();
1155
1367
  const canShow2up = this.options.controls.twoPage.visible;
1156
1368
  if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1157
1369
  this.switchMode(this.constMode2up);
1158
1370
  }
1159
1371
 
1160
1372
  this.isFullscreenActive = false;
1161
- this.updateBrClasses();
1162
1373
 
1163
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', () => {
1164
- this.resize();
1165
- });
1166
- this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
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
1167
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');
1168
1407
  };
1169
1408
 
1170
1409
  /**
@@ -1178,7 +1417,7 @@ BookReader.prototype.currentIndex = function() {
1178
1417
  return this.firstIndex; // $$$ TODO page in center of view would be better
1179
1418
  } else if (this.mode == this.constMode2up) {
1180
1419
  // Only allow indices that are actually present in book
1181
- return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
1420
+ return utils.clamp(this.firstIndex, 0, this.book.getNumLeafs() - 1);
1182
1421
  } else {
1183
1422
  throw 'currentIndex called for unimplemented mode ' + this.mode;
1184
1423
  }
@@ -1193,13 +1432,11 @@ BookReader.prototype.currentIndex = function() {
1193
1432
  */
1194
1433
  BookReader.prototype.updateFirstIndex = function(
1195
1434
  index,
1196
- { suppressFragmentChange = false } = {}
1435
+ { suppressFragmentChange = false } = {},
1197
1436
  ) {
1198
- // Called multiple times when defaults contains "mode/1up",
1199
- // including after init(). Skip fragment change if no index change
1200
- if (this.firstIndex === index) {
1201
- suppressFragmentChange = true;
1202
- }
1437
+ // If there's no change, do nothing
1438
+ if (this.firstIndex === index) return;
1439
+
1203
1440
  this.firstIndex = index;
1204
1441
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1205
1442
  this.trigger(BookReader.eventNames.fragmentChange);
@@ -1207,22 +1444,21 @@ BookReader.prototype.updateFirstIndex = function(
1207
1444
  // If there's an initial search we stop suppressing global URL changes
1208
1445
  // when local suppression ends
1209
1446
  // This seems to correctly handle multiple calls during mode/1up
1210
- if (this.options.initialSearchTerm && !suppressFragmentChange) {
1447
+ if (this.plugins.search?.options.initialSearchTerm && !suppressFragmentChange) {
1211
1448
  this.suppressFragmentChange = false;
1212
1449
  }
1213
- this.trigger('pageChanged');
1214
- 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);
1215
1455
  };
1216
1456
 
1217
1457
  /**
1218
1458
  * Flip the right page over onto the left
1219
1459
  */
1220
- BookReader.prototype.right = function() {
1221
- if ('rl' != this.pageProgression) {
1222
- this.next();
1223
- } else {
1224
- this.prev();
1225
- }
1460
+ BookReader.prototype.right = function({ ariaLive = true, triggerStop = true } = {}) {
1461
+ this.jumpToIndex('right', { ariaLive, triggerStop });
1226
1462
  };
1227
1463
 
1228
1464
  /**
@@ -1239,12 +1475,8 @@ BookReader.prototype.rightmost = function() {
1239
1475
  /**
1240
1476
  * Flip the left page over onto the right
1241
1477
  */
1242
- BookReader.prototype.left = function() {
1243
- if ('rl' != this.pageProgression) {
1244
- this.prev();
1245
- } else {
1246
- this.next();
1247
- }
1478
+ BookReader.prototype.left = function({ ariaLive = true, triggerStop = true } = {}) {
1479
+ this.jumpToIndex('left', { ariaLive, triggerStop });
1248
1480
  };
1249
1481
 
1250
1482
  /**
@@ -1258,283 +1490,42 @@ BookReader.prototype.leftmost = function() {
1258
1490
  }
1259
1491
  };
1260
1492
 
1261
- BookReader.prototype.next = function() {
1262
- if (this.constMode2up == this.mode) {
1263
- this.trigger(BookReader.eventNames.stop);
1264
- this.flipFwdToIndex(null);
1265
- } else {
1266
- if (this.firstIndex < this.lastDisplayableIndex()) {
1267
- this.jumpToIndex(this.firstIndex + 1);
1268
- }
1269
- }
1270
- };
1271
-
1272
- BookReader.prototype.prev = function() {
1273
- const isOnFrontPage = this.firstIndex < 1;
1274
- if (isOnFrontPage) return;
1275
-
1276
- if (this.constMode2up == this.mode) {
1277
- this.trigger(BookReader.eventNames.stop);
1278
- this.flipBackToIndex(null);
1279
- } else {
1280
- if (this.firstIndex >= 1) {
1281
- this.jumpToIndex(this.firstIndex - 1);
1282
- }
1283
- }
1284
- };
1285
-
1286
- BookReader.prototype.first = function() {
1287
- this.jumpToIndex(this.firstDisplayableIndex());
1288
- };
1289
-
1290
- BookReader.prototype.last = function() {
1291
- this.jumpToIndex(this.lastDisplayableIndex());
1292
- };
1293
-
1294
- /**
1295
- * Scrolls down one screen view
1296
- */
1297
- BookReader.prototype.scrollDown = function() {
1298
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1299
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1300
- // Whole pages are visible, scroll whole page only
1301
- return this.next();
1302
- }
1303
-
1304
- this.refs.$brContainer.stop(true).animate(
1305
- { scrollTop: '+=' + this._scrollAmount() + 'px'},
1306
- 400, 'easeInOutExpo'
1307
- );
1308
- return true;
1309
- } else {
1310
- return false;
1311
- }
1312
- };
1313
-
1314
- /**
1315
- * Scrolls up one screen view
1316
- */
1317
- BookReader.prototype.scrollUp = function() {
1318
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1319
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1320
- // Whole pages are visible, scroll whole page only
1321
- return this.prev();
1322
- }
1323
-
1324
- this.refs.$brContainer.stop(true).animate(
1325
- { scrollTop: '-=' + this._scrollAmount() + 'px'},
1326
- 400, 'easeInOutExpo'
1327
- );
1328
- return true;
1329
- } else {
1330
- return false;
1331
- }
1332
- };
1333
-
1334
1493
  /**
1335
- * The amount to scroll vertically in integer pixels
1494
+ * @param {object} options
1495
+ * @param {boolean} [options.triggerStop = true]
1496
+ * @param {number | 'fast' | 'slow'} [options.flipSpeed]
1497
+ * @param {boolean} [options.ariaLive = true]
1336
1498
  */
1337
- BookReader.prototype._scrollAmount = function() {
1338
- if (this.constMode1up == this.mode) {
1339
- // Overlap by % of page size
1340
- return parseInt(this.refs.$brContainer.prop('clientHeight') - this._models.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
1341
- }
1342
-
1343
- return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
1499
+ BookReader.prototype.next = function({
1500
+ triggerStop = true,
1501
+ flipSpeed = null,
1502
+ ariaLive = true,
1503
+ } = {}) {
1504
+ this.jumpToIndex('next', { ariaLive, flipSpeed, triggerStop });
1344
1505
  };
1345
1506
 
1346
1507
  /**
1347
- * @deprecated No longer used; will be remove in v5
1508
+ * @param {object} options
1509
+ * @param {boolean} [options.triggerStop = true]
1510
+ * @param {number | 'fast' | 'slow'} [options.flipSpeed]
1511
+ * @param {boolean} [options.ariaLive = true]
1348
1512
  */
1349
- BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
1350
- console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
1513
+ BookReader.prototype.prev = function({
1514
+ triggerStop = true,
1515
+ flipSpeed = null,
1516
+ ariaLive = true,
1517
+ } = {}) {
1518
+ this.jumpToIndex('prev', { ariaLive, flipSpeed, triggerStop });
1351
1519
  };
1352
1520
 
1353
- /**
1354
- * @deprecated No longer used; will be remove in v5
1355
- */
1356
- BookReader.prototype.pruneUnusedImgs = function() {
1357
- console.warn('Call to deprecated function: BookReader.pruneUnused. No-op.');
1521
+ BookReader.prototype.first = function({ ariaLive = true, triggerStop = false } = {}) {
1522
+ this.jumpToIndex(0, { ariaLive, triggerStop });
1358
1523
  };
1359
1524
 
1360
- /************************/
1361
- /** Mode1Up extensions **/
1362
- /************************/
1363
- /** @deprecated not used outside BookReader */
1364
- BookReader.prototype.prepareOnePageView = Mode1Up.prototype.prepare;
1365
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'prepare', 'prepareOnePageView');
1366
- /** @deprecated not used outside BookReader */
1367
- BookReader.prototype.drawLeafsOnePage = Mode1Up.prototype.drawLeafs;
1368
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'drawLeafs', 'drawLeafsOnePage');
1369
- /** @deprecated not used outside BookReader */
1370
- BookReader.prototype.zoom1up = Mode1Up.prototype.zoom;
1371
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'zoom', 'zoom1up');
1372
- /** @deprecated not used outside Mode1Up */
1373
- BookReader.prototype.onePageGetAutofitWidth = Mode1Up.prototype.getAutofitWidth;
1374
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitWidth', 'onePageGetAutofitWidth');
1375
- /** @deprecated not used outside Mode1Up, BookReader */
1376
- BookReader.prototype.onePageGetAutofitHeight = Mode1Up.prototype.getAutofitHeight;
1377
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitHeight', 'onePageGetAutofitHeight');
1378
- /** @deprecated not used outside Mode1Up, BookReader */
1379
- BookReader.prototype.onePageGetPageTop = Mode1Up.prototype.getPageTop;
1380
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getPageTop', 'onePageGetPageTop');
1381
- /** @deprecated not used outside Mode1Up, BookReader */
1382
- BookReader.prototype.onePageCalculateReductionFactors = Mode1Up.prototype.calculateReductionFactors;
1383
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateReductionFactors', 'onePageCalculateReductionFactors');
1384
- /** @deprecated not used outside Mode1Up, BookReader */
1385
- BookReader.prototype.resizePageView1up = Mode1Up.prototype.resizePageView;
1386
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'resizePageView', 'resizePageView1up');
1387
- /** @deprecated not used outside Mode1Up */
1388
- BookReader.prototype.onePageCalculateViewDimensions = Mode1Up.prototype.calculateViewDimensions;
1389
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateViewDimensions', 'onePageCalculateViewDimensions');
1390
- /** @deprecated not used outside Mode1Up */
1391
- BookReader.prototype.centerX1up = Mode1Up.prototype.centerX;
1392
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerX', 'centerX1up');
1393
- /** @deprecated not used outside Mode1Up */
1394
- BookReader.prototype.centerY1up = Mode1Up.prototype.centerY;
1395
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerY', 'centerY1up');
1396
-
1397
- /************************/
1398
- /** Mode2Up extensions **/
1399
- /************************/
1400
- /** @deprecated not used outside Mode2Up */
1401
- BookReader.prototype.zoom2up = Mode2Up.prototype.zoom;
1402
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'zoom', 'zoom2up');
1403
- BookReader.prototype.twoPageGetAutofitReduce = Mode2Up.prototype.getAutofitReduce;
1404
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getAutofitReduce', 'twoPageGetAutofitReduce');
1405
- BookReader.prototype.flipBackToIndex = Mode2Up.prototype.flipBackToIndex;
1406
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipBackToIndex', 'flipBackToIndex');
1407
- BookReader.prototype.flipFwdToIndex = Mode2Up.prototype.flipFwdToIndex;
1408
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipFwdToIndex', 'flipFwdToIndex');
1409
- BookReader.prototype.setHilightCss2UP = Mode2Up.prototype.setHilightCss;
1410
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setHilightCss', 'setHilightCss2UP');
1411
- /** @deprecated not used outside Mode2Up */
1412
- BookReader.prototype.setClickHandler2UP = Mode2Up.prototype.setClickHandler;
1413
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setClickHandler', 'setClickHandler2UP');
1414
- /** @deprecated not used outside Mode2Up */
1415
- BookReader.prototype.drawLeafsTwoPage = Mode2Up.prototype.drawLeafs;
1416
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'drawLeafs', 'drawLeafsTwoPage');
1417
- /** @deprecated not used outside BookReader */
1418
- BookReader.prototype.prepareTwoPageView = Mode2Up.prototype.prepareTwoPageView;
1419
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareTwoPageView', 'prepareTwoPageView');
1420
- /** @deprecated not used outside Mode2Up */
1421
- BookReader.prototype.prepareTwoPagePopUp = Mode2Up.prototype.preparePopUp;
1422
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'preparePopUp', 'prepareTwoPagePopUp');
1423
- /** @deprecated not used outside BookReader, Mode2Up */
1424
- BookReader.prototype.calculateSpreadSize = Mode2Up.prototype.calculateSpreadSize;
1425
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateSpreadSize', 'calculateSpreadSize');
1426
- /** @deprecated not used outside BookReader, Mode2Up */
1427
- BookReader.prototype.getIdealSpreadSize = Mode2Up.prototype.getIdealSpreadSize;
1428
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getIdealSpreadSize', 'getIdealSpreadSize');
1429
- /** @deprecated not used outside BookReader, Mode2Up */
1430
- BookReader.prototype.getSpreadSizeFromReduce = Mode2Up.prototype.getSpreadSizeFromReduce;
1431
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getSpreadSizeFromReduce', 'getSpreadSizeFromReduce');
1432
- /** @deprecated unused */
1433
- BookReader.prototype.twoPageIsZoomedIn = Mode2Up.prototype.isZoomedIn;
1434
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'isZoomedIn', 'twoPageIsZoomedIn');
1435
- /** @deprecated not used outside BookReader */
1436
- BookReader.prototype.twoPageCalculateReductionFactors = Mode2Up.prototype.calculateReductionFactors;
1437
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateReductionFactors', 'twoPageCalculateReductionFactors');
1438
- /** @deprecated unused */
1439
- BookReader.prototype.twoPageSetCursor = Mode2Up.prototype.setCursor;
1440
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setCursor', 'twoPageSetCursor');
1441
- /** @deprecated unused outside BookReader, Mode2Up */
1442
- BookReader.prototype.flipLeftToRight = Mode2Up.prototype.flipLeftToRight;
1443
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipLeftToRight', 'flipLeftToRight');
1444
- /** @deprecated unused outside BookReader, Mode2Up */
1445
- BookReader.prototype.flipRightToLeft = Mode2Up.prototype.flipRightToLeft;
1446
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipRightToLeft', 'flipRightToLeft');
1447
- /** @deprecated unused outside Mode2Up */
1448
- BookReader.prototype.setMouseHandlers2UP = Mode2Up.prototype.setMouseHandlers;
1449
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setMouseHandlers', 'setMouseHandlers2UP');
1450
- /** @deprecated unused outside BookReader, Mode2Up */
1451
- BookReader.prototype.prepareFlipLeftToRight = Mode2Up.prototype.prepareFlipLeftToRight;
1452
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipLeftToRight', 'prepareFlipLeftToRight');
1453
- /** @deprecated unused outside BookReader, Mode2Up */
1454
- BookReader.prototype.prepareFlipRightToLeft = Mode2Up.prototype.prepareFlipRightToLeft;
1455
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipRightToLeft', 'prepareFlipRightToLeft');
1456
- /** @deprecated unused outside Mode2Up */
1457
- BookReader.prototype.getPageWidth2UP = Mode2Up.prototype.getPageWidth;
1458
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getPageWidth', 'getPageWidth2UP');
1459
- /** @deprecated unused outside Mode2Up */
1460
- BookReader.prototype.twoPageGutter = Mode2Up.prototype.gutter;
1461
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutter', 'twoPageGutter');
1462
- /** @deprecated unused outside Mode2Up */
1463
- BookReader.prototype.twoPageTop = Mode2Up.prototype.top;
1464
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'top', 'twoPageTop');
1465
- /** @deprecated unused outside Mode2Up */
1466
- BookReader.prototype.twoPageCoverWidth = Mode2Up.prototype.coverWidth;
1467
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'coverWidth', 'twoPageCoverWidth');
1468
- /** @deprecated unused outside Mode2Up */
1469
- BookReader.prototype.twoPageGetViewCenter = Mode2Up.prototype.getViewCenter;
1470
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getViewCenter', 'twoPageGetViewCenter');
1471
- /** @deprecated unused outside BookReader, Mode2Up */
1472
- BookReader.prototype.twoPageCenterView = Mode2Up.prototype.centerView;
1473
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'centerView', 'twoPageCenterView');
1474
- /** @deprecated unused outside Mode2Up */
1475
- BookReader.prototype.twoPageFlipAreaHeight = Mode2Up.prototype.flipAreaHeight;
1476
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaHeight', 'twoPageFlipAreaHeight');
1477
- /** @deprecated unused outside Mode2Up */
1478
- BookReader.prototype.twoPageFlipAreaWidth = Mode2Up.prototype.flipAreaWidth;
1479
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaWidth', 'twoPageFlipAreaWidth');
1480
- /** @deprecated unused outside BookReader, Mode2Up */
1481
- BookReader.prototype.twoPageFlipAreaTop = Mode2Up.prototype.flipAreaTop;
1482
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaTop', 'twoPageFlipAreaTop');
1483
- /** @deprecated unused outside Mode2Up */
1484
- BookReader.prototype.twoPageLeftFlipAreaLeft = Mode2Up.prototype.leftFlipAreaLeft;
1485
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leftFlipAreaLeft', 'twoPageLeftFlipAreaLeft');
1486
- /** @deprecated unused outside Mode2Up */
1487
- BookReader.prototype.twoPageRightFlipAreaLeft = Mode2Up.prototype.rightFlipAreaLeft;
1488
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'rightFlipAreaLeft', 'twoPageRightFlipAreaLeft');
1489
- /** @deprecated unused outside BookReader, Mode2Up */
1490
- BookReader.prototype.gutterOffsetForIndex = Mode2Up.prototype.gutterOffsetForIndex;
1491
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutterOffsetForIndex', 'gutterOffsetForIndex');
1492
- /** @deprecated unused outside BookReader, Mode2Up */
1493
- BookReader.prototype.leafEdgeWidth = Mode2Up.prototype.leafEdgeWidth;
1494
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leafEdgeWidth', 'leafEdgeWidth');
1495
- /** @deprecated unused outside BookReader, Mode2Up */
1496
- BookReader.prototype.jumpIndexForLeftEdgePageX = Mode2Up.prototype.jumpIndexForLeftEdgePageX;
1497
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForLeftEdgePageX', 'jumpIndexForLeftEdgePageX');
1498
- /** @deprecated unused outside BookReader, Mode2Up */
1499
- BookReader.prototype.jumpIndexForRightEdgePageX = Mode2Up.prototype.jumpIndexForRightEdgePageX;
1500
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForRightEdgePageX', 'jumpIndexForRightEdgePageX');
1501
- /** @deprecated unused outside Mode2Up */
1502
- BookReader.prototype.prefetch = Mode2Up.prototype.prefetch;
1503
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prefetch', 'prefetch');
1504
- /** @deprecated unused outside Mode2Up */
1505
- BookReader.prototype.setSpreadIndices = Mode2Up.prototype.setSpreadIndices;
1506
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setSpreadIndices', 'setSpreadIndices');
1507
- /**
1508
- * Immediately stop flip animations. Callbacks are triggered.
1509
- */
1510
- BookReader.prototype.stopFlipAnimations = function() {
1511
- this.trigger(BookReader.eventNames.stop);
1512
-
1513
- // Stop animation, clear queue, trigger callbacks
1514
- if (this.leafEdgeTmp) {
1515
- $(this.leafEdgeTmp).stop(false, true);
1516
- }
1517
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1518
- $(this.$container).stop(false, true);
1519
- });
1520
-
1521
- // And again since animations also queued in callbacks
1522
- if (this.leafEdgeTmp) {
1523
- $(this.leafEdgeTmp).stop(false, true);
1524
- }
1525
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1526
- $(this.$container).stop(false, true);
1527
- });
1525
+ BookReader.prototype.last = function({ ariaLive = true, triggerStop = false } = {}) {
1526
+ this.jumpToIndex(this.book.getNumLeafs() - 1, { ariaLive, triggerStop });
1528
1527
  };
1529
1528
 
1530
- /**
1531
- * Returns true if keyboard navigation should be disabled for the event
1532
- * @param {Event}
1533
- * @return {boolean}
1534
- */
1535
- BookReader.prototype.keyboardNavigationIsDisabled = function(event) {
1536
- return event.target.tagName == "INPUT";
1537
- };
1538
1529
 
1539
1530
  /**
1540
1531
  * @template TClass extends { br: BookReader }
@@ -1558,30 +1549,9 @@ function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
1558
1549
  /***********************/
1559
1550
  /** Navbar extensions **/
1560
1551
  /***********************/
1552
+ /** This cannot be removed yet because plugin.tts.js overrides it */
1561
1553
  BookReader.prototype.initNavbar = Navbar.prototype.init;
1562
1554
  exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
1563
- BookReader.prototype.switchNavbarControls = Navbar.prototype.switchNavbarControls;
1564
- exposeOverrideableMethod(Navbar, '_components.navbar', 'switchNavbarControls');
1565
- BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButton;
1566
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
1567
- BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
1568
- exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
1569
- /** @deprecated */
1570
- BookReader.prototype.initEmbedNavbar = Navbar.prototype.initEmbed;
1571
- exposeOverrideableMethod(Navbar, '_components.navbar', 'initEmbed', 'initEmbedNavbar');
1572
- /** @deprecated unused */
1573
- BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
1574
- /** @deprecated unused outside this file */
1575
- BookReader.prototype.updateNavPageNum = Navbar.prototype.updateNavPageNum;
1576
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavPageNum');
1577
- /** @deprecated unused outside this file */
1578
- BookReader.prototype.updateNavIndex = Navbar.prototype.updateNavIndex;
1579
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavIndex');
1580
- /** @deprecated unused outside this file */
1581
- BookReader.prototype.updateNavIndexThrottled = utils.throttle(BookReader.prototype.updateNavIndex, 250, false);
1582
- /** @deprecated unused */
1583
- BookReader.prototype.updateNavIndexDebounced = utils.debounce(BookReader.prototype.updateNavIndex, 500, false);
1584
-
1585
1555
 
1586
1556
  /************************/
1587
1557
  /** Toolbar extensions **/
@@ -1596,515 +1566,15 @@ BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1596
1566
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1597
1567
  BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1598
1568
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
1599
- /** @deprecated zoom no longer in toolbar */
1600
- BookReader.prototype.updateToolbarZoom = Toolbar.prototype.updateToolbarZoom;
1601
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'updateToolbarZoom');
1602
- /** @deprecated unused */
1603
- BookReader.prototype.blankInfoDiv = blankInfoDiv;
1604
- /** @deprecated unused */
1605
- BookReader.prototype.blankShareDiv = blankShareDiv;
1606
- /** @deprecated unused */
1607
- BookReader.prototype.createPopup = createPopup;
1608
-
1609
- /**
1610
- * Bind navigation handlers
1611
- */
1612
- BookReader.prototype.bindNavigationHandlers = function() {
1613
- const self = this;
1614
-
1615
- // Note the mobile plugin attaches itself to body, so we need to select outside
1616
- const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
1617
- // Map of jIcon class -> click handler
1618
- const navigationControls = {
1619
- book_left: () => {
1620
- this.trigger(BookReader.eventNames.stop);
1621
- this.left();
1622
- },
1623
- book_right: () => {
1624
- this.trigger(BookReader.eventNames.stop);
1625
- this.right();
1626
- },
1627
- book_up: () => {
1628
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1629
- this.scrollUp();
1630
- } else {
1631
- this.prev();
1632
- }
1633
- },
1634
- book_down: () => {
1635
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1636
- this.scrollDown();
1637
- } else {
1638
- this.next();
1639
- }
1640
- },
1641
- book_top: this.first.bind(this),
1642
- book_bottom: this.last.bind(this),
1643
- book_leftmost: this.leftmost.bind(this),
1644
- book_rightmost: this.rightmost.bind(this),
1645
- onepg: () => {
1646
- this.switchMode(self.constMode1up);
1647
- },
1648
- thumb: () => {
1649
- this.switchMode(self.constModeThumb);
1650
- },
1651
- twopg: () => {
1652
- this.switchMode(self.constMode2up);
1653
- },
1654
- zoom_in: () => {
1655
- this.trigger(BookReader.eventNames.stop);
1656
- this.zoom(1);
1657
- this.trigger(BookReader.eventNames.zoomIn);
1658
- },
1659
- zoom_out: () => {
1660
- this.trigger(BookReader.eventNames.stop);
1661
- this.zoom(-1);
1662
- this.trigger(BookReader.eventNames.zoomOut);
1663
- },
1664
- full: () => {
1665
- if (this.ui == 'embed') {
1666
- var url = this.$('.BRembedreturn a').attr('href');
1667
- window.open(url);
1668
- } else {
1669
- this.toggleFullscreen();
1670
- }
1671
- },
1672
- };
1673
-
1674
- jIcons.filter('.fit').bind('fit', function() {
1675
- // XXXmang implement autofit zoom
1676
- });
1677
-
1678
- for (const control in navigationControls) {
1679
- jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1680
- navigationControls[control]()
1681
- return false;
1682
- });
1683
- }
1684
-
1685
- var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1686
- var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1687
-
1688
- this.$('.BRnavCntl').click(
1689
- function() {
1690
- var promises = [];
1691
- // TODO don't use magic constants
1692
- // TODO move this to a function
1693
- if ($brNavCntlBtmEl.hasClass('BRdn')) {
1694
- if (self.refs.$BRtoolbar)
1695
- promises.push(self.refs.$BRtoolbar.animate(
1696
- {top: self.getToolBarHeight() * -1}
1697
- ).promise());
1698
- promises.push(self.$('.BRfooter').animate({bottom: self.getFooterHeight() * -1}).promise());
1699
- $brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
1700
- $brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
1701
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
1702
- self.$('.BRnavCntl').delay(1000).animate({opacity:.75}, 1000);
1703
- } else {
1704
- if (self.refs.$BRtoolbar)
1705
- promises.push(self.refs.$BRtoolbar.animate({top:0}).promise());
1706
- promises.push(self.$('.BRfooter').animate({bottom:0}).promise());
1707
- $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1708
- $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1709
- self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
1710
- self.$('.BRvavCntl').animate({opacity:1})
1711
- }
1712
- $.when.apply($, promises).done(function() {
1713
- // Only do full resize in auto mode and need to recalc. size
1714
- if (self.mode == self.constMode2up && self.twoPage.autofit != null
1715
- && self.twoPage.autofit != 'none'
1716
- ) {
1717
- self.resize();
1718
- } else if (self.mode == self.constMode1up && self.onePage.autofit != null
1719
- && self.onePage.autofit != 'none') {
1720
- self.resize();
1721
- } else {
1722
- // Don't do a full resize to avoid redrawing images
1723
- self.resizeBRcontainer();
1724
- }
1725
- });
1726
- }
1727
- );
1728
- $brNavCntlBtmEl.mouseover(function() {
1729
- if ($(this).hasClass('BRup')) {
1730
- self.$('.BRnavCntl').animate({opacity:1},250);
1731
- }
1732
- }).mouseleave(function() {
1733
- if ($(this).hasClass('BRup')) {
1734
- self.$('.BRnavCntl').animate({opacity:.75},250);
1735
- }
1736
- });
1737
- $brNavCntlTopEl.mouseover(function() {
1738
- if ($(this).hasClass('BRdn')) {
1739
- self.$('.BRnavCntl').animate({opacity:1},250);
1740
- }
1741
- }).mouseleave(function() {
1742
- if ($(this).hasClass('BRdn')) {
1743
- self.$('.BRnavCntl').animate({opacity:.75},250);
1744
- }
1745
- });
1746
-
1747
- this.initSwipeData();
1748
-
1749
- $(document).off('mousemove.navigation', this.el);
1750
- $(document).on(
1751
- 'mousemove.navigation',
1752
- this.el,
1753
- { 'br': this },
1754
- this.navigationMousemoveHandler
1755
- );
1756
-
1757
- $(document).off('mousedown.swipe', '.BRpageimage');
1758
- $(document).on(
1759
- 'mousedown.swipe',
1760
- '.BRpageimage',
1761
- { 'br': this },
1762
- this.swipeMousedownHandler
1763
- );
1764
-
1765
- this.bindMozTouchHandlers();
1766
- };
1767
-
1768
- /**
1769
- * Unbind navigation handlers
1770
- */
1771
- BookReader.prototype.unbindNavigationHandlers = function() {
1772
- $(document).off('mousemove.navigation', this.el);
1773
- };
1774
-
1775
- /**
1776
- * Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
1777
- */
1778
- BookReader.prototype.navigationMousemoveHandler = function(event) {
1779
- // $$$ possibly not great to be calling this for every mousemove
1780
- if (event.data['br'].uiAutoHide) {
1781
- // 77px is an approximate height of the Internet Archive Top Nav
1782
- // 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
1783
- var navkey = $(document).height() - 75;
1784
- if ((event.pageY < 76) || (event.pageY > navkey)) {
1785
- // inside or near navigation elements
1786
- event.data['br'].hideNavigation();
1787
- } else {
1788
- event.data['br'].showNavigation();
1789
- }
1790
- }
1791
- };
1792
-
1793
- BookReader.prototype.initSwipeData = function(clientX, clientY) {
1794
- /*
1795
- * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
1796
- */
1797
- this._swipe = {
1798
- mightBeSwiping: false,
1799
- didSwipe: false,
1800
- mightBeDraggin: false,
1801
- didDrag: false,
1802
- startTime: (new Date).getTime(),
1803
- startX: clientX,
1804
- startY: clientY,
1805
- lastX: clientX,
1806
- lastY: clientY,
1807
- deltaX: 0,
1808
- deltaY: 0,
1809
- deltaT: 0
1810
- }
1811
- };
1812
-
1813
- BookReader.prototype.swipeMousedownHandler = function(event) {
1814
- var self = event.data['br'];
1815
-
1816
- // We should be the last bubble point for the page images
1817
- // Disable image drag and select, but keep right-click
1818
- if (event.which == 3) {
1819
- return !self.protected;
1820
- }
1821
-
1822
- $(event.target).bind('mouseout.swipe',
1823
- { 'br': self},
1824
- self.swipeMouseupHandler
1825
- ).bind('mouseup.swipe',
1826
- { 'br': self},
1827
- self.swipeMouseupHandler
1828
- ).bind('mousemove.swipe',
1829
- { 'br': self },
1830
- self.swipeMousemoveHandler
1831
- );
1832
-
1833
- self.initSwipeData(event.clientX, event.clientY);
1834
- self._swipe.mightBeSwiping = true;
1835
- self._swipe.mightBeDragging = true;
1836
-
1837
- event.preventDefault();
1838
- event.returnValue = false;
1839
- event.cancelBubble = true;
1840
- return false;
1841
- };
1842
-
1843
- BookReader.prototype.swipeMousemoveHandler = function(event) {
1844
- var self = event.data['br'];
1845
- var _swipe = self._swipe;
1846
- if (! _swipe.mightBeSwiping) {
1847
- return;
1848
- }
1849
-
1850
- // Update swipe data
1851
- _swipe.deltaX = event.clientX - _swipe.startX;
1852
- _swipe.deltaY = event.clientY - _swipe.startY;
1853
- _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
1854
-
1855
- var absX = Math.abs(_swipe.deltaX);
1856
- var absY = Math.abs(_swipe.deltaY);
1857
-
1858
- // Minimum distance in the amount of tim to trigger the swipe
1859
- var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
1860
- var maxSwipeTime = 400;
1861
-
1862
- // Check for horizontal swipe
1863
- if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
1864
- _swipe.mightBeSwiping = false; // only trigger once
1865
- _swipe.didSwipe = true;
1866
- if (self.mode == self.constMode2up) {
1867
- if (_swipe.deltaX < 0) {
1868
- self.right();
1869
- } else {
1870
- self.left();
1871
- }
1872
- }
1873
- }
1874
-
1875
- if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
1876
- if (_swipe.mightBeDragging) {
1877
- // Dragging
1878
- _swipe.didDrag = true;
1879
- self.refs.$brContainer
1880
- .scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
1881
- .scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
1882
- }
1883
- }
1884
- _swipe.lastX = event.clientX;
1885
- _swipe.lastY = event.clientY;
1886
-
1887
- event.preventDefault();
1888
- event.returnValue = false;
1889
- event.cancelBubble = true;
1890
- return false;
1891
- };
1892
-
1893
- BookReader.prototype.swipeMouseupHandler = function(event) {
1894
- var _swipe = event.data['br']._swipe;
1895
- _swipe.mightBeSwiping = false;
1896
- _swipe.mightBeDragging = false;
1897
-
1898
- $(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
1899
-
1900
- if (_swipe.didSwipe || _swipe.didDrag) {
1901
- // Swallow event if completed swipe gesture
1902
- event.preventDefault();
1903
- event.returnValue = false;
1904
- event.cancelBubble = true;
1905
- return false;
1906
- }
1907
- return true;
1908
- };
1909
-
1910
- BookReader.prototype.bindMozTouchHandlers = function() {
1911
- var self = this;
1912
-
1913
- // Currently only want touch handlers in 2up
1914
- this.refs.$br.bind('MozTouchDown', function(event) {
1915
- if (this.mode == self.constMode2up) {
1916
- event.preventDefault();
1917
- }
1918
- })
1919
- .bind('MozTouchMove', function(event) {
1920
- if (this.mode == self.constMode2up) {
1921
- event.preventDefault();
1922
- }
1923
- })
1924
- .bind('MozTouchUp', function(event) {
1925
- if (this.mode == self.constMode2up) {
1926
- event.preventDefault();
1927
- }
1928
- });
1929
- };
1930
-
1931
- /**
1932
- * Returns true if the navigation elements are currently visible
1933
- * @return {boolean}
1934
- */
1935
- BookReader.prototype.navigationIsVisible = function() {
1936
- // $$$ doesn't account for transitioning states, nav must be fully visible to return true
1937
- var toolpos = this.refs.$BRtoolbar.position();
1938
- var tooltop = toolpos.top;
1939
- return tooltop == 0;
1940
- };
1941
-
1942
- /**
1943
- * Main controller that sets navigation into view.
1944
- * Defaults to SHOW the navigation chrome
1945
- */
1946
- BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
1947
- var animationLength = this.constNavAnimationDuration;
1948
- var animationType = 'linear';
1949
- var resizePageContainer = function resizePageContainer () {
1950
- /* main page container fills whole container */
1951
- if (this.constMode2up !== this.mode) {
1952
- var animate = true;
1953
- this.resizeBRcontainer(animate);
1954
- }
1955
- this.trigger(BookReader.eventNames.navToggled);
1956
- }.bind(this);
1957
-
1958
- var toolbarHeight = 0;
1959
- var navbarHeight = 0;
1960
- if (hide) {
1961
- toolbarHeight = this.getToolBarHeight() * -1;
1962
- navbarHeight = this.getFooterHeight() * -1;
1963
-
1964
- this.refs.$BRtoolbar.addClass('js-menu-hide');
1965
- this.refs.$BRfooter.addClass('js-menu-hide');
1966
- } else {
1967
- this.refs.$BRtoolbar.removeClass('js-menu-hide');
1968
- this.refs.$BRfooter.removeClass('js-menu-hide');
1969
- }
1970
-
1971
- this.refs.$BRtoolbar.animate(
1972
- { top: toolbarHeight },
1973
- animationLength,
1974
- animationType,
1975
- resizePageContainer
1976
- );
1977
- this.refs.$BRfooter.animate(
1978
- { bottom: navbarHeight },
1979
- animationLength,
1980
- animationType,
1981
- resizePageContainer
1982
- );
1983
- };
1984
- /**
1985
- * Hide navigation elements, if visible
1986
- */
1987
- BookReader.prototype.hideNavigation = function() {
1988
- // Check if navigation is showing
1989
- if (this.navigationIsVisible()) {
1990
- var hide = true;
1991
- this.setNavigationView(hide);
1992
- }
1993
- };
1994
-
1995
- /**
1996
- * Show navigation elements
1997
- */
1998
- BookReader.prototype.showNavigation = function() {
1999
- // Check if navigation is hidden
2000
- if (!this.navigationIsVisible()) {
2001
- this.setNavigationView();
2002
- }
2003
- };
2004
-
2005
- /**
2006
- * Returns the index of the first visible page, dependent on the mode.
2007
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2008
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2009
- * @return {number}
2010
- */
2011
- BookReader.prototype.firstDisplayableIndex = function() {
2012
- if (this.mode != this.constMode2up) {
2013
- return 0;
2014
- }
2015
-
2016
- if ('rl' != this.pageProgression) {
2017
- // LTR
2018
- if (this._models.book.getPageSide(0) == 'L') {
2019
- return 0;
2020
- } else {
2021
- return -1;
2022
- }
2023
- } else {
2024
- // RTL
2025
- if (this._models.book.getPageSide(0) == 'R') {
2026
- return 0;
2027
- } else {
2028
- return -1;
2029
- }
2030
- }
2031
- };
2032
-
2033
- /**
2034
- * Returns the index of the last visible page, dependent on the mode.
2035
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2036
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2037
- * @return {number}
2038
- */
2039
- BookReader.prototype.lastDisplayableIndex = function() {
2040
-
2041
- var lastIndex = this._models.book.getNumLeafs() - 1;
2042
-
2043
- if (this.mode != this.constMode2up) {
2044
- return lastIndex;
2045
- }
2046
-
2047
- if ('rl' != this.pageProgression) {
2048
- // LTR
2049
- if (this._models.book.getPageSide(lastIndex) == 'R') {
2050
- return lastIndex;
2051
- } else {
2052
- return lastIndex + 1;
2053
- }
2054
- } else {
2055
- // RTL
2056
- if (this._models.book.getPageSide(lastIndex) == 'L') {
2057
- return lastIndex;
2058
- } else {
2059
- return lastIndex + 1;
2060
- }
2061
- }
2062
- };
2063
-
2064
1569
 
2065
1570
  /**************************/
2066
1571
  /** BookModel extensions **/
2067
1572
  /**************************/
2068
- /** @deprecated not used outside */
2069
- BookReader.prototype.getMedianPageSize = BookModel.prototype.getMedianPageSize;
2070
- exposeOverrideableMethod(BookModel, '_models.book', 'getMedianPageSize');
2071
- BookReader.prototype._getPageWidth = BookModel.prototype._getPageWidth;
2072
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageWidth');
2073
- BookReader.prototype._getPageHeight = BookModel.prototype._getPageHeight;
2074
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageHeight');
2075
- BookReader.prototype.getPageIndex = BookModel.prototype.getPageIndex;
2076
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndex');
2077
- /** @deprecated not used outside */
2078
- BookReader.prototype.getPageIndices = BookModel.prototype.getPageIndices;
2079
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndices');
2080
- BookReader.prototype.getPageName = BookModel.prototype.getPageName;
2081
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageName');
2082
- BookReader.prototype.getNumLeafs = BookModel.prototype.getNumLeafs;
2083
- exposeOverrideableMethod(BookModel, '_models.book', 'getNumLeafs');
2084
- BookReader.prototype.getPageWidth = BookModel.prototype.getPageWidth;
2085
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageWidth');
2086
- BookReader.prototype.getPageHeight = BookModel.prototype.getPageHeight;
2087
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageHeight');
1573
+ // Must modify petabox extension, which expects this on the prototype
1574
+ // before removing.
2088
1575
  BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
2089
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageURI');
2090
- BookReader.prototype.getPageSide = BookModel.prototype.getPageSide;
2091
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageSide');
2092
- BookReader.prototype.getPageNum = BookModel.prototype.getPageNum;
2093
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageNum');
2094
- BookReader.prototype.getPageProp = BookModel.prototype.getPageProp;
2095
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageProp');
2096
- BookReader.prototype.getSpreadIndices = BookModel.prototype.getSpreadIndices;
2097
- exposeOverrideableMethod(BookModel, '_models.book', 'getSpreadIndices');
2098
- BookReader.prototype.leafNumToIndex = BookModel.prototype.leafNumToIndex;
2099
- exposeOverrideableMethod(BookModel, '_models.book', 'leafNumToIndex');
2100
- BookReader.prototype.parsePageString = BookModel.prototype.parsePageString;
2101
- exposeOverrideableMethod(BookModel, '_models.book', 'parsePageString');
2102
- /** @deprecated unused */
2103
- BookReader.prototype._getDataFlattened = BookModel.prototype._getDataFlattened;
2104
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataFlattened');
2105
- /** @deprecated unused */
2106
- BookReader.prototype._getDataProp = BookModel.prototype._getDataProp;
2107
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataProp');
1576
+ exposeOverrideableMethod(BookModel, 'book', 'getPageURI');
1577
+
2108
1578
 
2109
1579
  // Parameter related functions
2110
1580
 
@@ -2123,7 +1593,7 @@ BookReader.prototype.updateFromParams = function(params) {
2123
1593
  if (mode) {
2124
1594
  this.switchMode(
2125
1595
  mode,
2126
- { init: init, suppressFragmentChange: !fragmentChange }
1596
+ { init: init, suppressFragmentChange: !fragmentChange },
2127
1597
  );
2128
1598
  }
2129
1599
 
@@ -2135,7 +1605,7 @@ BookReader.prototype.updateFromParams = function(params) {
2135
1605
  }
2136
1606
  } else if ('undefined' != typeof(params.page)) {
2137
1607
  // $$$ this assumes page numbers are unique
2138
- if (params.page != this._models.book.getPageNum(this.currentIndex())) {
1608
+ if (params.page != this.book.getPageNum(this.currentIndex())) {
2139
1609
  this.jumpToPage(params.page);
2140
1610
  }
2141
1611
  }
@@ -2144,8 +1614,8 @@ BookReader.prototype.updateFromParams = function(params) {
2144
1614
  // process /search
2145
1615
  // @deprecated for urlMode 'history'
2146
1616
  // Continues to work for urlMode 'hash'
2147
- if (this.enableSearch && 'undefined' != typeof(params.search)) {
2148
- if (this.searchTerm !== params.search) {
1617
+ if (this.plugins.search?.enabled && 'undefined' != typeof(params.search)) {
1618
+ if (this.plugins.search.searchTerm !== params.search) {
2149
1619
  this.$('.BRsearchInput').val(params.search);
2150
1620
  }
2151
1621
  }
@@ -2169,7 +1639,7 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2169
1639
  // check there are enough pages to display
2170
1640
  // $$$ this is a workaround for the mis-feature that we can't display
2171
1641
  // short books in 2up mode
2172
- if (this._models.book.getNumLeafs() < 2) {
1642
+ if (this.book.getNumLeafs() < 2) {
2173
1643
  return false;
2174
1644
  }
2175
1645
  }
@@ -2177,31 +1647,6 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2177
1647
  return true;
2178
1648
  };
2179
1649
 
2180
-
2181
- /**
2182
- * @deprecated. Use PageModel.getURISrcSet. Slated for removal in v5.
2183
- * Returns the srcset with correct URIs or void string if out of range
2184
- * Also makes the reduce argument optional
2185
- * @param {number} index
2186
- * @param {number} [reduce]
2187
- * @param {number} [rotate]
2188
- * @return {string}
2189
- */
2190
- BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2191
- const page = this._models.book.getPage(index, false);
2192
- // Synthesize page
2193
- if (!page) return "";
2194
-
2195
- // reduce not passed in
2196
- // $$$ this probably won't work for thumbnail mode
2197
- if ('undefined' == typeof(reduce)) {
2198
- reduce = page.height / this.twoPage.height;
2199
- }
2200
-
2201
- return page.getURISrcSet(reduce, rotate);
2202
- }
2203
-
2204
-
2205
1650
  /**
2206
1651
  * Returns the page URI or transparent image if out of range
2207
1652
  * Also makes the reduce argument optional
@@ -2211,7 +1656,7 @@ BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2211
1656
  * @return {string}
2212
1657
  */
2213
1658
  BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2214
- const page = this._models.book.getPage(index, false);
1659
+ const page = this.book.getPage(index, false);
2215
1660
  // Synthesize page
2216
1661
  if (!page) return this.imagesBaseURL + "transparent.png";
2217
1662
 
@@ -2225,21 +1670,37 @@ BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2225
1670
  };
2226
1671
 
2227
1672
  /**
2228
- * @param {string}
1673
+ * @param {string} msg
1674
+ * @param {function|undefined} onCloseCallback
2229
1675
  */
2230
- BookReader.prototype.showProgressPopup = function(msg) {
1676
+ BookReader.prototype.showProgressPopup = function(msg, onCloseCallback) {
2231
1677
  if (this.popup) return;
2232
1678
 
2233
1679
  this.popup = document.createElement("div");
2234
1680
  $(this.popup).prop('className', 'BRprogresspopup');
2235
- var bar = document.createElement("div");
1681
+
1682
+ if (typeof(onCloseCallback) === 'function') {
1683
+ const closeButton = document.createElement('button');
1684
+ closeButton.setAttribute('title', 'close');
1685
+ closeButton.setAttribute('class', 'close-popup');
1686
+ const icon = document.createElement('span');
1687
+ icon.setAttribute('class', 'icon icon-close-dark');
1688
+ $(closeButton).append(icon);
1689
+ closeButton.addEventListener('click', () => {
1690
+ onCloseCallback();
1691
+ this.removeProgressPopup();
1692
+ });
1693
+ $(this.popup).append(closeButton);
1694
+ }
1695
+
1696
+ const bar = document.createElement("div");
2236
1697
  $(bar).css({
2237
- height: '20px'
1698
+ height: '20px',
2238
1699
  }).prop('className', 'BRprogressbar');
2239
1700
  $(this.popup).append(bar);
2240
1701
 
2241
1702
  if (msg) {
2242
- var msgdiv = document.createElement("div");
1703
+ const msgdiv = document.createElement("div");
2243
1704
  msgdiv.innerHTML = msg;
2244
1705
  $(this.popup).append(msgdiv);
2245
1706
  }
@@ -2261,7 +1722,7 @@ BookReader.prototype.initUIStrings = function() {
2261
1722
  // the toolbar and nav bar easier
2262
1723
 
2263
1724
  // Setup tooltips -- later we could load these from a file for i18n
2264
- var titles = {
1725
+ const titles = {
2265
1726
  '.logo': 'Go to Archive.org', // $$$ update after getting OL record
2266
1727
  '.zoom_in': 'Zoom in',
2267
1728
  '.zoom_out': 'Zoom out',
@@ -2274,15 +1735,12 @@ BookReader.prototype.initUIStrings = function() {
2274
1735
  '.bookmark': 'Bookmark this page',
2275
1736
  '.share': 'Share this book',
2276
1737
  '.info': 'About this book',
2277
- '.full': 'Toggle fullscreen',
1738
+ '.full': 'Go fullscreen',
1739
+ '.toggle_slider': 'Toggle page controls',
2278
1740
  '.book_left': 'Flip left',
2279
1741
  '.book_right': 'Flip right',
2280
- '.book_up': 'Page up',
2281
- '.book_down': 'Page down',
2282
1742
  '.play': 'Play',
2283
1743
  '.pause': 'Pause',
2284
- '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
2285
- '.BRup': 'Show/hide nav bar',
2286
1744
  '.book_top': 'First page',
2287
1745
  '.book_bottom': 'Last page',
2288
1746
  '.book_leftmost': 'First page',
@@ -2293,10 +1751,10 @@ BookReader.prototype.initUIStrings = function() {
2293
1751
  titles['.book_rightmost'] = 'First page';
2294
1752
  }
2295
1753
 
2296
- for (var icon in titles) {
1754
+ for (const icon in titles) {
2297
1755
  this.$(icon).prop('title', titles[icon]);
2298
1756
  }
2299
- }
1757
+ };
2300
1758
 
2301
1759
  /**
2302
1760
  * Reloads images. Useful when some images might have failed.
@@ -2304,7 +1762,7 @@ BookReader.prototype.initUIStrings = function() {
2304
1762
  BookReader.prototype.reloadImages = function() {
2305
1763
  this.refs.$brContainer.find('img').each(function(index, elem) {
2306
1764
  if (!elem.complete || elem.naturalHeight === 0) {
2307
- var src = elem.src;
1765
+ const src = elem.src;
2308
1766
  elem.src = '';
2309
1767
  setTimeout(function() {
2310
1768
  elem.src = src;
@@ -2318,16 +1776,16 @@ BookReader.prototype.reloadImages = function() {
2318
1776
  * @return {number}
2319
1777
  */
2320
1778
  BookReader.prototype.getFooterHeight = function() {
2321
- 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;
2322
1780
  if ($heightEl && this.refs.$BRfooter) {
2323
- var outerHeight = $heightEl.outerHeight();
2324
- var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
1781
+ const outerHeight = $heightEl.outerHeight();
1782
+ const bottom = parseInt(this.refs.$BRfooter.css('bottom'));
2325
1783
  if (!isNaN(outerHeight) && !isNaN(bottom)) {
2326
1784
  return outerHeight + bottom;
2327
1785
  }
2328
1786
  }
2329
1787
  return 0;
2330
- }
1788
+ };
2331
1789
 
2332
1790
  // Basic Usage built-in Methods (can be overridden through options)
2333
1791
  // This implementation uses options.data value for populating BookReader
@@ -2337,10 +1795,11 @@ BookReader.prototype.getFooterHeight = function() {
2337
1795
  * @return {Object}
2338
1796
  */
2339
1797
  BookReader.prototype.paramsFromCurrent = function() {
2340
- var params = {};
1798
+ const params = {};
2341
1799
 
2342
- var index = this.currentIndex();
2343
- var pageNum = this._models.book.getPageNum(index);
1800
+ // Path params
1801
+ const index = this.currentIndex();
1802
+ const pageNum = this.book.getPageNum(index);
2344
1803
  if ((pageNum === 0) || pageNum) {
2345
1804
  params.page = pageNum;
2346
1805
  }
@@ -2348,12 +1807,19 @@ BookReader.prototype.paramsFromCurrent = function() {
2348
1807
  params.index = index;
2349
1808
  params.mode = this.mode;
2350
1809
 
1810
+ // Unused params
2351
1811
  // $$$ highlight
2352
1812
  // $$$ region
2353
1813
 
2354
- // search
2355
- if (this.enableSearch) {
2356
- 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;
2357
1823
  }
2358
1824
 
2359
1825
  return params;
@@ -2372,7 +1838,7 @@ BookReader.prototype.paramsFromCurrent = function() {
2372
1838
  * @return {Object}
2373
1839
  */
2374
1840
  BookReader.prototype.paramsFromFragment = function(fragment) {
2375
- var params = {};
1841
+ const params = {};
2376
1842
 
2377
1843
  // For backwards compatibility we allow an initial # character
2378
1844
  // (as from window.location.hash) but don't require it
@@ -2381,7 +1847,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2381
1847
  }
2382
1848
 
2383
1849
  // Simple #nn syntax
2384
- var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
1850
+ const oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
2385
1851
  if ( !isNaN(oldStyleLeafNum) ) {
2386
1852
  params.index = oldStyleLeafNum;
2387
1853
 
@@ -2390,9 +1856,9 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2390
1856
  }
2391
1857
 
2392
1858
  // Split into key-value pairs
2393
- var urlArray = fragment.split('/');
2394
- var urlHash = {};
2395
- 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) {
2396
1862
  urlHash[urlArray[i]] = urlArray[i + 1];
2397
1863
  }
2398
1864
 
@@ -2408,7 +1874,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2408
1874
  // Index and page
2409
1875
  if ('undefined' != typeof(urlHash['page'])) {
2410
1876
  // page was set -- may not be int
2411
- params.page = urlHash['page'];
1877
+ params.page = decodeURIComponent(urlHash['page']);
2412
1878
  }
2413
1879
 
2414
1880
  // $$$ process /region
@@ -2422,7 +1888,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2422
1888
 
2423
1889
  // $$$ process /theme
2424
1890
  if (urlHash['theme'] != undefined) {
2425
- params.theme = urlHash['theme']
1891
+ params.theme = urlHash['theme'];
2426
1892
  }
2427
1893
  return params;
2428
1894
  };
@@ -2440,11 +1906,10 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2440
1906
  * @return {string}
2441
1907
  */
2442
1908
  BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2443
- const separator = '/';
2444
1909
  const fragments = [];
2445
1910
 
2446
1911
  if ('undefined' != typeof(params.page)) {
2447
- fragments.push('page', params.page);
1912
+ fragments.push('page', encodeURIComponent(params.page));
2448
1913
  } else {
2449
1914
  if ('undefined' != typeof(params.index)) {
2450
1915
  // Don't have page numbering but we do have the index
@@ -2470,15 +1935,18 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2470
1935
 
2471
1936
  // search
2472
1937
  if (params.search && urlMode === 'hash') {
2473
- fragments.push('search', params.search);
1938
+ fragments.push('search', utils.encodeURIComponentPlus(params.search));
2474
1939
  }
2475
1940
 
2476
- return utils.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
1941
+ return fragments.join('/');
2477
1942
  };
2478
1943
 
2479
1944
  /**
2480
1945
  * Create, update querystring from the params object
2481
1946
  *
1947
+ * Handles:
1948
+ * view=
1949
+ * q=
2482
1950
  * @param {Object} params
2483
1951
  * @param {string} currQueryString
2484
1952
  * @param {string} [urlMode]
@@ -2487,30 +1955,32 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2487
1955
  BookReader.prototype.queryStringFromParams = function(
2488
1956
  params,
2489
1957
  currQueryString,
2490
- urlMode = 'hash'
1958
+ urlMode = 'hash',
2491
1959
  ) {
2492
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
+
2493
1970
  if (params.search && urlMode === 'history') {
2494
- newParams.set('q', params.search)
1971
+ newParams.set('q', params.search);
2495
1972
  }
2496
1973
  // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
2497
1974
  // Note: This method returns the query string without the question mark.
2498
1975
  const result = newParams.toString();
2499
1976
  return result ? '?' + result : '';
2500
- }
1977
+ };
2501
1978
 
2502
1979
  /**
2503
1980
  * Helper to select within instance's elements
2504
1981
  */
2505
1982
  BookReader.prototype.$ = function(selector) {
2506
1983
  return this.refs.$br.find(selector);
2507
- }
2508
-
2509
- /**
2510
- * Polyfill for deprecated method
2511
- */
2512
- jQuery.curCSS = function(element, prop, val) {
2513
- return jQuery(element).css(prop, val);
2514
1984
  };
2515
1985
 
2516
1986
  window.BookReader = BookReader;