@internetarchive/bookreader 5.0.0-7 → 5.0.0-70-a1

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 (307) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +73 -10
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +396 -1129
  6. package/BookReader/BookReader.js +1 -1
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1788 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +19 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -1
  13. package/BookReader/icons/2up.svg +1 -1
  14. package/BookReader/icons/advance.svg +1 -1
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -1
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -1
  19. package/BookReader/icons/fullscreen_exit.svg +1 -1
  20. package/BookReader/icons/hamburger.svg +1 -1
  21. package/BookReader/icons/left-arrow.svg +1 -1
  22. package/BookReader/icons/magnify-minus.svg +1 -1
  23. package/BookReader/icons/magnify-plus.svg +1 -1
  24. package/BookReader/icons/magnify.svg +1 -1
  25. package/BookReader/icons/pause.svg +1 -1
  26. package/BookReader/icons/play.svg +1 -1
  27. package/BookReader/icons/playback-speed.svg +1 -1
  28. package/BookReader/icons/read-aloud.svg +1 -1
  29. package/BookReader/icons/review.svg +1 -1
  30. package/BookReader/icons/thumbnails.svg +1 -1
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -1
  33. package/BookReader/images/BRicons.svg +3 -3
  34. package/BookReader/images/books_graphic.svg +1 -1
  35. package/BookReader/images/icon_book.svg +1 -1
  36. package/BookReader/images/icon_bookmark.svg +1 -1
  37. package/BookReader/images/icon_gear.svg +1 -1
  38. package/BookReader/images/icon_hamburger.svg +1 -1
  39. package/BookReader/images/icon_home.svg +1 -1
  40. package/BookReader/images/icon_info.svg +1 -1
  41. package/BookReader/images/icon_one_page.svg +1 -1
  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 -1
  45. package/BookReader/images/icon_search_button.svg +1 -1
  46. package/BookReader/images/icon_share.svg +1 -1
  47. package/BookReader/images/icon_skip-ahead.svg +1 -1
  48. package/BookReader/images/icon_skip-back.svg +1 -1
  49. package/BookReader/images/icon_speaker.svg +1 -1
  50. package/BookReader/images/icon_speaker_open.svg +1 -1
  51. package/BookReader/images/icon_thumbnails.svg +1 -1
  52. package/BookReader/images/icon_toc.svg +1 -1
  53. package/BookReader/images/icon_two_pages.svg +1 -1
  54. package/BookReader/images/marker_chap-off.svg +1 -1
  55. package/BookReader/images/marker_chap-on.svg +1 -1
  56. package/BookReader/images/marker_srch-on.svg +1 -1
  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 -1
  60. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  61. package/BookReader/plugins/plugin.autoplay.js +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  63. package/BookReader/plugins/plugin.chapters.js +25 -1
  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 -1
  67. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  68. package/BookReader/plugins/plugin.resume.js +1 -1
  69. package/BookReader/plugins/plugin.resume.js.map +1 -1
  70. package/BookReader/plugins/plugin.search.js +2 -1
  71. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  72. package/BookReader/plugins/plugin.search.js.map +1 -1
  73. package/BookReader/plugins/plugin.text_selection.js +2 -1
  74. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  75. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  76. package/BookReader/plugins/plugin.tts.js +1 -1
  77. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  78. package/BookReader/plugins/plugin.tts.js.map +1 -1
  79. package/BookReader/plugins/plugin.url.js +1 -1
  80. package/BookReader/plugins/plugin.url.js.map +1 -1
  81. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
  82. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  83. package/BookReader/webcomponents-bundle.js +3 -0
  84. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  85. package/BookReader/webcomponents-bundle.js.map +1 -0
  86. package/BookReaderDemo/BookReaderDemo.css +16 -19
  87. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  88. package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
  89. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  90. package/BookReaderDemo/IADemoBr.js +147 -0
  91. package/BookReaderDemo/demo-advanced.html +2 -2
  92. package/BookReaderDemo/demo-autoplay.html +2 -3
  93. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  94. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  95. package/BookReaderDemo/demo-fullscreen.html +2 -4
  96. package/BookReaderDemo/demo-iiif.html +2 -1
  97. package/BookReaderDemo/demo-iiif.js +0 -1
  98. package/BookReaderDemo/demo-internetarchive.html +213 -17
  99. package/BookReaderDemo/demo-multiple.html +2 -1
  100. package/BookReaderDemo/demo-preview-pages.html +2 -1
  101. package/BookReaderDemo/demo-simple.html +2 -1
  102. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
  103. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  104. package/BookReaderDemo/immersion-1up.html +2 -2
  105. package/BookReaderDemo/immersion-mode.html +2 -4
  106. package/BookReaderDemo/toggle_controls.html +3 -2
  107. package/BookReaderDemo/view_mode.html +2 -1
  108. package/BookReaderDemo/viewmode-cycle.html +2 -3
  109. package/CHANGELOG.md +279 -0
  110. package/README.md +14 -1
  111. package/babel.config.js +20 -0
  112. package/codecov.yml +6 -0
  113. package/index.html +4 -1
  114. package/jsconfig.json +19 -0
  115. package/netlify.toml +9 -0
  116. package/package.json +69 -60
  117. package/renovate.json +52 -0
  118. package/scripts/preversion.js +4 -1
  119. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  120. package/src/BookNavigator/assets/button-base.js +4 -2
  121. package/src/BookNavigator/assets/ia-logo.js +17 -0
  122. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  123. package/src/BookNavigator/assets/icon_close.js +1 -1
  124. package/src/BookNavigator/book-navigator.js +586 -0
  125. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  126. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  127. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  128. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  129. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  130. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  131. package/src/BookNavigator/delete-modal-actions.js +1 -1
  132. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  133. package/src/BookNavigator/downloads/downloads.js +41 -25
  134. package/src/BookNavigator/search/search-provider.js +49 -27
  135. package/src/BookNavigator/search/search-results.js +23 -9
  136. package/src/BookNavigator/sharing.js +27 -0
  137. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  138. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  139. package/src/BookNavigator/volumes/volumes-provider.js +95 -0
  140. package/src/BookReader/BookModel.js +64 -34
  141. package/src/BookReader/DragScrollable.js +233 -0
  142. package/src/BookReader/Mode1Up.js +56 -351
  143. package/src/BookReader/Mode1UpLit.js +388 -0
  144. package/src/BookReader/Mode2Up.js +73 -1318
  145. package/src/BookReader/Mode2UpLit.js +776 -0
  146. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  147. package/src/BookReader/ModeSmoothZoom.js +312 -0
  148. package/src/BookReader/ModeThumb.js +18 -12
  149. package/src/BookReader/Navbar/Navbar.js +12 -38
  150. package/src/BookReader/PageContainer.js +81 -6
  151. package/src/BookReader/ReduceSet.js +1 -1
  152. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  153. package/src/BookReader/events.js +2 -3
  154. package/src/BookReader/options.js +24 -2
  155. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  156. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  157. package/src/BookReader/utils/SelectionObserver.js +43 -0
  158. package/src/BookReader/utils.js +118 -13
  159. package/src/BookReader.js +423 -1056
  160. package/src/assets/icons/magnify-minus.svg +3 -7
  161. package/src/assets/icons/magnify-plus.svg +3 -7
  162. package/src/assets/icons/voice.svg +1 -0
  163. package/src/css/BookReader.scss +1 -5
  164. package/src/css/_BRBookmarks.scss +1 -1
  165. package/src/css/_BRComponent.scss +1 -1
  166. package/src/css/_BRmain.scss +16 -0
  167. package/src/css/_BRnav.scss +11 -38
  168. package/src/css/_BRpages.scss +149 -40
  169. package/src/css/_BRsearch.scss +67 -21
  170. package/src/css/_TextSelection.scss +87 -27
  171. package/src/css/_colorbox.scss +2 -2
  172. package/src/css/_controls.scss +20 -7
  173. package/src/css/_icons.scss +1 -1
  174. package/src/ia-bookreader/ia-bookreader.js +224 -0
  175. package/src/plugins/plugin.archive_analytics.js +3 -3
  176. package/src/plugins/plugin.autoplay.js +5 -11
  177. package/src/plugins/plugin.chapters.js +211 -186
  178. package/src/plugins/plugin.resume.js +3 -3
  179. package/src/plugins/plugin.text_selection.js +464 -134
  180. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  181. package/src/plugins/search/plugin.search.js +142 -125
  182. package/src/plugins/search/utils.js +43 -0
  183. package/src/plugins/search/view.js +33 -58
  184. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  185. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  186. package/src/plugins/tts/PageChunk.js +15 -21
  187. package/src/plugins/tts/PageChunkIterator.js +8 -12
  188. package/src/plugins/tts/WebTTSEngine.js +87 -71
  189. package/src/plugins/tts/plugin.tts.js +95 -126
  190. package/src/plugins/tts/utils.js +0 -25
  191. package/src/plugins/url/UrlPlugin.js +193 -0
  192. package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
  193. package/src/util/browserSniffing.js +22 -0
  194. package/src/util/docCookies.js +21 -2
  195. package/tests/e2e/README.md +37 -0
  196. package/tests/e2e/autoplay.test.js +2 -2
  197. package/tests/e2e/base.test.js +8 -16
  198. package/tests/e2e/helpers/base.js +53 -48
  199. package/tests/e2e/helpers/debug.js +1 -1
  200. package/tests/e2e/helpers/params.js +17 -0
  201. package/tests/e2e/helpers/rightToLeft.js +8 -14
  202. package/tests/e2e/helpers/search.js +73 -0
  203. package/tests/e2e/models/Navigation.js +20 -37
  204. package/tests/e2e/rightToLeft.test.js +4 -5
  205. package/tests/e2e/viewmode.test.js +40 -33
  206. package/tests/jest/BookNavigator/book-navigator.test.js +658 -0
  207. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  208. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  209. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  210. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  211. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  212. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  213. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  214. package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +109 -60
  215. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  216. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  217. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +80 -0
  218. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  219. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  220. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  221. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  222. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  223. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  224. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  225. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  226. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  227. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
  228. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  229. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  230. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  231. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  232. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  233. package/tests/jest/BookReader/utils/SelectionObserver.test.js +43 -0
  234. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  235. package/tests/jest/BookReader/utils.test.js +229 -0
  236. package/tests/jest/BookReader.keyboard.test.js +190 -0
  237. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  238. package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
  239. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  240. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  241. package/tests/jest/plugins/plugin.chapters.test.js +145 -0
  242. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  243. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  244. package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
  245. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  246. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +25 -47
  247. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
  248. package/tests/jest/plugins/search/utils.js +25 -0
  249. package/tests/jest/plugins/search/utils.test.js +29 -0
  250. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  251. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  252. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  253. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  254. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  255. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  256. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  257. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
  258. package/tests/jest/setup.js +3 -0
  259. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  260. package/tests/jest/util/docCookies.test.js +24 -0
  261. package/tests/{util → jest/util}/strings.test.js +1 -1
  262. package/tests/{utils.js → jest/utils.js} +38 -0
  263. package/webpack.config.js +11 -6
  264. package/.babelrc +0 -12
  265. package/.dependabot/config.yml +0 -6
  266. package/.testcaferc.json +0 -5
  267. package/BookReader/bookreader-component-bundle.js +0 -1450
  268. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  269. package/BookReader/bookreader-component-bundle.js.map +0 -1
  270. package/BookReader/jquery-1.10.1.js +0 -2
  271. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  272. package/BookReader/plugins/plugin.menu_toggle.js +0 -2
  273. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  274. package/BookReader/plugins/plugin.mobile_nav.js +0 -2
  275. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  276. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  277. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  278. package/karma.conf.js +0 -23
  279. package/src/BookNavigator/BookModel.js +0 -14
  280. package/src/BookNavigator/BookNavigator.js +0 -446
  281. package/src/BookNavigator/assets/book-loader.js +0 -27
  282. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  283. package/src/BookNavigator/search/a-search-result.js +0 -55
  284. package/src/BookReader/DebugConsole.js +0 -54
  285. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  286. package/src/ItemNavigator/ItemNavigator.js +0 -376
  287. package/src/ItemNavigator/providers/sharing.js +0 -29
  288. package/src/css/_MobileNav.scss +0 -194
  289. package/src/dragscrollable-br.js +0 -261
  290. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  291. package/src/plugins/plugin.mobile_nav.js +0 -287
  292. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  293. package/tests/BookReader/DebugConsole.test.js +0 -25
  294. package/tests/BookReader/Mode1Up.test.js +0 -164
  295. package/tests/BookReader/Mode2Up.test.js +0 -247
  296. package/tests/BookReader/utils.test.js +0 -109
  297. package/tests/e2e/helpers/desktopSearch.js +0 -72
  298. package/tests/e2e/helpers/mobileSearch.js +0 -85
  299. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  300. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  301. package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
  302. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  303. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  304. package/tests/plugins/plugin.chapters.test.js +0 -130
  305. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  306. package/tests/plugins/plugin.text_selection.test.js +0 -203
  307. 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,15 +1121,28 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1128
