@internetarchive/bookreader 5.0.0-8-multiple-files → 5.0.0-80

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 (321) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +78 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +442 -1393
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -12
  13. package/BookReader/icons/2up.svg +1 -15
  14. package/BookReader/icons/advance.svg +3 -26
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -0
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -17
  19. package/BookReader/icons/fullscreen_exit.svg +1 -17
  20. package/BookReader/icons/hamburger.svg +1 -15
  21. package/BookReader/icons/left-arrow.svg +1 -12
  22. package/BookReader/icons/magnify-minus.svg +1 -16
  23. package/BookReader/icons/magnify-plus.svg +1 -17
  24. package/BookReader/icons/magnify.svg +1 -15
  25. package/BookReader/icons/pause.svg +1 -23
  26. package/BookReader/icons/play.svg +1 -22
  27. package/BookReader/icons/playback-speed.svg +1 -34
  28. package/BookReader/icons/read-aloud.svg +1 -22
  29. package/BookReader/icons/review.svg +3 -22
  30. package/BookReader/icons/thumbnails.svg +1 -17
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -22
  33. package/BookReader/images/BRicons.svg +5 -94
  34. package/BookReader/images/books_graphic.svg +1 -177
  35. package/BookReader/images/icon_book.svg +1 -12
  36. package/BookReader/images/icon_bookmark.svg +1 -12
  37. package/BookReader/images/icon_gear.svg +1 -14
  38. package/BookReader/images/icon_hamburger.svg +1 -20
  39. package/BookReader/images/icon_home.svg +1 -21
  40. package/BookReader/images/icon_info.svg +1 -11
  41. package/BookReader/images/icon_one_page.svg +1 -8
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -15
  45. package/BookReader/images/icon_search_button.svg +1 -8
  46. package/BookReader/images/icon_share.svg +1 -9
  47. package/BookReader/images/icon_skip-ahead.svg +1 -6
  48. package/BookReader/images/icon_skip-back.svg +2 -13
  49. package/BookReader/images/icon_speaker.svg +1 -18
  50. package/BookReader/images/icon_speaker_open.svg +1 -10
  51. package/BookReader/images/icon_thumbnails.svg +1 -12
  52. package/BookReader/images/icon_toc.svg +1 -5
  53. package/BookReader/images/icon_two_pages.svg +1 -9
  54. package/BookReader/images/marker_chap-off.svg +1 -11
  55. package/BookReader/images/marker_chap-on.svg +1 -11
  56. package/BookReader/images/marker_srch-on.svg +1 -11
  57. package/BookReader/jquery-3.js +2 -0
  58. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  59. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -165
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +22 -301
  64. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  65. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  66. package/BookReader/plugins/plugin.iframe.js +1 -74
  67. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  68. package/BookReader/plugins/plugin.iiif.js +2 -0
  69. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  70. package/BookReader/plugins/plugin.resume.js +1 -368
  71. package/BookReader/plugins/plugin.resume.js.map +1 -1
  72. package/BookReader/plugins/plugin.search.js +2 -1420
  73. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  74. package/BookReader/plugins/plugin.search.js.map +1 -1
  75. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  76. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  77. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  78. package/BookReader/plugins/plugin.tts.js +2 -9193
  79. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  80. package/BookReader/plugins/plugin.tts.js.map +1 -1
  81. package/BookReader/plugins/plugin.url.js +1 -269
  82. package/BookReader/plugins/plugin.url.js.map +1 -1
  83. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  85. package/BookReader/webcomponents-bundle.js +3 -0
  86. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  87. package/BookReader/webcomponents-bundle.js.map +1 -0
  88. package/BookReaderDemo/BookReaderDemo.css +18 -19
  89. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  90. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +147 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-autoplay.html +2 -3
  95. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  96. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  97. package/BookReaderDemo/demo-fullscreen.html +2 -4
  98. package/BookReaderDemo/demo-iiif.html +99 -12
  99. package/BookReaderDemo/demo-internetarchive.html +214 -18
  100. package/BookReaderDemo/demo-multiple.html +2 -1
  101. package/BookReaderDemo/demo-preview-pages.html +2 -1
  102. package/BookReaderDemo/demo-simple.html +2 -1
  103. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  104. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  105. package/BookReaderDemo/immersion-1up.html +2 -2
  106. package/BookReaderDemo/immersion-mode.html +2 -4
  107. package/BookReaderDemo/toggle_controls.html +3 -2
  108. package/BookReaderDemo/view_mode.html +2 -1
  109. package/BookReaderDemo/viewmode-cycle.html +2 -3
  110. package/CHANGELOG.md +545 -33
  111. package/README.md +14 -1
  112. package/babel.config.js +20 -0
  113. package/codecov.yml +6 -0
  114. package/index.html +4 -1
  115. package/jsconfig.json +19 -0
  116. package/netlify.toml +9 -0
  117. package/package.json +70 -60
  118. package/renovate.json +52 -0
  119. package/scripts/preversion.js +0 -1
  120. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  121. package/src/BookNavigator/assets/button-base.js +9 -2
  122. package/src/BookNavigator/assets/ia-logo.js +17 -0
  123. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  124. package/src/BookNavigator/assets/icon_close.js +1 -1
  125. package/src/BookNavigator/book-navigator.js +590 -0
  126. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  127. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  128. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  129. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  130. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  131. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  132. package/src/BookNavigator/delete-modal-actions.js +1 -1
  133. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  134. package/src/BookNavigator/downloads/downloads.js +41 -25
  135. package/src/BookNavigator/search/search-provider.js +80 -28
  136. package/src/BookNavigator/search/search-results.js +28 -25
  137. package/src/BookNavigator/sharing.js +27 -0
  138. package/src/BookNavigator/viewable-files.js +95 -0
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  140. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  141. package/src/BookReader/BookModel.js +64 -34
  142. package/src/BookReader/DragScrollable.js +233 -0
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +776 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +18 -12
  150. package/src/BookReader/Navbar/Navbar.js +14 -40
  151. package/src/BookReader/PageContainer.js +81 -6
  152. package/src/BookReader/ReduceSet.js +1 -1
  153. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +27 -2
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils.js +118 -13
  160. package/src/BookReader.js +446 -1062
  161. package/src/assets/icons/close-circle-dark.svg +1 -0
  162. package/src/assets/icons/magnify-minus.svg +3 -7
  163. package/src/assets/icons/magnify-plus.svg +3 -7
  164. package/src/assets/icons/voice.svg +1 -0
  165. package/src/css/BookReader.scss +1 -17
  166. package/src/css/_BRBookmarks.scss +1 -1
  167. package/src/css/_BRComponent.scss +1 -1
  168. package/src/css/_BRmain.scss +33 -27
  169. package/src/css/_BRnav.scss +12 -39
  170. package/src/css/_BRpages.scss +149 -40
  171. package/src/css/_BRsearch.scss +68 -230
  172. package/src/css/_BRtoolbar.scss +5 -5
  173. package/src/css/_TextSelection.scss +87 -27
  174. package/src/css/_colorbox.scss +2 -2
  175. package/src/css/_controls.scss +20 -7
  176. package/src/css/_icons.scss +7 -1
  177. package/src/ia-bookreader/ia-bookreader.js +224 -0
  178. package/src/plugins/plugin.archive_analytics.js +3 -3
  179. package/src/plugins/plugin.autoplay.js +5 -11
  180. package/src/plugins/plugin.chapters.js +237 -191
  181. package/src/plugins/plugin.iiif.js +151 -0
  182. package/src/plugins/plugin.resume.js +3 -3
  183. package/src/plugins/plugin.text_selection.js +464 -134
  184. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  185. package/src/plugins/search/plugin.search.js +175 -120
  186. package/src/plugins/search/utils.js +43 -0
  187. package/src/plugins/search/view.js +64 -202
  188. package/src/plugins/tts/AbstractTTSEngine.js +71 -40
  189. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  190. package/src/plugins/tts/PageChunk.js +15 -21
  191. package/src/plugins/tts/PageChunkIterator.js +8 -12
  192. package/src/plugins/tts/WebTTSEngine.js +87 -71
  193. package/src/plugins/tts/plugin.tts.js +96 -127
  194. package/src/plugins/tts/utils.js +15 -25
  195. package/src/plugins/url/UrlPlugin.js +191 -0
  196. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  197. package/src/util/browserSniffing.js +22 -0
  198. package/src/util/docCookies.js +21 -2
  199. package/tests/e2e/README.md +37 -0
  200. package/tests/e2e/autoplay.test.js +2 -2
  201. package/tests/e2e/base.test.js +8 -16
  202. package/tests/e2e/helpers/base.js +53 -48
  203. package/tests/e2e/helpers/debug.js +1 -1
  204. package/tests/e2e/helpers/params.js +17 -0
  205. package/tests/e2e/helpers/rightToLeft.js +8 -14
  206. package/tests/e2e/helpers/search.js +73 -0
  207. package/tests/e2e/models/Navigation.js +20 -37
  208. package/tests/e2e/rightToLeft.test.js +4 -5
  209. package/tests/e2e/viewmode.test.js +40 -33
  210. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  211. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  212. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  213. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  214. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  215. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  216. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  217. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  218. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  219. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  220. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  221. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  222. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  223. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  224. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  225. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  226. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  227. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  228. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  229. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  230. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  231. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  232. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  233. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  234. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  235. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  236. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  237. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  238. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  239. package/tests/jest/BookReader/utils.test.js +229 -0
  240. package/tests/jest/BookReader.keyboard.test.js +190 -0
  241. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  242. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  243. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  244. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  245. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  246. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  247. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  248. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  249. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  250. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  251. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  252. package/tests/jest/plugins/search/utils.js +25 -0
  253. package/tests/jest/plugins/search/utils.test.js +29 -0
  254. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
  255. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  256. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  257. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  258. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  259. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  260. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  261. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  262. package/tests/jest/setup.js +3 -0
  263. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  264. package/tests/jest/util/docCookies.test.js +24 -0
  265. package/tests/{util → jest/util}/strings.test.js +1 -1
  266. package/tests/{utils.js → jest/utils.js} +38 -0
  267. package/webpack.config.js +12 -6
  268. package/.babelrc +0 -12
  269. package/.dependabot/config.yml +0 -6
  270. package/.testcaferc.json +0 -5
  271. package/BookReader/bookreader-component-bundle.js +0 -14312
  272. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  273. package/BookReader/bookreader-component-bundle.js.map +0 -1
  274. package/BookReader/icons/sort-ascending.svg +0 -1
  275. package/BookReader/icons/sort-descending.svg +0 -1
  276. package/BookReader/jquery-1.10.1.js +0 -108
  277. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  278. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  279. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  280. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  281. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  282. package/BookReaderDemo/IIIFBookReader.js +0 -207
  283. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  284. package/BookReaderDemo/demo-iiif.js +0 -26
  285. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  286. package/karma.conf.js +0 -23
  287. package/src/BookNavigator/BookModel.js +0 -14
  288. package/src/BookNavigator/BookNavigator.js +0 -452
  289. package/src/BookNavigator/assets/book-loader.js +0 -27
  290. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  291. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  292. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  293. package/src/BookNavigator/search/a-search-result.js +0 -55
  294. package/src/BookNavigator/volumes/volumes-provider.js +0 -76
  295. package/src/BookNavigator/volumes/volumes.js +0 -161
  296. package/src/BookReader/DebugConsole.js +0 -54
  297. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  298. package/src/ItemNavigator/ItemNavigator.js +0 -372
  299. package/src/ItemNavigator/providers/sharing.js +0 -29
  300. package/src/assets/icons/sort-ascending.svg +0 -1
  301. package/src/assets/icons/sort-descending.svg +0 -1
  302. package/src/css/_MobileNav.scss +0 -194
  303. package/src/dragscrollable-br.js +0 -261
  304. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  305. package/src/plugins/plugin.mobile_nav.js +0 -287
  306. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  307. package/tests/BookReader/DebugConsole.test.js +0 -25
  308. package/tests/BookReader/Mode1Up.test.js +0 -164
  309. package/tests/BookReader/Mode2Up.test.js +0 -247
  310. package/tests/BookReader/utils.test.js +0 -109
  311. package/tests/e2e/helpers/desktopSearch.js +0 -72
  312. package/tests/e2e/helpers/mobileSearch.js +0 -85
  313. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  314. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  315. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  316. package/tests/karma/BookNavigator/volumes.test.js +0 -101
  317. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  318. package/tests/plugins/plugin.chapters.test.js +0 -130
  319. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  320. package/tests/plugins/plugin.text_selection.test.js +0 -203
  321. package/tests/util/docCookies.test.js +0 -15
