@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.
- package/.github/workflows/npm-publish.yml +2 -12
- package/CHANGELOG.md +7 -0
- package/README.md +0 -2
- package/package.json +1 -1
- package/scripts/version.js +0 -3
- package/BookReader/BookReader.css +0 -2251
- package/BookReader/BookReader.js +0 -3
- package/BookReader/BookReader.js.LICENSE.txt +0 -72
- package/BookReader/BookReader.js.map +0 -1
- package/BookReader/ia-bookreader-bundle.js +0 -1782
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +0 -7
- package/BookReader/ia-bookreader-bundle.js.map +0 -1
- package/BookReader/icons/1up.svg +0 -1
- package/BookReader/icons/2up.svg +0 -1
- package/BookReader/icons/advance.svg +0 -3
- package/BookReader/icons/chevron-right.svg +0 -1
- package/BookReader/icons/close-circle-dark.svg +0 -1
- package/BookReader/icons/close-circle.svg +0 -1
- package/BookReader/icons/fullscreen.svg +0 -1
- package/BookReader/icons/fullscreen_exit.svg +0 -1
- package/BookReader/icons/hamburger.svg +0 -1
- package/BookReader/icons/left-arrow.svg +0 -1
- package/BookReader/icons/magnify-minus.svg +0 -1
- package/BookReader/icons/magnify-plus.svg +0 -1
- package/BookReader/icons/magnify.svg +0 -1
- package/BookReader/icons/pause.svg +0 -1
- package/BookReader/icons/play.svg +0 -1
- package/BookReader/icons/playback-speed.svg +0 -1
- package/BookReader/icons/read-aloud.svg +0 -1
- package/BookReader/icons/review.svg +0 -3
- package/BookReader/icons/thumbnails.svg +0 -1
- package/BookReader/icons/voice.svg +0 -1
- package/BookReader/icons/volume-full.svg +0 -1
- package/BookReader/images/BRicons.png +0 -0
- package/BookReader/images/BRicons.svg +0 -5
- package/BookReader/images/BRicons_ia.png +0 -0
- package/BookReader/images/back_pages.png +0 -0
- package/BookReader/images/book_bottom_icon.png +0 -0
- package/BookReader/images/book_down_icon.png +0 -0
- package/BookReader/images/book_left_icon.png +0 -0
- package/BookReader/images/book_leftmost_icon.png +0 -0
- package/BookReader/images/book_right_icon.png +0 -0
- package/BookReader/images/book_rightmost_icon.png +0 -0
- package/BookReader/images/book_top_icon.png +0 -0
- package/BookReader/images/book_up_icon.png +0 -0
- package/BookReader/images/books_graphic.svg +0 -1
- package/BookReader/images/booksplit.png +0 -0
- package/BookReader/images/control_pause_icon.png +0 -0
- package/BookReader/images/control_play_icon.png +0 -0
- package/BookReader/images/embed_icon.png +0 -0
- package/BookReader/images/hypothesis.ico +0 -0
- package/BookReader/images/icon-home-ia.png +0 -0
- package/BookReader/images/icon_OL-logo-xs.png +0 -0
- package/BookReader/images/icon_alert-xs.png +0 -0
- package/BookReader/images/icon_book.svg +0 -1
- package/BookReader/images/icon_bookmark.svg +0 -1
- package/BookReader/images/icon_close-pop.png +0 -0
- package/BookReader/images/icon_download.png +0 -0
- package/BookReader/images/icon_gear.svg +0 -1
- package/BookReader/images/icon_hamburger.svg +0 -1
- package/BookReader/images/icon_home.png +0 -0
- package/BookReader/images/icon_home.svg +0 -1
- package/BookReader/images/icon_home_ia.png +0 -0
- package/BookReader/images/icon_indicator.png +0 -0
- package/BookReader/images/icon_info.svg +0 -1
- package/BookReader/images/icon_one_page.svg +0 -1
- package/BookReader/images/icon_pause.svg +0 -1
- package/BookReader/images/icon_play.svg +0 -1
- package/BookReader/images/icon_playback-rate.svg +0 -1
- package/BookReader/images/icon_return.png +0 -0
- package/BookReader/images/icon_search_button.svg +0 -1
- package/BookReader/images/icon_share.svg +0 -1
- package/BookReader/images/icon_skip-ahead.svg +0 -1
- package/BookReader/images/icon_skip-back.svg +0 -2
- package/BookReader/images/icon_speaker.svg +0 -1
- package/BookReader/images/icon_speaker_open.svg +0 -1
- package/BookReader/images/icon_thumbnails.svg +0 -1
- package/BookReader/images/icon_toc.svg +0 -1
- package/BookReader/images/icon_two_pages.svg +0 -1
- package/BookReader/images/icon_zoomer.png +0 -0
- package/BookReader/images/loading.gif +0 -0
- package/BookReader/images/logo_icon.png +0 -0
- package/BookReader/images/marker_chap-off.png +0 -0
- package/BookReader/images/marker_chap-off.svg +0 -1
- package/BookReader/images/marker_chap-off_ia.png +0 -0
- package/BookReader/images/marker_chap-on.png +0 -0
- package/BookReader/images/marker_chap-on.svg +0 -1
- package/BookReader/images/marker_srch-on.svg +0 -1
- package/BookReader/images/marker_srchchap-off.png +0 -0
- package/BookReader/images/marker_srchchap-on.png +0 -0
- package/BookReader/images/nav_control-dn.png +0 -0
- package/BookReader/images/nav_control-dn_ia.png +0 -0
- package/BookReader/images/nav_control-up.png +0 -0
- package/BookReader/images/nav_control-up_ia.png +0 -0
- package/BookReader/images/nav_control.png +0 -0
- package/BookReader/images/one_page_mode_icon.png +0 -0
- package/BookReader/images/paper-badge.png +0 -0
- package/BookReader/images/print_icon.png +0 -0
- package/BookReader/images/progressbar.gif +0 -0
- package/BookReader/images/right_edges.png +0 -0
- package/BookReader/images/slider.png +0 -0
- package/BookReader/images/slider_ia.png +0 -0
- package/BookReader/images/thumbnail_mode_icon.png +0 -0
- package/BookReader/images/transparent.png +0 -0
- package/BookReader/images/two_page_mode_icon.png +0 -0
- package/BookReader/images/unviewable_page.png +0 -0
- package/BookReader/images/zoom_in_icon.png +0 -0
- package/BookReader/images/zoom_out_icon.png +0 -0
- package/BookReader/jquery-3.js +0 -2
- package/BookReader/jquery-3.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.archive_analytics.js +0 -2
- package/BookReader/plugins/plugin.archive_analytics.js.map +0 -1
- package/BookReader/plugins/plugin.autoplay.js +0 -2
- package/BookReader/plugins/plugin.autoplay.js.map +0 -1
- package/BookReader/plugins/plugin.chapters.js +0 -26
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.chapters.js.map +0 -1
- package/BookReader/plugins/plugin.experiments.js +0 -3
- package/BookReader/plugins/plugin.experiments.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.experiments.js.map +0 -1
- package/BookReader/plugins/plugin.iframe.js +0 -2
- package/BookReader/plugins/plugin.iframe.js.map +0 -1
- package/BookReader/plugins/plugin.iiif.js +0 -2
- package/BookReader/plugins/plugin.iiif.js.map +0 -1
- package/BookReader/plugins/plugin.resume.js +0 -2
- package/BookReader/plugins/plugin.resume.js.map +0 -1
- package/BookReader/plugins/plugin.search.js +0 -3
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.search.js.map +0 -1
- package/BookReader/plugins/plugin.text_selection.js +0 -3
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +0 -1
- package/BookReader/plugins/plugin.text_selection.js.map +0 -1
- package/BookReader/plugins/plugin.tts.js +0 -3
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +0 -29
- package/BookReader/plugins/plugin.tts.js.map +0 -1
- package/BookReader/plugins/plugin.url.js +0 -2
- package/BookReader/plugins/plugin.url.js.map +0 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +0 -2
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +0 -1
- package/BookReader/webcomponents-bundle.js +0 -3
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +0 -9
- package/BookReader/webcomponents-bundle.js.map +0 -1
- package/src/BookReader/BookModel.js +0 -564
- package/src/BookReader/DragScrollable.js +0 -233
- package/src/BookReader/ImageCache.js +0 -149
- package/src/BookReader/Mode1Up.js +0 -110
- package/src/BookReader/Mode1UpLit.js +0 -388
- package/src/BookReader/Mode2Up.js +0 -107
- package/src/BookReader/Mode2UpLit.js +0 -777
- package/src/BookReader/ModeCoordinateSpace.js +0 -29
- package/src/BookReader/ModeSmoothZoom.js +0 -312
- package/src/BookReader/ModeThumb.js +0 -344
- package/src/BookReader/Navbar/Navbar.js +0 -355
- package/src/BookReader/PageContainer.js +0 -172
- package/src/BookReader/ReduceSet.js +0 -26
- package/src/BookReader/Toolbar/Toolbar.js +0 -362
- package/src/BookReader/events.js +0 -19
- package/src/BookReader/options.js +0 -387
- package/src/BookReader/utils/HTMLDimensionsCacher.js +0 -44
- package/src/BookReader/utils/ScrollClassAdder.js +0 -31
- package/src/BookReader/utils/SelectionObserver.js +0 -45
- package/src/BookReader/utils/classes.js +0 -36
- package/src/BookReader/utils.js +0 -313
- package/tests/jest/BookReader/BookModel.test.js +0 -372
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +0 -263
- package/tests/jest/BookReader/ImageCache.test.js +0 -150
- package/tests/jest/BookReader/Mode1UpLit.test.js +0 -73
- package/tests/jest/BookReader/Mode2Up.test.js +0 -98
- package/tests/jest/BookReader/Mode2UpLit.test.js +0 -190
- package/tests/jest/BookReader/ModeCoordinateSpace.test.js +0 -16
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +0 -218
- package/tests/jest/BookReader/ModeThumb.test.js +0 -71
- package/tests/jest/BookReader/Navbar/Navbar.test.js +0 -182
- package/tests/jest/BookReader/PageContainer.test.js +0 -249
- package/tests/jest/BookReader/ReduceSet.test.js +0 -38
- package/tests/jest/BookReader/Toolbar/Toolbar.test.js +0 -26
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +0 -59
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +0 -49
- package/tests/jest/BookReader/utils/SelectionObserver.test.js +0 -57
- package/tests/jest/BookReader/utils/classes.test.js +0 -88
- 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
|
-
}
|