@internetarchive/bookreader 5.0.0-5-multiple-files → 5.0.0-50-a1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (289) hide show
  1. package/.eslintrc.js +17 -15
  2. package/.github/workflows/node.js.yml +77 -6
  3. package/.github/workflows/npm-publish.yml +6 -20
  4. package/.testcaferc.js +10 -0
  5. package/BookReader/BookReader.css +131 -339
  6. package/BookReader/BookReader.js +2 -21564
  7. package/BookReader/BookReader.js.LICENSE.txt +24 -0
  8. package/BookReader/BookReader.js.map +1 -1
  9. package/BookReader/ia-bookreader-bundle.js +1493 -0
  10. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +17 -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 +1 -304
  64. package/BookReader/plugins/plugin.chapters.js.map +1 -1
  65. package/BookReader/plugins/plugin.iframe.js +1 -74
  66. package/BookReader/plugins/plugin.iframe.js.map +1 -1
  67. package/BookReader/plugins/plugin.mobile_nav.js +1 -334
  68. package/BookReader/plugins/plugin.mobile_nav.js.map +1 -1
  69. package/BookReader/plugins/plugin.resume.js +1 -368
  70. package/BookReader/plugins/plugin.resume.js.map +1 -1
  71. package/BookReader/plugins/plugin.search.js +1 -1420
  72. package/BookReader/plugins/plugin.search.js.map +1 -1
  73. package/BookReader/plugins/plugin.text_selection.js +1 -1080
  74. package/BookReader/plugins/plugin.text_selection.js.map +1 -1
  75. package/BookReader/plugins/plugin.tts.js +2 -9193
  76. package/BookReader/plugins/plugin.tts.js.map +1 -1
  77. package/BookReader/plugins/plugin.url.js +1 -269
  78. package/BookReader/plugins/plugin.url.js.map +1 -1
  79. package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -379
  80. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
  81. package/BookReader/webcomponents-bundle.js +3 -0
  82. package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
  83. package/BookReader/webcomponents-bundle.js.map +1 -0
  84. package/BookReaderDemo/BookReaderDemo.css +14 -1
  85. package/BookReaderDemo/IADemoBr.js +148 -0
  86. package/BookReaderDemo/demo-advanced.html +2 -2
  87. package/BookReaderDemo/demo-autoplay.html +2 -1
  88. package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
  89. package/BookReaderDemo/demo-fullscreen-mobile.html +2 -1
  90. package/BookReaderDemo/demo-fullscreen.html +2 -1
  91. package/BookReaderDemo/demo-iiif.html +2 -1
  92. package/BookReaderDemo/demo-internetarchive.html +84 -17
  93. package/BookReaderDemo/demo-multiple.html +2 -1
  94. package/BookReaderDemo/demo-preview-pages.html +2 -1
  95. package/BookReaderDemo/demo-simple.html +2 -1
  96. package/BookReaderDemo/demo-vendor-fullscreen.html +2 -1
  97. package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
  98. package/BookReaderDemo/immersion-1up.html +2 -1
  99. package/BookReaderDemo/immersion-mode.html +2 -1
  100. package/BookReaderDemo/toggle_controls.html +2 -1
  101. package/BookReaderDemo/view_mode.html +2 -1
  102. package/BookReaderDemo/viewmode-cycle.html +2 -3
  103. package/CHANGELOG.md +202 -0
  104. package/README.md +14 -1
  105. package/babel.config.js +18 -0
  106. package/codecov.yml +6 -0
  107. package/index.html +3 -0
  108. package/jsconfig.json +19 -0
  109. package/package.json +66 -56
  110. package/renovate.json +52 -0
  111. package/scripts/preversion.js +4 -1
  112. package/src/BookNavigator/assets/bookmark-colors.js +1 -1
  113. package/src/BookNavigator/assets/button-base.js +9 -2
  114. package/src/BookNavigator/assets/ia-logo.js +17 -0
  115. package/src/BookNavigator/assets/icon_checkmark.js +1 -1
  116. package/src/BookNavigator/assets/icon_close.js +1 -1
  117. package/src/BookNavigator/assets/icon_sort_asc.js +5 -0
  118. package/src/BookNavigator/assets/{icon_sort_ascending.js → icon_sort_desc.js} +2 -2
  119. package/src/BookNavigator/assets/icon_sort_neutral.js +5 -0
  120. package/src/BookNavigator/assets/icon_volumes.js +11 -0
  121. package/src/BookNavigator/book-navigator.js +583 -0
  122. package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
  123. package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
  124. package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
  125. package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
  126. package/src/BookNavigator/bookmarks/bookmarks-provider.js +21 -8
  127. package/src/BookNavigator/bookmarks/ia-bookmarks.js +102 -66
  128. package/src/BookNavigator/delete-modal-actions.js +1 -1
  129. package/src/BookNavigator/downloads/downloads-provider.js +36 -21
  130. package/src/BookNavigator/downloads/downloads.js +41 -25
  131. package/src/BookNavigator/search/a-search-result.js +18 -13
  132. package/src/BookNavigator/search/search-provider.js +80 -28
  133. package/src/BookNavigator/search/search-results.js +10 -18
  134. package/src/BookNavigator/sharing.js +27 -0
  135. package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
  136. package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
  137. package/src/BookNavigator/volumes/volumes-provider.js +93 -63
  138. package/src/BookNavigator/volumes/volumes.js +40 -46
  139. package/src/BookReader/BookModel.js +0 -29
  140. package/src/BookReader/DebugConsole.js +3 -3
  141. package/src/BookReader/DragScrollable.js +233 -0
  142. package/src/BookReader/Mode1Up.js +51 -351
  143. package/src/BookReader/Mode1UpLit.js +441 -0
  144. package/src/BookReader/Mode2Up.js +120 -105
  145. package/src/BookReader/ModeSmoothZoom.js +179 -0
  146. package/src/BookReader/ModeThumb.js +17 -11
  147. package/src/BookReader/Navbar/Navbar.js +10 -36
  148. package/src/BookReader/PageContainer.js +69 -6
  149. package/src/BookReader/ReduceSet.js +1 -1
  150. package/src/BookReader/Toolbar/Toolbar.js +10 -37
  151. package/src/BookReader/options.js +10 -0
  152. package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
  153. package/src/BookReader/utils/ScrollClassAdder.js +31 -0
  154. package/src/BookReader/utils.js +92 -13
  155. package/src/BookReader.js +431 -620
  156. package/src/assets/icons/close-circle-dark.svg +1 -0
  157. package/src/assets/icons/magnify-minus.svg +3 -7
  158. package/src/assets/icons/magnify-plus.svg +3 -7
  159. package/src/assets/icons/voice.svg +1 -0
  160. package/src/css/BookReader.scss +0 -12
  161. package/src/css/_BRComponent.scss +1 -1
  162. package/src/css/_BRmain.scss +19 -24
  163. package/src/css/_BRnav.scss +4 -26
  164. package/src/css/_BRpages.scss +35 -0
  165. package/src/css/_BRsearch.scss +25 -216
  166. package/src/css/_TextSelection.scss +14 -17
  167. package/src/css/_colorbox.scss +2 -2
  168. package/src/css/_controls.scss +17 -5
  169. package/src/css/_icons.scss +6 -0
  170. package/src/ia-bookreader/ia-bookreader.js +224 -0
  171. package/src/plugins/plugin.autoplay.js +4 -4
  172. package/src/plugins/plugin.chapters.js +28 -35
  173. package/src/plugins/plugin.mobile_nav.js +11 -10
  174. package/src/plugins/plugin.resume.js +3 -3
  175. package/src/plugins/plugin.text_selection.js +26 -39
  176. package/src/plugins/plugin.vendor-fullscreen.js +4 -4
  177. package/src/plugins/search/plugin.search.js +174 -116
  178. package/src/plugins/search/view.js +63 -179
  179. package/src/plugins/tts/AbstractTTSEngine.js +46 -37
  180. package/src/plugins/tts/FestivalTTSEngine.js +13 -14
  181. package/src/plugins/tts/PageChunk.js +15 -21
  182. package/src/plugins/tts/PageChunkIterator.js +8 -12
  183. package/src/plugins/tts/WebTTSEngine.js +66 -69
  184. package/src/plugins/tts/plugin.tts.js +92 -109
  185. package/src/plugins/tts/utils.js +0 -9
  186. package/src/plugins/url/UrlPlugin.js +184 -0
  187. package/src/plugins/{plugin.url.js → url/plugin.url.js} +28 -6
  188. package/src/util/manifestGenerator.js +0 -0
  189. package/tests/e2e/README.md +37 -0
  190. package/tests/e2e/autoplay.test.js +2 -2
  191. package/tests/e2e/base.test.js +7 -7
  192. package/tests/e2e/helpers/base.js +9 -3
  193. package/tests/e2e/helpers/debug.js +1 -1
  194. package/tests/e2e/helpers/desktopSearch.js +14 -13
  195. package/tests/e2e/helpers/mobileSearch.js +3 -3
  196. package/tests/e2e/helpers/params.js +17 -0
  197. package/tests/e2e/models/Navigation.js +13 -4
  198. package/tests/e2e/rightToLeft.test.js +4 -5
  199. package/tests/e2e/viewmode.test.js +38 -33
  200. package/tests/jest/BookNavigator/book-navigator.test.js +634 -0
  201. package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
  202. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
  203. package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
  204. package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
  205. package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
  206. package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
  207. package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
  208. package/tests/{karma/BookNavigator → jest/BookNavigator/search}/search-results.test.js +102 -58
  209. package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
  210. package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
  211. package/tests/jest/BookNavigator/volumes/volumes-provider.test.js +184 -0
  212. package/tests/jest/BookNavigator/volumes/volumes.test.js +97 -0
  213. package/tests/{BookReader → jest/BookReader}/BookModel.test.js +34 -14
  214. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +176 -0
  215. package/tests/{BookReader → jest/BookReader}/DebugConsole.test.js +1 -1
  216. package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
  217. package/tests/jest/BookReader/Mode1UpLit.test.js +92 -0
  218. package/tests/{BookReader → jest/BookReader}/Mode2Up.test.js +36 -15
  219. package/tests/jest/BookReader/ModeSmoothZoom.test.js +149 -0
  220. package/tests/jest/BookReader/ModeThumb.test.js +71 -0
  221. package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +7 -7
  222. package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
  223. package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
  224. package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
  225. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
  226. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
  227. package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
  228. package/tests/jest/BookReader/utils.test.js +186 -0
  229. package/tests/jest/BookReader.keyboard.test.js +190 -0
  230. package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
  231. package/tests/{BookReader.test.js → jest/BookReader.test.js} +18 -37
  232. package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
  233. package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
  234. package/tests/{plugins → jest/plugins}/plugin.chapters.test.js +10 -11
  235. package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
  236. package/tests/{plugins → jest/plugins}/plugin.mobile_nav.test.js +5 -5
  237. package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
  238. package/tests/{plugins → jest/plugins}/plugin.text_selection.test.js +39 -47
  239. package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
  240. package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +63 -47
  241. package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +35 -6
  242. package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +9 -9
  243. package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
  244. package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
  245. package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
  246. package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +1 -1
  247. package/tests/{plugins → jest/plugins}/tts/utils.test.js +3 -28
  248. package/tests/jest/plugins/url/UrlPlugin.test.js +190 -0
  249. package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +33 -14
  250. package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
  251. package/tests/{util → jest/util}/docCookies.test.js +1 -1
  252. package/tests/{util → jest/util}/strings.test.js +1 -1
  253. package/tests/{utils.js → jest/utils.js} +38 -0
  254. package/webpack.config.js +11 -5
  255. package/.babelrc +0 -12
  256. package/.dependabot/config.yml +0 -6
  257. package/.testcaferc.json +0 -5
  258. package/BookReader/bookreader-component-bundle.js +0 -14275
  259. package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
  260. package/BookReader/bookreader-component-bundle.js.map +0 -1
  261. package/BookReader/icons/sort-ascending.svg +0 -1
  262. package/BookReader/icons/sort-descending.svg +0 -1
  263. package/BookReader/jquery-1.10.1.js +0 -108
  264. package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
  265. package/BookReader/plugins/plugin.menu_toggle.js +0 -369
  266. package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
  267. package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
  268. package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
  269. package/karma.conf.js +0 -23
  270. package/src/BookNavigator/BookModel.js +0 -14
  271. package/src/BookNavigator/BookNavigator.js +0 -448
  272. package/src/BookNavigator/assets/book-loader.js +0 -27
  273. package/src/BookNavigator/assets/icon_sort_descending.js +0 -5
  274. package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
  275. package/src/BookReaderComponent/BookReaderComponent.js +0 -112
  276. package/src/ItemNavigator/ItemNavigator.js +0 -373
  277. package/src/ItemNavigator/providers/sharing.js +0 -29
  278. package/src/assets/icons/sort-ascending.svg +0 -1
  279. package/src/assets/icons/sort-descending.svg +0 -1
  280. package/src/dragscrollable-br.js +0 -261
  281. package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
  282. package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
  283. package/tests/BookReader/Mode1Up.test.js +0 -164
  284. package/tests/BookReader/utils.test.js +0 -109
  285. package/tests/e2e/ia-production/ia-prod-base.js +0 -17
  286. package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
  287. package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
  288. package/tests/karma/BookNavigator/volumes.test.js +0 -101
  289. package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
