@internetarchive/bookreader 5.0.0-9-multiple-files → 5.0.0-90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. package/.eslintrc.js +21 -19
  2. package/.github/workflows/node.js.yml +81 -7
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +505 -1442
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +20 -20
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1782 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
  11. package/BookReader/ia-bookreader-bundle.js.map +1 -0
  12. package/BookReader/icons/1up.svg +1 -12
  13. package/BookReader/icons/2up.svg +1 -15
  14. package/BookReader/icons/advance.svg +3 -26
  15. package/BookReader/icons/chevron-right.svg +1 -1
  16. package/BookReader/icons/close-circle-dark.svg +1 -0
  17. package/BookReader/icons/close-circle.svg +1 -1
  18. package/BookReader/icons/fullscreen.svg +1 -17
  19. package/BookReader/icons/fullscreen_exit.svg +1 -17
  20. package/BookReader/icons/hamburger.svg +1 -15
  21. package/BookReader/icons/left-arrow.svg +1 -12
  22. package/BookReader/icons/magnify-minus.svg +1 -16
  23. package/BookReader/icons/magnify-plus.svg +1 -17
  24. package/BookReader/icons/magnify.svg +1 -15
  25. package/BookReader/icons/pause.svg +1 -23
  26. package/BookReader/icons/play.svg +1 -22
  27. package/BookReader/icons/playback-speed.svg +1 -34
  28. package/BookReader/icons/read-aloud.svg +1 -22
  29. package/BookReader/icons/review.svg +3 -22
  30. package/BookReader/icons/thumbnails.svg +1 -17
  31. package/BookReader/icons/voice.svg +1 -0
  32. package/BookReader/icons/volume-full.svg +1 -22
  33. package/BookReader/images/BRicons.svg +5 -94
  34. package/BookReader/images/books_graphic.svg +1 -177
  35. package/BookReader/images/icon_book.svg +1 -12
  36. package/BookReader/images/icon_bookmark.svg +1 -12
  37. package/BookReader/images/icon_gear.svg +1 -14
  38. package/BookReader/images/icon_hamburger.svg +1 -20
  39. package/BookReader/images/icon_home.svg +1 -21
  40. package/BookReader/images/icon_info.svg +1 -11
  41. package/BookReader/images/icon_one_page.svg +1 -8
  42. package/BookReader/images/icon_pause.svg +1 -1
  43. package/BookReader/images/icon_play.svg +1 -1
  44. package/BookReader/images/icon_playback-rate.svg +1 -15
  45. package/BookReader/images/icon_search_button.svg +1 -8
  46. package/BookReader/images/icon_share.svg +1 -9
  47. package/BookReader/images/icon_skip-ahead.svg +1 -6
  48. package/BookReader/images/icon_skip-back.svg +2 -13
  49. package/BookReader/images/icon_speaker.svg +1 -18
  50. package/BookReader/images/icon_speaker_open.svg +1 -10
  51. package/BookReader/images/icon_thumbnails.svg +1 -12
  52. package/BookReader/images/icon_toc.svg +1 -5
  53. package/BookReader/images/icon_two_pages.svg +1 -9
  54. package/BookReader/images/marker_chap-off.svg +1 -11
  55. package/BookReader/images/marker_chap-on.svg +1 -11
  56. package/BookReader/images/marker_srch-on.svg +1 -11
  57. package/BookReader/images/unviewable_page.png +0 -0
  58. package/BookReader/jquery-3.js +2 -0
  59. package/BookReader/jquery-3.js.LICENSE.txt +24 -0
  60. package/BookReader/plugins/plugin.archive_analytics.js +1 -172
  61. package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
  62. package/BookReader/plugins/plugin.autoplay.js +1 -165
  63. package/BookReader/plugins/plugin.autoplay.js.map +1 -1
  64. package/BookReader/plugins/plugin.chapters.js +22 -301
  65. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
  66. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  67. package/BookReader/plugins/plugin.iframe.js +1 -74
  68. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  69. package/BookReader/plugins/plugin.iiif.js +2 -0
  70. package/BookReader/plugins/plugin.iiif.js.map +1 -0
  71. package/BookReader/plugins/plugin.resume.js +1 -368
  72. package/BookReader/plugins/plugin.resume.js.map +1 -1
  73. package/BookReader/plugins/plugin.search.js +2 -1420
  74. package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
  75. package/BookReader/plugins/plugin.search.js.map +1 -1
  76. package/BookReader/plugins/plugin.text_selection.js +2 -1080
  77. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
  78. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  79. package/BookReader/plugins/plugin.tts.js +2 -9193
  80. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
  81. package/BookReader/plugins/plugin.tts.js.map +1 -1
  82. package/BookReader/plugins/plugin.url.js +1 -269
  83. package/BookReader/plugins/plugin.url.js.map +1 -1
  84. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  85. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  86. package/BookReader/webcomponents-bundle.js +3 -0
  87. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  88. package/BookReader/webcomponents-bundle.js.map +1 -0
  89. package/BookReaderDemo/BookReaderDemo.css +18 -19
  90. package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
  91. package/BookReaderDemo/BookReaderJSSimple.js +1 -0
  92. package/BookReaderDemo/IADemoBr.js +144 -0
  93. package/BookReaderDemo/demo-advanced.html +2 -2
  94. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  95. package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
  96. package/BookReaderDemo/demo-fullscreen.html +2 -4
  97. package/BookReaderDemo/demo-iiif.html +99 -12
  98. package/BookReaderDemo/demo-internetarchive.html +214 -18
  99. package/BookReaderDemo/demo-multiple.html +2 -1
  100. package/BookReaderDemo/demo-preview-pages.html +526 -525
  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 +595 -33
  110. package/README.md +14 -1
  111. package/babel.config.js +20 -0
  112. package/codecov.yml +6 -0
  113. package/index.html +5 -2
  114. package/jsconfig.json +19 -0
  115. package/netlify.toml +9 -0
  116. package/package.json +70 -62
  117. package/renovate.json +52 -0
  118. package/scripts/preversion.js +0 -1
  119. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  120. package/src/BookNavigator/assets/button-base.js +10 -2
  121. package/src/BookNavigator/assets/ia-logo.js +17 -0
  122. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  123. package/src/BookNavigator/assets/icon_close.js +1 -1
  124. package/src/BookNavigator/book-navigator.js +590 -0
  125. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  126. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  127. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  128. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +4 -9
  129. package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
  130. package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
  131. package/src/BookNavigator/delete-modal-actions.js +1 -1
  132. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  133. package/src/BookNavigator/downloads/downloads.js +29 -25
  134. package/src/BookNavigator/search/search-provider.js +80 -28
  135. package/src/BookNavigator/search/search-results.js +29 -26
  136. package/src/BookNavigator/sharing.js +27 -0
  137. package/src/BookNavigator/viewable-files.js +95 -0
  138. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +13 -12
  139. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +7 -7
  140. package/src/BookReader/BookModel.js +76 -41
  141. package/src/BookReader/DragScrollable.js +233 -0
  142. package/src/BookReader/ImageCache.js +48 -15
  143. package/src/BookReader/Mode1Up.js +56 -351
  144. package/src/BookReader/Mode1UpLit.js +388 -0
  145. package/src/BookReader/Mode2Up.js +73 -1318
  146. package/src/BookReader/Mode2UpLit.js +777 -0
  147. package/src/BookReader/ModeCoordinateSpace.js +29 -0
  148. package/src/BookReader/ModeSmoothZoom.js +312 -0
  149. package/src/BookReader/ModeThumb.js +19 -13
  150. package/src/BookReader/Navbar/Navbar.js +70 -54
  151. package/src/BookReader/PageContainer.js +116 -22
  152. package/src/BookReader/ReduceSet.js +3 -3
  153. package/src/BookReader/Toolbar/Toolbar.js +14 -41
  154. package/src/BookReader/events.js +2 -3
  155. package/src/BookReader/options.js +73 -15
  156. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  157. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  158. package/src/BookReader/utils/SelectionObserver.js +45 -0
  159. package/src/BookReader/utils/classes.js +1 -1
  160. package/src/BookReader/utils.js +128 -13
  161. package/src/BookReader.js +562 -1078
  162. package/src/BookReaderPlugin.js +44 -0
  163. package/src/assets/icons/close-circle-dark.svg +1 -0
  164. package/src/assets/icons/magnify-minus.svg +3 -7
  165. package/src/assets/icons/magnify-plus.svg +3 -7
  166. package/src/assets/icons/voice.svg +1 -0
  167. package/src/assets/images/unviewable_page.png +0 -0
  168. package/src/css/BookReader.scss +1 -17
  169. package/src/css/_BRBookmarks.scss +1 -1
  170. package/src/css/_BRComponent.scss +1 -1
  171. package/src/css/_BRicon.scss +8 -2
  172. package/src/css/_BRmain.scss +33 -27
  173. package/src/css/_BRnav.scss +12 -42
  174. package/src/css/_BRpages.scss +170 -42
  175. package/src/css/_BRsearch.scss +68 -230
  176. package/src/css/_BRtoolbar.scss +5 -5
  177. package/src/css/_TextSelection.scss +87 -27
  178. package/src/css/_colorbox.scss +2 -2
  179. package/src/css/_controls.scss +24 -7
  180. package/src/css/_icons.scss +7 -1
  181. package/src/ia-bookreader/ia-bookreader.js +224 -0
  182. package/src/plugins/plugin.archive_analytics.js +84 -78
  183. package/src/plugins/plugin.autoplay.js +99 -104
  184. package/src/plugins/plugin.chapters.js +237 -191
  185. package/src/plugins/plugin.iframe.js +1 -1
  186. package/src/plugins/plugin.iiif.js +141 -0
  187. package/src/plugins/plugin.resume.js +53 -50
  188. package/src/plugins/plugin.text_selection.js +503 -175
  189. package/src/plugins/plugin.vendor-fullscreen.js +7 -7
  190. package/src/plugins/search/plugin.search.js +183 -121
  191. package/src/plugins/search/utils.js +43 -0
  192. package/src/plugins/search/view.js +67 -202
  193. package/src/plugins/tts/AbstractTTSEngine.js +75 -45
  194. package/src/plugins/tts/FestivalTTSEngine.js +21 -31
  195. package/src/plugins/tts/PageChunk.js +16 -23
  196. package/src/plugins/tts/PageChunkIterator.js +11 -17
  197. package/src/plugins/tts/WebTTSEngine.js +88 -72
  198. package/src/plugins/tts/plugin.tts.js +310 -350
  199. package/src/plugins/tts/utils.js +16 -26
  200. package/src/plugins/url/UrlPlugin.js +191 -0
  201. package/src/plugins/{plugin.url.js → url/plugin.url.js} +47 -18
  202. package/src/util/browserSniffing.js +22 -0
  203. package/src/util/docCookies.js +21 -2
  204. package/src/util/strings.js +1 -0
  205. package/tests/e2e/README.md +37 -0
  206. package/tests/e2e/autoplay.test.js +9 -6
  207. package/tests/e2e/base.test.js +8 -16
  208. package/tests/e2e/helpers/base.js +55 -50
  209. package/tests/e2e/helpers/debug.js +1 -1
  210. package/tests/e2e/helpers/mockSearch.js +19 -22
  211. package/tests/e2e/helpers/params.js +17 -0
  212. package/tests/e2e/helpers/rightToLeft.js +8 -14
  213. package/tests/e2e/helpers/search.js +73 -0
  214. package/tests/e2e/models/Navigation.js +20 -37
  215. package/tests/e2e/rightToLeft.test.js +4 -5
  216. package/tests/e2e/viewmode.test.js +40 -33
  217. package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
  218. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  219. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  220. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  221. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  222. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  223. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  224. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  225. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +109 -60
  226. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  227. package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
  228. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  229. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
  230. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
  231. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  232. package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
  233. package/tests/jest/BookReader/Mode2Up.test.js +98 -0
  234. package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
  235. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
  236. package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
  237. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  238. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +42 -29
  239. package/tests/jest/BookReader/PageContainer.test.js +238 -0
  240. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  241. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +3 -3
  242. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  243. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  244. package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
  245. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  246. package/tests/jest/BookReader/utils.test.js +250 -0
  247. package/tests/jest/BookReader.keyboard.test.js +190 -0
  248. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +10 -2
  249. package/tests/{BookReader.test.js → jest/BookReader.test.js} +43 -53
  250. package/tests/jest/plugins/plugin.archive_analytics.test.js +20 -0
  251. package/tests/jest/plugins/plugin.autoplay.test.js +35 -0
  252. package/tests/jest/plugins/plugin.chapters.test.js +195 -0
  253. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +4 -4
  254. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +22 -35
  255. package/tests/jest/plugins/plugin.text_selection.test.js +316 -0
  256. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  257. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +26 -47
  258. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +42 -9
  259. package/tests/jest/plugins/search/utils.js +25 -0
  260. package/tests/jest/plugins/search/utils.test.js +29 -0
  261. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +30 -10
  262. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  263. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  264. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  265. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
  266. package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
  267. package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
  268. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +57 -18
  269. package/tests/jest/setup.js +3 -0
  270. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  271. package/tests/jest/util/docCookies.test.js +24 -0
  272. package/tests/{util → jest/util}/strings.test.js +1 -1
  273. package/tests/{utils.js → jest/utils.js} +38 -0
  274. package/webpack.config.js +16 -10
  275. package/.babelrc +0 -12
  276. package/.dependabot/config.yml +0 -6
  277. package/.testcaferc.json +0 -5
  278. package/BookReader/bookreader-component-bundle.js +0 -14330
  279. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  280. package/BookReader/bookreader-component-bundle.js.map +0 -1
  281. package/BookReader/icons/sort-ascending.svg +0 -1
  282. package/BookReader/icons/sort-descending.svg +0 -1
  283. package/BookReader/jquery-1.10.1.js +0 -108
  284. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  285. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  286. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  287. package/BookReader/plugins/plugin.mobile_nav.js +0 -335
  288. package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
  289. package/BookReaderDemo/BookReaderJSAutoplay.js +0 -56
  290. package/BookReaderDemo/IIIFBookReader.js +0 -207
  291. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  292. package/BookReaderDemo/demo-autoplay.html +0 -38
  293. package/BookReaderDemo/demo-iiif.js +0 -26
  294. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  295. package/karma.conf.js +0 -23
  296. package/src/BookNavigator/BookModel.js +0 -14
  297. package/src/BookNavigator/BookNavigator.js +0 -452
  298. package/src/BookNavigator/assets/book-loader.js +0 -27
  299. package/src/BookNavigator/assets/icon_sort_ascending.js +0 -5
  300. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  301. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  302. package/src/BookNavigator/search/a-search-result.js +0 -55
  303. package/src/BookNavigator/volumes/volumes-provider.js +0 -108
  304. package/src/BookNavigator/volumes/volumes.js +0 -162
  305. package/src/BookReader/DebugConsole.js +0 -54
  306. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  307. package/src/ItemNavigator/ItemNavigator.js +0 -372
  308. package/src/ItemNavigator/providers/sharing.js +0 -29
  309. package/src/assets/icons/sort-ascending.svg +0 -1
  310. package/src/assets/icons/sort-descending.svg +0 -1
  311. package/src/css/_MobileNav.scss +0 -194
  312. package/src/dragscrollable-br.js +0 -261
  313. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  314. package/src/plugins/plugin.mobile_nav.js +0 -287
  315. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  316. package/tests/BookReader/DebugConsole.test.js +0 -25
  317. package/tests/BookReader/Mode1Up.test.js +0 -164
  318. package/tests/BookReader/Mode2Up.test.js +0 -247
  319. package/tests/BookReader/PageContainer.test.js +0 -115
  320. package/tests/BookReader/utils.test.js +0 -109
  321. package/tests/e2e/helpers/desktopSearch.js +0 -72
  322. package/tests/e2e/helpers/mobileSearch.js +0 -85
  323. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  324. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  325. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  326. package/tests/karma/BookNavigator/volumes.test.js +0 -133
  327. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
  328. package/tests/plugins/plugin.archive_analytics.test.js +0 -23
  329. package/tests/plugins/plugin.autoplay.test.js +0 -52
  330. package/tests/plugins/plugin.chapters.test.js +0 -130
  331. package/tests/plugins/plugin.mobile_nav.test.js +0 -66
  332. package/tests/plugins/plugin.text_selection.test.js +0 -203
  333. package/tests/util/docCookies.test.js +0 -15