1121
  }
1129
1122
 
1130
1123
  this.isFullscreenActive = true;
1131
- this.animating = true;
1132
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear',() => {
1133
- this.resize();
1134
- this.jumpToIndex(currentIndex);
1135
- this.animating = false;
1136
- });
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);
1137
1133
 
1138
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
1139
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');
1140
1146
  };
1141
1147
 
1142
1148
  /**
@@ -1146,27 +1152,35 @@ BookReader.prototype.enterFullscreen = function(bindKeyboardControls = true) {
1146
1152
  * - fires custom event
1147
1153
  * @param { boolean } bindKeyboardControls
1148
1154
  */
1149
- BookReader.prototype.exitFullScreen = function() {
1150
- this.refs.$brContainer.css('opacity', 0);
1151
-
1152
- $(document).unbind('keyup', this._fullscreenCloseHandler);
1153
-
1154
- var windowWidth = $(window).width();
1155
+ BookReader.prototype.exitFullScreen = async function () {
1156
+ this.refs.$br.addClass('BRfullscreenAnimation');
1157
+ $(document).off('keyup', this._fullscreenCloseHandler);
1155
1158
 
1159
+ const windowWidth = $(window).width();
1156
1160
  const canShow2up = this.options.controls.twoPage.visible;
1157
1161
  if (canShow2up && (windowWidth <= this.onePageMinBreakpoint)) {
1158
1162
  this.switchMode(this.constMode2up);
1159
1163
  }
1160
1164
 
1161
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
+
1162
1170
  this.updateBrClasses();
1163
- this.animating = true;
1164
- this.refs.$brContainer.animate({opacity: 1}, 'fast', 'linear', () => {
1165
- this.resize();
1166
- this.animating = false;
1167
- });
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
+ }
1179
+
1168
1180
  this.textSelectionPlugin?.stopPageFlip(this.refs.$brContainer);
1169
- this.trigger(BookReader.eventNames.fullscreenToggled);
1181
+ // Remove "?view=theater"
1182
+ this.trigger(BookReader.eventNames.fragmentChange);
1183
+ this.refs.$br.removeClass('BRfullscreenAnimation');
1170
1184
  };
1171
1185
 