@@ -1,16 +1,21 @@
1
1
  // @ts-check
2
2
  // effect.js gives acces to extra easing function (e.g. easeInSine)
3
3
  import 'jquery-ui/ui/effect.js';
4
- import '../dragscrollable-br.js';
5
4
  import { clamp } from './utils.js';
6
5
  import { EVENTS } from './events.js';
6
+ import { ModeSmoothZoom } from "./ModeSmoothZoom.js";
7
+ import { HTMLDimensionsCacher } from './utils/HTMLDimensionsCacher.js';
8
+ import { DragScrollable } from './DragScrollable.js';
9
+ import { ScrollClassAdder } from './utils/ScrollClassAdder.js';
7
10
 
8
11
  /** @typedef {import('../BookReader.js').default} BookReader */
9
12
  /** @typedef {import('./BookModel.js').BookModel} BookModel */
10
13
  /** @typedef {import('./BookModel.js').PageIndex} PageIndex */
11
14
  /** @typedef {import('./options.js').BookReaderOptions} BookReaderOptions */
12
15
  /** @typedef {import('./PageContainer.js').PageContainer} PageContainer */
16
+ /** @typedef {import('./ModeSmoothZoom').SmoothZoomable} SmoothZoomable */
13
17
 
18
+ /** @implements {SmoothZoomable} */
14
19
  export class Mode2Up {
15
20
  /**
16
21
  * @param {BookReader} br
@@ -27,6 +32,28 @@ export class Mode2Up {
27
32
 
28
33
  /** @type {{ [index: number]: PageContainer }} */
29
34
  this.pageContainers = {};
35
+
36
+ /** @type {ModeSmoothZoom} */
37
+ this.smoothZoomer = null;
38
+ this._scale = 1;
39
+ this.scaleCenter = { x: 0.5, y: 0.5 };
40
+
41
+ /** @type {ScrollClassAdder} */
42
+ this.scrollClassAdder = null;
43
+ }
44
+
45
+ get $container() {
46
+ return this.br.refs.$brContainer[0];
47
+ }
48
+ get $visibleWorld() {
49
+ return this.br.refs.$brTwoPageView?.[0];
50
+ }
51
+
52
+ get scale() { return this._scale; }
53
+ set scale(newVal) {
54
+ this.$visibleWorld.style.transform = `scale(${newVal})`;
55
+ this.updateViewportOnZoom(newVal, this._scale);
56
+ this._scale = newVal;
30
57
  }