@@ -0,0 +1,29 @@
1
+ import { calcScreenDPI } from './utils';
2
+
3
+ /**
4
+ * There are a few different "coordinate spaces" at play in BR:
5
+ * (1) World units: i.e. inches. Unless otherwise stated, all computations
6
+ * are done in world units.
7
+ * (2) Rendered Pixels: i.e. img.width = '300'. Note this does _not_ take
8
+ * into account zoom scaling.
9
+ * (3) Visible Pixels: Just rendered pixels, but taking into account scaling.
10
+ */
11
+ export class ModeCoordinateSpace {
12
+ screenDPI = calcScreenDPI();
13
+
14
+ /**
15
+ * @param {{ scale: number }} mode
16
+ */
17
+ constructor(mode) {
18
+ this.mode = mode;
19
+ }
20
+
21
+ worldUnitsToRenderedPixels = (/** @type {number} */inches) => inches * this.screenDPI;
22
+ renderedPixelsToWorldUnits = (/** @type {number} */px) => px / this.screenDPI;
23
+
24
+ renderedPixelsToVisiblePixels = (/** @type {number} */px) => px * this.mode.scale;
25
+ visiblePixelsToRenderedPixels = (/** @type {number} */px) => px / this.mode.scale;
26
+
27
+ worldUnitsToVisiblePixels = (/** @type {number} */px) => this.renderedPixelsToVisiblePixels(this.worldUnitsToRenderedPixels(px));
28
+ visiblePixelsToWorldUnits = (/** @type {number} */px) => this.renderedPixelsToWorldUnits(this.visiblePixelsToRenderedPixels(px));
29
+ }
@@ -0,0 +1,312 @@
1
+ // @ts-check
2
+ import interact from 'interactjs';
3
+ import { isIOS, isSamsungInternet } from '../util/browserSniffing.js';
4
+ import { sleep } from './utils.js';
5
+ /** @typedef {import('./utils/HTMLDimensionsCacher.js').HTMLDimensionsCacher} HTMLDimensionsCacher */
6
+
7
+ /**
8
+ * @typedef {object} SmoothZoomable
9
+ * @property {HTMLElement} $container
10
+ * @property {HTMLElement} $visibleWorld
11
+ * @property {import("./options.js").AutoFitValues} autoFit
12
+ * @property {number} scale
13
+ * @property {HTMLDimensionsCacher} htmlDimensionsCacher
14
+ * @property {function(): void} [attachScrollListeners]
15
+ * @property {function(): void} [detachScrollListeners]
16
+ */
17
+
18
+ /** Manages pinch-zoom, ctrl-wheel, and trackpad pinch smooth zooming. */
19
+ export class ModeSmoothZoom {
20
+ /** Position (in unit-less, [0, 1] coordinates) in client to scale around */
21
+ scaleCenter = { x: 0.5, y: 0.5 };
22
+
23
+ /** @param {SmoothZoomable} mode */
24
+ constructor(mode) {
25
+ /** @type {SmoothZoomable} */
26
+ this.mode = mode;
27
+
28
+ /** Whether a pinch is currently happening */
29
+ this.pinching = false;
30
+ /** Non-null when a scale has been enqueued/is being processed by the buffer function */
31
+ this.pinchMoveFrame = null;
32
+ /** Promise for the current/enqueued pinch move frame. Resolves when it is complete. */
33
+ this.pinchMoveFramePromise = Promise.resolve();
34
+ this.oldScale = 1;
35
+ /** @type {{ scale: number, clientX: number, clientY: number }}} */
36
+ this.lastEvent = null;
37
+ this.attached = false;
38
+
39
+ /** @type {function(function(): void): any} */
40
+ this.bufferFn = window.requestAnimationFrame.bind(window);
41
+ }
42
+
43
+ attach() {
44
+ if (this.attached) return;
45
+
46
+ this.attachCtrlZoom();
47
+
48
+ // GestureEvents work only on Safari; they're too glitchy to use
49
+ // fully, but they can sometimes help error correct when interact
50
+ // misses an end/start event on Safari due to Safari bugs.
51
+ this.mode.$container.addEventListener('gesturestart', this._pinchStart);
52
+ this.mode.$container.addEventListener('gesturechange', this._preventEvent);
53
+ this.mode.$container.addEventListener('gestureend', this._pinchEnd);
54
+
55
+ if (isIOS()) {
56
+ this.touchesMonitor = new TouchesMonitor(this.mode.$container);
57
+ this.touchesMonitor.attach();
58
+ }
59
+
60
+ this.mode.$container.style.touchAction = "pan-x pan-y";
61
+
62
+ // The pinch listeners
63
+ this.interact = interact(this.mode.$container);
64
+ this.interact.gesturable({
65
+ listeners: {
66
+ start: this._pinchStart,
67
+ end: this._pinchEnd,
68
+ },
69
+ });
70
+ if (isSamsungInternet()) {
71
+ // Samsung internet pinch-zoom will not work unless we disable
72
+ // all touch actions. So use interact.js' built-in drag support
73
+ // to handle moving on that browser.
74
+ this.mode.$container.style.touchAction = "none";
75
+ this.interact
76
+ .draggable({
77
+ inertia: {
78
+ resistance: 2,
79
+ minSpeed: 100,
80
+ allowResume: true,
81
+ },
82
+ listeners: { move: this._dragMove },
83
+ });
84
+ }
85
+
86
+
87
+ this.attached = true;
88
+ }
89
+
90
+ detach() {
91
+ this.detachCtrlZoom();
92
+
93
+ // GestureEvents work only on Safari; they interfere with Hammer,
94
+ // so block them.
95
+ this.mode.$container.removeEventListener('gesturestart', this._pinchStart);
96
+ this.mode.$container.removeEventListener('gesturechange', this._preventEvent);
97
+ this.mode.$container.removeEventListener('gestureend', this._pinchEnd);
98
+
99
+ this.touchesMonitor?.detach?.();
100
+
101
+ // The pinch listeners
102
+ this.interact.unset();
103
+ interact.removeDocument(document);
104
+
105
+ this.attached = false;
106
+ }
107
+
108
+ /** @param {Event} ev */
109
+ _preventEvent = (ev) => {
110
+ ev.preventDefault();
111
+ return false;
112
+ }
113
+
114
+ _pinchStart = async () => {
115
+ // Safari calls gesturestart twice!
116
+ if (this.pinching) return;
117
+ if (isIOS()) {
118
+ // Safari sometimes causes a pinch to trigger when there's only one touch!
119
+ await sleep(0); // touches monitor can receive the touch event late
120
+ if (this.touchesMonitor.touches < 2) return;
121
+ }
122
+ this.pinching = true;
123
+
124
+ // Do this in case the pinchend hasn't fired yet.
125
+ this.oldScale = 1;
126
+ this.mode.$visibleWorld.classList.add("BRsmooth-zooming");
127
+ this.mode.$visibleWorld.style.willChange = "transform";
128
+ this.mode.autoFit = "none";
129
+ this.detachCtrlZoom();
130
+ this.mode.detachScrollListeners?.();
131
+
132
+ this.interact.gesturable({
133
+ listeners: {
134
+ start: this._pinchStart,
135
+ move: this._pinchMove,
136
+ end: this._pinchEnd,
137
+ },
138
+ });
139
+ }
140
+
141
+ /** @param {{ scale: number, clientX: number, clientY: number }}} e */
142
+ _pinchMove = async (e) => {
143
+ if (!this.pinching) return;
144
+ this.lastEvent = {
145
+ scale: e.scale,
146
+ clientX: e.clientX,
147
+ clientY: e.clientY,
148
+ };
149
+ if (!this.pinchMoveFrame) {
150
+ // Buffer these events; only update the scale when request animation fires
151
+ this.pinchMoveFrame = this.bufferFn(this._drawPinchZoomFrame);
152
+ }
153
+ }
154
+
155
+ _pinchEnd = async () => {
156
+ if (!this.pinching) return;
157
+ this.pinching = false;
158
+ this.interact.gesturable({
159
+ listeners: {
160
+ start: this._pinchStart,
161
+ end: this._pinchEnd,
162
+ },
163
+ });
164
+ // Want this to happen after the pinchMoveFrame,
165
+ // if one is in progress; otherwise setting oldScale
166
+ // messes up the transform.
167
+ await this.pinchMoveFramePromise;
168
+ this.scaleCenter = { x: 0.5, y: 0.5 };
169
+ this.oldScale = 1;
170
+ this.mode.$visibleWorld.classList.remove("BRsmooth-zooming");
171
+ this.mode.$visibleWorld.style.willChange = "auto";
172
+ this.attachCtrlZoom();
173
+ this.mode.attachScrollListeners?.();
174
+ }
175
+
176
+ _drawPinchZoomFrame = async () => {
177
+ // Because of the buffering/various timing locks,
178
+ // this can be called after the pinch has ended, which
179
+ // results in a janky zoom after the pinch.
180
+ if (!this.pinching) {
181
+ this.pinchMoveFrame = null;
182
+ return;
183
+ }
184
+
185
+ this.mode.$container.style.overflow = "hidden";
186
+ this.pinchMoveFramePromiseRes = null;
187
+ this.pinchMoveFramePromise = new Promise(
188
+ (res) => (this.pinchMoveFramePromiseRes = res),
189
+ );
190
+ this.updateScaleCenter({
191
+ clientX: this.lastEvent.clientX,
192
+ clientY: this.lastEvent.clientY,
193
+ });
194
+ const curScale = this.mode.scale;
195
+ const newScale = curScale * this.lastEvent.scale / this.oldScale;
196
+
197
+ if (curScale != newScale) {
198
+ this.mode.scale = newScale;
199
+ await this.pinchMoveFramePromise;
200
+ }
201
+ this.mode.$container.style.overflow = "auto";
202
+ this.oldScale = this.lastEvent.scale;
203
+ this.pinchMoveFrame = null;
204
+ }
205
+
206
+ _dragMove = async (e) => {
207
+ if (this.pinching) {
208
+ await this._pinchEnd();
209
+ }
210
+ this.mode.$container.scrollTop -= e.dy;
211
+ this.mode.$container.scrollLeft -= e.dx;
212
+ }
213
+
214
+ /** @private */
215
+ attachCtrlZoom() {
216
+ window.addEventListener("wheel", this._handleCtrlWheel, { passive: false });
217
+ }
218
+
219
+ /** @private */
220
+ detachCtrlZoom() {
221
+ window.removeEventListener("wheel", this._handleCtrlWheel);
222
+ }
223
+
224
+ /** @param {WheelEvent} ev **/
225
+ _handleCtrlWheel = (ev) => {
226
+ if (!ev.ctrlKey) return;
227
+ ev.preventDefault();
228
+ const zoomMultiplier =
229
+ // Zooming on macs was painfully slow; likely due to their better
230
+ // trackpads. Give them a higher zoom rate.
231
+ /Mac/i.test(navigator.platform)
232
+ ? 0.045
233
+ : // This worked well for me on Windows
234
+ 0.03;
235
+
236
+ // Zoom around the cursor
237
+ this.updateScaleCenter(ev);
238
+ this.mode.autoFit = "none";
239
+ this.mode.scale *= 1 - Math.sign(ev.deltaY) * zoomMultiplier;
240
+ }
241
+
242
+ /**
243
+ * @param {object} param0
244
+ * @param {number} param0.clientX
245
+ * @param {number} param0.clientY
246
+ */
247
+ updateScaleCenter({ clientX, clientY }) {
248
+ const bc = this.mode.htmlDimensionsCacher.boundingClientRect;
249
+ this.scaleCenter = {
250
+ x: (clientX - bc.left) / this.mode.htmlDimensionsCacher.clientWidth,
251
+ y: (clientY - bc.top) / this.mode.htmlDimensionsCacher.clientHeight,
252
+ };
253
+ }
254
+
255
+ /**
256
+ * @param {number} newScale
257
+ * @param {number} oldScale
258
+ */
259
+ updateViewportOnZoom(newScale, oldScale) {
260
+ const container = this.mode.$container;
261
+ const { scrollTop: T, scrollLeft: L } = container;
262
+ const W = this.mode.htmlDimensionsCacher.clientWidth;
263
+ const H = this.mode.htmlDimensionsCacher.clientHeight;
264
+
265
+ // Scale factor change
266
+ const F = newScale / oldScale;
267
+
268
+ // Where in the viewport the zoom is centered on
269
+ const XPOS = this.scaleCenter.x;
270
+ const YPOS = this.scaleCenter.y;
271
+ const oldCenter = {
272
+ x: L + XPOS * W,
273
+ y: T + YPOS * H,
274
+ };
275
+ const newCenter = {
276
+ x: F * oldCenter.x,
277
+ y: F * oldCenter.y,
278
+ };
279
+
280
+ container.scrollTop = newCenter.y - YPOS * H;
281
+ container.scrollLeft = newCenter.x - XPOS * W;
282
+ this.pinchMoveFramePromiseRes?.();
283
+ }
284
+ }
285
+
286
+ export class TouchesMonitor {
287
+ /**
288
+ * @param {HTMLElement} container
289
+ */
290
+ constructor(container) {
291
+ /** @type {HTMLElement} */
292
+ this.container = container;
293
+ this.touches = 0;
294
+ }
295
+
296
+ attach() {
297
+ this.container.addEventListener("touchstart", this._updateTouchCount);
298
+ this.container.addEventListener("touchend", this._updateTouchCount);
299
+ }
300
+
301
+ detach() {
302
+ this.container.removeEventListener("touchstart", this._updateTouchCount);
303
+ this.container.removeEventListener("touchend", this._updateTouchCount);
304
+ }
305
+
306
+ /**
307
+ * @param {TouchEvent} ev
308
+ */
309
+ _updateTouchCount = (ev) => {
310
+ this.touches = ev.touches.length;
311
+ }
312
+ }
@@ -1,6 +1,7 @@
1
1
  // @ts-check