package/src/BookReader.js CHANGED
@@ -19,31 +19,21 @@ 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';
@@ -52,10 +42,6 @@ import { ImageCache } from './BookReader/ImageCache.js';
52
42
  import { PageContainer } from './BookReader/PageContainer.js';
53
43
  import { NAMED_REDUCE_SETS } from './BookReader/ReduceSet';
54
44
 
55
- if (location.toString().indexOf('_debugShowConsole=true') != -1) {
56
- $(() => new DebugConsole().init());
57
- }
58
-
59
45
  /**
60
46
  * BookReader
61
47
  * @param {BookReaderOptions} options
@@ -152,24 +138,20 @@ BookReader.prototype.setup = function(options) {
152
138
  this.ui = options.ui;
153
139
  this.uiAutoHide = options.uiAutoHide;
154
140
 
155
- this.thumbWidth = 100; // will be overridden during prepareThumbnailView
141
+ this.thumbWidth = 100; // will be overridden during this._modes.modeThumb.prepare();
156
142
  this.thumbRowBuffer = options.thumbRowBuffer;
157
143
  this.thumbColumns = options.thumbColumns;
158
144
  this.thumbMaxLoading = options.thumbMaxLoading;
159
145
  this.thumbPadding = options.thumbPadding;
160
146
  this.displayedRows = [];
161
-
162
147
  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
148
 
168
149
  this.animating = false;
169
- this.flipSpeed = options.flipSpeed;
150
+ this.flipSpeed = typeof options.flipSpeed === 'number' ? options.flipSpeed : {
151
+ 'fast': 200,
152
+ 'slow': 600,
153
+ }[options.flipSpeed] || 400;
170
154
  this.flipDelay = options.flipDelay;
171
- this.twoPagePopUp = null;
172
- this.leafEdgeTmp = null;
173
155
 
174
156
  /**
175
157
  * Represents the first displayed index
@@ -178,8 +160,7 @@ BookReader.prototype.setup = function(options) {
178
160
  * @property {number|null} firstIndex
179
161
  */
180
162
  this.firstIndex = null;
181
- this.lastDisplayableIndex2up = null;
182
- this.isFullscreenActive = false;
163
+ this.isFullscreenActive = options.startFullscreen || false;
183
164
  this.lastScroll = null;
184
165
 
185
166
  this.showLogo = options.showLogo;
@@ -212,26 +193,22 @@ BookReader.prototype.setup = function(options) {
212
193
 
213
194
  // Assign the data methods
214
195
  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
196
 
225
197
  /** @type {{[name: string]: JQuery}} */
226
198
  this.refs = {};
227
199
 
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
- };
200
+ /** The book being displayed in BookReader*/
201
+ this.book = new BookModel(this);
202
+
203
+ if (options.getNumLeafs) this.book.getNumLeafs = options.getNumLeafs.bind(this);
204
+ if (options.getPageWidth) this.book.getPageWidth = options.getPageWidth.bind(this);
205
+ if (options.getPageHeight) this.book.getPageHeight = options.getPageHeight.bind(this);
206
+ if (options.getPageURI) this.book.getPageURI = options.getPageURI.bind(this);
207
+ if (options.getPageSide) this.book.getPageSide = options.getPageSide.bind(this);
208
+ if (options.getPageNum) this.book.getPageNum = options.getPageNum.bind(this);
209
+ if (options.getPageProp) this.book.getPageProp = options.getPageProp.bind(this);
210
+ if (options.getSpreadIndices) this.book.getSpreadIndices = options.getSpreadIndices.bind(this);
211
+ if (options.leafNumToIndex) this.book.leafNumToIndex = options.leafNumToIndex.bind(this);
235
212
 
236
213
  /**
237
214
  * @private Components are 'subchunks' of bookreader functionality, usually UI related
@@ -245,14 +222,14 @@ BookReader.prototype.setup = function(options) {
245
222
  };
246
223
 
247
224
  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),
225
+ mode1Up: new Mode1Up(this, this.book),
226
+ mode2Up: new Mode2Up(this, this.book),
227
+ modeThumb: new ModeThumb(this, this.book),
251
228
  };
252
229
 
253
230
  /** Stores classes which we want to expose (selectively) some methods as overridable */