31
58
 
32
59
  /**
@@ -42,18 +69,6 @@ export class Mode2Up {
42
69
  }
43
70
  }
44
71
 
45
- /**
46
- * @template T
47
- * @param {HTMLElement} element
48
- * @param {T} data
49
- * @param {function(HTMLElement, { data: T }): void} handler
50
- */
51
- setClickHandler(element, data, handler) {
52
- $(element).unbind('mouseup').bind('mouseup', data, function(e) {
53
- handler(this, e);
54
- });
55
- }
56
-
57
72
  /**
58
73
  * Draws book spread,
59
74
  * sets event handlers,
@@ -74,9 +89,7 @@ export class Mode2Up {
74
89
  .appendTo($twoPageViewEl);
75
90
 
76
91
  this.displayedIndices = [this.br.twoPage.currentIndexL, this.br.twoPage.currentIndexR];
77
- this.setMouseHandlers();
78
92
  this.br.displayedIndices = this.displayedIndices;
79
- this.br.updateToolbarZoom(this.br.reduce);
80
93
  this.br.trigger('pageChanged');
81
94
  }
82
95
 
@@ -104,7 +117,7 @@ export class Mode2Up {
104
117
 
105
118
  // Prepare view with new center to minimize visual glitches
106
119
  const drawNewSpread = true;
107
- this.prepareTwoPageView(oldCenter.percentageX, oldCenter.percentageY, drawNewSpread);
120
+ this.prepare(oldCenter.percentageX, oldCenter.percentageY, drawNewSpread);
108
121
  }
109
122
 
110
123
  /**
@@ -136,7 +149,7 @@ export class Mode2Up {
136
149
  * @param {number} centerPercentageY
137
150
  * @param {Boolean} drawNewSpread
138
151
  */
