@internetarchive/bookreader 5.0.0-115 → 5.0.0-117
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 +44 -18
- package/BookReader/BookReader.js +1 -43
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +109 -67
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.archive_analytics.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +2 -2
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.experiments.js +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 +1 -1
- package/BookReader/plugins/plugin.resume.js +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 +63 -1
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.translate.js +65 -3
- 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 +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/README.md +3 -1
- package/jsconfig.json +2 -3
- package/package.json +38 -31
- package/src/BookReader.js +24 -20
- package/src/css/_TextSelection.scss +51 -18
- package/src/plugins/plugin.chapters.js +8 -4
- package/src/plugins/plugin.iframe.js +36 -37
- package/src/plugins/plugin.text_selection.js +40 -101
- package/src/plugins/search/plugin.search.js +4 -8
- package/src/plugins/tts/plugin.tts.js +3 -8
- package/src/plugins/url/UrlPlugin.js +12 -0
- package/src/util/TextSelectionManager.js +221 -154
- package/src/util/dom.js +88 -0
- package/src/util/generators.js +89 -0
package/src/util/dom.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import { clamp } from '../BookReader/utils.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Walks up the DOM tree starting at (but not including) the given element,
|
|
6
|
+
* returning the nearest ancestor that can actually scroll its overflow.
|
|
7
|
+
* @param {Element} el
|
|
8
|
+
* @return {Element | null}
|
|
9
|
+
*/
|
|
10
|
+
export function findScrollableAncestor(el) {
|
|
11
|
+
let current = el.parentElement;
|
|
12
|
+
while (current) {
|
|
13
|
+
const style = getComputedStyle(current);
|
|
14
|
+
const canScrollY = (style.overflowY === 'auto' || style.overflowY === 'scroll') && current.scrollHeight > current.clientHeight;
|
|
15
|
+
const canScrollX = (style.overflowX === 'auto' || style.overflowX === 'scroll') && current.scrollWidth > current.clientWidth;
|
|
16
|
+
if (canScrollY || canScrollX) return current;
|
|
17
|
+
current = current.parentElement;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Computes the scroll offset for a single axis needed to align an element
|
|
24
|
+
* within a container, per the same semantics as scrollIntoView's block/inline.
|
|
25
|
+
* @param {number} elStart
|
|
26
|
+
* @param {number} elEnd
|
|
27
|
+
* @param {number} containerStart
|
|
28
|
+
* @param {number} containerEnd
|
|
29
|
+
* @param {number} currentScroll
|
|
30
|
+
* @param {'start' | 'center' | 'end' | 'nearest'} align
|
|
31
|
+
* @return {number}
|
|
32
|
+
*/
|
|
33
|
+
function computeAxisScroll(elStart, elEnd, containerStart, containerEnd, currentScroll, align) {
|
|
34
|
+
let delta = 0;
|
|
35
|
+
switch (align) {
|
|
36
|
+
case 'start':
|
|
37
|
+
delta = elStart - containerStart;
|
|
38
|
+
break;
|
|
39
|
+
case 'end':
|
|
40
|
+
delta = elEnd - containerEnd;
|
|
41
|
+
break;
|
|
42
|
+
case 'center':
|
|
43
|
+
delta = (elStart + elEnd) / 2 - (containerStart + containerEnd) / 2;
|
|
44
|
+
break;
|
|
45
|
+
case 'nearest':
|
|
46
|
+
default:
|
|
47
|
+
if (elStart < containerStart) delta = elStart - containerStart;
|
|
48
|
+
else if (elEnd > containerEnd) delta = elEnd - containerEnd;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
return currentScroll + delta;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scrolls the given element into view within a single scroll container.
|
|
56
|
+
*
|
|
57
|
+
* Unlike the native `Element.scrollIntoView`, this never walks up and scrolls
|
|
58
|
+
* every scrollable ancestor in turn (which can otherwise unexpectedly move
|
|
59
|
+
* the whole page) — it only ever adjusts the scroll position of one
|
|
60
|
+
* container: either the one explicitly passed in, or the nearest scrollable
|
|
61
|
+
* ancestor found via {@link findScrollableAncestor}.
|
|
62
|
+
* @param {Element} el
|
|
63
|
+
* @param {object} [options]
|
|
64
|
+
* @param {'auto' | 'smooth'} [options.behavior]
|
|
65
|
+
* @param {'start' | 'center' | 'end' | 'nearest'} [options.block]
|
|
66
|
+
* @param {'start' | 'center' | 'end' | 'nearest'} [options.inline]
|
|
67
|
+
* @param {Element} [options.scrollContainer] The scrollable element whose
|
|
68
|
+
* scroll position should be adjusted. Defaults to `findScrollableAncestor(el)`.
|
|
69
|
+
*/
|
|
70
|
+
export function singleScrollIntoView(el, options) {
|
|
71
|
+
const { block = 'start', inline = 'nearest', behavior = 'auto', scrollContainer = findScrollableAncestor(el) } = options || {};
|
|
72
|
+
if (!scrollContainer) {
|
|
73
|
+
el.scrollIntoView({ block, inline, behavior });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
78
|
+
const elRect = el.getBoundingClientRect();
|
|
79
|
+
|
|
80
|
+
const top = computeAxisScroll(elRect.top, elRect.bottom, containerRect.top, containerRect.bottom, scrollContainer.scrollTop, block);
|
|
81
|
+
const left = computeAxisScroll(elRect.left, elRect.right, containerRect.left, containerRect.right, scrollContainer.scrollLeft, inline);
|
|
82
|
+
|
|
83
|
+
scrollContainer.scrollTo({
|
|
84
|
+
top: clamp(top, 0, scrollContainer.scrollHeight - scrollContainer.clientHeight),
|
|
85
|
+
left: clamp(left, 0, scrollContainer.scrollWidth - scrollContainer.clientWidth),
|
|
86
|
+
behavior,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @template T
|
|
5
|
+
* Get the i-th element of an iterable
|
|
6
|
+
* @param {Iterable<T>} iterable
|
|
7
|
+
* @param {number} index
|
|
8
|
+
*/
|
|
9
|
+
export function genAt(iterable, index) {
|
|
10
|
+
let i = 0;
|
|
11
|
+
for (const x of iterable) {
|
|
12
|
+
if (i == index) return x;
|
|
13
|
+
i++;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @template T
|
|
20
|
+
* Generator version of filter
|
|
21
|
+
* @param {Iterable<T>} iterable
|
|
22
|
+
* @param {function(T): boolean} fn
|
|
23
|
+
*/
|
|
24
|
+
export function* genFilter(iterable, fn) {
|
|
25
|
+
for (const x of iterable) {
|
|
26
|
+
if (fn(x)) yield x;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @template TFrom, TTo
|
|
32
|
+
* Generator version of map
|
|
33
|
+
* @param {Iterable<TFrom>} gen
|
|
34
|
+
* @param {function(TFrom): TTo} fn
|
|
35
|
+
* @returns {Iterable<TTo>}
|
|
36
|
+
*/
|
|
37
|
+
export function* genMap(gen, fn) {
|
|
38
|
+
for (const x of gen) yield fn(x);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @template T
|
|
43
|
+
* Generator that provides a sliding window of 3 elements,
|
|
44
|
+
* prev, current, and next.
|
|
45
|
+
* @param {Iterable<T>} gen
|
|
46
|
+
* @returns {Iterable<[T | undefined, T, T | undefined]>}
|
|
47
|
+
*/
|
|
48
|
+
export function* lookAroundWindow(gen) {
|
|
49
|
+
let prev = undefined;
|
|
50
|
+
let cur = undefined;
|
|
51
|
+
let next = undefined;
|
|
52
|
+
for (const x of gen) {
|
|
53
|
+
if (typeof cur !== 'undefined') {
|
|
54
|
+
next = x;
|
|
55
|
+
yield [prev, cur, next];
|
|
56
|
+
}
|
|
57
|
+
prev = cur;
|
|
58
|
+
cur = x;
|
|
59
|
+
next = undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof cur !== 'undefined') {
|
|
63
|
+
yield [prev, cur, next];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* @template T1, T2
|
|
69
|
+
* Lazy zip implementation to avoid importing lodash
|
|
70
|
+
* Expects iterators to be of the same length
|
|
71
|
+
* @param {Iterable<T1>} gen1
|
|
72
|
+
* @param {Iterable<T2>} gen2
|
|
73
|
+
* @returns {Iterable<[T1, T2]>}
|
|
74
|
+
*/
|
|
75
|
+
export function* zip(gen1, gen2) {
|
|
76
|
+
const it1 = gen1[Symbol.iterator]();
|
|
77
|
+
const it2 = gen2[Symbol.iterator]();
|
|
78
|
+
while (true) {
|
|
79
|
+
const r1 = it1.next();
|
|
80
|
+
const r2 = it2.next();
|
|
81
|
+
if (r1.done && r2.done) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (r1.done || r2.done) {
|
|
85
|
+
throw new Error('zip: one of the iterators is done');
|
|
86
|
+
}
|
|
87
|
+
yield [r1.value, r2.value];
|
|
88
|
+
}
|
|
89
|
+
}
|