@internetarchive/bookreader 5.0.0-94 → 5.0.0-95

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 (181) hide show
  1. package/.github/workflows/npm-publish.yml +2 -12
  2. package/CHANGELOG.md +7 -0
  3. package/README.md +0 -2
  4. package/package.json +1 -1
  5. package/scripts/version.js +0 -3
  6. package/BookReader/BookReader.css +0 -2251
  7. package/BookReader/BookReader.js +0 -3
  8. package/BookReader/BookReader.js.LICENSE.txt +0 -72
  9. package/BookReader/BookReader.js.map +0 -1
  10. package/BookReader/ia-bookreader-bundle.js +0 -1782
  11. package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +0 -7
  12. package/BookReader/ia-bookreader-bundle.js.map +0 -1
  13. package/BookReader/icons/1up.svg +0 -1
  14. package/BookReader/icons/2up.svg +0 -1
  15. package/BookReader/icons/advance.svg +0 -3
  16. package/BookReader/icons/chevron-right.svg +0 -1
  17. package/BookReader/icons/close-circle-dark.svg +0 -1
  18. package/BookReader/icons/close-circle.svg +0 -1
  19. package/BookReader/icons/fullscreen.svg +0 -1
  20. package/BookReader/icons/fullscreen_exit.svg +0 -1
  21. package/BookReader/icons/hamburger.svg +0 -1
  22. package/BookReader/icons/left-arrow.svg +0 -1
  23. package/BookReader/icons/magnify-minus.svg +0 -1
  24. package/BookReader/icons/magnify-plus.svg +0 -1
  25. package/BookReader/icons/magnify.svg +0 -1
  26. package/BookReader/icons/pause.svg +0 -1
  27. package/BookReader/icons/play.svg +0 -1
  28. package/BookReader/icons/playback-speed.svg +0 -1
  29. package/BookReader/icons/read-aloud.svg +0 -1
  30. package/BookReader/icons/review.svg +0 -3
  31. package/BookReader/icons/thumbnails.svg +0 -1
  32. package/BookReader/icons/voice.svg +0 -1
  33. package/BookReader/icons/volume-full.svg +0 -1
  34. package/BookReader/images/BRicons.png +0 -0
  35. package/BookReader/images/BRicons.svg +0 -5
  36. package/BookReader/images/BRicons_ia.png +0 -0
  37. package/BookReader/images/back_pages.png +0 -0
  38. package/BookReader/images/book_bottom_icon.png +0 -0
  39. package/BookReader/images/book_down_icon.png +0 -0
  40. package/BookReader/images/book_left_icon.png +0 -0
  41. package/BookReader/images/book_leftmost_icon.png +0 -0
  42. package/BookReader/images/book_right_icon.png +0 -0
  43. package/BookReader/images/book_rightmost_icon.png +0 -0
  44. package/BookReader/images/book_top_icon.png +0 -0
  45. package/BookReader/images/book_up_icon.png +0 -0
  46. package/BookReader/images/books_graphic.svg +0 -1
  47. package/BookReader/images/booksplit.png +0 -0
  48. package/BookReader/images/control_pause_icon.png +0 -0
  49. package/BookReader/images/control_play_icon.png +0 -0
  50. package/BookReader/images/embed_icon.png +0 -0
  51. package/BookReader/images/hypothesis.ico +0 -0
  52. package/BookReader/images/icon-home-ia.png +0 -0
  53. package/BookReader/images/icon_OL-logo-xs.png +0 -0
  54. package/BookReader/images/icon_alert-xs.png +0 -0
  55. package/BookReader/images/icon_book.svg +0 -1
  56. package/BookReader/images/icon_bookmark.svg +0 -1
  57. package/BookReader/images/icon_close-pop.png +0 -0
  58. package/BookReader/images/icon_download.png +0 -0
  59. package/BookReader/images/icon_gear.svg +0 -1
  60. package/BookReader/images/icon_hamburger.svg +0 -1
  61. package/BookReader/images/icon_home.png +0 -0
  62. package/BookReader/images/icon_home.svg +0 -1
  63. package/BookReader/images/icon_home_ia.png +0 -0
  64. package/BookReader/images/icon_indicator.png +0 -0
  65. package/BookReader/images/icon_info.svg +0 -1
  66. package/BookReader/images/icon_one_page.svg +0 -1
  67. package/BookReader/images/icon_pause.svg +0 -1
  68. package/BookReader/images/icon_play.svg +0 -1
  69. package/BookReader/images/icon_playback-rate.svg +0 -1
  70. package/BookReader/images/icon_return.png +0 -0
  71. package/BookReader/images/icon_search_button.svg +0 -1
  72. package/BookReader/images/icon_share.svg +0 -1
  73. package/BookReader/images/icon_skip-ahead.svg +0 -1
  74. package/BookReader/images/icon_skip-back.svg +0 -2
  75. package/BookReader/images/icon_speaker.svg +0 -1
  76. package/BookReader/images/icon_speaker_open.svg +0 -1
  77. package/BookReader/images/icon_thumbnails.svg +0 -1
  78. package/BookReader/images/icon_toc.svg +0 -1
  79. package/BookReader/images/icon_two_pages.svg +0 -1
  80. package/BookReader/images/icon_zoomer.png +0 -0
  81. package/BookReader/images/loading.gif +0 -0
  82. package/BookReader/images/logo_icon.png +0 -0
  83. package/BookReader/images/marker_chap-off.png +0 -0
  84. package/BookReader/images/marker_chap-off.svg +0 -1
  85. package/BookReader/images/marker_chap-off_ia.png +0 -0
  86. package/BookReader/images/marker_chap-on.png +0 -0
  87. package/BookReader/images/marker_chap-on.svg +0 -1
  88. package/BookReader/images/marker_srch-on.svg +0 -1
  89. package/BookReader/images/marker_srchchap-off.png +0 -0
  90. package/BookReader/images/marker_srchchap-on.png +0 -0
  91. package/BookReader/images/nav_control-dn.png +0 -0
  92. package/BookReader/images/nav_control-dn_ia.png +0 -0
  93. package/BookReader/images/nav_control-up.png +0 -0
  94. package/BookReader/images/nav_control-up_ia.png +0 -0
  95. package/BookReader/images/nav_control.png +0 -0
  96. package/BookReader/images/one_page_mode_icon.png +0 -0
  97. package/BookReader/images/paper-badge.png +0 -0
  98. package/BookReader/images/print_icon.png +0 -0
  99. package/BookReader/images/progressbar.gif +0 -0
  100. package/BookReader/images/right_edges.png +0 -0
  101. package/BookReader/images/slider.png +0 -0
  102. package/BookReader/images/slider_ia.png +0 -0
  103. package/BookReader/images/thumbnail_mode_icon.png +0 -0
  104. package/BookReader/images/transparent.png +0 -0
  105. package/BookReader/images/two_page_mode_icon.png +0 -0
  106. package/BookReader/images/unviewable_page.png +0 -0
  107. package/BookReader/images/zoom_in_icon.png +0 -0
  108. package/BookReader/images/zoom_out_icon.png +0 -0
  109. package/BookReader/jquery-3.js +0 -2
  110. package/BookReader/jquery-3.js.LICENSE.txt +0 -24
  111. package/BookReader/plugins/plugin.archive_analytics.js +0 -2
  112. package/BookReader/plugins/plugin.archive_analytics.js.map +0 -1
  113. package/BookReader/plugins/plugin.autoplay.js +0 -2
  114. package/BookReader/plugins/plugin.autoplay.js.map +0 -1
  115. package/BookReader/plugins/plugin.chapters.js +0 -26
  116. package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +0 -1
  117. package/BookReader/plugins/plugin.chapters.js.map +0 -1
  118. package/BookReader/plugins/plugin.experiments.js +0 -3
  119. package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +0 -1
  120. package/BookReader/plugins/plugin.experiments.js.map +0 -1
  121. package/BookReader/plugins/plugin.iframe.js +0 -2
  122. package/BookReader/plugins/plugin.iframe.js.map +0 -1
  123. package/BookReader/plugins/plugin.iiif.js +0 -2
  124. package/BookReader/plugins/plugin.iiif.js.map +0 -1
  125. package/BookReader/plugins/plugin.resume.js +0 -2
  126. package/BookReader/plugins/plugin.resume.js.map +0 -1
  127. package/BookReader/plugins/plugin.search.js +0 -3
  128. package/BookReader/plugins/plugin.search.js.LICENSE.txt +0 -1
  129. package/BookReader/plugins/plugin.search.js.map +0 -1
  130. package/BookReader/plugins/plugin.text_selection.js +0 -3
  131. package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +0 -1
  132. package/BookReader/plugins/plugin.text_selection.js.map +0 -1
  133. package/BookReader/plugins/plugin.tts.js +0 -3
  134. package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -29
  135. package/BookReader/plugins/plugin.tts.js.map +0 -1
  136. package/BookReader/plugins/plugin.url.js +0 -2
  137. package/BookReader/plugins/plugin.url.js.map +0 -1
  138. package/BookReader/plugins/plugin.vendor-fullscreen.js +0 -2
  139. package/BookReader/plugins/plugin.vendor-fullscreen.js.map +0 -1
  140. package/BookReader/webcomponents-bundle.js +0 -3
  141. package/BookReader/webcomponents-bundle.js.LICENSE.txt +0 -9
  142. package/BookReader/webcomponents-bundle.js.map +0 -1
  143. package/src/BookReader/BookModel.js +0 -564
  144. package/src/BookReader/DragScrollable.js +0 -233
  145. package/src/BookReader/ImageCache.js +0 -149
  146. package/src/BookReader/Mode1Up.js +0 -110
  147. package/src/BookReader/Mode1UpLit.js +0 -388
  148. package/src/BookReader/Mode2Up.js +0 -107
  149. package/src/BookReader/Mode2UpLit.js +0 -777
  150. package/src/BookReader/ModeCoordinateSpace.js +0 -29
  151. package/src/BookReader/ModeSmoothZoom.js +0 -312
  152. package/src/BookReader/ModeThumb.js +0 -344
  153. package/src/BookReader/Navbar/Navbar.js +0 -355
  154. package/src/BookReader/PageContainer.js +0 -172
  155. package/src/BookReader/ReduceSet.js +0 -26
  156. package/src/BookReader/Toolbar/Toolbar.js +0 -362
  157. package/src/BookReader/events.js +0 -19
  158. package/src/BookReader/options.js +0 -387
  159. package/src/BookReader/utils/HTMLDimensionsCacher.js +0 -44
  160. package/src/BookReader/utils/ScrollClassAdder.js +0 -31
  161. package/src/BookReader/utils/SelectionObserver.js +0 -45
  162. package/src/BookReader/utils/classes.js +0 -36
  163. package/src/BookReader/utils.js +0 -313
  164. package/tests/jest/BookReader/BookModel.test.js +0 -372
  165. package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +0 -263
  166. package/tests/jest/BookReader/ImageCache.test.js +0 -150
  167. package/tests/jest/BookReader/Mode1UpLit.test.js +0 -73
  168. package/tests/jest/BookReader/Mode2Up.test.js +0 -98
  169. package/tests/jest/BookReader/Mode2UpLit.test.js +0 -190
  170. package/tests/jest/BookReader/ModeCoordinateSpace.test.js +0 -16
  171. package/tests/jest/BookReader/ModeSmoothZoom.test.js +0 -218
  172. package/tests/jest/BookReader/ModeThumb.test.js +0 -71
  173. package/tests/jest/BookReader/Navbar/Navbar.test.js +0 -182
  174. package/tests/jest/BookReader/PageContainer.test.js +0 -249
  175. package/tests/jest/BookReader/ReduceSet.test.js +0 -38
  176. package/tests/jest/BookReader/Toolbar/Toolbar.test.js +0 -26
  177. package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +0 -59
  178. package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +0 -49
  179. package/tests/jest/BookReader/utils/SelectionObserver.test.js +0 -57
  180. package/tests/jest/BookReader/utils/classes.test.js +0 -88
  181. package/tests/jest/BookReader/utils.test.js +0 -250