254
231
  this._overrideable = {
255
- '_models.book': this._models.book,
232
+ 'book': this.book,
256
233
  '_components.navbar': this._components.navbar,
257
234
  '_components.toolbar': this._components.toolbar,
258
235
  '_modes.mode1Up': this._modes.mode1Up,
@@ -261,21 +238,54 @@ BookReader.prototype.setup = function(options) {
261
238
  };
262
239
 
263
240
  /** Image cache for general image fetching */
264
- this.imageCache = new ImageCache(this._models.book, {
241
+ this.imageCache = new ImageCache(this.book, {
265
242
  useSrcSet: this.options.useSrcSet,
266
243
  reduceSet: this.reduceSet,
267
244
  });
245
+
246
+ /**
247
+ * Flag if BookReader has "focus" for keyboard shortcuts
248
+ * Initially true, set to false when:
249
+ * - BookReader scrolled out of view
250
+ * Set to true when:
251
+ * - BookReader scrolled into view
252
+ */
253
+ this.hasKeyFocus = true;
268
254
  };
269
255
 
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; }
256
+ /**
257
+ * Get all the HTML Elements that are being/can be rendered.
258
+ * Includes cached elements which might be rendered again.
259
+ */
260
+ BookReader.prototype.getActivePageContainerElements = function() {
261
+ let containerEls = Object.values(this._modes.mode2Up.mode2UpLit.pageContainerCache).map(pc => pc.$container[0])
262
+ .concat(Object.values(this._modes.mode1Up.mode1UpLit.pageContainerCache).map(pc => pc.$container[0]));
263
+ if (this.mode == this.constModeThumb) {
264
+ containerEls = containerEls.concat(this.$('.BRpagecontainer').toArray());
265
+ }
266
+ return containerEls;
267
+ };
268
+
269
+ /**
270
+ * Get the HTML Elements for the rendered page. Note there can be more than one, since
271
+ * (at least as of writing) different modes can maintain different caches.
272
+ * @param {PageIndex} pageIndex
273
+ */
274
+ BookReader.prototype.getActivePageContainerElementsForIndex = function(pageIndex) {
275
+ return [
276
+ this._modes.mode2Up.mode2UpLit.pageContainerCache[pageIndex]?.$container?.[0],
277
+ this._modes.mode1Up.mode1UpLit.pageContainerCache[pageIndex]?.$container?.[0],
278
+ ...(this.mode == this.constModeThumb ? this.$(`.pagediv${pageIndex}`).toArray() : [])
279
+ ].filter(x => x);
280
+ };
281
+
282
+ Object.defineProperty(BookReader.prototype, 'activeMode', {
283
+ /** @return {Mode1Up | Mode2Up | ModeThumb} */
284
+ get() { return {
285
+ 1: this._modes.mode1Up,
286
+ 2: this._modes.mode2Up,
287
+ 3: this._modes.modeThumb,
288
+ }[this.mode]; },
279
289
  });
280
290
 
281
291
  /**
@@ -290,15 +300,15 @@ BookReader.util = utils;
290
300
  * @private
291
301
  */
292
302
  BookReader.prototype.extendParams = function(params, newParams) {
293
- var modifiedNewParams = $.extend({}, newParams);
303
+ const modifiedNewParams = $.extend({}, newParams);
294
304
  if ('undefined' != typeof(modifiedNewParams.page)) {
295
- var pageIndex = this._models.book.parsePageString(modifiedNewParams.page);
305
+ const pageIndex = this.book.parsePageString(modifiedNewParams.page);
296
306
  if (!isNaN(pageIndex))
297
307
  modifiedNewParams.index = pageIndex;
298
308
  delete modifiedNewParams.page;
299
309
  }
300
310
  $.extend(params, modifiedNewParams);
301
- }
311
+ };
302
312
 
303
313
  /**
304
314
  * Parses params from from various initialization contexts (url, cookie, options)
@@ -306,7 +316,7 @@ BookReader.prototype.extendParams = function(params, newParams) {
306
316
  * @return {object} the parsed params
307
317
  */
