@internetarchive/bookreader 5.0.0-65 → 5.0.0-67
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/BookReader/BookReader.css +16 -28
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.LICENSE.txt +0 -6
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +102 -114
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +24 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +1 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReaderDemo/demo-internetarchive.html +41 -3
- package/BookReaderDemo/toggle_controls.html +1 -1
- package/CHANGELOG.md +8 -0
- package/index.html +1 -1
- package/package.json +5 -4
- package/src/BookNavigator/book-navigator.js +4 -3
- package/src/BookReader/BookModel.js +12 -0
- package/src/BookReader/Mode1Up.js +1 -1
- package/src/BookReader/Mode1UpLit.js +0 -3
- package/src/BookReader/Mode2UpLit.js +0 -4
- package/src/BookReader/ModeSmoothZoom.js +153 -52
- package/src/BookReader/Navbar/Navbar.js +2 -2
- package/src/css/_BRnav.scss +5 -2
- package/src/css/_BRsearch.scss +6 -2
- package/src/css/_MobileNav.scss +0 -26
- package/src/css/_controls.scss +3 -2
- package/src/plugins/plugin.chapters.js +201 -169
- package/src/plugins/tts/plugin.tts.js +1 -1
- package/src/util/browserSniffing.js +22 -0
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +75 -32
- package/tests/jest/plugins/plugin.chapters.test.js +93 -76
|
@@ -161,6 +161,44 @@
|
|
|
161
161
|
</li>
|
|
162
162
|
</ul>
|
|
163
163
|
</li>
|
|
164
|
+
<li>
|
|
165
|
+
Chapters
|
|
166
|
+
<ul>
|
|
167
|
+
<li>
|
|
168
|
+
Normal book with chapters
|
|
169
|
+
<ul>
|
|
170
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=adventureofsherl0000unse">
|
|
171
|
+
<i>The Adventures of Sherlock Holmes</i> by Arthur Conan Doyle
|
|
172
|
+
</a></li>
|
|
173
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=jalna00dela">
|
|
174
|
+
<i>Jalna</i> by Mazo de la Roche
|
|
175
|
+
</a></li>
|
|
176
|
+
</ul>
|
|
177
|
+
</li>
|
|
178
|
+
<li>
|
|
179
|
+
Book preview
|
|
180
|
+
<ul>
|
|
181
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=peoplewareproduc00dema_0">
|
|
182
|
+
<i>Peopleware</i> by Tom DeMarco and Timothy Lister
|
|
183
|
+
</a></li>
|
|
184
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=passionforbooksb00rabi">
|
|
185
|
+
<i>A Passion for Books</i>
|
|
186
|
+
</a></li>
|
|
187
|
+
</ul>
|
|
188
|
+
</li>
|
|
189
|
+
<li>
|
|
190
|
+
Book with levelled chapters
|
|
191
|
+
<ul>
|
|
192
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=notredamedepar01hugo">
|
|
193
|
+
<i>Notre-Dame de Paris: Tome Premier</i> par Victor Hugo
|
|
194
|
+
</a></li>
|
|
195
|
+
<li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=alanturingenigma0000hodg">
|
|
196
|
+
<i>Alan Turing: The Enigma</i> by Andrew Hodges
|
|
197
|
+
</a></li>
|
|
198
|
+
</ul>
|
|
199
|
+
</li>
|
|
200
|
+
</ul>
|
|
201
|
+
</li>
|
|
164
202
|
</ul>
|
|
165
203
|
</details>
|
|
166
204
|
<div class="demo">
|
|
@@ -220,9 +258,9 @@
|
|
|
220
258
|
|
|
221
259
|
// analytics stub
|
|
222
260
|
window.archive_analytics = {
|
|
223
|
-
send_event_no_sampling: (category, action, label) => console.log('~~~ NO SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
224
|
-
send_event: (category, action, label) => console.log('~~~ send_event SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
225
|
-
send_ping: (category, action, label) => console.log('~~~ send_ping SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
261
|
+
send_event_no_sampling: (category, action, label) => {}, //console.log('~~~ NO SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
262
|
+
send_event: (category, action, label) => {}, //console.log('~~~ send_event SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
263
|
+
send_ping: (category, action, label) => {}, //console.log('~~~ send_ping SAMPLE EVENT CALLED: ', { category, action, label }),
|
|
226
264
|
}
|
|
227
265
|
</script>
|
|
228
266
|
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
template: function(br) {
|
|
41
41
|
return '<button class="BRicon ' + this.className + ' desktop-only js-tooltip" data-param="foo">' +
|
|
42
42
|
'<div class="icon icon-onepg"></div>' +
|
|
43
|
-
'<span class="
|
|
43
|
+
'<span class="BRtooltip">Overridden control</span>' +
|
|
44
44
|
'</button>';
|
|
45
45
|
}
|
|
46
46
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# 5.0.0-67
|
|
2
|
+
- Feature: Re-enable chapters plugin + migrate off mmenu @cdrini
|
|
3
|
+
- Fix: Disable tooltips on touchscreens + fix on IA @cdrini
|
|
4
|
+
- Dev: Remove core-js update block; issue resolved in core-js @cdrini
|
|
5
|
+
|
|
6
|
+
# 5.0.0-66
|
|
7
|
+
- Fix: Pinch zooming on iPad/iPhone, Samsung Internet @cdrini
|
|
8
|
+
|
|
1
9
|
# 5.0.0-65
|
|
2
10
|
- Dev: Remove Debug console dev helper @cdrini
|
|
3
11
|
- Dev: Fix deno esm.sh esbuild erroring @cdrini
|
package/index.html
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<!-- plugin.search.js -->
|
|
22
22
|
<li><a href="BookReaderDemo/demo-internetarchive.html?ocaid=theworksofplato01platiala">From Internet Archive</a></li>
|
|
23
23
|
<!-- plugin.search.js -->
|
|
24
|
-
<li><a href="BookReaderDemo/demo-internetarchive.html?ocaid=
|
|
24
|
+
<li><a href="BookReaderDemo/demo-internetarchive.html?ocaid=adventureofsherl0000unse">From Internet Archive - a book with CHAPTERS</a></li>
|
|
25
25
|
<li><a href="BookReaderDemo/demo-iiif.html">IIIF</a></li>
|
|
26
26
|
<li><a href="BookReaderDemo/demo-autoplay.html">Autoplay (kiosk mode)</a></li>
|
|
27
27
|
<li><a href="BookReaderDemo/demo-plugin-menu-toggle.html">Plugin: Full screen menu toggle</a></li>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@internetarchive/bookreader",
|
|
3
|
-
"version": "5.0.0-
|
|
3
|
+
"version": "5.0.0-67",
|
|
4
4
|
"description": "The Internet Archive BookReader.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"private": false,
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@internetarchive/ia-activity-indicator": "^0.0.4",
|
|
29
|
-
"@internetarchive/ia-item-navigator": "^1.0.
|
|
29
|
+
"@internetarchive/ia-item-navigator": "^1.0.4",
|
|
30
30
|
"@internetarchive/ia-sharing-options": "^1.0.2",
|
|
31
31
|
"@internetarchive/icon-bookmark": "^1.3.4",
|
|
32
32
|
"@internetarchive/icon-dl": "^1.3.4",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"@internetarchive/icon-magnify-plus": "^1.3.4",
|
|
36
36
|
"@internetarchive/icon-search": "^1.3.4",
|
|
37
37
|
"@internetarchive/icon-share": "^1.3.4",
|
|
38
|
+
"@internetarchive/icon-toc": "^1.3.4",
|
|
38
39
|
"@internetarchive/icon-visual-adjustment": "^1.3.4",
|
|
39
40
|
"@internetarchive/modal-manager": "^0.2.12",
|
|
40
41
|
"@internetarchive/shared-resize-observer": "^0.2.0",
|
|
@@ -57,8 +58,8 @@
|
|
|
57
58
|
"eslint": "^7.32.0",
|
|
58
59
|
"eslint-plugin-no-jquery": "^2.7.0",
|
|
59
60
|
"eslint-plugin-testcafe": "^0.2.1",
|
|
60
|
-
"hammerjs": "^2.0.8",
|
|
61
61
|
"http-server": "14.1.1",
|
|
62
|
+
"interactjs": "^1.10.18",
|
|
62
63
|
"iso-language-codes": "1.1.0",
|
|
63
64
|
"jest": "29.6.2",
|
|
64
65
|
"jest-environment-jsdom": "^29.4.3",
|
|
@@ -121,7 +122,7 @@
|
|
|
121
122
|
"DOCS:update:test-deps": "If CI succeeds, these should be good to update",
|
|
122
123
|
"update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest codecov@latest eslint@7 eslint-plugin-testcafe@latest jest@latest sinon@latest testcafe@latest",
|
|
123
124
|
"DOCS:update:build-deps": "These can cause strange changes, so do an npm run build + check file size (git diff --stat), and check the site is as expected",
|
|
124
|
-
"update:build-deps": "npm i @babel/core@latest @babel/preset-env@latest @babel/plugin-proposal-class-properties@latest @babel/plugin-proposal-decorators@latest babel-loader@latest core-js@
|
|
125
|
+
"update:build-deps": "npm i @babel/core@latest @babel/preset-env@latest @babel/plugin-proposal-class-properties@latest @babel/plugin-proposal-decorators@latest babel-loader@latest core-js@latest regenerator-runtime@latest sass@latest svgo@latest webpack@latest webpack-cli@latest",
|
|
125
126
|
"codecov": "npx codecov"
|
|
126
127
|
}
|
|
127
128
|
}
|
|
@@ -239,7 +239,7 @@ export class BookNavigator extends LitElement {
|
|
|
239
239
|
});
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
this.menuProviders
|
|
242
|
+
Object.assign(this.menuProviders, providers);
|
|
243
243
|
this.addMenuShortcut('search');
|
|
244
244
|
this.addMenuShortcut('volumes');
|
|
245
245
|
this.updateMenuContents();
|
|
@@ -306,9 +306,9 @@ export class BookNavigator extends LitElement {
|
|
|
306
306
|
*/
|
|
307
307
|
updateMenuContents() {
|
|
308
308
|
const {
|
|
309
|
-
search, downloads, visualAdjustments, share, bookmarks, volumes
|
|
309
|
+
search, downloads, visualAdjustments, share, bookmarks, volumes, chapters
|
|
310
310
|
} = this.menuProviders;
|
|
311
|
-
const availableMenus = [volumes, search, bookmarks, visualAdjustments, share].filter((menu) => !!menu);
|
|
311
|
+
const availableMenus = [volumes, chapters, search, bookmarks, visualAdjustments, share].filter((menu) => !!menu);
|
|
312
312
|
|
|
313
313
|
if (this.shouldShowDownloadsMenu()) {
|
|
314
314
|
downloads?.update(this.downloadableTypes);
|
|
@@ -410,6 +410,7 @@ export class BookNavigator extends LitElement {
|
|
|
410
410
|
bindEventListeners() {
|
|
411
411
|
window.addEventListener('BookReader:PostInit', (e) => {
|
|
412
412
|
this.bookreader = e.detail.props;
|
|
413
|
+
this.bookreader.shell = this;
|
|
413
414
|
this.bookReaderLoaded = true;
|
|
414
415
|
this.bookReaderCannotLoad = false;
|
|
415
416
|
this.emitLoadingStatusUpdate(true);
|
|
@@ -30,6 +30,18 @@ export class BookModel {
|
|
|
30
30
|
this._medianPageSize = null;
|
|
31
31
|
/** @type {[PageData[], number]} */
|
|
32
32
|
this._getDataFlattenedCached = null;
|
|
33
|
+
|
|
34
|
+
// Heal missing first page number assertion
|
|
35
|
+
const pages = this._getDataFlattened();
|
|
36
|
+
const firstNumberedPageIndex = pages.findIndex(page => page.pageNum != undefined && !isNaN(parseFloat(page.pageNum)));
|
|
37
|
+
if (firstNumberedPageIndex != -1 && firstNumberedPageIndex > 0) {
|
|
38
|
+
const pageNum = parseFloat(pages[firstNumberedPageIndex].pageNum);
|
|
39
|
+
if (!isNaN(pageNum)) {
|
|
40
|
+
// Note: Since the pages are always sorted in increasing pageNum/index
|
|
41
|
+
// order, this will work for both left-to-right and right-to-left books
|
|
42
|
+
pages[firstNumberedPageIndex - 1].pageNum = pageNum - 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
33
45
|
}
|
|
34
46
|
|
|
35
47
|
/** Get median width/height of page in inches. Memoized for performance. */
|
|
@@ -51,7 +51,7 @@ export class Mode1Up {
|
|
|
51
51
|
new DragScrollable(this.mode1UpLit, {
|
|
52
52
|
preventDefault: true,
|
|
53
53
|
dragSelector: '.br-mode-1up__visible-world',
|
|
54
|
-
// Only handle mouse events; let browser/
|
|
54
|
+
// Only handle mouse events; let browser/interact.js handle touch
|
|
55
55
|
dragstart: 'mousedown',
|
|
56
56
|
dragcontinue: 'mousemove',
|
|
57
57
|
dragend: 'mouseup',
|
|
@@ -47,9 +47,6 @@ export class Mode1UpLit extends LitElement {
|
|
|
47
47
|
|
|
48
48
|
@property({ type: Number })
|
|
49
49
|
scale = 1;
|
|
50
|
-
/** Position (in unit-less, [0, 1] coordinates) in client to scale around */
|
|
51
|
-
@property({ type: Object })
|
|
52
|
-
scaleCenter = { x: 0.5, y: 0.5 };
|
|
53
50
|
|
|
54
51
|
/************** VIRTUAL-SCROLLING PROPERTIES **************/
|
|
55
52
|
|
|
@@ -41,10 +41,6 @@ export class Mode2UpLit extends LitElement {
|
|
|
41
41
|
|
|
42
42
|
initialScale = 1;
|
|
43
43
|
|
|
44
|
-
/** Position (in unit-less, [0, 1] coordinates) in client to scale around */
|
|
45
|
-
@property({ type: Object })
|
|
46
|
-
scaleCenter = { x: 0.5, y: 0.5 };
|
|
47
|
-
|
|
48
44
|
/** @type {import('./options').AutoFitValues} */
|
|
49
45
|
@property({ type: String })
|
|
50
46
|
autoFit = 'auto';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import
|
|
2
|
+
import interact from 'interactjs';
|
|
3
|
+
import { isIOS, isSamsungInternet } from '../util/browserSniffing.js';
|
|
4
|
+
import { sleep } from './utils.js';
|
|
3
5
|
/** @typedef {import('./utils/HTMLDimensionsCacher.js').HTMLDimensionsCacher} HTMLDimensionsCacher */
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -8,7 +10,6 @@ import Hammer from "hammerjs";
|
|
|
8
10
|
* @property {HTMLElement} $visibleWorld
|
|
9
11
|
* @property {import("./options.js").AutoFitValues} autoFit
|
|
10
12
|
* @property {number} scale
|
|
11
|
-
* @property {{ x: number, y: number }} scaleCenter
|
|
12
13
|
* @property {HTMLDimensionsCacher} htmlDimensionsCacher
|
|
13
14
|
* @property {function(): void} [attachScrollListeners]
|
|
14
15
|
* @property {function(): void} [detachScrollListeners]
|
|
@@ -16,31 +17,27 @@ import Hammer from "hammerjs";
|
|
|
16
17
|
|
|
17
18
|
/** Manages pinch-zoom, ctrl-wheel, and trackpad pinch smooth zooming. */
|
|
18
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
|
+
|
|
19
23
|
/** @param {SmoothZoomable} mode */
|
|
20
24
|
constructor(mode) {
|
|
21
25
|
/** @type {SmoothZoomable} */
|
|
22
26
|
this.mode = mode;
|
|
23
27
|
|
|
28
|
+
/** Whether a pinch is currently happening */
|
|
29
|
+
this.pinching = false;
|
|
24
30
|
/** Non-null when a scale has been enqueued/is being processed by the buffer function */
|
|
25
31
|
this.pinchMoveFrame = null;
|
|
26
32
|
/** Promise for the current/enqueued pinch move frame. Resolves when it is complete. */
|
|
27
33
|
this.pinchMoveFramePromise = Promise.resolve();
|
|
28
34
|
this.oldScale = 1;
|
|
29
|
-
/** @type {{ scale: number,
|
|
35
|
+
/** @type {{ scale: number, clientX: number, clientY: number }}} */
|
|
30
36
|
this.lastEvent = null;
|
|
31
37
|
this.attached = false;
|
|
32
38
|
|
|
33
39
|
/** @type {function(function(): void): any} */
|
|
34
40
|
this.bufferFn = window.requestAnimationFrame.bind(window);
|
|
35
|
-
|
|
36
|
-
// Hammer.js by default set userSelect to None; we don't want that!
|
|
37
|
-
// TODO: Is there any way to do this not globally on Hammer?
|
|
38
|
-
delete Hammer.defaults.cssProps.userSelect;
|
|
39
|
-
this.hammer = new Hammer.Manager(this.mode.$container, {
|
|
40
|
-
touchAction: "pan-x pan-y",
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
this.hammer.add(new Hammer.Pinch());
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
attach() {
|
|
@@ -48,17 +45,44 @@ export class ModeSmoothZoom {
|
|
|
48
45
|
|
|
49
46
|
this.attachCtrlZoom();
|
|
50
47
|
|
|
51
|
-
// GestureEvents work only on Safari; they
|
|
52
|
-
//
|
|
53
|
-
|
|
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);
|
|
54
52
|
this.mode.$container.addEventListener('gesturechange', this._preventEvent);
|
|
55
|
-
this.mode.$container.addEventListener('gestureend', this.
|
|
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";
|
|
56
61
|
|
|
57
62
|
// The pinch listeners
|
|
58
|
-
this.
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
|
|
62
86
|
|
|
63
87
|
this.attached = true;
|
|
64
88
|
}
|
|
@@ -68,15 +92,15 @@ export class ModeSmoothZoom {
|
|
|
68
92
|
|
|
69
93
|
// GestureEvents work only on Safari; they interfere with Hammer,
|
|
70
94
|
// so block them.
|
|
71
|
-
this.mode.$container.removeEventListener('gesturestart', this.
|
|
95
|
+
this.mode.$container.removeEventListener('gesturestart', this._pinchStart);
|
|
72
96
|
this.mode.$container.removeEventListener('gesturechange', this._preventEvent);
|
|
73
|
-
this.mode.$container.removeEventListener('gestureend', this.
|
|
97
|
+
this.mode.$container.removeEventListener('gestureend', this._pinchEnd);
|
|
98
|
+
|
|
99
|
+
this.touchesMonitor?.detach?.();
|
|
74
100
|
|
|
75
101
|
// The pinch listeners
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
this.hammer.off("pinchend", this._pinchEnd);
|
|
79
|
-
this.hammer.off("pinchcancel", this._pinchCancel);
|
|
102
|
+
this.interact.unset();
|
|
103
|
+
interact.removeDocument(document);
|
|
80
104
|
|
|
81
105
|
this.attached = false;
|
|
82
106
|
}
|
|
@@ -87,7 +111,16 @@ export class ModeSmoothZoom {
|
|
|
87
111
|
return false;
|
|
88
112
|
}
|
|
89
113
|
|
|
90
|
-
_pinchStart = () => {
|
|
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
|
+
|
|
91
124
|
// Do this in case the pinchend hasn't fired yet.
|
|
92
125
|
this.oldScale = 1;
|
|
93
126
|
this.mode.$visibleWorld.classList.add("BRsmooth-zooming");
|
|
@@ -95,37 +128,44 @@ export class ModeSmoothZoom {
|
|
|
95
128
|
this.mode.autoFit = "none";
|
|
96
129
|
this.detachCtrlZoom();
|
|
97
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
|
+
});
|
|
98
139
|
}
|
|
99
140
|
|
|
100
|
-
/** @param {{ scale: number,
|
|
141
|
+
/** @param {{ scale: number, clientX: number, clientY: number }}} e */
|
|
101
142
|
_pinchMove = async (e) => {
|
|
102
|
-
this.
|
|
143
|
+
if (!this.pinching) return;
|
|
144
|
+
this.lastEvent = {
|
|
145
|
+
scale: e.scale,
|
|
146
|
+
clientX: e.clientX,
|
|
147
|
+
clientY: e.clientY,
|
|
148
|
+
};
|
|
103
149
|
if (!this.pinchMoveFrame) {
|
|
104
|
-
let pinchMoveFramePromiseRes = null;
|
|
105
|
-
this.pinchMoveFramePromise = new Promise(
|
|
106
|
-
(res) => (pinchMoveFramePromiseRes = res)
|
|
107
|
-
);
|
|
108
|
-
|
|
109
150
|
// Buffer these events; only update the scale when request animation fires
|
|
110
|
-
this.pinchMoveFrame = this.bufferFn(
|
|
111
|
-
this.updateScaleCenter({
|
|
112
|
-
clientX: this.lastEvent.center.x,
|
|
113
|
-
clientY: this.lastEvent.center.y,
|
|
114
|
-
});
|
|
115
|
-
this.mode.scale *= this.lastEvent.scale / this.oldScale;
|
|
116
|
-
this.oldScale = this.lastEvent.scale;
|
|
117
|
-
this.pinchMoveFrame = null;
|
|
118
|
-
pinchMoveFramePromiseRes();
|
|
119
|
-
});
|
|
151
|
+
this.pinchMoveFrame = this.bufferFn(this._drawPinchZoomFrame);
|
|
120
152
|
}
|
|
121
153
|
}
|
|
122
154
|
|
|
123
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
|
+
});
|
|
124
164
|
// Want this to happen after the pinchMoveFrame,
|
|
125
165
|
// if one is in progress; otherwise setting oldScale
|
|
126
166
|
// messes up the transform.
|
|
127
167
|
await this.pinchMoveFramePromise;
|
|
128
|
-
this.
|
|
168
|
+
this.scaleCenter = { x: 0.5, y: 0.5 };
|
|
129
169
|
this.oldScale = 1;
|
|
130
170
|
this.mode.$visibleWorld.classList.remove("BRsmooth-zooming");
|
|
131
171
|
this.mode.$visibleWorld.style.willChange = "auto";
|
|
@@ -133,10 +173,42 @@ export class ModeSmoothZoom {
|
|
|
133
173
|
this.mode.attachScrollListeners?.();
|
|
134
174
|
}
|
|
135
175
|
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
|
|
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;
|
|
140
212
|
}
|
|
141
213
|
|
|
142
214
|
/** @private */
|
|
@@ -174,7 +246,7 @@ export class ModeSmoothZoom {
|
|
|
174
246
|
*/
|
|
175
247
|
updateScaleCenter({ clientX, clientY }) {
|
|
176
248
|
const bc = this.mode.htmlDimensionsCacher.boundingClientRect;
|
|
177
|
-
this.
|
|
249
|
+
this.scaleCenter = {
|
|
178
250
|
x: (clientX - bc.left) / this.mode.htmlDimensionsCacher.clientWidth,
|
|
179
251
|
y: (clientY - bc.top) / this.mode.htmlDimensionsCacher.clientHeight,
|
|
180
252
|
};
|
|
@@ -194,8 +266,8 @@ export class ModeSmoothZoom {
|
|
|
194
266
|
const F = newScale / oldScale;
|
|
195
267
|
|
|
196
268
|
// Where in the viewport the zoom is centered on
|
|
197
|
-
const XPOS = this.
|
|
198
|
-
const YPOS = this.
|
|
269
|
+
const XPOS = this.scaleCenter.x;
|
|
270
|
+
const YPOS = this.scaleCenter.y;
|
|
199
271
|
const oldCenter = {
|
|
200
272
|
x: L + XPOS * W,
|
|
201
273
|
y: T + YPOS * H,
|
|
@@ -207,5 +279,34 @@ export class ModeSmoothZoom {
|
|
|
207
279
|
|
|
208
280
|
container.scrollTop = newCenter.y - YPOS * H;
|
|
209
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;
|
|
210
311
|
}
|
|
211
312
|
}
|
|
@@ -41,7 +41,7 @@ export class Navbar {
|
|
|
41
41
|
return `<li>
|
|
42
42
|
<button class="BRicon ${option.className}" title="${option.label}">
|
|
43
43
|
<div class="icon icon-${option.iconClassName}"></div>
|
|
44
|
-
<span class="
|
|
44
|
+
<span class="BRtooltip">${option.label}</span>
|
|
45
45
|
</button>
|
|
46
46
|
</li>`;
|
|
47
47
|
}
|
|
@@ -130,7 +130,7 @@ export class Navbar {
|
|
|
130
130
|
.removeClass()
|
|
131
131
|
.addClass(`icon icon-${iconClass}`)
|
|
132
132
|
.end()
|
|
133
|
-
.find('.
|
|
133
|
+
.find('.BRtooltip')
|
|
134
134
|
.text(tooltipText);
|
|
135
135
|
}
|
|
136
136
|
|
package/src/css/_BRnav.scss
CHANGED
|
@@ -104,8 +104,11 @@
|
|
|
104
104
|
border-radius: 2px;
|
|
105
105
|
background: transparent;
|
|
106
106
|
outline: none;
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
@media (hover: hover) {
|
|
108
|
+
/* styles to apply on devices that support hover */
|
|
109
|
+
&:hover .BRtooltip {
|
|
110
|
+
display: block;
|
|
111
|
+
}
|
|
109
112
|
}
|
|
110
113
|
&.hide {
|
|
111
114
|
display: none;
|
package/src/css/_BRsearch.scss
CHANGED
|
@@ -98,6 +98,10 @@
|
|
|
98
98
|
// background-image: url(images/marker_chap-on.svg);
|
|
99
99
|
background-color: blue;
|
|
100
100
|
}
|
|
101
|
+
.BRchapterPage {
|
|
102
|
+
font-size: 0.85em;
|
|
103
|
+
opacity: .8;
|
|
104
|
+
}
|
|
101
105
|
}
|
|
102
106
|
.BRsearch {
|
|
103
107
|
width: 9px;
|
|
@@ -130,8 +134,8 @@
|
|
|
130
134
|
}
|
|
131
135
|
footer {
|
|
132
136
|
text-align: center;
|
|
133
|
-
font-
|
|
134
|
-
|
|
137
|
+
font-size: 0.85em;
|
|
138
|
+
opacity: .8;
|
|
135
139
|
}
|
|
136
140
|
mark {
|
|
137
141
|
color: $searchResultText;
|
package/src/css/_MobileNav.scss
CHANGED
|
@@ -166,29 +166,3 @@ html.mm-background .BookReader {
|
|
|
166
166
|
@media (min-width: ($brBreakPointMobile + 1)) {
|
|
167
167
|
.BRtoolbar.responsive { display: block; }
|
|
168
168
|
}
|
|
169
|
-
|
|
170
|
-
li.BRtable-contents-el {
|
|
171
|
-
line-height: 150%;
|
|
172
|
-
padding: 10px 10px 10px 20px;
|
|
173
|
-
overflow: hidden;
|
|
174
|
-
color: $controlsText;
|
|
175
|
-
|
|
176
|
-
&.chapter-clickable {
|
|
177
|
-
font-weight: normal;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
&.current-chapter {
|
|
181
|
-
background-color: lightblue;
|
|
182
|
-
}
|
|
183
|
-
//resetting mmenu styling for span inside .table-contents-el
|
|
184
|
-
> span {
|
|
185
|
-
display: inline;
|
|
186
|
-
padding: 0;
|
|
187
|
-
white-space: normal;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
.BRTOCElementPage {
|
|
191
|
-
font-size: 0.85em;
|
|
192
|
-
opacity: .8;
|
|
193
|
-
}
|
|
194
|
-
}
|
package/src/css/_controls.scss
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
.BRcontrols {
|
|
2
2
|
width: 100%;
|
|
3
3
|
|
|
4
|
-
.
|
|
4
|
+
.BRtooltip {
|
|
5
5
|
display: none;
|
|
6
6
|
position: absolute;
|
|
7
7
|
width: auto;
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
color: $controlsText;
|
|
15
15
|
border-radius: 3px;
|
|
16
16
|
background: $tooltipBG;
|
|
17
|
+
pointer-events: none;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
.full .
|
|
20
|
+
.full .BRtooltip {
|
|
20
21
|
left: auto;
|
|
21
22
|
right: 0;
|
|
22
23
|
transform: translateX(0);
|