1172
1186
  /**
@@ -1180,7 +1194,7 @@ BookReader.prototype.currentIndex = function() {
1180
1194
  return this.firstIndex; // $$$ TODO page in center of view would be better
1181
1195
  } else if (this.mode == this.constMode2up) {
1182
1196
  // Only allow indices that are actually present in book
1183
- return utils.clamp(this.firstIndex, 0, this._models.book.getNumLeafs() - 1);
1197
+ return utils.clamp(this.firstIndex, 0, this.book.getNumLeafs() - 1);
1184
1198
  } else {
1185
1199
  throw 'currentIndex called for unimplemented mode ' + this.mode;
1186
1200
  }
@@ -1197,11 +1211,9 @@ BookReader.prototype.updateFirstIndex = function(
1197
1211
  index,
1198
1212
  { suppressFragmentChange = false } = {}
1199
1213
  ) {
1200
- // Called multiple times when defaults contains "mode/1up",
1201
- // including after init(). Skip fragment change if no index change
1202
- if (this.firstIndex === index) {
1203
- suppressFragmentChange = true;
1204
- }
1214
+ // If there's no change, do nothing
1215
+ if (this.firstIndex === index) return;
1216
+
1205
1217
  this.firstIndex = index;
1206
1218
  if (!(this.suppressFragmentChange || suppressFragmentChange)) {
1207
1219
  this.trigger(BookReader.eventNames.fragmentChange);
@@ -1212,8 +1224,12 @@ BookReader.prototype.updateFirstIndex = function(
1212
1224
  if (this.options.initialSearchTerm && !suppressFragmentChange) {
1213
1225
  this.suppressFragmentChange = false;
1214
1226
  }
1215
- this.trigger('pageChanged');
1216
- 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);
1217
1233
  };
1218
1234
 
1219
1235
  /**
@@ -1260,24 +1276,24 @@ BookReader.prototype.leftmost = function() {
1260
1276
  }
1261
1277
  };
1262
1278
 
1263
- BookReader.prototype.next = function() {
1279
+ BookReader.prototype.next = function({triggerStop = true} = {}) {
1264
1280
  if (this.constMode2up == this.mode) {
1265
- this.trigger(BookReader.eventNames.stop);
1266
- this.flipFwdToIndex(null);
1281
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1282
+ this._modes.mode2Up.mode2UpLit.flipAnimation('next');
1267
1283
  } else {
1268
- if (this.firstIndex < this.lastDisplayableIndex()) {
1284
+ if (this.firstIndex < this.book.getNumLeafs() - 1) {
1269
1285
  this.jumpToIndex(this.firstIndex + 1);
1270
1286
  }
1271
1287
  }
1272
1288
  };
1273
1289
 
1274
- BookReader.prototype.prev = function() {
1290
+ BookReader.prototype.prev = function({triggerStop = true} = {}) {
1275
1291
  const isOnFrontPage = this.firstIndex < 1;
1276
1292
  if (isOnFrontPage) return;
1277
1293
 
1278
1294
  if (this.constMode2up == this.mode) {
1279
- this.trigger(BookReader.eventNames.stop);
1280
- this.flipBackToIndex(null);
1295
+ if (triggerStop) this.trigger(BookReader.eventNames.stop);
1296
+ this._modes.mode2Up.mode2UpLit.flipAnimation('prev');
1281
1297
  } else {
1282
1298
  if (this.firstIndex >= 1) {
1283
1299
  this.jumpToIndex(this.firstIndex - 1);
@@ -1286,257 +1302,13 @@ BookReader.prototype.prev = function() {
1286
1302
  };
1287
1303
 
1288
1304
  BookReader.prototype.first = function() {
1289
- this.jumpToIndex(this.firstDisplayableIndex());
1305
+ this.jumpToIndex(0);
1290
1306
  };
1291
1307
 
1292
1308
  BookReader.prototype.last = function() {
1293
- this.jumpToIndex(this.lastDisplayableIndex());
1294
- };
1295
-
1296
- /**
1297
- * Scrolls down one screen view
1298
- */
1299
- BookReader.prototype.scrollDown = function() {
1300
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1301
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1302
- // Whole pages are visible, scroll whole page only
1303
- return this.next();
1304
- }
1305
-
1306
- this.refs.$brContainer.stop(true).animate(
1307
- { scrollTop: '+=' + this._scrollAmount() + 'px'},
1308
- 400, 'easeInOutExpo'
1309
- );
1310
- return true;
1311
- } else {
1312
- return false;
1313
- }
1314
- };
1315
-
1316
- /**
1317
- * Scrolls up one screen view
1318
- */
1319
- BookReader.prototype.scrollUp = function() {
1320
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1321
- if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
1322
- // Whole pages are visible, scroll whole page only
1323
- return this.prev();
1324
- }
1325
-
1326
- this.refs.$brContainer.stop(true).animate(
1327
- { scrollTop: '-=' + this._scrollAmount() + 'px'},
1328
- 400, 'easeInOutExpo'
1329
- );
1330
- return true;
1331
- } else {
1332
- return false;
1333
- }
1334
- };
1335
-
1336
- /**
1337
- * The amount to scroll vertically in integer pixels
1338
- */
1339
- BookReader.prototype._scrollAmount = function() {
1340
- if (this.constMode1up == this.mode) {
1341
- // Overlap by % of page size
1342
- return parseInt(this.refs.$brContainer.prop('clientHeight') - this._models.book.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
1343
- }
1344
-
1345
- return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
1346
- };
1347
-
1348
- /**
1349
- * @deprecated No longer used; will be remove in v5
1350
- */
1351
- BookReader.prototype.prefetchImg = async function(index, fetchNow = false) {
1352
- console.warn('Call to deprecated function: BookReader.prefetchImg. No-op.');
1353
- };
1354
-
1355
- /**
1356
- * @deprecated No longer used; will be remove in v5
1357
- */
1358
- BookReader.prototype.pruneUnusedImgs = function() {
1359
- console.warn('Call to deprecated function: BookReader.pruneUnused. No-op.');
1309
+ this.jumpToIndex(this.book.getNumLeafs() - 1);
1360
1310
  };
1361
1311
 
1362
- /************************/
1363
- /** Mode1Up extensions **/
1364
- /************************/
1365
- /** @deprecated not used outside BookReader */
1366
- BookReader.prototype.prepareOnePageView = Mode1Up.prototype.prepare;
1367
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'prepare', 'prepareOnePageView');
1368
- /** @deprecated not used outside BookReader */
1369
- BookReader.prototype.drawLeafsOnePage = Mode1Up.prototype.drawLeafs;
1370
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'drawLeafs', 'drawLeafsOnePage');
1371
- /** @deprecated not used outside BookReader */
1372
- BookReader.prototype.zoom1up = Mode1Up.prototype.zoom;
1373
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'zoom', 'zoom1up');
1374
- /** @deprecated not used outside Mode1Up */
1375
- BookReader.prototype.onePageGetAutofitWidth = Mode1Up.prototype.getAutofitWidth;
1376
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitWidth', 'onePageGetAutofitWidth');
1377
- /** @deprecated not used outside Mode1Up, BookReader */
1378
- BookReader.prototype.onePageGetAutofitHeight = Mode1Up.prototype.getAutofitHeight;
1379
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getAutofitHeight', 'onePageGetAutofitHeight');
1380
- /** @deprecated not used outside Mode1Up, BookReader */
1381
- BookReader.prototype.onePageGetPageTop = Mode1Up.prototype.getPageTop;
1382
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'getPageTop', 'onePageGetPageTop');
1383
- /** @deprecated not used outside Mode1Up, BookReader */
1384
- BookReader.prototype.onePageCalculateReductionFactors = Mode1Up.prototype.calculateReductionFactors;
1385
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateReductionFactors', 'onePageCalculateReductionFactors');
1386
- /** @deprecated not used outside Mode1Up, BookReader */
1387
- BookReader.prototype.resizePageView1up = Mode1Up.prototype.resizePageView;
1388
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'resizePageView', 'resizePageView1up');
1389
- /** @deprecated not used outside Mode1Up */
1390
- BookReader.prototype.onePageCalculateViewDimensions = Mode1Up.prototype.calculateViewDimensions;
1391
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'calculateViewDimensions', 'onePageCalculateViewDimensions');
1392
- /** @deprecated not used outside Mode1Up */
1393
- BookReader.prototype.centerX1up = Mode1Up.prototype.centerX;
1394
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerX', 'centerX1up');
1395
- /** @deprecated not used outside Mode1Up */
1396
- BookReader.prototype.centerY1up = Mode1Up.prototype.centerY;
1397
- exposeOverrideableMethod(Mode1Up, '_modes.mode1Up', 'centerY', 'centerY1up');
1398
-
1399
- /************************/
1400
- /** Mode2Up extensions **/
1401
- /************************/
1402
- /** @deprecated not used outside Mode2Up */
1403
- BookReader.prototype.zoom2up = Mode2Up.prototype.zoom;
1404
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'zoom', 'zoom2up');
1405
- BookReader.prototype.twoPageGetAutofitReduce = Mode2Up.prototype.getAutofitReduce;
1406
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getAutofitReduce', 'twoPageGetAutofitReduce');
1407
- BookReader.prototype.flipBackToIndex = Mode2Up.prototype.flipBackToIndex;
1408
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipBackToIndex', 'flipBackToIndex');
1409
- BookReader.prototype.flipFwdToIndex = Mode2Up.prototype.flipFwdToIndex;
1410
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipFwdToIndex', 'flipFwdToIndex');
1411
- BookReader.prototype.setHilightCss2UP = Mode2Up.prototype.setHilightCss;
1412
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setHilightCss', 'setHilightCss2UP');
1413
- /** @deprecated not used outside Mode2Up */
1414
- BookReader.prototype.setClickHandler2UP = Mode2Up.prototype.setClickHandler;
1415
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setClickHandler', 'setClickHandler2UP');
1416
- /** @deprecated not used outside Mode2Up */
1417
- BookReader.prototype.drawLeafsTwoPage = Mode2Up.prototype.drawLeafs;
1418
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'drawLeafs', 'drawLeafsTwoPage');
1419
- /** @deprecated not used outside BookReader */
1420
- BookReader.prototype.prepareTwoPageView = Mode2Up.prototype.prepareTwoPageView;
1421
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareTwoPageView', 'prepareTwoPageView');
1422
- /** @deprecated not used outside Mode2Up */
1423
- BookReader.prototype.prepareTwoPagePopUp = Mode2Up.prototype.preparePopUp;
1424
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'preparePopUp', 'prepareTwoPagePopUp');
1425
- /** @deprecated not used outside BookReader, Mode2Up */
1426
- BookReader.prototype.calculateSpreadSize = Mode2Up.prototype.calculateSpreadSize;
1427
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateSpreadSize', 'calculateSpreadSize');
1428
- /** @deprecated not used outside BookReader, Mode2Up */
1429
- BookReader.prototype.getIdealSpreadSize = Mode2Up.prototype.getIdealSpreadSize;
1430
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getIdealSpreadSize', 'getIdealSpreadSize');
1431
- /** @deprecated not used outside BookReader, Mode2Up */
1432
- BookReader.prototype.getSpreadSizeFromReduce = Mode2Up.prototype.getSpreadSizeFromReduce;
1433
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getSpreadSizeFromReduce', 'getSpreadSizeFromReduce');
1434
- /** @deprecated unused */
1435
- BookReader.prototype.twoPageIsZoomedIn = Mode2Up.prototype.isZoomedIn;
1436
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'isZoomedIn', 'twoPageIsZoomedIn');
1437
- /** @deprecated not used outside BookReader */
1438
- BookReader.prototype.twoPageCalculateReductionFactors = Mode2Up.prototype.calculateReductionFactors;
1439
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'calculateReductionFactors', 'twoPageCalculateReductionFactors');
1440
- /** @deprecated unused */
1441
- BookReader.prototype.twoPageSetCursor = Mode2Up.prototype.setCursor;
1442
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setCursor', 'twoPageSetCursor');
1443
- /** @deprecated unused outside BookReader, Mode2Up */
1444
- BookReader.prototype.flipLeftToRight = Mode2Up.prototype.flipLeftToRight;
1445
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipLeftToRight', 'flipLeftToRight');
1446
- /** @deprecated unused outside BookReader, Mode2Up */
1447
- BookReader.prototype.flipRightToLeft = Mode2Up.prototype.flipRightToLeft;
1448
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipRightToLeft', 'flipRightToLeft');
1449
- /** @deprecated unused outside Mode2Up */
1450
- BookReader.prototype.setMouseHandlers2UP = Mode2Up.prototype.setMouseHandlers;
1451
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setMouseHandlers', 'setMouseHandlers2UP');
1452
- /** @deprecated unused outside BookReader, Mode2Up */
1453
- BookReader.prototype.prepareFlipLeftToRight = Mode2Up.prototype.prepareFlipLeftToRight;
1454
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipLeftToRight', 'prepareFlipLeftToRight');
1455
- /** @deprecated unused outside BookReader, Mode2Up */
1456
- BookReader.prototype.prepareFlipRightToLeft = Mode2Up.prototype.prepareFlipRightToLeft;
1457
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prepareFlipRightToLeft', 'prepareFlipRightToLeft');
1458
- /** @deprecated unused outside Mode2Up */
1459
- BookReader.prototype.getPageWidth2UP = Mode2Up.prototype.getPageWidth;
1460
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getPageWidth', 'getPageWidth2UP');
1461
- /** @deprecated unused outside Mode2Up */
1462
- BookReader.prototype.twoPageGutter = Mode2Up.prototype.gutter;
1463
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutter', 'twoPageGutter');
1464
- /** @deprecated unused outside Mode2Up */
1465
- BookReader.prototype.twoPageTop = Mode2Up.prototype.top;
1466
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'top', 'twoPageTop');
1467
- /** @deprecated unused outside Mode2Up */
1468
- BookReader.prototype.twoPageCoverWidth = Mode2Up.prototype.coverWidth;
1469
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'coverWidth', 'twoPageCoverWidth');
1470
- /** @deprecated unused outside Mode2Up */
1471
- BookReader.prototype.twoPageGetViewCenter = Mode2Up.prototype.getViewCenter;
1472
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'getViewCenter', 'twoPageGetViewCenter');
1473
- /** @deprecated unused outside BookReader, Mode2Up */
1474
- BookReader.prototype.twoPageCenterView = Mode2Up.prototype.centerView;
1475
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'centerView', 'twoPageCenterView');
1476
- /** @deprecated unused outside Mode2Up */
1477
- BookReader.prototype.twoPageFlipAreaHeight = Mode2Up.prototype.flipAreaHeight;
1478
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaHeight', 'twoPageFlipAreaHeight');
1479
- /** @deprecated unused outside Mode2Up */
1480
- BookReader.prototype.twoPageFlipAreaWidth = Mode2Up.prototype.flipAreaWidth;
1481
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaWidth', 'twoPageFlipAreaWidth');
1482
- /** @deprecated unused outside BookReader, Mode2Up */
1483
- BookReader.prototype.twoPageFlipAreaTop = Mode2Up.prototype.flipAreaTop;
1484
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'flipAreaTop', 'twoPageFlipAreaTop');
1485
- /** @deprecated unused outside Mode2Up */
1486
- BookReader.prototype.twoPageLeftFlipAreaLeft = Mode2Up.prototype.leftFlipAreaLeft;
1487
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leftFlipAreaLeft', 'twoPageLeftFlipAreaLeft');
1488
- /** @deprecated unused outside Mode2Up */
1489
- BookReader.prototype.twoPageRightFlipAreaLeft = Mode2Up.prototype.rightFlipAreaLeft;
1490
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'rightFlipAreaLeft', 'twoPageRightFlipAreaLeft');
1491
- /** @deprecated unused outside BookReader, Mode2Up */
1492
- BookReader.prototype.gutterOffsetForIndex = Mode2Up.prototype.gutterOffsetForIndex;
1493
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'gutterOffsetForIndex', 'gutterOffsetForIndex');
1494
- /** @deprecated unused outside BookReader, Mode2Up */
1495
- BookReader.prototype.leafEdgeWidth = Mode2Up.prototype.leafEdgeWidth;
1496
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'leafEdgeWidth', 'leafEdgeWidth');
1497
- /** @deprecated unused outside BookReader, Mode2Up */
1498
- BookReader.prototype.jumpIndexForLeftEdgePageX = Mode2Up.prototype.jumpIndexForLeftEdgePageX;
1499
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForLeftEdgePageX', 'jumpIndexForLeftEdgePageX');
1500
- /** @deprecated unused outside BookReader, Mode2Up */
1501
- BookReader.prototype.jumpIndexForRightEdgePageX = Mode2Up.prototype.jumpIndexForRightEdgePageX;
1502
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'jumpIndexForRightEdgePageX', 'jumpIndexForRightEdgePageX');
1503
- /** @deprecated unused outside Mode2Up */
1504
- BookReader.prototype.prefetch = Mode2Up.prototype.prefetch;
1505
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'prefetch', 'prefetch');
1506
- /** @deprecated unused outside Mode2Up */
1507
- BookReader.prototype.setSpreadIndices = Mode2Up.prototype.setSpreadIndices;
1508
- exposeOverrideableMethod(Mode2Up, '_modes.mode2Up', 'setSpreadIndices', 'setSpreadIndices');
1509
- /**
1510
- * Immediately stop flip animations. Callbacks are triggered.
1511
- */
1512
- BookReader.prototype.stopFlipAnimations = function() {
1513
- this.trigger(BookReader.eventNames.stop);
1514
-
1515
- // Stop animation, clear queue, trigger callbacks
1516
- if (this.leafEdgeTmp) {
1517
- $(this.leafEdgeTmp).stop(false, true);
1518
- }
1519
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1520
- $(this.$container).stop(false, true);
1521
- });
1522
-
1523
- // And again since animations also queued in callbacks
1524
- if (this.leafEdgeTmp) {
1525
- $(this.leafEdgeTmp).stop(false, true);
1526
- }
1527
- jQuery.each(this._modes.mode2Up.pageContainers, function() {
1528
- $(this.$container).stop(false, true);
1529
- });
1530
- };
1531
-
1532
- /**
1533
- * Returns true if keyboard navigation should be disabled for the event
1534
- * @param {Event}
1535
- * @return {boolean}
1536
- */
1537
- BookReader.prototype.keyboardNavigationIsDisabled = function(event) {
1538
- return event.target.tagName == "INPUT";
1539
- };
1540
1312
 