308
318
  BookReader.prototype.initParams = function() {
309
- var params = {};
319
+ const params = {};
310
320
  // Flag initializing for updateFromParams()
311
321
  params.init = true;
312
322
 
@@ -323,8 +333,8 @@ BookReader.prototype.initParams = function() {
323
333
 
324
334
  // If we have a title leaf, use that as the default instead of index 0,
325
335
  // 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);
336
+ if ('undefined' != typeof(this.titleLeaf) && this.book.getNumLeafs() > 2) {
337
+ params.index = this.book.leafNumToIndex(this.titleLeaf);
328
338
  } else {
329
339
  params.index = 0;
330
340
  }
@@ -355,7 +365,7 @@ BookReader.prototype.initParams = function() {
355
365
  // Check for URL plugin
356
366
  if (this.options.enableUrlPlugin) {
357
367
  // Params explicitly set in URL take precedence over all other methods
358
- var urlParams = this.paramsFromFragment(this.urlReadFragment());
368
+ let urlParams = this.paramsFromFragment(this.urlReadFragment());
359
369
 
360
370
  // Get params if hash fragment available with 'history' urlMode
361
371
  const hasHashURL = !Object.keys(urlParams).length && this.urlReadHashFragment();
@@ -377,7 +387,7 @@ BookReader.prototype.initParams = function() {
377
387
  // Check for Search plugin
378
388
  if (this.options.enableSearch) {
379
389
  // Go to first result only if no default or URL page
380
- this.goToFirstResult = !params.pageFound
390
+ this.options.goToFirstResult = !params.pageFound;
381
391
 
382
392
  // If initialSearchTerm not set
383
393
  if (!this.options.initialSearchTerm) {
@@ -389,7 +399,7 @@ BookReader.prototype.initParams = function() {
389
399
  } else {
390
400
  // If we have a query string: q=[term]
391
401
  const searchParams = new URLSearchParams(this.readQueryString());
392
- const searchTerm = searchParams.get('q')
402
+ const searchTerm = searchParams.get('q');
393
403
  if (searchTerm) {
394
404
  this.options.initialSearchTerm = utils.decodeURIComponentPlus(searchTerm);
395
405
  }
@@ -401,21 +411,21 @@ BookReader.prototype.initParams = function() {
401
411
  this.suppressFragmentChange = !params.fragmentChange;
402
412
 
403
413
  return params;
404
- }
414
+ };
405
415
 
406
416
  /**
407
417
  * Allow mocking of window.location.search
408
418
  */
409
419
  BookReader.prototype.getLocationSearch = function () {
410
420
  return window.location.search;
411
- }
421
+ };
412
422
 
413
423
  /**
414
424
  * Allow mocking of window.location.hash
415
425
  */
416
426
  BookReader.prototype.getLocationHash = function () {
417
427
  return window.location.hash;
418
- }
428
+ };
419
429
 
420
430
  /**
421
431
  * Return URL or fragment querystring
@@ -428,37 +438,63 @@ BookReader.prototype.readQueryString = function() {
428
438
  const hash = this.getLocationHash();
429
439
  const found = hash.search(/\?\w+=/);
430
440
  return found > -1 ? hash.slice(found) : '';
431
- }
441
+ };
432
442
 
433
443
  /**
434
444
  * Determines the initial mode for starting if a mode is not already
435
445
  * present in the params argument
436
446
  * @param {object} params
437
- * @return {number} the mode
447
+ * @return {1 | 2 | 3} the initial mode
438
448
  */
439
449
  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;
450
+ // if mobile breakpoint, we always show this.constMode1up mode
451
+ const windowWidth = $(window).width();
452
+ const isMobile = windowWidth && windowWidth <= this.onePageMinBreakpoint;
453
+
454
+ let initialMode;
455
+ if (params.mode) {
456
+ initialMode = params.mode;
457
+ } else if (isMobile) {
458
+ initialMode = this.constMode1up;
452
459
  } else {
453
- nextMode = this.constMode2up;
460
+ initialMode = this.constMode2up;
461
+ }
462
+
463
+ if (!this.canSwitchToMode(initialMode)) {
464
+ initialMode = this.constMode1up;
454
465
  }
455
466
 
456
- if (!this.canSwitchToMode(nextMode)) {
457
- nextMode = this.constMode1up;
467
+ // override defaults mode via `options.defaults` metadata
468
+ if (this.options.defaults) {
469
+ try {
470
+ initialMode = _modeStringToNumber(this.options.defaults);
471
+ } catch (e) {
472
+ // Can ignore this error
473
+ }
458
474
  }
459
- return nextMode;
475
+
476
+ return initialMode;
460
477
  };
461
478
 
479
+ /**
480
+ * Converts a mode string to a the mode numeric constant
481
+ * @param {'mode/1up'|'mode/2up'|'mode/thumb'} modeString
482
+ * @return {1 | 2 | 3}
483
+ */
484
+ export function _modeStringToNumber(modeString) {
485
+ const MAPPING = {
486
+ 'mode/1up': 1,
487
+ 'mode/2up': 2,
488
+ 'mode/thumb': 3,
489
+ };
490
+
491
+ if (!(modeString in MAPPING)) {
492
+ throw new OptionsParseError(`Invalid mode string: ${modeString}`);
493
+ }
494
+
495
+ return MAPPING[modeString];
496
+ }
497
+
462
498
  /**
463
499
  * This is called by the client to initialize BookReader.
464
500
  * It renders onto the DOM. It should only be called once.
@@ -467,7 +503,7 @@ BookReader.prototype.init = function() {
467
503
  this.init.initComplete = false;
468
504
  this.pageScale = this.reduce; // preserve current reduce
469
505
 
470
- var params = this.initParams();
506
+ const params = this.initParams();
471
507
 
472
508
  this.firstIndex = params.index ? params.index : 0;
473
509
 
@@ -497,19 +533,16 @@ BookReader.prototype.init = function() {
497
533
  // Explicitly ensure this.mode exists for initNavbar() below
498
534
  this.mode = params.mode;
499
535
 
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();
508
- }
536
+ // Display Navigation
537
+ if (this.options.showToolbar) {
538
+ this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
539
+ }
540
+ if (this.options.showNavbar) { // default navigation
541
+ this.initNavbar();
509
542
  }
510
543
 
511
544
  // Switch navbar controls on mobile/desktop
512
- this.switchNavbarControls();
545
+ this._components.navbar.switchNavbarControls();
513
546
 
514
547
  this.resizeBRcontainer();
515
548
  this.updateFromParams(params);
@@ -521,17 +554,17 @@ BookReader.prototype.init = function() {
521
554
  this.setupKeyListeners();
522
555
 
523
556
  this.lastScroll = (new Date().getTime());
524
- this.refs.$brContainer.bind('scroll', this, function(e) {
557
+ this.refs.$brContainer.on('scroll', this, function(e) {
525
558
  // Note, this scroll event fires for both user, and js generated calls
526
559
  // It is functioning in some cases as the primary triggerer for rendering
527
560
  e.data.lastScroll = (new Date().getTime());
528
- if (e.data.constMode2up != e.data.mode) {
561
+ if (e.data.constModeThumb == e.data.mode) {
529
562
  e.data.drawLeafsThrottled();
530
563
  }
531
564
  });
532
565
 
533
566
  if (this.options.autoResize) {
534
- $(window).bind('resize', this, function(e) {
567
+ $(window).on('resize', this, function(e) {
535
568
  e.data.resize();
536
569
  });
537
570
  $(window).on("orientationchange", this, function(e) {
@@ -548,15 +581,17 @@ BookReader.prototype.init = function() {
548
581
  this.suppressFragmentChange = false;
549
582
  }
550
583
 
584
+ if (this.options.startFullscreen) {
585
+ this.enterFullscreen(true);
586
+ }
587
+
551
588
  this.init.initComplete = true;
552
589
  this.trigger(BookReader.eventNames.PostInit);
553
590
 
554
591
  // Must be called after this.init.initComplete set to true to allow
555
592
  // BookReader.prototype.resize to run.
556
- if (this.options.startFullscreen) {
557
- this.toggleFullscreen();
558
- }
559
- }
593
+
594
+ };
560
595
 
561
596
  /**
562
597
  * @param {EVENTS} name
@@ -564,7 +599,6 @@ BookReader.prototype.init = function() {
564
599
  */
565
600
  BookReader.prototype.trigger = function(name, props = this) {
566
601
  const eventName = 'BookReader:' + name;
567
- $(document).trigger(eventName, props);
568
602
 
569
603
  utils.polyfillCustomEvent(window);
570
604
  window.dispatchEvent(new CustomEvent(eventName, {
@@ -572,14 +606,15 @@ BookReader.prototype.trigger = function(name, props = this) {
572
606
  composed: true,
573
607
  detail: { props },
574
608
  }));
609
+ $(document).trigger(eventName, props);
575
610
  };
576
611
 
577
612
  BookReader.prototype.bind = function(name, callback) {
578
- $(document).bind('BookReader:' + name, callback);
613
+ $(document).on('BookReader:' + name, callback);
579
614
  };
580
615
 
581
616
  BookReader.prototype.unbind = function(name, callback) {
582
- $(document).unbind('BookReader:' + name, callback);
617
+ $(document).off('BookReader:' + name, callback);
583
618
  };
584
619
 
585
620
  /**
@@ -591,135 +626,147 @@ BookReader.prototype.resize = function() {
591
626
  this.resizeBRcontainer();
592
627
 
593
628
  // Switch navbar controls on mobile/desktop
594
- this.switchNavbarControls();
629
+ this._components.navbar.switchNavbarControls();
595
630
 
596
631
  if (this.constMode1up == this.mode) {
597
632
  if (this.onePage.autofit != 'none') {
598
- this.resizePageView1up();
633
+ this._modes.mode1Up.resizePageView();
599
634
  this.centerPageView();
600
- if (this.enableSearch) this.updateSearchHilites(); //deletes highlights but does not call remove()
601
635
  } else {
602
636
  this.centerPageView();
603
637
  this.displayedIndices = [];
604
- if (this.enableSearch) this.updateSearchHilites(); //deletes highlights but does not call remove()
605
638
  this.drawLeafsThrottled();
606
639
  }
607
640
  } else if (this.constModeThumb == this.mode) {
608
- this.prepareThumbnailView();
641
+ this._modes.modeThumb.prepare();
609
642
  } 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
- }
643
+ this._modes.mode2Up.resizePageView();
631
644
  }
632
645
  this.trigger(BookReader.eventNames.resize);
633
646
  };
634
647
 
635
648
  /**
636
- * Binds keyboard event listeners
649
+ * Binds keyboard and keyboard focus event listeners
637
650
  */
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();
651
+ BookReader.prototype.setupKeyListeners = function () {
652
+
653
+ // Keyboard focus by BookReader in viewport
654
+ //
655
+ // Intersection observer and callback sets BookReader keyboard
656
+ // "focus" flag off when the BookReader is not in the viewport.
657
+ if (window.IntersectionObserver) {
658
+ const observer = new IntersectionObserver((entries) => {
659
+ entries.forEach((entry) => {
660
+ if (entry.intersectionRatio === 0) {
661
+ this.hasKeyFocus = false;
662
+ } else {
663
+ this.hasKeyFocus = true;
677
664
  }
678
- break;
679
- case KEY_END:
665
+ });
666
+ }, {
667
+ root: null,
668
+ rootMargin: '0px',
669
+ threshold: [0, 0.05, 1],
670
+ });
671
+ observer.observe(this.refs.$br[0]);
672
+ }
673
+
674
+ // Keyboard listeners
675
+ document.addEventListener('keydown', (e) => {
676
+
677
+ // Ignore if BookReader "focus" flag not set
678
+ if (!this.hasKeyFocus) {
679
+ return;
680
+ }
681
+
682
+ // Ignore if modifiers are active.
683
+ if (e.getModifierState('Control') ||
684
+ e.getModifierState('Alt') ||
685
+ e.getModifierState('Meta') ||
686
+ e.getModifierState('Win') /* hack for IE */) {
687
+ return;
688
+ }
689
+
690
+ // Ignore in input elements
691
+ if (utils.isInputActive()) {
692
+ return;
693
+ }
694
+
695
+ // KeyboardEvent code values:
696
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code/code_values
697
+ switch (e.key) {
698
+
699
+ // Page navigation
700
+ case "Home":
701
+ e.preventDefault();
702
+ this.first();
703
+ break;
704
+ case "End":
705
+ e.preventDefault();
706
+ this.last();
707
+ break;
708
+ case "ArrowDown":
709
+ case "PageDown":
710
+ case "Down": // hack for IE and old Gecko
711
+ // In 1up and thumb mode page scrolling handled by browser
712
+ if (this.constMode2up === this.mode) {
680
713
  e.preventDefault();
681
- self.last();
682
- break;
683
- case KEY_HOME:
714
+ this.next();
715
+ }
716
+ break;
717
+ case "ArrowUp":
718
+ case "PageUp":
719
+ case "Up": // hack for IE and old Gecko
720
+ // In 1up and thumb mode page scrolling handled by browser
721
+ if (this.constMode2up === this.mode) {
684
722
  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:
723
+ this.prev();
724
+ }
725
+ break;
726
+ case "ArrowLeft":
727
+ case "Left": // hack for IE and old Gecko
728
+ // No y-scrolling in thumb mode
729
+ if (this.constModeThumb != this.mode) {
702
730
  e.preventDefault();
703
- self.zoom(-1);
704
- break;
705
- case KEY_EQUAL:
706
- case KEY_EQUAL_F:
707
- case KEY_NUMPAD_ADD:
731
+ this.left();
732
+ }
733
+ break;
734
+ case "ArrowRight":
735
+ case "Right": // hack for IE and old Gecko
736
+ // No y-scrolling in thumb mode
737
+ if (this.constModeThumb != this.mode) {
708
738
  e.preventDefault();
709
- self.zoom(+1);
710
- break;
739
+ this.right();
711
740
  }
741
+ break;
742
+ // Zoom
743
+ case '-':
744
+ case 'Subtract':
745
+ e.preventDefault();
746
+ this.zoom(-1);
747
+ break;
748
+ case '+':
749
+ case '=':
750
+ case 'Add':
751
+ e.preventDefault();
752
+ this.zoom(1);
753
+ break;
754
+ // Fullscreen
755
+ case 'F':
756
+ case 'f':
757
+ e.preventDefault();
758
+ this.toggleFullscreen();
759
+ break;
712
760
  }
713
761
  });
714
762
  };
715
763
 
716
764
  BookReader.prototype.drawLeafs = function() {
717
765
  if (this.constMode1up == this.mode) {
718
- this.drawLeafsOnePage();
719
- } else if (this.constModeThumb == this.mode) {
720
- this.drawLeafsThumbnail();
766
+ // Not needed for Mode1Up anymore
767
+ return;
721
768
  } else {
722
- this.drawLeafsTwoPage();
769
+ this.activeMode.drawLeafs();
723
770
  }
724
771
  };
725
772
 
@@ -728,7 +775,7 @@ BookReader.prototype.drawLeafs = function() {
728
775
  * @param {PageIndex} index
729
776
  */
730
777
  BookReader.prototype._createPageContainer = function(index) {
731
- return new PageContainer(this._models.book.getPage(index, false), {
778
+ return new PageContainer(this.book.getPage(index, false), {
732
779
  isProtected: this.protected,
733
780
  imageCache: this.imageCache,
734
781
  loadingImage: this.imagesBaseURL + 'loading.gif',
@@ -742,8 +789,8 @@ BookReader.prototype.bindGestures = function(jElement) {
742
789
  // when you move the book with one finger and then add another
743
790
  // finger to pinch. Gestures are aware of scroll state.
744
791
 
745
- var self = this;
746
- var numTouches = 1;
792
+ const self = this;
793
+ let numTouches = 1;
747
794
 
748
795
  jElement.unbind('touchmove').bind('touchmove', function(e) {
749
796
  if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
@@ -764,24 +811,6 @@ BookReader.prototype.bindGestures = function(jElement) {
764
811
  });
765
812
  };
766
813
 
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
814
  /**
786
815
  * A throttled version of drawLeafs
787
816
  */
@@ -794,29 +823,11 @@ BookReader.prototype.drawLeafsThrottled = utils.throttle(
794
823
  * @param {number} direction Pass 1 to zoom in, anything else to zoom out
795
824
  */
796
825
  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
826
+ if (direction == 1) {
827
+ this.activeMode.zoom('in');
828
+ } else {
829
+ this.activeMode.zoom('out');
818
830
  }
819
-
820
831
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
821
832
  return;
822
833
  };
@@ -842,11 +853,11 @@ BookReader.prototype.resizeBRcontainer = function(animate) {
842
853
  bottom: this.getFooterHeight()
843
854
  });
844
855
  }
845
- }
856
+ };
846
857
 
847
858
  BookReader.prototype.centerPageView = function() {
848
- var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
849
- var clientWidth = this.refs.$brContainer.prop('clientWidth');
859
+ const scrollWidth = this.refs.$brContainer.prop('scrollWidth');
860
+ const clientWidth = this.refs.$brContainer.prop('clientWidth');
850
861
  if (scrollWidth > clientWidth) {
851
862
  this.refs.$brContainer.prop('scrollLeft', (scrollWidth - clientWidth) / 2);
852
863
  }
@@ -934,7 +945,7 @@ BookReader.prototype._reduceSort = (a, b) => a.reduce - b.reduce;
934
945
  * @return {boolean} Returns true if page could be found, false otherwise.
935
946
  */
936
947
  BookReader.prototype.jumpToPage = function(pageNum) {
937
- var pageIndex = this._models.book.parsePageString(pageNum);
948
+ const pageIndex = this.book.parsePageString(pageNum);
938
949
 
939
950
  if ('undefined' != typeof(pageIndex)) {
940
951
  this.jumpToIndex(pageIndex);
@@ -950,11 +961,9 @@ BookReader.prototype.jumpToPage = function(pageNum) {
950
961
  * @param {PageIndex} index
951
962
  */
952
963
  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
- }
964
+ return this.displayedIndices ? this.displayedIndices.includes(index) :
965
+ this.currentIndex() == index;
966
+ };
958
967
 
959
968
  /**
960
969
  * Changes the current page
@@ -965,7 +974,7 @@ BookReader.prototype._isIndexDisplayed = function(index) {
965
974
  */
966
975
  BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
967
976
  // Don't jump into specific unviewable page
968
- const page = this._models.book.getPage(index);
977
+ const page = this.book.getPage(index);
969
978
  if (!page.isViewable && page.unviewablesStart != page.index) {
970
979
  // If already in unviewable range, jump to end of that range
971
980
  const alreadyInPreview = this._isIndexDisplayed(page.unviewablesStart);
@@ -975,19 +984,12 @@ BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
975
984
 
976
985
  this.trigger(BookReader.eventNames.stop);
977
986
 
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);
984
- }
987
+ this.activeMode.jumpToIndex(index, pageX, pageY, noAnimate);
985
988
  };
986
989
 
987
990
  /**
988
991
  * Return mode or 1up if initial thumb
989
992
  * @param {number}
990
- * @see BookReader.prototype.drawLeafsThumbnail
991
993
  */
992
994
  BookReader.prototype.getPrevReadMode = function(mode) {
993
995
  if (mode === BookReader.constMode1up || mode === BookReader.constMode2up) {
@@ -996,7 +998,7 @@ BookReader.prototype.getPrevReadMode = function(mode) {
996
998
  // Initial thumb, return 1up
997
999
  return BookReader.constMode1up;
998
1000
  }
999
- }
1001
+ };
1000
1002
 
1001
1003
  /**
1002
1004
  * Switches the mode (eg 1up 2up thumb)
@@ -1024,10 +1026,13 @@ BookReader.prototype.switchMode = function(
1024
1026
  }
1025
1027
 
1026
1028
  this.trigger(BookReader.eventNames.stop);
1027
- if (this.enableSearch) this.removeSearchHilites();
1028
1029
 
1029
1030
  this.prevReadMode = this.getPrevReadMode(this.mode);
1030
1031
 
1032
+ if (this.mode != mode) {
1033
+ this.activeMode.unprepare?.();
1034
+ }
1035
+
1031
1036
  this.mode = mode;
1032
1037
 
1033
1038
  // reinstate scale if moving from thumbnail view
@@ -1040,42 +1045,31 @@ BookReader.prototype.switchMode = function(
1040
1045
 
1041
1046
  // XXX maybe better to preserve zoom in each mode
1042
1047
  if (this.constMode1up == mode) {
1043
- this.onePageCalculateReductionFactors();
1044
- this.reduce = this.quantizeReduce(this.reduce, this.onePage.reductionFactors);
1045
- this.prepareOnePageView();
1048
+ this._modes.mode1Up.prepare();
1046
1049
  } else if (this.constModeThumb == mode) {
1047
1050
  this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
1048
- this.prepareThumbnailView();
1051
+ this._modes.modeThumb.prepare();
1049
1052
  } 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
1053
+ this._modes.mode2Up.prepare();
1060
1054
  }
1061
1055
 
1062
1056
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1063
1057
  this.trigger(BookReader.eventNames.fragmentChange);
1064
1058
  }
1065
- var eventName = mode + 'PageViewSelected';
1059
+ const eventName = mode + 'PageViewSelected';
1066
1060
  this.trigger(BookReader.eventNames[eventName]);
1067
1061
 
1068
1062
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1069
1063
  };
1070
1064
 
1071
1065
  BookReader.prototype.updateBrClasses = function() {
1072
- var modeToClass = {};
1066
+ const modeToClass = {};
1073
1067
  modeToClass[this.constMode1up] = 'BRmode1up';
1074
- modeToClass[this.constMode2up] = 'BRmode2Up';
1068
+ modeToClass[this.constMode2up] = 'BRmode2up';
1075
1069
  modeToClass[this.constModeThumb] = 'BRmodeThumb';
1076
1070
 
1077
1071
  this.refs.$br
1078
- .removeClass('BRmode1up BRmode2Up BRmodeThumb')
1072
+ .removeClass('BRmode1up BRmode2up BRmodeThumb')
1079
1073
  .addClass(modeToClass[this.mode]);
1080
1074
 
1081
1075
  if (this.isFullscreen()) {
@@ -1095,31 +1089,30 @@ BookReader.prototype.isFullscreen = function() {
1095
1089
  * Toggles fullscreen
1096
1090
  * @param { boolean } bindKeyboardControls
1097
1091
  */
1098
- BookReader.prototype.toggleFullscreen = function(bindKeyboardControls = true) {
1092
+ BookReader.prototype.toggleFullscreen = async function(bindKeyboardControls = true) {
1099
1093
  if (this.isFullscreen()) {
1100
- this.exitFullScreen();
1094
+ await this.exitFullScreen();
1101
1095
  } else {
1102
- this.enterFullscreen(bindKeyboardControls);
1096
+ await this.enterFullscreen(bindKeyboardControls);
1103
1097
  }
1104
1098
  };
1105
1099
 
1106
1100
  /**
1107
1101
  * Enters fullscreen
1108
1102
  * including:
1109
- * - animation
1110
1103
  * - binds keyboard controls
1111
1104
  * - fires custom event
1112
1105
  * @param { boolean } bindKeyboardControls
1113
1106
  */
1114
- BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1107
+ BookReader.prototype.enterFullscreen = async function(bindKeyboardControls = true) {
1108
+ this.refs.$br.addClass('BRfullscreenAnimation');
1115
1109
  const currentIndex = this.currentIndex();
1116
- this.refs.$brContainer.css('opacity', 0);
1117
1110
 
1118
1111
  if (bindKeyboardControls) {
1119
1112
  this._fullscreenCloseHandler = (e) => {
1120
1113
  if (e.keyCode === 27) this.toggleFullscreen();
1121
1114
  };
1122
- $(document).keyup(this._fullscreenCloseHandler);
1115
+ $(document).on("keyup", this._fullscreenCloseHandler);
1123
1116
  }
1124
1117
 
1125
1118
  const windowWidth = $(window).width();
@@ -1128,14 +1121,28 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1128
1121
  }
1129
1122
 
1130
1123
  this.isFullscreenActive = true;
1131
-
1132
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear',() => {
1133
- this.resize();
1134
- this.jumpToIndex(currentIndex);
1135
- });
1124
+ // prioritize class updates so CSS can propagate
1125
+ this.updateBrClasses();
1126
+ if (this.activeMode instanceof Mode1Up) {
1127
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(currentIndex));
1128
+ // Need the new scale to be applied before calling jumpToIndex
1129
+ this.activeMode.mode1UpLit.requestUpdate();
1130
+ await this.activeMode.mode1UpLit.updateComplete;
1131
+ }
1132
+ this.jumpToIndex(currentIndex);
1136
1133
 
1137
1134
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1135
+ // Add "?view=theater"
1136
+ this.trigger(BookReader.eventNames.fragmentChange);
1137
+ // trigger event here, so that animations,
1138
+ // class updates happen before book-nav relays to web components
1138
1139
  this.trigger(BookReader.eventNames.fullscreenToggled);
1140
+
1141
+ // resize book after all events & css updates
1142
+ await new Promise(resolve => setTimeout(resolve, 0));
1143
+
1144
+ this.resize();
1145
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1139
1146
  };
1140
1147
 
1141
1148
  /**
@@ -1145,26 +1152,35 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1145
1152
  * - fires custom event
1146
1153
  * @param { boolean } bindKeyboardControls
1147
1154
  */
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();
1155
+ BookReader.prototype.exitFullScreen = async function () {
1156
+ this.refs.$br.addClass('BRfullscreenAnimation');
1157
+ $(document).off('keyup', this._fullscreenCloseHandler);
1154
1158
 
1159
+ const windowWidth = $(window).width();
1155
1160
  const canShow2up = this.options.controls.twoPage.visible;
1156
1161
  if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1157
1162
  this.switchMode(this.constMode2up);
1158
1163
  }
1159
1164
 
1160
1165
  this.isFullscreenActive = false;
1166
+ // Trigger fullscreen event immediately
1167
+ // so that book-nav can relay to web components
1168
+ this.trigger(BookReader.eventNames.fullscreenToggled);
1169
+
1161
1170
  this.updateBrClasses();
1171
+ await new Promise(resolve => setTimeout(resolve, 0));
1172
+ this.resize();
1173
+
1174
+ if (this.activeMode instanceof Mode1Up) {
1175
+ this.activeMode.mode1UpLit.scale = this.activeMode.mode1UpLit.computeDefaultScale(this.book.getPage(this.currentIndex()));
1176
+ this.activeMode.mode1UpLit.requestUpdate();
1177
+ await this.activeMode.mode1UpLit.updateComplete;
1178
+ }
1162
1179
 
1163
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', () => {
1164
- this.resize();
1165
- });
1166
1180
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1167
- this.trigger(BookReader.eventNames.fullscreenToggled);
1181
+ // Remove "?view=theater"
1182
+ this.trigger(BookReader.eventNames.fragmentChange);
1183
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1168
1184
  };
1169
1185
 
1170
1186
  /**
@@ -1178,7 +1194,7 @@ BookReader.prototype.currentIndex = function() {
1178
1194
  return this.firstIndex; // $$$ TODO page in center of view would be better
1179
1195
  } else if (this.mode == this.constMode2up) {
1180
1196
  // Only allow indices that are actually present in book
1181
- return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
1197
+ return utils.clamp(this.firstIndex, 0, this.book.getNumLeafs() - 1);
1182
1198
  } else {
1183
1199
  throw 'currentIndex called for unimplemented mode ' + this.mode;
1184
1200
  }
@@ -1195,11 +1211,9 @@ BookReader.prototype.updateFirstIndex = function(
1195
1211
  index,
1196
1212
  { suppressFragmentChange = false } = {}
1197
1213
  ) {
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
- }
1214
+ // If there's no change, do nothing
1215
+ if (this.firstIndex === index) return;
1216
+
1203
1217
  this.firstIndex = index;
1204
1218
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1205
1219
  this.trigger(BookReader.eventNames.fragmentChange);
@@ -1210,8 +1224,12 @@ BookReader.prototype.updateFirstIndex = function(
1210
1224
  if (this.options.initialSearchTerm && !suppressFragmentChange) {
1211
1225
  this.suppressFragmentChange = false;
1212
1226
  }
1213
- this.trigger('pageChanged');
1214
- this.updateNavIndexThrottled(index);
1227
+
1228
+ this.trigger(BookReader.eventNames.pageChanged);
1229
+
1230
+ // event to know if user is actively reading
1231
+ this.trigger(BookReader.eventNames.userAction);
1232
+ this._components.navbar.updateNavIndexThrottled(index);
1215
1233
  };
1216
1234
 
1217
1235
  /**
@@ -1258,24 +1276,24 @@ BookReader.prototype.leftmost = function() {
1258
1276
  }
1259
1277
  };
1260
1278
 
1261
- BookReader.prototype.next = function() {
1279
+ BookReader.prototype.next = function({triggerStop = true} = {}) {
1262
1280
  if (this.constMode2up == this.mode) {
1263
- this.trigger(BookReader.eventNames.stop);
1264
- this.flipFwdToIndex(null);
1281
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1282
+ this._modes.mode2Up.mode2UpLit.flipAnimation('next');
1265
1283
  } else {
1266
- if (this.firstIndex < this.lastDisplayableIndex()) {
1284
+ if (this.firstIndex < this.book.getNumLeafs() - 1) {
1267
1285
  this.jumpToIndex(this.firstIndex + 1);
1268
1286
  }
1269
1287
  }
1270
1288
  };
1271
1289
 
1272
- BookReader.prototype.prev = function() {
1290
+ BookReader.prototype.prev = function({triggerStop = true} = {}) {
1273
1291
  const isOnFrontPage = this.firstIndex < 1;
1274
1292
  if (isOnFrontPage) return;
1275
1293
 
1276
1294
  if (this.constMode2up == this.mode) {
1277
- this.trigger(BookReader.eventNames.stop);
1278
- this.flipBackToIndex(null);
1295
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1296
+ this._modes.mode2Up.mode2UpLit.flipAnimation('prev');
1279
1297
  } else {
1280
1298
  if (this.firstIndex >= 1) {
1281
1299
  this.jumpToIndex(this.firstIndex - 1);
@@ -1284,257 +1302,13 @@ BookReader.prototype.prev = function() {
1284
1302
  };
1285
1303
 
1286
1304
  BookReader.prototype.first = function() {
1287
- this.jumpToIndex(this.firstDisplayableIndex());
1305
+ this.jumpToIndex(0);
1288
1306
  };
1289
1307
 
1290
1308
  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
- /**
1335
- * The amount to scroll vertically in integer pixels
1336
- */
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'));
1344
- };
1345
-
1346
- /**
1347
- * @deprecated No longer used; will be remove in v5
1348
- */
1349
- BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
1350
- console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
1351
- };
1352
-
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.');
1358
- };
1359
-
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
- });
1309
+ this.jumpToIndex(this.book.getNumLeafs() - 1);
1528
1310
  };