@@ -1,29 +0,0 @@
1
- import { calcScreenDPI } from './utils.js';
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
- }
@@ -1,312 +0,0 @@
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,344 +0,0 @@
1
- // @ts-check
2
- import { notInArray, clamp } from './utils.js';
3
- import { EVENTS } from './events.js';
4
- import { DragScrollable } from './DragScrollable.js';
5
- /** @typedef {import('../BookREader.js').default} BookReader */
6
- /** @typedef {import('./BookModel.js').PageIndex} PageIndex */
7
- /** @typedef {import('./BookModel.js').BookModel} BookModel */
8
-
9
- /** @typedef {JQuery} $lazyLoadImgPlaceholder * jQuery element with data attributes: leaf, reduce */
10
-
11
- export class ModeThumb {
12
- name = 'thumb'
13
-
14
- /**
15
- * @param {BookReader} br
16
- * @param {BookModel} bookModel
17
- */
18
- constructor(br, bookModel) {
19
- this.br = br;
20
- this.book = bookModel;
21
- }
22
-
23
- /**
24
- * Draws the thumbnail view
25
- * @param {number} [seekIndex] If seekIndex is defined, the view will be drawn
26
- * with that page visible (without any animated scrolling).
27
- *
28
- * Creates place holder for image to load after gallery has been drawn
29
- */
30
- drawLeafs(seekIndex) {
31
- const { floor } = Math;
32
- const { book } = this;
33
- const viewWidth = this.br.refs.$brContainer.prop('scrollWidth') - 20; // width minus buffer
34
-
35
- let leafHeight;
36
- let rightPos = 0;
37
- let bottomPos = 0;
38
- let maxRight = 0;
39
- let currentRow = 0;
40
- let leafIndex = 0;
41
- /** @type {Array<{ leafs?: Array<{num: PageIndex, left: number}>, height?: number, top?: number }>} */
42
- const leafMap = [];
43
-
44
- // Will be set to top of requested seek index, if set
45
- let seekTop;
46
-
47
- // Calculate the position of every thumbnail. $$$ cache instead of calculating on every draw
48
- // make `leafMap`
49
- for (const page of book.pagesIterator({ combineConsecutiveUnviewables: true })) {
50
- const leafWidth = this.br.thumbWidth;
51
- if (rightPos + (leafWidth + this.br.thumbPadding) > viewWidth) {
52
- currentRow++;
53
- rightPos = 0;
54
- leafIndex = 0;
55
- }
56
-
57
- // Init current row in leafMap
58
- if (!leafMap[currentRow]) {
59
- leafMap[currentRow] = {};
60
- }
61
- if (!leafMap[currentRow].leafs) {
62
- leafMap[currentRow].leafs = [];
63
- leafMap[currentRow].height = 0;
64
- leafMap[currentRow].top = 0;
65
- }
66
- leafMap[currentRow].leafs[leafIndex] = {
67
- num: page.index,
68
- left: rightPos,
69
- };
70
-
71
- leafHeight = floor((page.height * this.br.thumbWidth) / page.width);
72
- if (leafHeight > leafMap[currentRow].height) {
73
- leafMap[currentRow].height = leafHeight;
74
- }
75
- if (leafIndex === 0) { bottomPos += this.br.thumbPadding + leafMap[currentRow].height; }
76
- rightPos += leafWidth + this.br.thumbPadding;
77
- if (rightPos > maxRight) { maxRight = rightPos; }
78
- leafIndex++;
79
-
80
- if (page.index == seekIndex) {
81
- seekTop = bottomPos - this.br.thumbPadding - leafMap[currentRow].height;
82
- }
83
- }
84
-
85
- // reset the bottom position based on thumbnails
86
- this.br.refs.$brPageViewEl.height(bottomPos);
87
-
88
- const pageViewBuffer = floor((this.br.refs.$brContainer.prop('scrollWidth') - maxRight) / 2) - 14;
89
-
90
- // If seekTop is defined, seeking was requested and target found
91
- if (typeof(seekTop) != 'undefined') {
92
- this.br.refs.$brContainer.scrollTop(seekTop);
93
- }
94
-
95
- const scrollTop = this.br.refs.$brContainer.prop('scrollTop');
96
- const scrollBottom = scrollTop + this.br.refs.$brContainer.height();
97
-
98
- let leafTop = 0;
99
- let leafBottom = 0;
100
- const rowsToDisplay = [];
101
- const imagesToDisplay = [];
102
-
103
- // Visible leafs with least/greatest index
104
- let leastVisible = book.getNumLeafs() - 1;
105
- let mostVisible = 0;
106
-
107
- // Determine the thumbnails in view
108
- for (let i = 0; i < leafMap.length; i++) {
109
- if (!leafMap[i]) { continue; }
110
- leafBottom += this.br.thumbPadding + leafMap[i].height;
111
- const topInView = (leafTop >= scrollTop) && (leafTop <= scrollBottom);
112
- const bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
113
- const middleInView = (leafTop <= scrollTop) && (leafBottom >= scrollBottom);
114
- if (topInView || bottomInView || middleInView) {
115
- rowsToDisplay.push(i);
116
- if (leafMap[i].leafs[0].num < leastVisible) {
117
- leastVisible = leafMap[i].leafs[0].num;
118
- }
119
- if (leafMap[i].leafs[leafMap[i].leafs.length - 1].num > mostVisible) {
120
- mostVisible = leafMap[i].leafs[leafMap[i].leafs.length - 1].num;
121
- }
122
- }
123
- if (leafTop > leafMap[i].top) { leafMap[i].top = leafTop; }
124
- leafTop = leafBottom;
125
- }
126
- // at this point, `rowsToDisplay` now has all the rows in view
127
-
128
- // create a buffer of preloaded rows before and after the visible rows
129
- const firstRow = rowsToDisplay[0];
130
- const lastRow = rowsToDisplay[rowsToDisplay.length - 1];
131
- for (let i = 1; i < this.br.thumbRowBuffer + 1; i++) {
132
- if (lastRow + i < leafMap.length) { rowsToDisplay.push(lastRow + i); }
133
- }
134
- for (let i = 1; i < this.br.thumbRowBuffer; i++) {
135
- if (firstRow - i >= 0) { rowsToDisplay.push(firstRow - i); }
136
- }
137
- rowsToDisplay.sort((a, b) => a - b);
138
-
139
- // Create the thumbnail divs and images (lazy loaded)
140
- for (const row of rowsToDisplay) {
141
- if (notInArray(row, this.br.displayedRows)) {
142
- if (!leafMap[row]) { continue; }
143
- for (const { num: leaf, left: leafLeft } of leafMap[row].leafs) {
144
- const leafWidth = this.br.thumbWidth;
145
- const leafHeight = floor((book.getPageHeight(leaf) * this.br.thumbWidth) / book.getPageWidth(leaf));
146
- const leafTop = leafMap[row].top;
147
- let left = leafLeft + pageViewBuffer;
148
- if ('rl' == this.br.pageProgression) {
149
- left = viewWidth - leafWidth - left;
150
- }
151
-
152
- left += this.br.thumbPadding;
153
- imagesToDisplay.push(leaf);
154
-
155
- /* get thumbnail's reducer */
156
- const idealReduce = floor(book.getPageWidth(leaf) / this.br.thumbWidth);
157
- const nearestFactor2 = 2 * Math.round(idealReduce / 2);
158
- const thumbReduce = nearestFactor2;
159
-
160
- const pageContainer = this.br._createPageContainer(leaf)
161
- .update({
162
- dimensions: {
163
- width: leafWidth,
164
- height: leafHeight,
165
- top: leafTop,
166
- left,
167
- },
168
- reduce: thumbReduce,
169
- });
170
-
171
- pageContainer.$container.data('leaf', leaf).on('mouseup', event => {
172
- // We want to suppress the fragmentChange triggers in `updateFirstIndex` and `switchMode`
173
- // because otherwise it repeatedly triggers listeners and we get in an infinite loop.
174
- // We manually trigger the `fragmentChange` once at the end.
175
- this.br.updateFirstIndex(leaf, { suppressFragmentChange: true });
176
- // as per request in webdev-4042, we want to switch 1-up mode while clicking on thumbnail leafs
177
- this.br.switchMode(this.br.constMode1up, { suppressFragmentChange: true });
178
-
179
- // shift viewModeOrder after clicking on thumbsnail leaf
180
- const nextModeID = this.br.viewModeOrder.shift();
181
- this.br.viewModeOrder.push(nextModeID);
182
- this.br._components.navbar.updateViewModeButton($('.viewmode'), 'twopg', 'Two-page view');
183
-
184
- this.br.trigger(EVENTS.fragmentChange);
185
- event.stopPropagation();
186
- });
187
-
188
- this.br.refs.$brPageViewEl.append(pageContainer.$container);
189
- }
190
- }
191
- }
192
-
193
- // Remove thumbnails that are not to be displayed
194
- for (const row of this.br.displayedRows) {
195
- if (notInArray(row, rowsToDisplay)) {
196
- for (const { num: index } of leafMap[row]?.leafs) {
197
- if (!imagesToDisplay?.includes(index)) {
198
- this.br.$(`.pagediv${index}`)?.remove();
199
- }
200
- }
201
- }
202
- }
203
-
204
- // Update which page is considered current to make sure a visible page is the current one
205
- const currentIndex = this.br.currentIndex();
206
- if (currentIndex < leastVisible) {
207
- this.br.updateFirstIndex(leastVisible);
208
- } else if (currentIndex > mostVisible) {
209
- this.br.updateFirstIndex(mostVisible);
210
- }
211
-
212
- // remember what rows are displayed
213
- this.br.displayedRows = rowsToDisplay.slice();
214
-
215
- // remove previous highlights
216
- this.br.$('.BRpagedivthumb_highlight').removeClass('BRpagedivthumb_highlight');
217
-
218
- // highlight current page
219
- this.br.$('.pagediv' + this.br.currentIndex()).addClass('BRpagedivthumb_highlight');
220
- }
221
-
222
- /**
223
- * Replaces placeholder image with real one
224
- *
225
- * @param {$lazyLoadImgPlaceholder} imgPlaceholder
226
- */
227
- lazyLoadImage(imgPlaceholder) {
228
- const leaf = $(imgPlaceholder).data('leaf');
229
- const reduce = $(imgPlaceholder).data('reduce');
230
- const $img = this.br.imageCache.image(leaf, reduce);
231
- const $parent = $(imgPlaceholder).parent();
232
- /* March 16, 2021 (isa) - manually append & remove, `replaceWith` currently loses closure scope */
233
- $($parent).append($img);
234
- $(imgPlaceholder).remove();
235
- }
236
-
237
- /**
238
- * @param {'in' | 'out'} direction
239
- */
240
- zoom(direction) {
241
- const oldColumns = this.br.thumbColumns;
242
- switch (direction) {
243
- case 'in':
244
- this.br.thumbColumns -= 1;
245
- break;
246
- case 'out':
247
- this.br.thumbColumns += 1;
248
- break;
249
- default:
250
- console.error(`Unsupported direction: ${direction}`);
251
- }
252
-
253
- // Limit zoom in/out columns
254
- this.br.thumbColumns = clamp(
255
- this.br.thumbColumns,
256
- this.br.options.thumbMinZoomColumns,
257
- this.br.options.thumbMaxZoomColumns,
258
- );
259
-
260
- if (this.br.thumbColumns != oldColumns) {
261
- this.br.displayedRows = []; /* force a gallery redraw */
262
- this.prepare();
263
- }
264
- }
265
-
266
- /**
267
- * Returns the width per thumbnail to display the requested number of columns
268
- * Note: #BRpageview must already exist since its width is used to calculate the
269
- * thumbnail width
270
- * @param {number} thumbnailColumns
271
- */
272
- getThumbnailWidth(thumbnailColumns) {
273
- const DEFAULT_THUMBNAIL_WIDTH = 100;
274
-
275
- const padding = (thumbnailColumns + 1) * this.br.thumbPadding;
276
- const width = (this.br.refs.$brPageViewEl.width() - padding) / (thumbnailColumns + 0.5); // extra 0.5 is for some space at sides
277
- const idealThumbnailWidth = Math.floor(width);
278
- return idealThumbnailWidth > 0 ? idealThumbnailWidth : DEFAULT_THUMBNAIL_WIDTH;
279
- }
280
-
281
- prepare() {
282
- this.br.refs.$brContainer.empty();
283
- this.br.refs.$brContainer.css({
284
- overflowY: 'scroll',
285
- overflowX: 'auto',
286
- });
287
-
288
- this.br.refs.$brPageViewEl = $("<div class='BRpageview'></div>");
289
- this.br.refs.$brContainer.append(this.br.refs.$brPageViewEl);
290
- this.dragScrollable = this.dragScrollable || new DragScrollable(this.br.refs.$brContainer[0], {preventDefault: true});
291
-
292
- this.br.bindGestures(this.br.refs.$brContainer);
293
-
294
- // $$$ keep select enabled for now since disabling it breaks keyboard
295
- // nav in FF 3.6 (https://bugs.edge.launchpad.net/bookreader/+bug/544666)
296
- // disableSelect(this.br.$('#BRpageview'));
297
- this.br.thumbWidth = this.getThumbnailWidth(this.br.thumbColumns);
298
- this.br.reduce = this.book.getPageWidth(0) / this.br.thumbWidth;
299
- this.br.displayedRows = [];
300
- // Draw leafs with current index directly in view (no animating to the index)
301
- this.drawLeafs(this.br.currentIndex());
302
- this.br.updateBrClasses();
303
- }
304
-
305
- /**
306
- * @param {PageIndex} index
307
- */
308
- jumpToIndex(index) {
309
- const { floor } = Math;
310
- const { book } = this;
311
- const viewWidth = this.br.refs.$brContainer.prop('scrollWidth') - 20; // width minus buffer
312
- const leafWidth = this.br.thumbWidth;
313
- let leafTop = 0;
314
- let rightPos = 0;
315
- let bottomPos = 0;
316
- let rowHeight = 0;
317
- let leafIndex = 0;
318
-
319
- for (let i = 0; i <= index; i++) {
320
- if (rightPos + (leafWidth + this.br.thumbPadding) > viewWidth) {
321
- rightPos = 0;
322
- rowHeight = 0;
323
- leafIndex = 0;
324
- }
325
-
326
- const leafHeight = floor((book.getPageHeight(leafIndex) * this.br.thumbWidth) / book.getPageWidth(leafIndex));
327
- if (leafHeight > rowHeight) { rowHeight = leafHeight; }
328
- if (leafIndex == 0) {
329
- leafTop = bottomPos;
330
- bottomPos += this.br.thumbPadding + rowHeight;
331
- }
332
- rightPos += leafWidth + this.br.thumbPadding;
333
- leafIndex++;
334
- }
335
- this.br.updateFirstIndex(index);
336
- if (this.br.refs.$brContainer.prop('scrollTop') == leafTop) {
337
- this.br.drawLeafs();
338
- } else {
339
- this.br.animating = true;
340
- this.br.refs.$brContainer.stop(true)
341
- .animate({ scrollTop: leafTop }, 'fast', () => { this.br.animating = false; });
342
- }
343
- }
344
- }