1541
1313
  /**
1542
1314
  * @template TClass extends { br: BookReader }
@@ -1560,30 +1332,9 @@ function exposeOverrideableMethod(Class, classKey, method, brMethod = method) {
1560
1332
  /***********************/
1561
1333
  /** Navbar extensions **/
1562
1334
  /***********************/
1335
+ /** This cannot be removed yet because plugin.tts.js overrides it */
1563
1336
  BookReader.prototype.initNavbar = Navbar.prototype.init;
1564
1337
  exposeOverrideableMethod(Navbar, '_components.navbar', 'init', 'initNavbar');
1565
- BookReader.prototype.switchNavbarControls = Navbar.prototype.switchNavbarControls;
1566
- exposeOverrideableMethod(Navbar, '_components.navbar', 'switchNavbarControls');
1567
- BookReader.prototype.updateViewModeButton = Navbar.prototype.updateViewModeButton;
1568
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateViewModeButton');
1569
- BookReader.prototype.getNavPageNumString = Navbar.prototype.getNavPageNumString;
1570
- exposeOverrideableMethod(Navbar, '_components.navbar', 'getNavPageNumString');
1571
- /** @deprecated */
1572
- BookReader.prototype.initEmbedNavbar = Navbar.prototype.initEmbed;
1573
- exposeOverrideableMethod(Navbar, '_components.navbar', 'initEmbed', 'initEmbedNavbar');
1574
- /** @deprecated unused */
1575
- BookReader.prototype.getNavPageNumHtml = getNavPageNumHtml;
1576
- /** @deprecated unused outside this file */
1577
- BookReader.prototype.updateNavPageNum = Navbar.prototype.updateNavPageNum;
1578
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavPageNum');
1579
- /** @deprecated unused outside this file */
1580
- BookReader.prototype.updateNavIndex = Navbar.prototype.updateNavIndex;
1581
- exposeOverrideableMethod(Navbar, '_components.navbar', 'updateNavIndex');
1582
- /** @deprecated unused outside this file */
1583
- BookReader.prototype.updateNavIndexThrottled = utils.throttle(BookReader.prototype.updateNavIndex, 250, false);
1584
- /** @deprecated unused */
1585
- BookReader.prototype.updateNavIndexDebounced = utils.debounce(BookReader.prototype.updateNavIndex, 500, false);
1586
-
1587
1338
 