1529
1311
 
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
1312
 
1539
1313
  /**
1540
1314
  * @template TClass extends { br: BookReader }
@@ -1558,30 +1332,9 @@ function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
1558
1332
  /***********************/
1559
1333
  /** Navbar extensions **/
1560
1334
  /***********************/
1335
+ /** This cannot be removed yet because plugin.tts.js overrides it */
1561
1336
  BookReader.prototype.initNavbar = Navbar.prototype.init;
1562
1337
  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
1338
 
1586
1339
  /************************/
1587
1340
  /** Toolbar extensions **/
@@ -1596,24 +1349,14 @@ BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1596
1349
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1597
1350
  BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1598
1351
  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
1352
 
1609
1353
  /**
1610
1354
  * Bind navigation handlers
1611
1355
  */
1612
1356
  BookReader.prototype.bindNavigationHandlers = function() {
1613
1357
  const self = this;
1358
+ const jIcons = this.$('.BRicon');
1614
1359
 
1615
- // Note the mobile plugin attaches itself to body, so we need to select outside
1616
- const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
1617
1360
  // Map of jIcon class -> click handler
1618
1361
  const navigationControls = {
1619
1362
  book_left: () => {
@@ -1624,20 +1367,6 @@ BookReader.prototype.bindNavigationHandlers = function() {
1624
1367
  this.trigger(BookReader.eventNames.stop);
1625
1368
  this.right();
1626
1369
  },
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
1370
  book_top: this.first.bind(this),
1642
1371
  book_bottom: this.last.bind(this),
1643
1372
  book_leftmost: this.leftmost.bind(this),
@@ -1663,7 +1392,7 @@ BookReader.prototype.bindNavigationHandlers = function() {
1663
1392
  },
1664
1393
  full: () => {
1665
1394
  if (this.ui == 'embed') {
1666
- var url = this.$('.BRembedreturn a').attr('href');
1395
+ const url = this.$('.BRembedreturn a').attr('href');
1667
1396
  window.open(url);
1668
1397
  } else {
1669
1398
  this.toggleFullscreen();
@@ -1671,23 +1400,25 @@ BookReader.prototype.bindNavigationHandlers = function() {
1671
1400
  },
1672
1401
  };
1673
1402
 
1674
- jIcons.filter('.fit').bind('fit', function() {
1675
- // XXXmang implement autofit zoom
1403
+ // custom event for auto-loan-renew in ia-book-actions
1404
+ // - to know if user is actively reading
1405
+ this.$('nav.BRcontrols li button').on('click', () => {
1406
+ this.trigger(BookReader.eventNames.userAction);
1676
1407
  });
1677
1408
 
1678
1409
  for (const control in navigationControls) {
1679
1410
  jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1680
- navigationControls[control]()
1411
+ navigationControls[control]();
1681
1412
  return false;
1682
1413
  });
1683
1414
  }
1684
1415
 
1685
- var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1686
- var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1416
+ const $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1417
+ const $brNavCntlTopEl = this.$('.BRnavCntlTop');
1687
1418
 
1688
1419
  this.$('.BRnavCntl').click(
1689
1420
  function() {
1690
- var promises = [];
1421
+ const promises = [];
1691
1422
  // TODO don't use magic constants
1692
1423
  // TODO move this to a function
1693
1424
  if ($brNavCntlBtmEl.hasClass('BRdn')) {
@@ -1707,7 +1438,7 @@ BookReader.prototype.bindNavigationHandlers = function() {
1707
1438
  $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1708
1439
  $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1709
1440
  self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
1710
- self.$('.BRvavCntl').animate({opacity:1})
1441
+ self.$('.BRvavCntl').animate({opacity:1});
1711
1442
  }
1712
1443
  $.when.apply($, promises).done(function() {
1713
1444
  // Only do full resize in auto mode and need to recalc. size
@@ -1725,386 +1456,38 @@ BookReader.prototype.bindNavigationHandlers = function() {
1725
1456
  });
1726
1457
  }
1727
1458
  );
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();
1459
+ $brNavCntlBtmEl
1460
+ .on("mouseover", function() {
1461
+ if ($(this).hasClass('BRup')) {
1462
+ self.$('.BRnavCntl').animate({opacity:1},250);
1871
1463
  }
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();
1464
+ })
1465
+ .on("mouseleave", function() {
1466
+ if ($(this).hasClass('BRup')) {
1467
+ self.$('.BRnavCntl').animate({opacity:.75},250);
1468
+ }
1469
+ });
1470
+ $brNavCntlTopEl
1471
+ .on("mouseover", function() {
1472
+ if ($(this).hasClass('BRdn')) {
1473
+ self.$('.BRnavCntl').animate({opacity:1},250);
1922
1474
  }
1923
1475
  })
1924
- .bind('MozTouchUp', function(event) {
1925
- if (this.mode == self.constMode2up) {
1926
- event.preventDefault();
1476
+ .on("mouseleave", function() {
1477
+ if ($(this).hasClass('BRdn')) {
1478
+ self.$('.BRnavCntl').animate({opacity:.75},250);
1927
1479
  }
1928
1480
  });
1929
1481
  };
1930
1482
 
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
-
2065
1483
  /**************************/
2066
1484
  /** BookModel extensions **/
2067
1485
  /**************************/
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');
1486
+ // Must modify petabox extension, which expects this on the prototype
1487
+ // before removing.
2088
1488
  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');
1489
+ exposeOverrideableMethod(BookModel, 'book', 'getPageURI');
1490
+
2108
1491
 
2109
1492
  // Parameter related functions
2110
1493
 
@@ -2135,7 +1518,7 @@ BookReader.prototype.updateFromParams = function(params) {
2135
1518
  }
2136
1519
  } else if ('undefined' != typeof(params.page)) {
2137
1520
  // $$$ this assumes page numbers are unique
2138
- if (params.page != this._models.book.getPageNum(this.currentIndex())) {
1521
+ if (params.page != this.book.getPageNum(this.currentIndex())) {
2139
1522
  this.jumpToPage(params.page);
2140
1523
  }
2141
1524
  }
@@ -2169,7 +1552,7 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2169
1552
  // check there are enough pages to display
2170
1553
  // $$$ this is a workaround for the mis-feature that we can't display
2171
1554
  // short books in 2up mode
2172
- if (this._models.book.getNumLeafs() < 2) {
1555
+ if (this.book.getNumLeafs() < 2) {
2173
1556
  return false;
2174
1557
  }
2175
1558
  }
@@ -2177,31 +1560,6 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2177
1560
  return true;
2178
1561
  };
2179
1562
 
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
1563
  /**
2206
1564
  * Returns the page URI or transparent image if out of range
2207
1565
  * Also makes the reduce argument optional
@@ -2211,7 +1569,7 @@ BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2211
1569
  * @return {string}
2212
1570
  */
2213
1571
  BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2214
- const page = this._models.book.getPage(index, false);
1572
+ const page = this.book.getPage(index, false);
2215
1573
  // Synthesize page
2216
1574
  if (!page) return this.imagesBaseURL + "transparent.png";
2217
1575
 
@@ -2225,21 +1583,37 @@ BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2225
1583
  };
2226
1584
 
2227
1585
  /**
2228
- * @param {string}
1586
+ * @param {string} msg
1587
+ * @param {function|undefined} onCloseCallback
2229
1588
  */
2230
- BookReader.prototype.showProgressPopup = function(msg) {
1589
+ BookReader.prototype.showProgressPopup = function(msg, onCloseCallback) {
2231
1590
  if (this.popup) return;
2232
1591
 
2233
1592
  this.popup = document.createElement("div");
2234
1593
  $(this.popup).prop('className', 'BRprogresspopup');
2235
- var bar = document.createElement("div");
1594
+
1595
+ if (typeof(onCloseCallback) === 'function') {
1596
+ const closeButton = document.createElement('button');
1597
+ closeButton.setAttribute('title', 'close');
1598
+ closeButton.setAttribute('class', 'close-popup');
1599
+ const icon = document.createElement('span');
1600
+ icon.setAttribute('class', 'icon icon-close-dark');
1601
+ $(closeButton).append(icon);
1602
+ closeButton.addEventListener('click', () => {
1603
+ onCloseCallback();
1604
+ this.removeProgressPopup();
1605
+ });
1606
+ $(this.popup).append(closeButton);
1607
+ }
1608
+
1609
+ const bar = document.createElement("div");
2236
1610
  $(bar).css({
2237
1611
  height: '20px'
2238
1612
  }).prop('className', 'BRprogressbar');
2239
1613
  $(this.popup).append(bar);
2240
1614
 
2241
1615
  if (msg) {
2242
- var msgdiv = document.createElement("div");
1616
+ const msgdiv = document.createElement("div");
2243
1617
  msgdiv.innerHTML = msg;
2244
1618
  $(this.popup).append(msgdiv);
2245
1619
  }
@@ -2261,7 +1635,7 @@ BookReader.prototype.initUIStrings = function() {
2261
1635
  // the toolbar and nav bar easier
2262
1636
 
2263
1637
  // Setup tooltips -- later we could load these from a file for i18n
2264
- var titles = {
1638
+ const titles = {
2265
1639
  '.logo': 'Go to Archive.org', // $$$ update after getting OL record
2266
1640
  '.zoom_in': 'Zoom in',
2267
1641
  '.zoom_out': 'Zoom out',
@@ -2277,8 +1651,6 @@ BookReader.prototype.initUIStrings = function() {
2277
1651
  '.full': 'Toggle fullscreen',
2278
1652
  '.book_left': 'Flip left',
2279
1653
  '.book_right': 'Flip right',
2280
- '.book_up': 'Page up',
2281
- '.book_down': 'Page down',
2282
1654
  '.play': 'Play',
2283
1655
  '.pause': 'Pause',
2284
1656
  '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
@@ -2293,10 +1665,10 @@ BookReader.prototype.initUIStrings = function() {
2293
1665
  titles['.book_rightmost'] = 'First page';
2294
1666
  }
2295
1667
 
2296
- for (var icon in titles) {
1668
+ for (const icon in titles) {
2297
1669
  this.$(icon).prop('title', titles[icon]);
2298
1670
  }
2299
- }
1671
+ };
2300
1672
 
2301
1673
  /**
2302
1674
  * Reloads images. Useful when some images might have failed.
@@ -2304,7 +1676,7 @@ BookReader.prototype.initUIStrings = function() {
2304
1676
  BookReader.prototype.reloadImages = function() {
2305
1677
  this.refs.$brContainer.find('img').each(function(index, elem) {
2306
1678
  if (!elem.complete || elem.naturalHeight === 0) {
2307
- var src = elem.src;
1679
+ const src = elem.src;
2308
1680
  elem.src = '';
2309
1681
  setTimeout(function() {
2310
1682
  elem.src = src;
@@ -2318,16 +1690,16 @@ BookReader.prototype.reloadImages = function() {
2318
1690
  * @return {number}
2319
1691
  */
2320
1692
  BookReader.prototype.getFooterHeight = function() {
2321
- var $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
1693
+ const $heightEl = this.mode == this.constMode2up ? this.refs.$BRfooter : this.refs.$BRnav;
2322
1694
  if ($heightEl && this.refs.$BRfooter) {
2323
- var outerHeight = $heightEl.outerHeight();
2324
- var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
1695
+ const outerHeight = $heightEl.outerHeight();
1696
+ const bottom = parseInt(this.refs.$BRfooter.css('bottom'));
2325
1697
  if (!isNaN(outerHeight) && !isNaN(bottom)) {
2326
1698
  return outerHeight + bottom;
2327
1699
  }
2328
1700
  }
2329
1701
  return 0;
2330
- }
1702
+ };
2331
1703
 
2332
1704
  // Basic Usage built-in Methods (can be overridden through options)
2333
1705
  // This implementation uses options.data value for populating BookReader
@@ -2337,10 +1709,11 @@ BookReader.prototype.getFooterHeight = function() {
2337
1709
  * @return {Object}
2338
1710
  */
2339
1711
  BookReader.prototype.paramsFromCurrent = function() {
2340
- var params = {};
1712
+ const params = {};
2341
1713
 
2342
- var index = this.currentIndex();
2343
- var pageNum = this._models.book.getPageNum(index);
1714
+ // Path params
1715
+ const index = this.currentIndex();
1716
+ const pageNum = this.book.getPageNum(index);
2344
1717
  if ((pageNum === 0) || pageNum) {
2345
1718
  params.page = pageNum;
2346
1719
  }
@@ -2348,10 +1721,17 @@ BookReader.prototype.paramsFromCurrent = function() {
2348
1721
  params.index = index;
2349
1722
  params.mode = this.mode;
2350
1723
 
1724
+ // Unused params
2351
1725
  // $$$ highlight
2352
1726
  // $$$ region
2353
1727
 
2354
- // search
1728
+ // Querystring params
1729
+ // View
1730
+ const fullscreenView = 'theater';
1731
+ if (this.isFullscreenActive) {
1732
+ params.view = fullscreenView;
1733
+ }
1734
+ // Search
2355
1735
  if (this.enableSearch) {
2356
1736
  params.search = this.searchTerm;
2357
1737
  }
@@ -2372,7 +1752,7 @@ BookReader.prototype.paramsFromCurrent = function() {
2372
1752
  * @return {Object}
2373
1753
  */
2374
1754
  BookReader.prototype.paramsFromFragment = function(fragment) {
2375
- var params = {};
1755
+ const params = {};
2376
1756
 
2377
1757
  // For backwards compatibility we allow an initial # character
2378
1758
  // (as from window.location.hash) but don't require it
@@ -2381,7 +1761,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2381
1761
  }
2382
1762
 
2383
1763
  // Simple #nn syntax
2384
- var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
1764
+ const oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
2385
1765
  if ( !isNaN(oldStyleLeafNum) ) {
2386
1766
  params.index = oldStyleLeafNum;
2387
1767
 
@@ -2390,9 +1770,9 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2390
1770
  }
2391
1771
 
2392
1772
  // Split into key-value pairs
2393
- var urlArray = fragment.split('/');
2394
- var urlHash = {};
2395
- for (var i = 0; i < urlArray.length; i += 2) {
1773
+ const urlArray = fragment.split('/');
1774
+ const urlHash = {};
1775
+ for (let i = 0; i < urlArray.length; i += 2) {
2396
1776
  urlHash[urlArray[i]] = urlArray[i + 1];
2397
1777
  }
2398
1778
 
@@ -2408,7 +1788,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2408
1788
  // Index and page
2409
1789
  if ('undefined' != typeof(urlHash['page'])) {
2410
1790
  // page was set -- may not be int
2411
- params.page = urlHash['page'];
1791
+ params.page = decodeURIComponent(urlHash['page']);
2412
1792
  }
2413
1793
 
2414
1794
  // $$$ process /region
@@ -2422,7 +1802,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2422
1802
 
2423
1803
  // $$$ process /theme
2424
1804
  if (urlHash['theme'] != undefined) {
2425
- params.theme = urlHash['theme']
1805
+ params.theme = urlHash['theme'];
2426
1806
  }
2427
1807
  return params;
2428
1808
  };
@@ -2440,11 +1820,10 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2440
1820
  * @return {string}
2441
1821
  */
2442
1822
  BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2443
- const separator = '/';
2444
1823
  const fragments = [];
2445
1824
 
2446
1825
  if ('undefined' != typeof(params.page)) {
2447
- fragments.push('page', params.page);
1826
+ fragments.push('page', encodeURIComponent(params.page));
2448
1827
  } else {
2449
1828
  if ('undefined' != typeof(params.index)) {
2450
1829
  // Don't have page numbering but we do have the index
@@ -2470,15 +1849,18 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2470
1849
 
2471
1850
  // search
2472
1851
  if (params.search && urlMode === 'hash') {
2473
- fragments.push('search', params.search);
1852
+ fragments.push('search', utils.encodeURIComponentPlus(params.search));
2474
1853
  }
2475
1854
 
2476
- return utils.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
1855
+ return fragments.join('/');
2477
1856
  };
2478
1857
 
2479
1858
  /**
2480
1859
  * Create, update querystring from the params object
2481
1860
  *
1861
+ * Handles:
1862
+ * view=
1863
+ * q=
2482
1864
  * @param {Object} params
2483
1865
  * @param {string} currQueryString
2484
1866
  * @param {string} [urlMode]
@@ -2490,27 +1872,29 @@ BookReader.prototype.queryStringFromParams = function(
2490
1872
  urlMode = 'hash'
2491
1873
  ) {
2492
1874
  const newParams = new URLSearchParams(currQueryString);
1875
+
1876
+ if (params.view) {
1877
+ // Set ?view=theater when fullscreen
1878
+ newParams.set('view', params.view);
1879
+ } else {
1880
+ // Remove
1881
+ newParams.delete('view');
1882
+ }
1883
+
2493
1884
  if (params.search && urlMode === 'history') {
2494
- newParams.set('q', params.search)
1885
+ newParams.set('q', params.search);
2495
1886
  }
2496
1887
  // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
2497
1888
  // Note: This method returns the query string without the question mark.
2498
1889
  const result = newParams.toString();
2499
1890
  return result ? '?' + result : '';
2500
- }
1891
+ };
2501
1892
 
2502
1893
  /**
2503
1894
  * Helper to select within instance's elements
2504
1895
  */
2505
1896
  BookReader.prototype.$ = function(selector) {
2506
1897
  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
1898
  };
2515
1899
 
2516
1900
  window.BookReader = BookReader;