2
2
  import { notInArray, clamp } from './utils.js';
3
3
  import { EVENTS } from './events.js';
4
+ import { DragScrollable } from './DragScrollable.js';
4
5
  /** @typedef {import('../BookREader.js').default} BookReader */
5
6
  /** @typedef {import('./BookModel.js').PageIndex} PageIndex */
6
7
  /** @typedef {import('./BookModel.js').BookModel} BookModel */
@@ -131,7 +132,7 @@ export class ModeThumb {
131
132
  for (let i = 1; i < this.br.thumbRowBuffer; i++) {
132
133
  if (firstRow - i >= 0) { rowsToDisplay.push(firstRow - i); }
133
134
  }
134
- rowsToDisplay.sort();
135
+ rowsToDisplay.sort((a, b) => a - b);
135
136
 
136
137
  // Create the thumbnail divs and images (lazy loaded)
137
138
  for (const row of rowsToDisplay) {
@@ -176,7 +177,7 @@ export class ModeThumb {
176
177
  // shift viewModeOrder after clicking on thumbsnail leaf
177
178
  const nextModeID = this.br.viewModeOrder.shift();
178
179
  this.br.viewModeOrder.push(nextModeID);
179
- this.br.updateViewModeButton($('.viewmode'), 'twopg', 'Two-page view');
180
+ this.br._components.navbar.updateViewModeButton($('.viewmode'), 'twopg', 'Two-page view');
180
181
 
181
182
  this.br.trigger(EVENTS.fragmentChange);
182
183
  event.stopPropagation();
@@ -214,8 +215,6 @@ export class ModeThumb {
214
215
 
215
216
  // highlight current page
216
217
  this.br.$('.pagediv' + this.br.currentIndex()).addClass('BRpagedivthumb_highlight');
217
-
218
- this.br.updateToolbarZoom(this.br.reduce);
219
218
  }
220
219
 
221
220
  /**
@@ -234,20 +233,27 @@ export class ModeThumb {
234
233
  }
235
234
 
236
235
  /**
237
- * @param {1 | -1} direction
236
+ * @param {'in' | 'out'} direction
238
237
  */
239
238
  zoom(direction) {
240
239
  const oldColumns = this.br.thumbColumns;
241
240
  switch (direction) {
242
- case -1:
243
- this.br.thumbColumns += 1;
244
- break;
245
- case 1:
241
+ case 'in':
246
242
  this.br.thumbColumns -= 1;
247
243
  break;
244
+ case 'out':
245
+ this.br.thumbColumns += 1;
246
+ break;
247
+ default:
248
+ console.error(`Unsupported direction: ${direction}`);
248
249
  }
249
250
 
250
- this.br.thumbColumns = clamp(this.br.thumbColumns, 2, 8);
251
+ // Limit zoom in/out columns
252
+ this.br.thumbColumns = clamp(
253
+ this.br.thumbColumns,
254
+ this.br.options.thumbMinZoomColumns,
255
+ this.br.options.thumbMaxZoomColumns,
256
+ );
251
257
 
252
258
  if (this.br.thumbColumns != oldColumns) {
253
259
  this.br.displayedRows = []; /* force a gallery redraw */
@@ -274,12 +280,12 @@ export class ModeThumb {
274
280
  this.br.refs.$brContainer.empty();
275
281
  this.br.refs.$brContainer.css({
276
282
  overflowY: 'scroll',
277
- overflowX: 'auto'
283
+ overflowX: 'auto',
278
284
  });
279
285
 
280
286
  this.br.refs.$brPageViewEl = $("<div class='BRpageview'></div>");
281
287
  this.br.refs.$brContainer.append(this.br.refs.$brPageViewEl);
282
- this.br.refs.$brContainer.dragscrollable({preventDefault:true});
288
+ this.dragScrollable = this.dragScrollable || new DragScrollable(this.br.refs.$brContainer[0], {preventDefault: true});
283
289
 
284
290
  this.br.bindGestures(this.br.refs.$brContainer);
285
291
 
@@ -330,7 +336,7 @@ export class ModeThumb {
330
336
  } else {
331
337
  this.br.animating = true;
332
338
  this.br.refs.$brContainer.stop(true)
333
- .animate({ scrollTop: leafTop }, 'fast', () => { this.br.animating = false });
339
+ .animate({ scrollTop: leafTop }, 'fast', () => { this.br.animating = false; });
334
340
  }
335
341
  }
336
342
  }
@@ -4,6 +4,7 @@ import 'jquery-ui/ui/widget.js';
4
4
  import 'jquery-ui/ui/widgets/mouse.js';
5
5
  import 'jquery-ui/ui/widgets/slider.js';
6
6
  import { EVENTS } from '../events.js';
7
+ import { throttle } from '../utils.js';
7
8
 
8
9
  export class Navbar {
9
10
  /**
@@ -21,12 +22,14 @@ export class Navbar {
21
22
 
22
23
  /** @type {Object} controls will be switch over "this.maximumControls" */
23
24
  this.minimumControls = [
24
- 'viewmode'
25
+ 'viewmode',
25
26
  ];
26
27
  /** @type {Object} controls will be switch over "this.minimumControls" */
27
28
  this.maximumControls = [
28
- 'book_left', 'book_right', 'zoom_in', 'zoom_out', 'onepg', 'twopg', 'thumb'
29
+ 'book_left', 'book_right', 'zoom_in', 'zoom_out', 'onepg', 'twopg', 'thumb',
29
30
  ];
31
+
32
+ this.updateNavIndexThrottled = throttle(this.updateNavIndex.bind(this), 250, false);
30
33
  }
31
34
 
32
35
  controlFor(controlName) {
@@ -38,7 +41,7 @@ export class Navbar {
38
41
  return `<li>
39
42
  <button class="BRicon ${option.className}" title="${option.label}">
40
43
  <div class="icon icon-${option.iconClassName}"></div>
41
- <span class="tooltip">${option.label}</span>
44
+ <span class="BRtooltip">${option.label}</span>
42
45
  </button>
43
46
  </li>`;
44
47
  }
@@ -112,7 +115,7 @@ export class Navbar {
112
115
  this.updateViewModeButton(
113
116
  $button,
114
117
  currentViewModeButton.className,
115
- currentViewModeButton.title
118
+ currentViewModeButton.title,
116
119
  );
117
120
  });
118
121
  }
@@ -127,7 +130,7 @@ export class Navbar {
127
130
  .removeClass()
128
131
  .addClass(`icon icon-${iconClass}`)
129
132
  .end()
130
- .find('.tooltip')
133
+ .find('.BRtooltip')
131
134
  .text(tooltipText);
132
135
  }
133
136
 
@@ -135,16 +138,43 @@ export class Navbar {
135
138
  * Switch navbar controls on mobile and desktop
136
139
  */
137
140
  switchNavbarControls() {
138
- // we don't want navbar controls switching with liner-notes
139
- if (this.br.options.bookType !== 'linerNotes') {
140
- if (this.br.refs.$brContainer.prop('clientWidth') < 640) {
141
+ if (this.br.refs.$brContainer.prop('clientWidth') < 640) {
142
+ this.showMinimumNavPageNum();
143
+ // we don't want navbar controls switching with liner-notes
144
+ if (this.br.options.bookType !== 'linerNotes') {
141
145
  this.showMinimumNavbarControls();
142
- } else {
146
+ }
147
+ } else {
148
+ this.showMaximumNavPageNum();
149
+ // we don't want navbar controls switching with liner-notes
150
+ if (this.br.options.bookType !== 'linerNotes') {
143
151
  this.showMaximumNavbarControls();
144
152
  }
145
153
  }
146
154
  }
147
155
 
156
+ /**
157
+ * Switch Book Nav page number display to minimum/mobile
158
+ */
159
+ showMinimumNavPageNum() {
160
+ const minElement = document.querySelector('.BRcurrentpage.BRmin');
161
+ const maxElement = document.querySelector('.BRcurrentpage.BRmax');
162
+
163
+ if (minElement) minElement.classList.remove('hide');
164
+ if (maxElement) maxElement.classList.add('hide');
165
+ }
166
+
167
+ /**
168
+ * Switch Book Nav page number display to maximum/desktop
169
+ */
170
+ showMaximumNavPageNum() {
171
+ const minElement = document.querySelector('.BRcurrentpage.BRmin');
172
+ const maxElement = document.querySelector('.BRcurrentpage.BRmax');
173
+
174
+ if (minElement) minElement.classList.add('hide');
175
+ if (maxElement) maxElement.classList.remove('hide');
176
+ }
177
+
148
178
  /**
149
179
  * Switch Book Navbar controls to minimised
150
180
  * NOTE: only `this.minimumControls` and `this.maximumControls` switch on resize
@@ -200,7 +230,10 @@ export class Navbar {
200
230
  <div class="BRpager"></div>
201
231
  <div class="BRnavline"></div>
202
232
  </div>
203
- <p><span class='BRcurrentpage'></span></p>
233
+ <p>
234
+ <span class="BRcurrentpage BRmax"></span>
235
+ <span class="BRcurrentpage BRmin"></span>
236
+ </p>
204
237
  </li>
205
238
  ${this._renderControls()}
206
239
  </ul>
@@ -213,9 +246,9 @@ export class Navbar {
213
246
  const $slider = this.$root.find('.BRpager').slider({
214
247
  animate: true,
215
248
  min: 0,
216
- max: br.getNumLeafs() - 1,
249
+ max: br.book.getNumLeafs() - 1,
217
250
  value: br.currentIndex(),
218
- range: "min"
251
+ range: "min",
219
252
  });
220
253
 
221
254
  $slider.on('slide', (event, ui) => {
@@ -240,53 +273,25 @@ export class Navbar {
240
273
  return this.$nav;
241
274
  }
242
275
 
243
- /**
244
- * Initialize the navigation bar when embedded
245
- */
246
- initEmbed() {
247
- const { br } = this;
248
- // IA-specific
249
- const thisLink = (window.location + '')
250
- .replace('?ui=embed','')
251
- .replace('/stream/', '/details/')
252
- .replace('#', '/');
253
- const logoHtml = br.showLogo ? `<a class="logo" href="${br.logoURL}" target="_blank"></a>` : '';
254
-
255
- br.refs.$BRfooter = this.$root = $('<div class="BRfooter"></div>');
256
- br.refs.$BRnav = this.$nav = $(
257
- `<div class="BRnav BRnavEmbed">
258
- ${logoHtml}
259
- <span class="BRembedreturn">
260
- <a href="${thisLink}" target="_blank">${br.bookTitle}</a>
261
- </span>
262
- <span class="BRtoolbarbuttons">
263
- <button class="BRicon book_left"></button>
264
- <button class="BRicon book_right"></button>
265
- <button class="BRicon full"></button>
266
- </span>
267
- </div>`);
268
- this.$root.append(this.$nav);
269
- br.refs.$br.append(this.$root);
270
- }
271
-
272
276
  /**
273
277
  * Returns the textual representation of the current page for the navbar
274
278
  * @param {number} index
279
+ * @param {boolean} [useMaxFormat = false]
275
280
  * @return {string}
276
281
  */
277
- getNavPageNumString(index) {
282
+ getNavPageNumString(index, useMaxFormat = false) {
278
283
  const { br } = this;
279
284
  // Accessible index starts at 0 (alas) so we add 1 to make human
280
- const pageNum = br.getPageNum(index);
281
- const pageType = br.getPageProp(index, 'pageType');
282
- const numLeafs = br.getNumLeafs();
285
+ const pageNum = br.book.getPageNum(index);
286
+ const pageType = br.book.getPageProp(index, 'pageType');
287
+ const numLeafs = br.book.getNumLeafs();
283
288
 
284
289
  if (!this.maxPageNum) {
285
290
  // Calculate Max page num (used for pagination display)
286
291
  let maxPageNum = 0;
287
292
  let pageNumVal;
288
293
  for (let i = 0; i < numLeafs; i++) {
289
- pageNumVal = br.getPageNum(i);
294
+ pageNumVal = parseFloat(br.book.getPageNum(i));
290
295
  if (!isNaN(pageNumVal) && pageNumVal > maxPageNum) {
291
296
  maxPageNum = pageNumVal;
292
297
  }
@@ -294,7 +299,8 @@ export class Navbar {
294
299
  this.maxPageNum = maxPageNum;
295
300
  }
296
301
 
297
- return getNavPageNumHtml(index, numLeafs, pageNum, pageType, this.maxPageNum);
302
+ return getNavPageNumHtml(index, numLeafs, pageNum, pageType, this.maxPageNum, useMaxFormat);
303
+
298
304
  }
299
305
 
300
306
  /**
@@ -302,7 +308,8 @@ export class Navbar {
302
308
  * @param {number} index
303
309
  */
304
310
  updateNavPageNum(index) {
305
- this.$root.find('.BRcurrentpage').html(this.getNavPageNumString(index));
311
+ this.$root.find('.BRcurrentpage.BRmax').html(this.getNavPageNumString(index, true));
312
+ this.$root.find('.BRcurrentpage.BRmin').html(this.getNavPageNumString(index));
306
313
  }
307
314
 
308
315
  /**
@@ -322,18 +329,27 @@ export class Navbar {
322
329
  * @param {number} index
323
330
  * @param {number} numLeafs
324
331
  * @param {number|string} pageNum
325
- * @param {*} pageType @deprecated
332
+ * @param {*} pageType - Deprecated
326
333
  * @param {number} maxPageNum
334
+ * @param {boolean} [useMaxFormat = false]
327
335
  * @return {string}
328
336
  */
329
- export function getNavPageNumHtml(index, numLeafs, pageNum, pageType, maxPageNum) {
337
+ export function getNavPageNumHtml(index, numLeafs, pageNum, pageType, maxPageNum, useMaxFormat = false) {
330
338
  const pageIsAsserted = pageNum[0] != 'n';
339
+ const pageIndex = index + 1;
340
+
341
+ if (!pageIsAsserted) {
342
+ pageNum = '—';
343
+ }
344
+
345
+ if (useMaxFormat === true) {
346
+ return `Page ${pageNum} (${pageIndex}/${numLeafs})`;
347
+ }
331
348
 
332
349
  if (!pageIsAsserted) {
333
- const pageIndex = index + 1;
334
- return `Page (${pageIndex} of ${numLeafs})`; // Page (8 of 10)
350
+ return `(${pageIndex} of ${numLeafs})`;
335
351
  }
336
352
 
337
- const bookLengthLabel = maxPageNum ? ` of ${maxPageNum}` : '';
338
- return `Page ${pageNum}${bookLengthLabel}`;
353
+ const bookLengthLabel = (maxPageNum && parseFloat(pageNum)) ? ` of ${maxPageNum}` : '';
354
+ return `${pageNum}${bookLengthLabel}`;
339
355
  }