1588
1339
  /************************/
1589
1340
  /** Toolbar extensions **/
@@ -1598,24 +1349,14 @@ BookReader.prototype.buildInfoDiv = Toolbar.prototype.buildInfoDiv;
1598
1349
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'buildInfoDiv');
1599
1350
  BookReader.prototype.getToolBarHeight = Toolbar.prototype.getToolBarHeight;
1600
1351
  exposeOverrideableMethod(Toolbar, '_components.toolbar', 'getToolBarHeight');
1601
- /** @deprecated zoom no longer in toolbar */
1602
- BookReader.prototype.updateToolbarZoom = Toolbar.prototype.updateToolbarZoom;
1603
- exposeOverrideableMethod(Toolbar, '_components.toolbar', 'updateToolbarZoom');
1604
- /** @deprecated unused */
1605
- BookReader.prototype.blankInfoDiv = blankInfoDiv;
1606
- /** @deprecated unused */
1607
- BookReader.prototype.blankShareDiv = blankShareDiv;
1608
- /** @deprecated unused */
1609
- BookReader.prototype.createPopup = createPopup;
1610
1352
 
1611
1353
  /**
1612
1354
  * Bind navigation handlers
1613
1355
  */
1614
1356
  BookReader.prototype.bindNavigationHandlers = function() {
1615
1357
  const self = this;
1358
+ const jIcons = this.$('.BRicon');
1616
1359
 
1617
- // Note the mobile plugin attaches itself to body, so we need to select outside
1618
- const jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
1619
1360
  // Map of jIcon class -> click handler
1620
1361
  const navigationControls = {
1621
1362
  book_left: () => {
@@ -1626,20 +1367,6 @@ BookReader.prototype.bindNavigationHandlers = function() {
1626
1367
  this.trigger(BookReader.eventNames.stop);
1627
1368
  this.right();
1628
1369
  },
1629
- book_up: () => {
1630
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1631
- this.scrollUp();
1632
- } else {
1633
- this.prev();
1634
- }
1635
- },
1636
- book_down: () => {
1637
- if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
1638
- this.scrollDown();
1639
- } else {
1640
- this.next();
1641
- }
1642
- },
1643
1370
  book_top: this.first.bind(this),
1644
1371
  book_bottom: this.last.bind(this),
1645
1372
  book_leftmost: this.leftmost.bind(this),
@@ -1665,7 +1392,7 @@ BookReader.prototype.bindNavigationHandlers = function() {
1665
1392
  },
1666
1393
  full: () => {
1667
1394
  if (this.ui == 'embed') {
1668
- var url = this.$('.BRembedreturn a').attr('href');
1395
+ const url = this.$('.BRembedreturn a').attr('href');
1669
1396
  window.open(url);
1670
1397
  } else {
1671
1398
  this.toggleFullscreen();
@@ -1673,23 +1400,25 @@ BookReader.prototype.bindNavigationHandlers = function() {
1673
1400
  },
1674
1401
  };
1675
1402
 
1676
- jIcons.filter('.fit').bind('fit', function() {
1677
- // 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);
1678
1407
  });
1679
1408
 
1680
1409
  for (const control in navigationControls) {
1681
1410
  jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1682
- navigationControls[control]()
1411
+ navigationControls[control]();
1683
1412
  return false;
1684
1413
  });
1685
1414
  }
1686
1415
 
1687
- var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1688
- var $brNavCntlTopEl = this.$('.BRnavCntlTop');
1416
+ const $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
1417
+ const $brNavCntlTopEl = this.$('.BRnavCntlTop');
1689
1418
 