139
- prepareTwoPageView(centerPercentageX, centerPercentageY, drawNewSpread = false) {
152
+ prepare(centerPercentageX, centerPercentageY, drawNewSpread = false) {
140
153
  // Some decisions about two page view:
141
154
  //
142
155
  // Both pages will be displayed at the same height, even if they were different physical/scanned
@@ -176,13 +189,22 @@ export class Mode2Up {
176
189
 
177
190
  // Add the two page view
178
191
  // $$$ Can we get everything set up and then append?
179
- const $twoPageViewEl = $('<div class="BRtwopageview"></div>');
180
- this.br.refs.$brTwoPageView = $twoPageViewEl;
192
+ this.br.refs.$brTwoPageView = this.br.refs.$brTwoPageView || $('<div class="BRtwopageview"></div>');
193
+ const $twoPageViewEl = this.br.refs.$brTwoPageView;
194
+ $twoPageViewEl.empty();
195
+ $twoPageViewEl[0].style.transformOrigin = '0 0';
181
196
  this.br.refs.$brContainer.append($twoPageViewEl);
182
197
 
183
198
  // Attaches to first child, so must come after we add the page view
184
- this.br.refs.$brContainer.dragscrollable({preventDefault:true});
185
- this.br.bindGestures(this.br.refs.$brContainer);
199
+ this.dragScrollable = this.dragScrollable || new DragScrollable(this.br.refs.$brContainer[0], {
200
+ preventDefault: true,
201
+ // Only handle mouse events; let browser/HammerJS handle touch
202
+ dragstart: 'mousedown',
203
+ dragcontinue: 'mousemove',
204
+ dragend: 'mouseup',
205
+ });
206
+
207
+ this.attachMouseHandlers();
186
208
 
187
209
  // $$$ calculate container size first
188
210
  this.br.refs?.$brTwoPageView.css(this.mainContainerCss);
@@ -218,14 +240,57 @@ export class Mode2Up {
218
240
  this.br.displayedIndices = [];
219
241
 
220
242
  this.drawLeafs();
221
- this.br.updateToolbarZoom(this.br.reduce);
243
+ this.br.updateBrClasses();
222
244
 
223
- if (this.br.enableSearch) {
224
- this.br.removeSearchHilites();
225
- this.br.updateSearchHilites();
245
+ this.smoothZoomer = this.smoothZoomer || new ModeSmoothZoom(this);
246
+ this.smoothZoomer.attach();
247
+ if (!this.scrollClassAdder) {
248
+ this.scrollClassAdder = new ScrollClassAdder(this.$container, 'BRscrolling-active');
226
249
  }
250
+ this.scrollClassAdder.detach();
251
+ this.scrollClassAdder.element = this.$container;
252
+ this.scrollClassAdder.attach();
227
253
 
228
- this.br.updateBrClasses();
254
+ this.htmlDimensionsCacher = this.htmlDimensionsCacher || new HTMLDimensionsCacher(this.$container);
255
+ }
256
+
257
+ unprepare() {
258
+ // Mode2Up attaches these listeners to the main BR container, so we need to
259
+ // detach these or it will cause issues for the other modes.
260
+ this.smoothZoomer.detach();
261
+ this.scrollClassAdder.detach();
262
+ }
263
+
264
+ /**
265
+ * @param {number} newScale
266
+ * @param {number} oldScale
267
+ */
268
+ updateViewportOnZoom(newScale, oldScale) {
269
+ const container = this.br.refs.$brContainer[0];
270
+ const { scrollTop: T, scrollLeft: L } = container;
271
+ const W = this.htmlDimensionsCacher.clientWidth;
272
+ const H = this.htmlDimensionsCacher.clientHeight;
273
+
274
+ // Scale factor change
275
+ const F = newScale / oldScale;
276
+
277
+ // Where in the viewport the zoom is centered on
278
+ const XPOS = this.scaleCenter.x;
279
+ const YPOS = this.scaleCenter.y;
280
+ const oldCenter = {
281
+ x: L + XPOS * W,
282
+ y: T + YPOS * H,
283
+ };
284
+ const newCenter = {
285
+ x: F * oldCenter.x,
286
+ y: F * oldCenter.y,
287
+ };
288
+ container.scrollTop = newCenter.y - YPOS * H;
289
+ container.scrollLeft = newCenter.x - XPOS * W;
290
+
291
+ // Also update the visible page containers to load in highres if necessary
292
+ this.pageContainers[this.br.twoPage.currentIndexL]?.update({ reduce: this.br.reduce / newScale });
293
+ this.pageContainers[this.br.twoPage.currentIndexR]?.update({ reduce: this.br.reduce / newScale });
229
294
  }
230
295
 
231
296
  prunePageContainers() {
@@ -324,7 +389,7 @@ export class Mode2Up {
324
389
 
325
390
  // Leaf edges
326
391
  this.br.twoPage.edgeWidth = spreadSize.totalLeafEdgeWidth; // The combined width of both edges
327
- this.br.twoPage.leafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
392
+ this.br.twoPage.leafEdgeWidthL = this.leafEdgeWidth(this.br.twoPage.currentIndexL);
328
393
  this.br.twoPage.leafEdgeWidthR = this.br.twoPage.edgeWidth - this.br.twoPage.leafEdgeWidthL;
329
394
 
330
395
 
@@ -461,21 +526,6 @@ export class Mode2Up {
461
526
  return spreadSize.reduce;
462
527
  }
463
528
 
464
- /**
465
- * Returns true if the pages extend past the edge of the view
466
- * @deprecated slated for deprecation by v5.0.0
467
- * @return {boolean}
468
- */
469
- isZoomedIn() {
470
- let isZoomedIn = false;
471
- if (this.br.twoPage.autofit != 'auto') {
472
- if (this.br.reduce < this.getAutofitReduce()) {
473
- isZoomedIn = true;
474
- }
475
- }
476
- return isZoomedIn;
477
- }
478
-
479
529
  calculateReductionFactors() {
480
530
  this.br.twoPage.reductionFactors = this.br.reductionFactors.concat([
481
531
  {
@@ -486,14 +536,6 @@ export class Mode2Up {
486
536
  this.br.twoPage.reductionFactors.sort(this.br._reduceSort);
487
537
  }
488
538
 
489
- /**
490
- * Set the cursor for two page view
491
- * @deprecated Since version 4.3.3. Will be deleted in version 5.0
492
- */
493
- setCursor() {
494
- console.warn('Call to deprecated method, Mode2Up.setCursor. No-op.');
495
- }
496
-
497
539
  /**
498
540
  * @param {Number|null} index to flip back one spread, pass index=null
499
541
  */
@@ -517,7 +559,7 @@ export class Mode2Up {
517
559
  if (prev.pageSide == 'R') index--;
518
560
  }
519
561
 
520
- this.br.updateNavIndexThrottled(index);
562
+ this.br._components.navbar.updateNavIndexThrottled(index);
521
563
 
522
564
  const previousIndices = this.book.getSpreadIndices(index);
523
565
 
@@ -547,8 +589,8 @@ export class Mode2Up {
547
589
  this.br.refs.$brContainer.addClass("BRpageFlipping");
548
590
  const leftLeaf = this.br.twoPage.currentIndexL;
549
591
 
550
- const oldLeafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
551
- const newLeafEdgeWidthL = this.br.leafEdgeWidth(newIndexL);
592
+ const oldLeafEdgeWidthL = this.leafEdgeWidth(this.br.twoPage.currentIndexL);
593
+ const newLeafEdgeWidthL = this.leafEdgeWidth(newIndexL);
552
594
  const leafEdgeTmpW = oldLeafEdgeWidthL - newLeafEdgeWidthL;
553
595
 
554
596
  const currWidthL = this.getPageWidth(leftLeaf);
@@ -616,8 +658,6 @@ export class Mode2Up {
616
658
 
617
659
  $(this.br.leafEdgeTmp).animate({left: gutter}, this.br.flipSpeed, 'easeInSine');
618
660
 
619
- if (this.br.enableSearch) this.br.removeSearchHilites();
620
-
621
661
  this.pageContainers[leftLeaf].$container.animate({width: '0px'}, this.br.flipSpeed, 'easeInSine', () => {
622
662
 
623
663
  $(this.br.leafEdgeTmp).animate({left: `${gutter + newWidthR}px`}, this.br.flipSpeed, 'easeOutSine');
@@ -663,10 +703,6 @@ export class Mode2Up {
663
703
 
664
704
  this.resizeSpread();
665
705
 
666
- if (this.br.enableSearch) this.br.updateSearchHilites();
667
-
668
- this.setMouseHandlers();
669
-
670
706
  if (this.br.animationFinishedCallback) {
671
707
  this.br.animationFinishedCallback();
672
708
  this.br.animationFinishedCallback = null;
@@ -690,11 +726,11 @@ export class Mode2Up {
690
726
  /**
691
727
  * @param {PageIndex} index
692
728
  */
693
- createPageContainer(index, fetch = false) {
729
+ createPageContainer(index) {
694
730
  if (!this.pageContainers[index]) {
695
731
  this.pageContainers[index] = this.br._createPageContainer(index);
696
732
  }
697
- this.pageContainers[index].update({ reduce: this.br.reduce });
733
+ this.pageContainers[index].update({ reduce: this.br.reduce / this.scale });
698
734
  return this.pageContainers[index];
699
735
  }
700
736
 
@@ -721,7 +757,7 @@ export class Mode2Up {
721
757
  }
722
758
  if (index > this.br.lastDisplayableIndex()) return;
723
759
 
724
- this.br.updateNavIndexThrottled(index);
760
+ this.br._components.navbar.updateNavIndexThrottled(index);
725
761
 
726
762
  this.br.animating = true;
727
763
 
@@ -747,9 +783,9 @@ export class Mode2Up {
747
783
  flipRightToLeft(newIndexL, newIndexR) {
748
784
  this.br.refs.$brContainer.addClass("BRpageFlipping");
749
785
 
750
- const oldLeafEdgeWidthL = this.br.leafEdgeWidth(this.br.twoPage.currentIndexL);
786
+ const oldLeafEdgeWidthL = this.leafEdgeWidth(this.br.twoPage.currentIndexL);
751
787
  const oldLeafEdgeWidthR = this.br.twoPage.edgeWidth - oldLeafEdgeWidthL;
752
- const newLeafEdgeWidthL = this.br.leafEdgeWidth(newIndexL);
788
+ const newLeafEdgeWidthL = this.leafEdgeWidth(newIndexL);
753
789
  const newLeafEdgeWidthR = this.br.twoPage.edgeWidth - newLeafEdgeWidthL;
754
790
 
755
791
  const leafEdgeTmpW = oldLeafEdgeWidthR - newLeafEdgeWidthR;
@@ -778,12 +814,17 @@ export class Mode2Up {
778
814
  $(this.leafEdgeR).css({width: `${newLeafEdgeWidthR}px`, left: `${gutter + newWidthR}px` });
779
815
  const speed = this.br.flipSpeed;
780
816
 
781
- if (this.br.enableSearch) this.br.removeSearchHilites();
782
-
783
817
  $(this.br.leafEdgeTmp).animate({left: gutter}, speed, 'easeInSine');
784
818
  this.pageContainers[this.br.twoPage.currentIndexR].$container.animate({width: '0px'}, speed, 'easeInSine', () => {
785
819
  this.br.$('BRgutter').css({left: `${gutter - this.br.twoPage.bookSpineDivWidth * 0.5}px`});
786
820
  $(this.br.leafEdgeTmp).animate({left: `${gutter - newWidthL - leafEdgeTmpW}px`}, speed, 'easeOutSine');
821
+
822
+ // Ensure the new left leaf is right-positioned before animating its width.
823
+ // Otherwise, it animates in the wrong direction.
824
+ this.pageContainers[newIndexL].$container.css({
825
+ right: `${$twoPageViewEl.prop('clientWidth') - gutter}px`,
826
+ left: ''
827
+ });
787
828
  this.pageContainers[newIndexL].$container.animate({width: `${newWidthL}px`}, speed, 'easeOutSine', () => {
788
829
  this.pageContainers[newIndexR].$container.css('zIndex', 2);
789
830
 
@@ -814,10 +855,6 @@ export class Mode2Up {
814
855
 
815
856
  this.resizeSpread();
816
857
 
817
- if (this.br.enableSearch) this.br.updateSearchHilites();
818
-
819
- this.setMouseHandlers();
820
-
821
858
  if (this.br.animationFinishedCallback) {
822
859
  this.br.animationFinishedCallback();
823
860
  this.br.animationFinishedCallback = null;
@@ -837,40 +874,18 @@ export class Mode2Up {
837
874
  });
838
875
  }
839
876
 
840
- setMouseHandlers() {
841
- /**
842
- * @param {HTMLElement} element
843
- * @param {JQuery.MouseDownEvent<HTMLElement, { self: Mode2Up, direction: 'R' | 'L'}>} e
844
- */
845
- const handler = function(element, e) {
846
- if (e.which == 3) {
847
- // right click
848
- return !e.data.self.br.protected;
849
- }
850
- e.data.self.br.trigger(EVENTS.stop);
851
- e.data.self.br[e.data.direction === 'L' ? 'left' : 'right']();
852
-
853
- // Removed event handler for mouse movement, seems not to be needed
854
- /*
855
- // Changes per WEBDEV-2737
856
- BookReader: zoomed-in 2 page view, clicking page should change the page
857
- $(element)
858
- .mousemove(function() {
859
- e.preventDefault();
860
- })
861
- */
862
- }
877
+ attachMouseHandlers() {
878
+ this.br.refs.$brTwoPageView
879
+ .off('mouseup').on('mouseup', ev => {
880
+ if (ev.which == 3) {
881
+ // right click
882
+ return !this.br.protected;
883
+ }
863
884
 
864
- this.setClickHandler(
865
- this.pageContainers[this.br.twoPage.currentIndexR].$container[0],
866
- { self: this, direction: 'R' },
867
- handler
868
- );
869
- this.setClickHandler(
870
- this.pageContainers[this.br.twoPage.currentIndexL].$container[0],
871
- { self: this, direction: 'L' },
872
- handler
873
- );
885
+ const $page = $(ev.target).closest('.BRpagecontainer');
886
+ if ($page.data('side') == 'L') this.br.left();
887
+ else if ($page.data('side') == 'R') this.br.right();
888
+ });
874
889
  }
875
890
 
876
891
  /**
@@ -1248,7 +1263,7 @@ export class Mode2Up {
1248
1263
  ...this.heightCss,
1249
1264
  left: `${this.br.twoPage.gutter - this.br.twoPage.scaledWL}px`,
1250
1265
  width: `${this.br.twoPage.scaledWL}px`,
1251
- }
1266
+ };
1252
1267
  }
1253
1268
 
1254
1269
  /** Left side book thickness */
@@ -1269,7 +1284,7 @@ export class Mode2Up {
1269
1284
  ...this.heightCss,
1270
1285
  left: `${this.br.twoPage.gutter}px`,
1271
1286
  width: `${this.br.twoPage.scaledWR}px`,
1272
- }
1287
+ };
1273
1288
  }
1274
1289
 
1275
1290
  /** Right side book thickness */
@@ -0,0 +1,179 @@
1
+ // @ts-check
2
+ import Hammer from "hammerjs";
3
+ /** @typedef {import('./utils/HTMLDimensionsCacher.js').HTMLDimensionsCacher} HTMLDimensionsCacher */
4
+
5
+ /**
6
+ * @typedef {object} SmoothZoomable
7
+ * @property {HTMLElement} $container
8
+ * @property {HTMLElement} $visibleWorld
9
+ * @property {number} scale
10
+ * @property {{ x: number, y: number }} scaleCenter
11
+ * @property {HTMLDimensionsCacher} htmlDimensionsCacher
12
+ * @property {function(): void} [attachScrollListeners]
13
+ * @property {function(): void} [detachScrollListeners]
14
+ */
15
+
16
+ /** Manages pinch-zoom, ctrl-wheel, and trackpad pinch smooth zooming. */
17
+ export class ModeSmoothZoom {
18
+ /** @param {SmoothZoomable} mode */
19
+ constructor(mode) {
20
+ /** @type {SmoothZoomable} */
21
+ this.mode = mode;
22
+
23
+ /** Non-null when a scale has been enqueued/is being processed by the buffer function */
24
+ this.pinchMoveFrame = null;
25
+ /** Promise for the current/enqueued pinch move frame. Resolves when it is complete. */
26
+ this.pinchMoveFramePromise = Promise.resolve();
27
+ this.oldScale = 1;
28
+ /** @type {{ scale: number, center: { x: number, y: number }}} */
29
+ this.lastEvent = null;
30
+ this.attached = false;
31
+
32
+ /** @type {function(function(): void): any} */
33
+ this.bufferFn = window.requestAnimationFrame.bind(window);
34
+
35
+ // Hammer.js by default set userSelect to None; we don't want that!
36
+ // TODO: Is there any way to do this not globally on Hammer?
37
+ delete Hammer.defaults.cssProps.userSelect;
38
+ this.hammer = new Hammer.Manager(this.mode.$container, {
39
+ touchAction: "pan-x pan-y",
40
+ });
41
+
42
+ this.hammer.add(new Hammer.Pinch());
43
+ }
44
+
45
+ attach() {
46
+ if (this.attached) return;
47
+
48
+ this.attachCtrlZoom();
49
+
50
+ // GestureEvents work only on Safari; they interfere with Hammer,
51
+ // so block them.
52
+ this.mode.$container.addEventListener('gesturestart', this._preventEvent);
53
+ this.mode.$container.addEventListener('gesturechange', this._preventEvent);
54
+ this.mode.$container.addEventListener('gestureend', this._preventEvent);
55
+
56
+ // The pinch listeners
57
+ this.hammer.on("pinchstart", this._pinchStart);
58
+ this.hammer.on("pinchmove", this._pinchMove);
59
+ this.hammer.on("pinchend", this._pinchEnd);
60
+ this.hammer.on("pinchcancel", this._pinchCancel);
61
+
62
+ this.attached = true;
63
+ }
64
+
65
+ detach() {
66
+ this.detachCtrlZoom();
67
+
68
+ // GestureEvents work only on Safari; they interfere with Hammer,
69
+ // so block them.
70
+ this.mode.$container.removeEventListener('gesturestart', this._preventEvent);
71
+ this.mode.$container.removeEventListener('gesturechange', this._preventEvent);
72
+ this.mode.$container.removeEventListener('gestureend', this._preventEvent);
73
+
74
+ // The pinch listeners
75
+ this.hammer.off("pinchstart", this._pinchStart);
76
+ this.hammer.off("pinchmove", this._pinchMove);
77
+ this.hammer.off("pinchend", this._pinchEnd);
78
+ this.hammer.off("pinchcancel", this._pinchCancel);
79
+
80
+ this.attached = false;
81
+ }
82
+
83
+ /** @param {Event} ev */
84
+ _preventEvent = (ev) => {
85
+ ev.preventDefault();
86
+ return false;
87
+ }
88
+
89
+ _pinchStart = () => {
90
+ // Do this in case the pinchend hasn't fired yet.
91
+ this.oldScale = 1;
92
+ this.mode.$visibleWorld.classList.add("BRsmooth-zooming");
93
+ this.mode.$visibleWorld.style.willChange = "transform";
94
+ this.detachCtrlZoom();
95
+ this.mode.detachScrollListeners?.();
96
+ }
97
+
98
+ /** @param {{ scale: number, center: { x: number, y: number }}} e */
99
+ _pinchMove = async (e) => {
100
+ this.lastEvent = e;
101
+ if (!this.pinchMoveFrame) {
102
+ let pinchMoveFramePromiseRes = null;
103
+ this.pinchMoveFramePromise = new Promise(
104
+ (res) => (pinchMoveFramePromiseRes = res)
105
+ );
106
+
107
+ // Buffer these events; only update the scale when request animation fires
108
+ this.pinchMoveFrame = this.bufferFn(() => {
109
+ this.updateScaleCenter({
110
+ clientX: this.lastEvent.center.x,
111
+ clientY: this.lastEvent.center.y,
112
+ });
113
+ this.mode.scale *= this.lastEvent.scale / this.oldScale;
114
+ this.oldScale = this.lastEvent.scale;
115
+ this.pinchMoveFrame = null;
116
+ pinchMoveFramePromiseRes();
117
+ });
118
+ }
119
+ }
120
+
121
+ _pinchEnd = async () => {
122
+ // Want this to happen after the pinchMoveFrame,
123
+ // if one is in progress; otherwise setting oldScale
124
+ // messes up the transform.
125
+ await this.pinchMoveFramePromise;
126
+ this.mode.scaleCenter = { x: 0.5, y: 0.5 };
127
+ this.oldScale = 1;
128
+ this.mode.$visibleWorld.classList.remove("BRsmooth-zooming");
129
+ this.mode.$visibleWorld.style.willChange = "auto";
130
+ this.attachCtrlZoom();
131
+ this.mode.attachScrollListeners?.();
132
+ }
133
+
134
+ _pinchCancel = async () => {
135
+ // iOS fires pinchcancel ~randomly; it looks like it sometimes
136
+ // thinks the pinch becomes a pan, at which point it cancels?
137
+ await this._pinchEnd();
138
+ }
139
+
140
+ /** @private */
141
+ attachCtrlZoom() {
142
+ window.addEventListener("wheel", this._handleCtrlWheel, { passive: false });
143
+ }
144
+
145
+ /** @private */
146
+ detachCtrlZoom() {
147
+ window.removeEventListener("wheel", this._handleCtrlWheel);
148
+ }
149
+
150
+ /** @param {WheelEvent} ev **/
151
+ _handleCtrlWheel = (ev) => {
152
+ if (!ev.ctrlKey) return;
153
+ ev.preventDefault();
154
+ const zoomMultiplier =
155
+ // Zooming on macs was painfully slow; likely due to their better
156
+ // trackpads. Give them a higher zoom rate.
157
+ /Mac/i.test(navigator.platform)
158
+ ? 0.045
159
+ : // This worked well for me on Windows
160
+ 0.03;
161
+
162
+ // Zoom around the cursor
163
+ this.updateScaleCenter(ev);
164
+ this.mode.scale *= 1 - Math.sign(ev.deltaY) * zoomMultiplier;
165
+ }
166
+
167
+ /**
168
+ * @param {object} param0
169
+ * @param {number} param0.clientX
170
+ * @param {number} param0.clientY
171
+ */
172
+ updateScaleCenter({ clientX, clientY }) {
173
+ const bc = this.mode.htmlDimensionsCacher.boundingClientRect;
174
+ this.mode.scaleCenter = {
175
+ x: (clientX - bc.left) / this.mode.htmlDimensionsCacher.clientWidth,
176
+ y: (clientY - bc.top) / this.mode.htmlDimensionsCacher.clientHeight,
177
+ };
178
+ }
179
+ }
@@ -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 */
@@ -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 */
@@ -279,7 +285,7 @@ export class ModeThumb {
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
  }