@internetarchive/bookreader 5.0.0-114 → 5.0.0-115
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/474.js.map +1 -1
- package/BookReader/BookReader.css +27 -10
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +20 -38
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.experiments.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +1 -1
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js.map +1 -1
- package/BookReader/plugins/plugin.resume.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.translate.js +1 -1
- package/BookReader/plugins/plugin.translate.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/plugins/translator-worker.js.map +1 -1
- package/BookReader/webcomponents-bundle.js.map +1 -1
- package/package.json +37 -31
- package/src/BookReader/utils/SelectionObserver.js +4 -2
- package/src/BookReader.js +2 -1
- package/src/css/_TextSelection.scss +25 -10
- package/src/plugins/plugin.iiif.js +7 -1
- package/src/plugins/tts/WebTTSEngine.js +2 -2
- package/src/plugins/tts/utils.js +0 -9
- package/src/util/TextSelectionManager.js +78 -27
- package/src/util/browserSniffing.js +9 -0
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-115",
|
|
4
4
|
"description": "The Internet Archive BookReader.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -32,21 +32,21 @@
|
|
|
32
32
|
"private": false,
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@internetarchive/bergamot-translator": "^0.4.9-ia.1",
|
|
35
|
-
"@internetarchive/ia-activity-indicator": "0.0
|
|
36
|
-
"@internetarchive/ia-item-navigator": "2.2.
|
|
37
|
-
"@internetarchive/icon-bookmark": "
|
|
38
|
-
"@internetarchive/icon-close": "1.4.
|
|
39
|
-
"@internetarchive/icon-dl": "
|
|
40
|
-
"@internetarchive/icon-edit-pencil": "
|
|
41
|
-
"@internetarchive/icon-ia-logo": "1.4.
|
|
42
|
-
"@internetarchive/icon-magnify-minus": "
|
|
43
|
-
"@internetarchive/icon-magnify-plus": "
|
|
44
|
-
"@internetarchive/icon-search": "
|
|
45
|
-
"@internetarchive/icon-share": "1.4.
|
|
46
|
-
"@internetarchive/icon-toc": "
|
|
47
|
-
"@internetarchive/icon-visual-adjustment": "
|
|
48
|
-
"@internetarchive/modal-manager": "2.0.
|
|
49
|
-
"@internetarchive/shared-resize-observer": "
|
|
35
|
+
"@internetarchive/ia-activity-indicator": "1.0.0",
|
|
36
|
+
"@internetarchive/ia-item-navigator": "2.2.3",
|
|
37
|
+
"@internetarchive/icon-bookmark": "1.4.1",
|
|
38
|
+
"@internetarchive/icon-close": "1.4.1",
|
|
39
|
+
"@internetarchive/icon-dl": "1.4.1",
|
|
40
|
+
"@internetarchive/icon-edit-pencil": "1.4.1",
|
|
41
|
+
"@internetarchive/icon-ia-logo": "1.4.1",
|
|
42
|
+
"@internetarchive/icon-magnify-minus": "1.4.1",
|
|
43
|
+
"@internetarchive/icon-magnify-plus": "1.4.1",
|
|
44
|
+
"@internetarchive/icon-search": "1.4.1",
|
|
45
|
+
"@internetarchive/icon-share": "1.4.1",
|
|
46
|
+
"@internetarchive/icon-toc": "1.4.1",
|
|
47
|
+
"@internetarchive/icon-visual-adjustment": "1.4.1",
|
|
48
|
+
"@internetarchive/modal-manager": "2.0.6",
|
|
49
|
+
"@internetarchive/shared-resize-observer": "0.2.0",
|
|
50
50
|
"interactjs": "^1.10.18",
|
|
51
51
|
"iso-language-codes": "2.0.0",
|
|
52
52
|
"jquery": "3.6.1",
|
|
@@ -58,36 +58,36 @@
|
|
|
58
58
|
"soundmanager2": "2.97.20170602"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
|
-
"@babel/core": "7.29.
|
|
62
|
-
"@babel/eslint-parser": "7.
|
|
61
|
+
"@babel/core": "7.29.7",
|
|
62
|
+
"@babel/eslint-parser": "7.29.7",
|
|
63
63
|
"@babel/plugin-proposal-class-properties": "7.18.6",
|
|
64
|
-
"@babel/plugin-proposal-decorators": "7.29.
|
|
65
|
-
"@babel/preset-env": "7.29.
|
|
66
|
-
"@iiif/presentation-2": "
|
|
67
|
-
"@iiif/presentation-3": "
|
|
64
|
+
"@babel/plugin-proposal-decorators": "7.29.7",
|
|
65
|
+
"@babel/preset-env": "7.29.7",
|
|
66
|
+
"@iiif/presentation-2": "1.0.4",
|
|
67
|
+
"@iiif/presentation-3": "2.2.3",
|
|
68
68
|
"@open-wc/testing-helpers": "3.0.1",
|
|
69
69
|
"@types/jest": "30.0.0",
|
|
70
70
|
"@webcomponents/webcomponentsjs": "^2.6.0",
|
|
71
71
|
"babel-loader": "10.1.1",
|
|
72
|
-
"concurrently": "
|
|
72
|
+
"concurrently": "10.0.3",
|
|
73
73
|
"core-js": "3.49.0",
|
|
74
|
-
"cpx2": "
|
|
74
|
+
"cpx2": "9.0.0",
|
|
75
75
|
"eslint": "7.32.0",
|
|
76
76
|
"eslint-plugin-no-jquery": "^2.7.0",
|
|
77
77
|
"eslint-plugin-testcafe": "0.2.1",
|
|
78
78
|
"http-server": "14.1.1",
|
|
79
79
|
"hypothesis": "^1.1627.0",
|
|
80
|
-
"jest": "30.
|
|
81
|
-
"jest-environment-jsdom": "30.
|
|
80
|
+
"jest": "30.4.2",
|
|
81
|
+
"jest-environment-jsdom": "30.4.1",
|
|
82
82
|
"live-server": "1.2.2",
|
|
83
83
|
"regenerator-runtime": "0.14.1",
|
|
84
|
-
"sass": "1.
|
|
85
|
-
"sinon": "
|
|
84
|
+
"sass": "1.100.0",
|
|
85
|
+
"sinon": "22.0.0",
|
|
86
86
|
"svgo": "4.0.1",
|
|
87
87
|
"testcafe": "3.7.4",
|
|
88
88
|
"testcafe-browser-provider-browserstack": "1.15.2",
|
|
89
|
-
"webpack": "5.
|
|
90
|
-
"webpack-cli": "7.0.
|
|
89
|
+
"webpack": "5.107.2",
|
|
90
|
+
"webpack-cli": "7.0.3"
|
|
91
91
|
},
|
|
92
92
|
"jest": {
|
|
93
93
|
"testEnvironment": "jsdom",
|
|
@@ -133,7 +133,13 @@
|
|
|
133
133
|
"test:e2e:dev": "npx testcafe --live --dev",
|
|
134
134
|
"DOCS:update:test-deps": "If CI succeeds, these should be good to update",
|
|
135
135
|
"update:test-deps": "npm i @babel/eslint-parser@latest @open-wc/testing-helpers@latest @types/jest@latest eslint@7 eslint-plugin-testcafe@latest jest@latest jest-environment-jsdom@latest sinon@latest testcafe@latest",
|
|
136
|
+
"update:test-deps:test": "npm run lint && npm run test && npm run test:e2e",
|
|
136
137
|
"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",
|
|
137
|
-
"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"
|
|
138
|
+
"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",
|
|
139
|
+
"update:build-deps:test": "npm run build",
|
|
140
|
+
"DOCS:update:dev-deps": "Packages used for development",
|
|
141
|
+
"update:dev-deps": "npm i @iiif/presentation-2@latest @iiif/presentation-3@latest concurrently@latest cpx2@latest http-server@latest live-server@latest",
|
|
142
|
+
"DOCS:update:ia-deps": "Packages by @internetarchive",
|
|
143
|
+
"update:ia-deps": "npm i @internetarchive/ia-activity-indicator@latest @internetarchive/ia-item-navigator@latest @internetarchive/icon-bookmark@latest @internetarchive/icon-close@latest @internetarchive/icon-dl@latest @internetarchive/icon-edit-pencil@latest @internetarchive/icon-ia-logo@latest @internetarchive/icon-magnify-minus@latest @internetarchive/icon-magnify-plus@latest @internetarchive/icon-search@latest @internetarchive/icon-share@latest @internetarchive/icon-toc@latest @internetarchive/icon-visual-adjustment@latest @internetarchive/modal-manager@latest @internetarchive/shared-resize-observer@latest"
|
|
138
144
|
}
|
|
139
145
|
}
|
|
@@ -4,7 +4,7 @@ export class SelectionObserver {
|
|
|
4
4
|
startedInSelector = false;
|
|
5
5
|
/** @type {HTMLElement} */
|
|
6
6
|
target = null;
|
|
7
|
-
/** @type {Node} */
|
|
7
|
+
/** @type {Node | null} */
|
|
8
8
|
lastKnownFocusNode = null;
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -30,6 +30,7 @@ export class SelectionObserver {
|
|
|
30
30
|
|
|
31
31
|
_onSelectionChange = () => {
|
|
32
32
|
const sel = window.getSelection();
|
|
33
|
+
if (!sel) return;
|
|
33
34
|
|
|
34
35
|
if (!this.selecting && sel.toString()) {
|
|
35
36
|
const target = $(sel.anchorNode).closest(this.selector)[0];
|
|
@@ -40,13 +41,14 @@ export class SelectionObserver {
|
|
|
40
41
|
this.handler('started', this.target);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
if (this.selecting &&
|
|
44
|
+
if (this.selecting && this.lastKnownFocusNode != sel.focusNode && sel.toString() && !sel.isCollapsed) {
|
|
44
45
|
this.lastKnownFocusNode = sel.focusNode;
|
|
45
46
|
this.handler('changed', this.target);
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
if (this.selecting && (sel.isCollapsed || !sel.toString() || !$(sel.anchorNode).closest(this.selector)[0])) {
|
|
49
50
|
this.selecting = false;
|
|
51
|
+
this.lastKnownFocusNode = null;
|
|
50
52
|
this.handler('cleared', this.target);
|
|
51
53
|
}
|
|
52
54
|
};
|
package/src/BookReader.js
CHANGED
|
@@ -151,7 +151,7 @@ BookReader.defaultOptions = DEFAULT_OPTIONS;
|
|
|
151
151
|
* @type {BookReaderOptions}
|
|
152
152
|
* This is here, just in case you need to absolutely override an option.
|
|
153
153
|
*/
|
|
154
|
-
BookReader.optionOverrides = {};
|
|
154
|
+
BookReader.optionOverrides = BookReader.optionOverrides || {};
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
157
|
* Setup
|
|
@@ -852,6 +852,7 @@ BookReader.prototype.setupKeyListeners = function () {
|
|
|
852
852
|
|
|
853
853
|
// Ignore if modifiers are active.
|
|
854
854
|
if (e.getModifierState('Control') ||
|
|
855
|
+
e.getModifierState('Shift') ||
|
|
855
856
|
e.getModifierState('Alt') ||
|
|
856
857
|
e.getModifierState('Meta') ||
|
|
857
858
|
e.getModifierState('Win') /* hack for IE */) {
|
|
@@ -153,7 +153,16 @@
|
|
|
153
153
|
color-scheme: dark;
|
|
154
154
|
padding: 2px;
|
|
155
155
|
overflow: hidden;
|
|
156
|
-
|
|
156
|
+
opacity: 1;
|
|
157
|
+
transition: border-radius 0.2s, opacity 0.2s;
|
|
158
|
+
|
|
159
|
+
@starting-style {
|
|
160
|
+
opacity: 0;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
.br-select-menu__root--scrolling {
|
|
164
|
+
opacity: 0;
|
|
165
|
+
pointer-events: none;
|
|
157
166
|
}
|
|
158
167
|
.br-select-menu__root:hover {
|
|
159
168
|
border-radius: 8px;
|
|
@@ -172,6 +181,7 @@
|
|
|
172
181
|
align-items: center;
|
|
173
182
|
border-radius: 20px;
|
|
174
183
|
font-family: inherit;
|
|
184
|
+
color: inherit;
|
|
175
185
|
border: 0;
|
|
176
186
|
transition: background-color 0.2s, border-radius 0.2s;
|
|
177
187
|
cursor: pointer;
|
|
@@ -191,18 +201,23 @@
|
|
|
191
201
|
}
|
|
192
202
|
|
|
193
203
|
.br-select-menu__label {
|
|
194
|
-
|
|
195
|
-
opacity: 0;
|
|
196
|
-
interpolate-size: allow-keywords;
|
|
197
|
-
transition: width 0.2s;
|
|
204
|
+
margin-left: 4px;
|
|
198
205
|
font-size: 12px;
|
|
199
206
|
}
|
|
200
207
|
|
|
201
|
-
|
|
202
|
-
.br-select-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
208
|
+
@media (hover: hover) {
|
|
209
|
+
.br-select-menu__label {
|
|
210
|
+
width: 0px;
|
|
211
|
+
margin-left: 0;
|
|
212
|
+
opacity: 0;
|
|
213
|
+
interpolate-size: allow-keywords;
|
|
214
|
+
transition: width 0.2s;
|
|
215
|
+
}
|
|
216
|
+
.br-select-menu__root:hover .br-select-menu__label {
|
|
217
|
+
width: auto;
|
|
218
|
+
margin-left: 4px;
|
|
219
|
+
opacity: 1;
|
|
220
|
+
}
|
|
206
221
|
}
|
|
207
222
|
|
|
208
223
|
.BRtextLayer .BRhighlight {
|
|
@@ -3,13 +3,19 @@ import { BookReaderPlugin } from '../BookReaderPlugin.js';
|
|
|
3
3
|
|
|
4
4
|
const BookReader = /** @type {typeof import('../BookReader').default} */(window.BookReader);
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* @extends BookReaderPlugin<IiifPlugin['options']>
|
|
8
|
+
*/
|
|
6
9
|
export class IiifPlugin extends BookReaderPlugin {
|
|
7
10
|
options = {
|
|
8
11
|
enabled: true,
|
|
9
|
-
/** @type {import('@iiif/presentation-3').Manifest | import('@iiif/presentation-2').Manifest} */
|
|
12
|
+
/** @type {import('@iiif/presentation-3').Manifest | import('@iiif/presentation-2').Manifest | null} */
|
|
10
13
|
manifest: null,
|
|
11
14
|
}
|
|
12
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @param {IiifPlugin['options']} options
|
|
18
|
+
*/
|
|
13
19
|
setup(options) {
|
|
14
20
|
super.setup(options);
|
|
15
21
|
this.manifest = this.options.manifest;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* global br */
|
|
2
|
-
import { isChrome, isFirefox } from '../../util/browserSniffing.js';
|
|
3
|
-
import {
|
|
2
|
+
import { isChrome, isFirefox, isAndroid } from '../../util/browserSniffing.js';
|
|
3
|
+
import { DEBUG_READ_ALOUD } from './utils.js';
|
|
4
4
|
import { promisifyEvent, sleep } from '../../BookReader/utils.js';
|
|
5
5
|
import AbstractTTSEngine from './AbstractTTSEngine.js';
|
|
6
6
|
/** @typedef {import("./AbstractTTSEngine.js").PageChunk} PageChunk */
|
package/src/plugins/tts/utils.js
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import langs from 'iso-language-codes';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Checks whether the current browser is on android
|
|
6
|
-
* @param {string} [userAgent]
|
|
7
|
-
* @return {boolean}
|
|
8
|
-
*/
|
|
9
|
-
export function isAndroid(userAgent = navigator.userAgent) {
|
|
10
|
-
return /android/i.test(userAgent);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
4
|
/** @type {{[lang: string]: string}} */
|
|
14
5
|
// Handle odd one-off language pairs that might show up over time
|
|
15
6
|
const specialLangs = {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { countWords } from './strings.js';
|
|
3
3
|
import { SelectionObserver } from "../BookReader/utils/SelectionObserver.js";
|
|
4
|
+
import { clamp } from "../BookReader/utils.js";
|
|
4
5
|
import { html, LitElement } from 'lit';
|
|
5
6
|
import { customElement, property, query } from 'lit/decorators.js';
|
|
6
7
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
7
8
|
import '@internetarchive/icon-share';
|
|
8
9
|
import '@internetarchive/icon-edit-pencil/icon-edit-pencil.js';
|
|
10
|
+
import { isIOS, isAndroid } from './browserSniffing.js';
|
|
9
11
|
|
|
10
12
|
const BR_HIGHLIGHTS_LOCAL_STORAGE_KEY = "BRhighlightStorage";
|
|
11
13
|
const MAX_FULL_QUOTE_URL_CHARS = 80;
|
|
@@ -55,15 +57,12 @@ export class TextSelectionManager {
|
|
|
55
57
|
// Set a class on the page to avoid hiding it when zooming/etc
|
|
56
58
|
this.br.refs.$br.find('.BRpagecontainer--hasSelection').removeClass('BRpagecontainer--hasSelection');
|
|
57
59
|
$(window.getSelection().anchorNode).closest('.BRpagecontainer').addClass('BRpagecontainer--hasSelection');
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
// Don't show while still doing selection
|
|
61
|
+
if (!this.mouseIsDown) this.showSelectMenu();
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
if (selectEvent == 'changed') {
|
|
63
|
-
|
|
64
|
-
if (this.mouseIsDown) {
|
|
65
|
-
this.hideSelectMenu();
|
|
66
|
-
} else if (window.getSelection()?.toString()) {
|
|
65
|
+
if (!this.mouseIsDown && window.getSelection()?.toString()) {
|
|
67
66
|
this.showSelectMenu();
|
|
68
67
|
}
|
|
69
68
|
}
|
|
@@ -165,7 +164,6 @@ export class TextSelectionManager {
|
|
|
165
164
|
// blocking selection
|
|
166
165
|
$(textLayer).on("mousedown.textSelectPluginHandler", (event) => {
|
|
167
166
|
this.mouseIsDown = true;
|
|
168
|
-
this.hideSelectMenu();
|
|
169
167
|
if ($(event.target).is(this.selectionElement.join(", "))) {
|
|
170
168
|
event.stopPropagation();
|
|
171
169
|
}
|
|
@@ -173,7 +171,6 @@ export class TextSelectionManager {
|
|
|
173
171
|
|
|
174
172
|
$(textLayer).on("mouseup.textSelectPluginHandler", (event) => {
|
|
175
173
|
this.mouseIsDown = false;
|
|
176
|
-
this.hideSelectMenu();
|
|
177
174
|
textLayer.style.pointerEvents = "none";
|
|
178
175
|
if (skipNextMouseup) {
|
|
179
176
|
skipNextMouseup = false;
|
|
@@ -199,7 +196,6 @@ export class TextSelectionManager {
|
|
|
199
196
|
if (event.which != 1) return;
|
|
200
197
|
this.mouseIsDown = true;
|
|
201
198
|
event.stopPropagation();
|
|
202
|
-
this.hideSelectMenu();
|
|
203
199
|
});
|
|
204
200
|
|
|
205
201
|
// Prevent page flip on click
|
|
@@ -221,7 +217,11 @@ export class TextSelectionManager {
|
|
|
221
217
|
document.body.append(this.selectMenu);
|
|
222
218
|
}
|
|
223
219
|
|
|
224
|
-
this.selectMenu.
|
|
220
|
+
if (this.selectMenu.open) {
|
|
221
|
+
this.selectMenu.repositionToSelection();
|
|
222
|
+
} else {
|
|
223
|
+
this.selectMenu.show();
|
|
224
|
+
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
hideSelectMenu() {
|
|
@@ -459,6 +459,9 @@ class BRSelectMenu extends LitElement {
|
|
|
459
459
|
@query('#copy-link-option')
|
|
460
460
|
copyLinkOption;
|
|
461
461
|
|
|
462
|
+
@property({type: Boolean, reflect: true})
|
|
463
|
+
open = false;
|
|
464
|
+
|
|
462
465
|
@property({type: Boolean})
|
|
463
466
|
copyLinkToHighlightEnabled = true;
|
|
464
467
|
|
|
@@ -479,6 +482,19 @@ class BRSelectMenu extends LitElement {
|
|
|
479
482
|
return this;
|
|
480
483
|
}
|
|
481
484
|
|
|
485
|
+
/** @type {number | null} */
|
|
486
|
+
_scrollTimeoutId = null;
|
|
487
|
+
|
|
488
|
+
_onScroll = () => {
|
|
489
|
+
this.classList.add('br-select-menu__root--scrolling');
|
|
490
|
+
if (this._scrollTimeoutId != null) clearTimeout(this._scrollTimeoutId);
|
|
491
|
+
this._scrollTimeoutId = window.setTimeout(() => {
|
|
492
|
+
this._scrollTimeoutId = null;
|
|
493
|
+
this.repositionToSelection();
|
|
494
|
+
this.classList.remove('br-select-menu__root--scrolling');
|
|
495
|
+
}, 150);
|
|
496
|
+
};
|
|
497
|
+
|
|
482
498
|
/** @override */
|
|
483
499
|
connectedCallback() {
|
|
484
500
|
super.connectedCallback();
|
|
@@ -487,10 +503,22 @@ class BRSelectMenu extends LitElement {
|
|
|
487
503
|
this.setAttribute('aria-orientation', 'vertical');
|
|
488
504
|
}
|
|
489
505
|
|
|
506
|
+
/** @override */
|
|
507
|
+
disconnectedCallback() {
|
|
508
|
+
super.disconnectedCallback();
|
|
509
|
+
window.removeEventListener('scroll', this._onScroll, { capture: true });
|
|
510
|
+
if (this._scrollTimeoutId != null) {
|
|
511
|
+
clearTimeout(this._scrollTimeoutId);
|
|
512
|
+
this._scrollTimeoutId = null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
490
516
|
renderCopyLinkToHighlightOption() {
|
|
517
|
+
// Mousedown needed to prevent selection from being cleared on iOS
|
|
491
518
|
return html`
|
|
492
519
|
<br-menu-option
|
|
493
520
|
id="copy-link-option"
|
|
521
|
+
@mousedown=${/** @param {MouseEvent} e */ (e) => e.preventDefault()}
|
|
494
522
|
@click=${this.handleCopyLinkToHighlight}
|
|
495
523
|
icon="share"
|
|
496
524
|
label="Copy Link to Highlight"
|
|
@@ -703,31 +731,54 @@ class BRSelectMenu extends LitElement {
|
|
|
703
731
|
this.show();
|
|
704
732
|
}
|
|
705
733
|
|
|
706
|
-
|
|
734
|
+
repositionToSelection() {
|
|
735
|
+
const SCREEN_MARGIN = 10;
|
|
736
|
+
const currentSelection = /** @type {Selection} */ (window.getSelection());
|
|
737
|
+
const range = currentSelection.getRangeAt(0);
|
|
738
|
+
const selectionRect = range.getBoundingClientRect();
|
|
739
|
+
|
|
740
|
+
const isMobile = isIOS() || isAndroid();
|
|
741
|
+
// Leave room for mobile teardrop selection handles
|
|
742
|
+
const SELECTION_PADDING = isAndroid() ? 25 : isIOS() ? 15 : 10;
|
|
743
|
+
// On mobile, avoid the native OS text-selection bar which appears near the top of the screen
|
|
744
|
+
const OS_BAR_H = isMobile ? 60 : 0;
|
|
745
|
+
const idealTop = (selectionRect.top < OS_BAR_H
|
|
746
|
+
? selectionRect.bottom + OS_BAR_H
|
|
747
|
+
: selectionRect.bottom) + SELECTION_PADDING;
|
|
748
|
+
const top = Math.max(SCREEN_MARGIN, idealTop) + window.scrollY;
|
|
749
|
+
|
|
750
|
+
const menuWidth = this.getBoundingClientRect().width;
|
|
751
|
+
// Excludes scrollbars
|
|
752
|
+
const windowWidth = document.documentElement.clientWidth;
|
|
753
|
+
const idealLeft = menuWidth > selectionRect.width
|
|
754
|
+
? selectionRect.left + selectionRect.width / 2 - menuWidth / 2
|
|
755
|
+
: selectionRect.left;
|
|
756
|
+
const left = clamp(idealLeft, SCREEN_MARGIN, windowWidth - menuWidth - SCREEN_MARGIN) + window.scrollX;
|
|
757
|
+
|
|
758
|
+
this.style.top = `${top}px`;
|
|
759
|
+
this.style.left = `${left}px`;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
async show() {
|
|
707
763
|
if (this.br.plugins.translate?.userToggleTranslate) return;
|
|
708
|
-
|
|
709
|
-
const start = currentSelection.anchorNode.parentElement;
|
|
710
|
-
const end = currentSelection.focusNode.parentElement; // will always be a text node
|
|
711
|
-
const height = 30;
|
|
712
|
-
const width = 60;
|
|
713
|
-
const startBoundingRect = start.getBoundingClientRect();
|
|
714
|
-
const endBoundingRect = end.getBoundingClientRect();
|
|
715
|
-
let hlButtonTop = startBoundingRect.top - height;
|
|
716
|
-
let hlButtonLeft = startBoundingRect.left - width;
|
|
717
|
-
if (currentSelection.direction == 'backward') {
|
|
718
|
-
hlButtonTop = endBoundingRect.top - height;
|
|
719
|
-
hlButtonLeft = endBoundingRect.left - width;
|
|
720
|
-
}
|
|
721
|
-
this.style.top = `${hlButtonTop}px`;
|
|
722
|
-
this.style.left = `${hlButtonLeft}px`;
|
|
764
|
+
|
|
723
765
|
this.style.zIndex = '1';
|
|
724
766
|
this.style.position = 'absolute';
|
|
725
767
|
this.style.display = 'block';
|
|
768
|
+
this.open = true;
|
|
769
|
+
this.classList.remove('br-select-menu__root--scrolling');
|
|
770
|
+
window.removeEventListener('scroll', this._onScroll, { capture: true });
|
|
771
|
+
window.addEventListener('scroll', this._onScroll, { capture: true, passive: true });
|
|
772
|
+
await this.updateComplete; // ensure Lit has rendered so getBoundingClientRect() returns real width
|
|
773
|
+
this.repositionToSelection();
|
|
726
774
|
this.clearNodesForRemoval();
|
|
727
775
|
}
|
|
728
776
|
|
|
729
|
-
hide
|
|
777
|
+
hide() {
|
|
778
|
+
if (!this.open) return;
|
|
730
779
|
this.style.display = 'none';
|
|
780
|
+
this.open = false;
|
|
781
|
+
window.removeEventListener('scroll', this._onScroll, { capture: true });
|
|
731
782
|
this.clearNodesForRemoval();
|
|
732
783
|
return;
|
|
733
784
|
}
|
|
@@ -51,6 +51,15 @@ export function isIOS() {
|
|
|
51
51
|
return 'ongesturestart' in window && navigator.maxTouchPoints > 0;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Checks whether the current browser is on android
|
|
56
|
+
* @param {string} [userAgent]
|
|
57
|
+
* @return {boolean}
|
|
58
|
+
*/
|
|
59
|
+
export function isAndroid(userAgent = navigator.userAgent) {
|
|
60
|
+
return /android/i.test(userAgent);
|
|
61
|
+
}
|
|
62
|
+
|
|
54
63
|
/**
|
|
55
64
|
* Checks whether the current browser is Samsung Internet
|
|
56
65
|
* https://stackoverflow.com/a/40684162/2317712
|