1690
1419
  this.$('.BRnavCntl').click(
1691
1420
  function() {
1692
- var promises = [];
1421
+ const promises = [];
1693
1422
  // TODO don't use magic constants
1694
1423
  // TODO move this to a function
1695
1424
  if ($brNavCntlBtmEl.hasClass('BRdn')) {
@@ -1709,7 +1438,7 @@ BookReader.prototype.bindNavigationHandlers = function() {
1709
1438
  $brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
1710
1439
  $brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
1711
1440
  self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
1712
- self.$('.BRvavCntl').animate({opacity:1})
1441
+ self.$('.BRvavCntl').animate({opacity:1});
1713
1442
  }
1714
1443
  $.when.apply($, promises).done(function() {
1715
1444
  // Only do full resize in auto mode and need to recalc. size
@@ -1727,386 +1456,38 @@ BookReader.prototype.bindNavigationHandlers = function() {
1727
1456
  });
1728
1457
  }
1729
1458
  );
1730
- $brNavCntlBtmEl.mouseover(function() {
1731
- if ($(this).hasClass('BRup')) {
1732
- self.$('.BRnavCntl').animate({opacity:1},250);
1733
- }
1734
- }).mouseleave(function() {
1735
- if ($(this).hasClass('BRup')) {
1736
- self.$('.BRnavCntl').animate({opacity:.75},250);
1737
- }
1738
- });
1739
- $brNavCntlTopEl.mouseover(function() {
1740
- if ($(this).hasClass('BRdn')) {
1741
- self.$('.BRnavCntl').animate({opacity:1},250);
1742
- }
1743
- }).mouseleave(function() {
1744
- if ($(this).hasClass('BRdn')) {
1745
- self.$('.BRnavCntl').animate({opacity:.75},250);
1746
- }
1747
- });
1748
-
1749
- this.initSwipeData();
1750
-
1751
- $(document).off('mousemove.navigation', this.el);
1752
- $(document).on(
1753
- 'mousemove.navigation',
1754
- this.el,
1755
- { 'br': this },
1756
- this.navigationMousemoveHandler
1757
- );
1758
-
1759
- $(document).off('mousedown.swipe', '.BRpageimage');
1760
- $(document).on(
1761
- 'mousedown.swipe',
1762
- '.BRpageimage',
1763
- { 'br': this },
1764
- this.swipeMousedownHandler
1765
- );
1766
-
1767
- this.bindMozTouchHandlers();
1768
- };
1769
-
1770
- /**
1771
- * Unbind navigation handlers
1772
- */
1773
- BookReader.prototype.unbindNavigationHandlers = function() {
1774
- $(document).off('mousemove.navigation', this.el);
1775
- };
1776
-
1777
- /**
1778
- * Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
1779
- */
1780
- BookReader.prototype.navigationMousemoveHandler = function(event) {
1781
- // $$$ possibly not great to be calling this for every mousemove
1782
- if (event.data['br'].uiAutoHide) {
1783
- // 77px is an approximate height of the Internet Archive Top Nav
1784
- // 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
1785
- var navkey = $(document).height() - 75;
1786
- if ((event.pageY < 76) || (event.pageY > navkey)) {
1787
- // inside or near navigation elements
1788
- event.data['br'].hideNavigation();
1789
- } else {
1790
- event.data['br'].showNavigation();
1791
- }
1792
- }
1793
- };
1794
-
1795
- BookReader.prototype.initSwipeData = function(clientX, clientY) {
1796
- /*
1797
- * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
1798
- */
1799
- this._swipe = {
1800
- mightBeSwiping: false,
1801
- didSwipe: false,
1802
- mightBeDraggin: false,
1803
- didDrag: false,
1804
- startTime: (new Date).getTime(),
1805
- startX: clientX,
1806
- startY: clientY,
1807
- lastX: clientX,
1808
- lastY: clientY,
1809
- deltaX: 0,
1810
- deltaY: 0,
1811
- deltaT: 0
1812
- }
1813
- };
1814
-
1815
- BookReader.prototype.swipeMousedownHandler = function(event) {
1816
- var self = event.data['br'];
1817
-
1818
- // We should be the last bubble point for the page images
1819
- // Disable image drag and select, but keep right-click
1820
- if (event.which == 3) {
1821
- return !self.protected;
1822
- }
1823
-
1824
- $(event.target).bind('mouseout.swipe',
1825
- { 'br': self},
1826
- self.swipeMouseupHandler
1827
- ).bind('mouseup.swipe',
1828
- { 'br': self},
1829
- self.swipeMouseupHandler
1830
- ).bind('mousemove.swipe',
1831
- { 'br': self },
1832
- self.swipeMousemoveHandler
1833
- );
1834
-
1835
- self.initSwipeData(event.clientX, event.clientY);
1836
- self._swipe.mightBeSwiping = true;
1837
- self._swipe.mightBeDragging = true;
1838
-
1839
- event.preventDefault();
1840
- event.returnValue = false;
1841
- event.cancelBubble = true;
1842
- return false;
1843
- };
1844
-
1845
- BookReader.prototype.swipeMousemoveHandler = function(event) {
1846
- var self = event.data['br'];
1847
- var _swipe = self._swipe;
1848
- if (! _swipe.mightBeSwiping) {
1849
- return;
1850
- }
1851
-
1852
- // Update swipe data
1853
- _swipe.deltaX = event.clientX - _swipe.startX;
1854
- _swipe.deltaY = event.clientY - _swipe.startY;
1855
- _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
1856
-
1857
- var absX = Math.abs(_swipe.deltaX);
1858
- var absY = Math.abs(_swipe.deltaY);
1859
-
1860
- // Minimum distance in the amount of tim to trigger the swipe
1861
- var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
1862
- var maxSwipeTime = 400;
1863
-
1864
- // Check for horizontal swipe
1865
- if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
1866
- _swipe.mightBeSwiping = false; // only trigger once
1867
- _swipe.didSwipe = true;
1868
- if (self.mode == self.constMode2up) {
1869
- if (_swipe.deltaX < 0) {
1870
- self.right();
1871
- } else {
1872
- self.left();
1459
+ $brNavCntlBtmEl
1460
+ .on("mouseover", function() {
1461
+ if ($(this).hasClass('BRup')) {
1462
+ self.$('.BRnavCntl').animate({opacity:1},250);
1873
1463
  }
1874
- }
1875
- }
1876
-
1877
- if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
1878
- if (_swipe.mightBeDragging) {
1879
- // Dragging
1880
- _swipe.didDrag = true;
1881
- self.refs.$brContainer
1882
- .scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
1883
- .scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
1884
- }
1885
- }
1886
- _swipe.lastX = event.clientX;
1887
- _swipe.lastY = event.clientY;
1888
-
1889
- event.preventDefault();
1890
- event.returnValue = false;
1891
- event.cancelBubble = true;
1892
- return false;
1893
- };
1894
-
1895
- BookReader.prototype.swipeMouseupHandler = function(event) {
1896
- var _swipe = event.data['br']._swipe;
1897
- _swipe.mightBeSwiping = false;
1898
- _swipe.mightBeDragging = false;
1899
-
1900
- $(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
1901
-
1902
- if (_swipe.didSwipe || _swipe.didDrag) {
1903
- // Swallow event if completed swipe gesture
1904
- event.preventDefault();
1905
- event.returnValue = false;
1906
- event.cancelBubble = true;
1907
- return false;
1908
- }
1909
- return true;
1910
- };
1911
-
1912
- BookReader.prototype.bindMozTouchHandlers = function() {
1913
- var self = this;
1914
-
1915
- // Currently only want touch handlers in 2up
1916
- this.refs.$br.bind('MozTouchDown', function(event) {
1917
- if (this.mode == self.constMode2up) {
1918
- event.preventDefault();
1919
- }
1920
- })
1921
- .bind('MozTouchMove', function(event) {
1922
- if (this.mode == self.constMode2up) {
1923
- event.preventDefault();
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);
1924
1474
  }
1925
1475
  })
1926
- .bind('MozTouchUp', function(event) {
1927
- if (this.mode == self.constMode2up) {
1928
- event.preventDefault();
1476
+ .on("mouseleave", function() {
1477
+ if ($(this).hasClass('BRdn')) {
1478
+ self.$('.BRnavCntl').animate({opacity:.75},250);
1929
1479
  }
1930
1480
  });
1931
1481
  };
1932
1482
 
1933
- /**
1934
- * Returns true if the navigation elements are currently visible
1935
- * @return {boolean}
1936
- */
1937
- BookReader.prototype.navigationIsVisible = function() {
1938
- // $$$ doesn't account for transitioning states, nav must be fully visible to return true
1939
- var toolpos = this.refs.$BRtoolbar.position();
1940
- var tooltop = toolpos.top;
1941
- return tooltop == 0;
1942
- };
1943
-
1944
- /**
1945
- * Main controller that sets navigation into view.
1946
- * Defaults to SHOW the navigation chrome
1947
- */
1948
- BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
1949
- var animationLength = this.constNavAnimationDuration;
1950
- var animationType = 'linear';
1951
- var resizePageContainer = function resizePageContainer () {
1952
- /* main page container fills whole container */
1953
- if (this.constMode2up !== this.mode) {
1954
- var animate = true;
1955
- this.resizeBRcontainer(animate);
1956
- }
1957
- this.trigger(BookReader.eventNames.navToggled);
1958
- }.bind(this);
1959
-
1960
- var toolbarHeight = 0;
1961
- var navbarHeight = 0;
1962
- if (hide) {
1963
- toolbarHeight = this.getToolBarHeight() * -1;
1964
- navbarHeight = this.getFooterHeight() * -1;
1965
-
1966
- this.refs.$BRtoolbar.addClass('js-menu-hide');
1967
- this.refs.$BRfooter.addClass('js-menu-hide');
1968
- } else {
1969
- this.refs.$BRtoolbar.removeClass('js-menu-hide');
1970
- this.refs.$BRfooter.removeClass('js-menu-hide');
1971
- }
1972
-
1973
- this.refs.$BRtoolbar.animate(
1974
- { top: toolbarHeight },
1975
- animationLength,
1976
- animationType,
1977
- resizePageContainer
1978
- );
1979
- this.refs.$BRfooter.animate(
1980
- { bottom: navbarHeight },
1981
- animationLength,
1982
- animationType,
1983
- resizePageContainer
1984
- );
1985
- };
1986
- /**
1987
- * Hide navigation elements, if visible
1988
- */
1989
- BookReader.prototype.hideNavigation = function() {
1990
- // Check if navigation is showing
1991
- if (this.navigationIsVisible()) {
1992
- var hide = true;
1993
- this.setNavigationView(hide);
1994
- }
1995
- };
1996
-
1997
- /**
1998
- * Show navigation elements
1999
- */
2000
- BookReader.prototype.showNavigation = function() {
2001
- // Check if navigation is hidden
2002
- if (!this.navigationIsVisible()) {
2003
- this.setNavigationView();
2004
- }
2005
- };
2006
-
2007
- /**
2008
- * Returns the index of the first visible page, dependent on the mode.
2009
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2010
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2011
- * @return {number}
2012
- */
2013
- BookReader.prototype.firstDisplayableIndex = function() {
2014
- if (this.mode != this.constMode2up) {
2015
- return 0;
2016
- }
2017
-
2018
- if ('rl' != this.pageProgression) {
2019
- // LTR
2020
- if (this._models.book.getPageSide(0) == 'L') {
2021
- return 0;
2022
- } else {
2023
- return -1;
2024
- }
2025
- } else {
2026
- // RTL
2027
- if (this._models.book.getPageSide(0) == 'R') {
2028
- return 0;
2029
- } else {
2030
- return -1;
2031
- }
2032
- }
2033
- };
2034
-
2035
- /**
2036
- * Returns the index of the last visible page, dependent on the mode.
2037
- * $$$ Currently we cannot display the front/back cover in 2-up and will need to update
2038
- * this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
2039
- * @return {number}
2040
- */
2041
- BookReader.prototype.lastDisplayableIndex = function() {
2042
-
2043
- var lastIndex = this._models.book.getNumLeafs() - 1;
2044
-
2045
- if (this.mode != this.constMode2up) {
2046
- return lastIndex;
2047
- }
2048
-
2049
- if ('rl' != this.pageProgression) {
2050
- // LTR
2051
- if (this._models.book.getPageSide(lastIndex) == 'R') {
2052
- return lastIndex;
2053
- } else {
2054
- return lastIndex + 1;
2055
- }
2056
- } else {
2057
- // RTL
2058
- if (this._models.book.getPageSide(lastIndex) == 'L') {
2059
- return lastIndex;
2060
- } else {
2061
- return lastIndex + 1;
2062
- }
2063
- }
2064
- };
2065
-
2066
-
2067
1483
  /**************************/
2068
1484
  /** BookModel extensions **/
2069
1485
  /**************************/
2070
- /** @deprecated not used outside */
2071
- BookReader.prototype.getMedianPageSize = BookModel.prototype.getMedianPageSize;
2072
- exposeOverrideableMethod(BookModel, '_models.book', 'getMedianPageSize');
2073
- BookReader.prototype._getPageWidth = BookModel.prototype._getPageWidth;
2074
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageWidth');
2075
- BookReader.prototype._getPageHeight = BookModel.prototype._getPageHeight;
2076
- exposeOverrideableMethod(BookModel, '_models.book', '_getPageHeight');
2077
- BookReader.prototype.getPageIndex = BookModel.prototype.getPageIndex;
2078
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndex');
2079
- /** @deprecated not used outside */
2080
- BookReader.prototype.getPageIndices = BookModel.prototype.getPageIndices;
2081
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageIndices');
2082
- BookReader.prototype.getPageName = BookModel.prototype.getPageName;
2083
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageName');
2084
- BookReader.prototype.getNumLeafs = BookModel.prototype.getNumLeafs;
2085
- exposeOverrideableMethod(BookModel, '_models.book', 'getNumLeafs');
2086
- BookReader.prototype.getPageWidth = BookModel.prototype.getPageWidth;
2087
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageWidth');
2088
- BookReader.prototype.getPageHeight = BookModel.prototype.getPageHeight;
2089
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageHeight');
1486
+ // Must modify petabox extension, which expects this on the prototype
1487
+ // before removing.
2090
1488
  BookReader.prototype.getPageURI = BookModel.prototype.getPageURI;
2091
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageURI');
2092
- BookReader.prototype.getPageSide = BookModel.prototype.getPageSide;
2093
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageSide');
2094
- BookReader.prototype.getPageNum = BookModel.prototype.getPageNum;
2095
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageNum');
2096
- BookReader.prototype.getPageProp = BookModel.prototype.getPageProp;
2097
- exposeOverrideableMethod(BookModel, '_models.book', 'getPageProp');
2098
- BookReader.prototype.getSpreadIndices = BookModel.prototype.getSpreadIndices;
2099
- exposeOverrideableMethod(BookModel, '_models.book', 'getSpreadIndices');
2100
- BookReader.prototype.leafNumToIndex = BookModel.prototype.leafNumToIndex;
2101
- exposeOverrideableMethod(BookModel, '_models.book', 'leafNumToIndex');
2102
- BookReader.prototype.parsePageString = BookModel.prototype.parsePageString;
2103
- exposeOverrideableMethod(BookModel, '_models.book', 'parsePageString');
2104
- /** @deprecated unused */
2105
- BookReader.prototype._getDataFlattened = BookModel.prototype._getDataFlattened;
2106
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataFlattened');
2107
- /** @deprecated unused */
2108
- BookReader.prototype._getDataProp = BookModel.prototype._getDataProp;
2109
- exposeOverrideableMethod(BookModel, '_models.book', '_getDataProp');
1489
+ exposeOverrideableMethod(BookModel, 'book', 'getPageURI');
1490
+
2110
1491
 
2111
1492
  // Parameter related functions
2112
1493
 
@@ -2137,7 +1518,7 @@ BookReader.prototype.updateFromParams = function(params) {
2137
1518
  }
2138
1519
  } else if ('undefined' != typeof(params.page)) {
2139
1520
  // $$$ this assumes page numbers are unique
2140
- if (params.page != this._models.book.getPageNum(this.currentIndex())) {
1521
+ if (params.page != this.book.getPageNum(this.currentIndex())) {
2141
1522
  this.jumpToPage(params.page);
2142
1523
  }
2143
1524
  }
@@ -2171,7 +1552,7 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2171
1552
  // check there are enough pages to display
2172
1553
  // $$$ this is a workaround for the mis-feature that we can't display
2173
1554
  // short books in 2up mode
2174
- if (this._models.book.getNumLeafs() < 2) {
1555
+ if (this.book.getNumLeafs() < 2) {
2175
1556
  return false;
2176
1557
  }
2177
1558
  }
@@ -2179,31 +1560,6 @@ BookReader.prototype.canSwitchToMode = function(mode) {
2179
1560
  return true;
2180
1561
  };
2181
1562
 
2182
-
2183
- /**
2184
- * @deprecated. Use PageModel.getURISrcSet. Slated for removal in v5.
2185
- * Returns the srcset with correct URIs or void string if out of range
2186
- * Also makes the reduce argument optional
2187
- * @param {number} index
2188
- * @param {number} [reduce]
2189
- * @param {number} [rotate]
2190
- * @return {string}
2191
- */
2192
- BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2193
- const page = this._models.book.getPage(index, false);
2194
- // Synthesize page
2195
- if (!page) return "";
2196
-
2197
- // reduce not passed in
2198
- // $$$ this probably won't work for thumbnail mode
2199
- if ('undefined' == typeof(reduce)) {
2200
- reduce = page.height / this.twoPage.height;
2201
- }
2202
-
2203
- return page.getURISrcSet(reduce, rotate);
2204
- }
2205
-
2206
-
2207
1563
  /**
2208
1564
  * Returns the page URI or transparent image if out of range
2209
1565
  * Also makes the reduce argument optional
@@ -2213,7 +1569,7 @@ BookReader.prototype._getPageURISrcset = function(index, reduce, rotate) {
2213
1569
  * @return {string}
2214
1570
  */
2215
1571
  BookReader.prototype._getPageURI = function(index, reduce, rotate) {
2216
- const page = this._models.book.getPage(index, false);
1572
+ const page = this.book.getPage(index, false);
2217
1573
  // Synthesize page
2218
1574
  if (!page) return this.imagesBaseURL + "transparent.png";
2219
1575
 
@@ -2279,7 +1635,7 @@ BookReader.prototype.initUIStrings = function() {
2279
1635
  // the toolbar and nav bar easier
2280
1636
 
2281
1637
  // Setup tooltips -- later we could load these from a file for i18n
2282
- var titles = {
1638
+ const titles = {
2283
1639
  '.logo': 'Go to Archive.org', // $$$ update after getting OL record
2284
1640
  '.zoom_in': 'Zoom in',
2285
1641
  '.zoom_out': 'Zoom out',
@@ -2295,8 +1651,6 @@ BookReader.prototype.initUIStrings = function() {
2295
1651
  '.full': 'Toggle fullscreen',
2296
1652
  '.book_left': 'Flip left',
2297
1653
  '.book_right': 'Flip right',
2298
- '.book_up': 'Page up',
2299
- '.book_down': 'Page down',
2300
1654
  '.play': 'Play',
2301
1655
  '.pause': 'Pause',
2302
1656
  '.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
@@ -2311,10 +1665,10 @@ BookReader.prototype.initUIStrings = function() {
2311
1665
  titles['.book_rightmost'] = 'First page';
2312
1666
  }
2313
1667
 
2314
- for (var icon in titles) {
1668
+ for (const icon in titles) {
2315
1669
  this.$(icon).prop('title', titles[icon]);
2316
1670
  }
2317
- }
1671
+ };
2318
1672
 
2319
1673
  /**
2320
1674
  * Reloads images. Useful when some images might have failed.
@@ -2322,7 +1676,7 @@ BookReader.prototype.initUIStrings = function() {
2322
1676
  BookReader.prototype.reloadImages = function() {
2323
1677
  this.refs.$brContainer.find('img').each(function(index, elem) {
2324
1678
  if (!elem.complete || elem.naturalHeight === 0) {
2325
- var src = elem.src;
1679
+ const src = elem.src;
2326
1680
  elem.src = '';
2327
1681
  setTimeout(function() {
2328
1682
  elem.src = src;
@@ -2336,16 +1690,16 @@ BookReader.prototype.reloadImages = function() {
2336
1690
  * @return {number}
2337
1691
  */
2338
1692
  BookReader.prototype.getFooterHeight = function() {
2339
- 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;
2340
1694
  if ($heightEl && this.refs.$BRfooter) {
2341
- var outerHeight = $heightEl.outerHeight();
2342
- var bottom = parseInt(this.refs.$BRfooter.css('bottom'));
1695
+ const outerHeight = $heightEl.outerHeight();
1696
+ const bottom = parseInt(this.refs.$BRfooter.css('bottom'));
2343
1697
  if (!isNaN(outerHeight) && !isNaN(bottom)) {
2344
1698
  return outerHeight + bottom;
2345
1699
  }
2346
1700
  }
2347
1701
  return 0;
2348
- }
1702
+ };
2349
1703
 
2350
1704
  // Basic Usage built-in Methods (can be overridden through options)
2351
1705
  // This implementation uses options.data value for populating BookReader
@@ -2355,10 +1709,11 @@ BookReader.prototype.getFooterHeight = function() {
2355
1709
  * @return {Object}
2356
1710
  */
2357
1711
  BookReader.prototype.paramsFromCurrent = function() {
2358
- var params = {};
1712
+ const params = {};
2359
1713
 
2360
- var index = this.currentIndex();
2361
- var pageNum = this._models.book.getPageNum(index);
1714
+ // Path params
1715
+ const index = this.currentIndex();
1716
+ const pageNum = this.book.getPageNum(index);
2362
1717
  if ((pageNum === 0) || pageNum) {
2363
1718
  params.page = pageNum;
2364
1719
  }
@@ -2366,10 +1721,17 @@ BookReader.prototype.paramsFromCurrent = function() {
2366
1721
  params.index = index;
2367
1722
  params.mode = this.mode;
2368
1723
 
1724
+ // Unused params
2369
1725
  // $$$ highlight
2370
1726
  // $$$ region
2371
1727
 
2372
- // search
1728
+ // Querystring params
1729
+ // View
1730
+ const fullscreenView = 'theater';
1731
+ if (this.isFullscreenActive) {
1732
+ params.view = fullscreenView;
1733
+ }
1734
+ // Search
2373
1735
  if (this.enableSearch) {
2374
1736
  params.search = this.searchTerm;
2375
1737
  }
@@ -2390,7 +1752,7 @@ BookReader.prototype.paramsFromCurrent = function() {
2390
1752
  * @return {Object}
2391
1753
  */
2392
1754
  BookReader.prototype.paramsFromFragment = function(fragment) {
2393
- var params = {};
1755
+ const params = {};
2394
1756
 
2395
1757
  // For backwards compatibility we allow an initial # character
2396
1758
  // (as from window.location.hash) but don't require it
@@ -2399,7 +1761,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2399
1761
  }
2400
1762
 
2401
1763
  // Simple #nn syntax
2402
- var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
1764
+ const oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
2403
1765
  if ( !isNaN(oldStyleLeafNum) ) {
2404
1766
  params.index = oldStyleLeafNum;
2405
1767
 
@@ -2408,9 +1770,9 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2408
1770
  }
2409
1771
 
2410
1772
  // Split into key-value pairs
2411
- var urlArray = fragment.split('/');
2412
- var urlHash = {};
2413
- 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) {
2414
1776
  urlHash[urlArray[i]] = urlArray[i + 1];
2415
1777
  }
2416
1778
 
@@ -2440,7 +1802,7 @@ BookReader.prototype.paramsFromFragment = function(fragment) {
2440
1802
 
2441
1803
  // $$$ process /theme
2442
1804
  if (urlHash['theme'] != undefined) {
2443
- params.theme = urlHash['theme']
1805
+ params.theme = urlHash['theme'];
2444
1806
  }
2445
1807
  return params;
2446
1808
  };
@@ -2497,6 +1859,9 @@ BookReader.prototype.fragmentFromParams = function(params, urlMode = 'hash') {
2497
1859
  /**
2498
1860
  * Create, update querystring from the params object
2499
1861
  *
1862
+ * Handles:
1863
+ * view=
1864
+ * q=
2500
1865
  * @param {Object} params
2501
1866
  * @param {string} currQueryString
2502
1867
  * @param {string} [urlMode]
@@ -2508,27 +1873,29 @@ BookReader.prototype.queryStringFromParams = function(
2508
1873
  urlMode = 'hash'
2509
1874
  ) {
2510
1875
  const newParams = new URLSearchParams(currQueryString);
1876
+
1877
+ if (params.view) {
1878
+ // Set ?view=theater when fullscreen
1879
+ newParams.set('view', params.view);
1880
+ } else {
1881
+ // Remove
1882
+ newParams.delete('view');
1883
+ }
1884
+
2511
1885
  if (params.search && urlMode === 'history') {
2512
- newParams.set('q', params.search)
1886
+ newParams.set('q', params.search);
2513
1887
  }
2514
1888
  // https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/toString
2515
1889
  // Note: This method returns the query string without the question mark.
2516
1890
  const result = newParams.toString();
2517
1891
  return result ? '?' + result : '';
2518
- }
1892
+ };
2519
1893
 
2520
1894
  /**
2521
1895
  * Helper to select within instance's elements
2522
1896
  */
2523
1897
  BookReader.prototype.$ = function(selector) {
2524
1898
  return this.refs.$br.find(selector);
2525
- }
2526
-
2527
- /**
2528
- * Polyfill for deprecated method
2529
- */
2530
- jQuery.curCSS = function(element, prop, val) {
2531
- return jQuery(element).css(prop, val);
2532
1899
  };
2533
1900
 
2534
1901
  window.BookReader = BookReader;