@internetarchive/bookreader 5.0.0-7-multiple-files → 5.0.0-70

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