@readium/navigator-html-injectables 2.3.1-alpha.2 → 2.4.0-beta.1
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/dist/index.js +852 -701
- package/dist/index.umd.cjs +24 -13
- package/package.json +8 -8
- package/src/Loader.ts +3 -3
- package/src/comms/comms.ts +5 -5
- package/src/comms/index.ts +3 -3
- package/src/helpers/css.ts +2 -4
- package/src/helpers/document.ts +13 -10
- package/src/helpers/locator.ts +2 -2
- package/src/index.ts +4 -4
- package/src/modules/Decorator.ts +18 -18
- package/src/modules/Module.ts +4 -4
- package/src/modules/ModuleLibrary.ts +15 -11
- package/src/modules/Peripherals.ts +70 -61
- package/src/modules/index.ts +4 -4
- package/src/modules/setup/FixedSetup.ts +6 -6
- package/src/modules/setup/ReflowableSetup.ts +6 -6
- package/src/modules/setup/Setup.ts +5 -5
- package/src/modules/setup/WebPubSetup.ts +5 -5
- package/src/modules/snapper/CJKVerticalSnapper.ts +322 -0
- package/src/modules/snapper/ColumnSnapper.ts +161 -99
- package/src/modules/snapper/ScrollSnapper.ts +18 -18
- package/src/modules/snapper/Snapper.ts +5 -5
- package/src/modules/snapper/WebPubSnapper.ts +15 -15
- package/src/protection/BulkCopyProtector.ts +15 -15
- package/src/protection/KeyCombinationManager.ts +8 -8
- package/src/protection/PatternAnalyzer.ts +18 -18
- package/src/protection/PrintProtector.ts +12 -12
- package/src/protection/SelectionAnalyzer.ts +1 -1
- package/src/protection/config.ts +4 -4
- package/src/protection/index.ts +6 -6
- package/src/vendor/hypothesis/anchoring/html.ts +1 -1
- package/src/vendor/hypothesis/anchoring/types.ts +1 -1
- package/types/src/Loader.d.ts +2 -2
- package/types/src/comms/comms.d.ts +2 -2
- package/types/src/comms/index.d.ts +3 -3
- package/types/src/helpers/css.d.ts +1 -2
- package/types/src/helpers/document.d.ts +1 -1
- package/types/src/index.d.ts +4 -4
- package/types/src/modules/Decorator.d.ts +4 -4
- package/types/src/modules/Module.d.ts +3 -3
- package/types/src/modules/ModuleLibrary.d.ts +2 -2
- package/types/src/modules/Peripherals.d.ts +10 -9
- package/types/src/modules/index.d.ts +4 -4
- package/types/src/modules/setup/FixedSetup.d.ts +4 -4
- package/types/src/modules/setup/ReflowableSetup.d.ts +3 -3
- package/types/src/modules/setup/Setup.d.ts +4 -4
- package/types/src/modules/setup/WebPubSetup.d.ts +3 -3
- package/types/src/modules/snapper/CJKVerticalSnapper.d.ts +57 -0
- package/types/src/modules/snapper/ColumnSnapper.d.ts +16 -5
- package/types/src/modules/snapper/ScrollSnapper.d.ts +5 -5
- package/types/src/modules/snapper/Snapper.d.ts +4 -4
- package/types/src/modules/snapper/WebPubSnapper.d.ts +5 -5
- package/types/src/protection/BulkCopyProtector.d.ts +1 -1
- package/types/src/protection/KeyCombinationManager.d.ts +3 -3
- package/types/src/protection/PrintProtector.d.ts +4 -4
- package/types/src/protection/config.d.ts +3 -3
- package/types/src/protection/index.d.ts +6 -6
- package/types/src/vendor/hypothesis/anchoring/html.d.ts +1 -1
- package/types/src/vendor/hypothesis/anchoring/types.d.ts +1 -1
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { Comms } from "../../comms";
|
|
2
|
-
import { Snapper } from "./Snapper";
|
|
3
|
-
import { getColumnCountPerScreen, isRTL, appendVirtualColumnIfNeeded } from "../../helpers/document";
|
|
4
|
-
import { easeInOutQuad } from "../../helpers/animation";
|
|
5
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
1
|
+
import { Comms } from "../../comms/index.ts";
|
|
2
|
+
import { Snapper } from "./Snapper.ts";
|
|
3
|
+
import { getColumnCountPerScreen, isRTL, appendVirtualColumnIfNeeded } from "../../helpers/document.ts";
|
|
4
|
+
import { easeInOutQuad } from "../../helpers/animation.ts";
|
|
5
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
6
6
|
import { Locator, LocatorLocations, LocatorText } from "@readium/shared";
|
|
7
|
-
import { rangeFromLocator } from "../../helpers/locator";
|
|
8
|
-
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom";
|
|
9
|
-
import { PatternAnalyzer } from "../../protection/PatternAnalyzer";
|
|
10
|
-
import { BaseSuspiciousActivityEvent } from "../Peripherals";
|
|
11
|
-
import { isTypedOMSupported } from "../../helpers/css";
|
|
7
|
+
import { rangeFromLocator } from "../../helpers/locator.ts";
|
|
8
|
+
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom.ts";
|
|
9
|
+
import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts";
|
|
10
|
+
import { BaseSuspiciousActivityEvent } from "../Peripherals.ts";
|
|
12
11
|
|
|
13
12
|
const COLUMN_SNAPPER_STYLE_ID = "readium-column-snapper-style";
|
|
14
13
|
|
|
@@ -42,7 +41,9 @@ export class ColumnSnapper extends Snapper {
|
|
|
42
41
|
// The reason we do this is because when the document is transformed (translate3d),
|
|
43
42
|
// the scrollLeft value is 0 because... reasons. So we have to use the cached value
|
|
44
43
|
// from this.alreadyScrollLeft instead.
|
|
45
|
-
|
|
44
|
+
// Note: for RTL, scrollLeft is negative, so we check !== 0, not > 0.
|
|
45
|
+
const sl = this.doc().scrollLeft;
|
|
46
|
+
return (sl !== 0) ? sl : this.alreadyScrollLeft;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
snapOffset(offset: number) {
|
|
@@ -50,13 +51,35 @@ export class ColumnSnapper extends Snapper {
|
|
|
50
51
|
return value - (value % this.wnd.innerWidth);
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Snap a non-negative normalized offset (distance from document start) to
|
|
56
|
+
* the nearest page boundary. Works identically regardless of reading direction.
|
|
57
|
+
*/
|
|
58
|
+
private snapNormOffset(offset: number): number {
|
|
59
|
+
const value = offset + 1;
|
|
60
|
+
return value - (value % this.wnd.innerWidth);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Normalized scroll position: distance from document start, always in
|
|
65
|
+
* [0, scrollWidth - innerWidth] regardless of reading direction.
|
|
66
|
+
* scrollLeft may be negative depending on the browser — Math.abs() normalizes it.
|
|
67
|
+
*/
|
|
68
|
+
private normScroll(): number {
|
|
69
|
+
const rtl = isRTL(this.wnd);
|
|
70
|
+
const raw = this.doc().scrollLeft || this.alreadyScrollLeft;
|
|
71
|
+
return rtl ? Math.abs(raw) : Math.max(0, this.wnd.scrollX > 0 ? this.wnd.scrollX : raw);
|
|
72
|
+
}
|
|
73
|
+
|
|
53
74
|
reportProgress() {
|
|
54
|
-
// Contrary to ScrollTop, Android slightly adds to scrollX
|
|
55
|
-
// So we do not need to round it up
|
|
56
|
-
const scrollX = this.wnd.scrollX;
|
|
57
75
|
const scrollWidth = this.cachedScrollWidth;
|
|
58
|
-
const
|
|
59
|
-
const
|
|
76
|
+
const viewportWidth = this.wnd.innerWidth;
|
|
77
|
+
const norm = this.normScroll();
|
|
78
|
+
const scrollable = Math.max(1, scrollWidth - viewportWidth);
|
|
79
|
+
|
|
80
|
+
// progress 0 = document start, 1 = document end — same regardless of direction.
|
|
81
|
+
const progress = Math.max(0, Math.min(1, norm / scrollable));
|
|
82
|
+
const viewportEnd = Math.max(0, Math.min(1, (norm + viewportWidth) / scrollWidth));
|
|
60
83
|
this.comms.send("progress", {
|
|
61
84
|
start: progress,
|
|
62
85
|
end: viewportEnd
|
|
@@ -70,11 +93,17 @@ export class ColumnSnapper extends Snapper {
|
|
|
70
93
|
if(this.overscroll !== 0 || this.shakeTimeout !== 0) return;
|
|
71
94
|
const doc = this.doc();
|
|
72
95
|
|
|
73
|
-
|
|
96
|
+
// Bounce toward the boundary that was hit.
|
|
97
|
+
// LTR: end is on the right → bounce-r. RTL: start is on the right (norm≈0),
|
|
98
|
+
// end is on the left → bounce direction flips based on which boundary we're at.
|
|
99
|
+
const atStart = this.normScroll() < 5;
|
|
100
|
+
const bounceClass = isRTL(this.wnd)
|
|
101
|
+
? (atStart ? "readium-bounce-r" : "readium-bounce-l")
|
|
102
|
+
: "readium-bounce-r";
|
|
103
|
+
doc.classList.add(bounceClass);
|
|
74
104
|
const curScrollLeft = this.scrollOffset();
|
|
75
105
|
this.shakeTimeout = this.wnd.setTimeout(() => {
|
|
76
|
-
doc.classList.remove(
|
|
77
|
-
doc.classList.remove("readium-bounce-r");
|
|
106
|
+
doc.classList.remove(bounceClass);
|
|
78
107
|
this.shakeTimeout = 0;
|
|
79
108
|
this.doc().scrollLeft = curScrollLeft;
|
|
80
109
|
}, 150);
|
|
@@ -84,7 +113,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
84
113
|
private alreadyScrollLeft = 0;
|
|
85
114
|
private overscroll = 0;
|
|
86
115
|
private cachedScrollWidth = 0; // We have to cache this because during overscroll (transform, or left) the width is incorrect due to browser
|
|
87
|
-
|
|
116
|
+
|
|
88
117
|
private takeOverSnap() {
|
|
89
118
|
this.snappingCancelled = true;
|
|
90
119
|
this.clearTouches();
|
|
@@ -93,36 +122,40 @@ export class ColumnSnapper extends Snapper {
|
|
|
93
122
|
// translate3d(XXXpx, 0px, 0px) -> slice 12 -> XXXpx, 0px, 0px) -> split "px" [0] -> XXX
|
|
94
123
|
this.overscroll = doc.style.transform?.length > 12 ? parseFloat(doc.style.transform.slice(12).split("px")[0]) : 0;
|
|
95
124
|
}
|
|
96
|
-
|
|
125
|
+
|
|
97
126
|
// Snaps the current offset to the page width.
|
|
98
127
|
snapCurrentOffset(smooth=false, noprogress=false) {
|
|
99
|
-
const
|
|
128
|
+
const rtl = isRTL(this.wnd);
|
|
100
129
|
const doc = this.doc();
|
|
101
|
-
const cdo = this.dragOffset();
|
|
102
130
|
const columnCount = getColumnCountPerScreen(this.wnd);
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
131
|
+
const maxScroll = this.cachedScrollWidth - this.wnd.innerWidth;
|
|
132
|
+
|
|
133
|
+
// Work in normalized (non-negative, distance-from-start) coordinates
|
|
134
|
+
// so all arithmetic is direction-agnostic.
|
|
135
|
+
const currentNorm = Math.min(Math.max(0, this.normScroll()), maxScroll);
|
|
136
|
+
|
|
137
|
+
// Forward drag: LTR = drag left (cdo > 0), RTL = drag right (cdo < 0).
|
|
138
|
+
// Normalize so that positive = moving forward in reading order.
|
|
139
|
+
const cdo = this.dragOffset();
|
|
140
|
+
const normalizedCdo = rtl ? -cdo : cdo;
|
|
141
|
+
const hurdle = (this.wnd.innerWidth / 3) * (normalizedCdo > 0 ? 2 : 1);
|
|
142
|
+
|
|
143
|
+
const snappedNorm = Math.min(maxScroll, Math.max(0, this.snapNormOffset(currentNorm + hurdle)));
|
|
144
|
+
|
|
145
|
+
// Convert back to signed scrollLeft for the actual DOM write.
|
|
146
|
+
const so = rtl ? -snappedNorm : snappedNorm;
|
|
147
|
+
const currentScrollLeft = rtl ? -currentNorm : currentNorm;
|
|
148
|
+
|
|
149
|
+
const direction = so > currentScrollLeft ? "right" : "left";
|
|
150
|
+
this.checkSuspiciousSnap(direction, Math.abs(so - currentScrollLeft));
|
|
151
|
+
|
|
152
|
+
if(smooth && so !== currentScrollLeft) { // Smooth snapping
|
|
118
153
|
this.snappingCancelled = false;
|
|
119
154
|
const position = (start: number, end: number, elapsed: number, period: number) => {
|
|
120
|
-
if (elapsed > period)
|
|
121
|
-
return end;
|
|
122
|
-
}
|
|
155
|
+
if (elapsed > period) return end;
|
|
123
156
|
return start + (end - start) * easeInOutQuad(elapsed / period);
|
|
124
|
-
}
|
|
125
|
-
const period =
|
|
157
|
+
};
|
|
158
|
+
const period = SNAP_DURATION * columnCount;
|
|
126
159
|
let startTime: number;
|
|
127
160
|
const step = (timestamp: number) => {
|
|
128
161
|
if(this.snappingCancelled) return;
|
|
@@ -131,16 +164,10 @@ export class ColumnSnapper extends Snapper {
|
|
|
131
164
|
const elapsed = timestamp - startTime;
|
|
132
165
|
|
|
133
166
|
const lpos = position(this.overscroll, 0, elapsed, period);
|
|
134
|
-
const spos = position(
|
|
167
|
+
const spos = position(currentScrollLeft, so, elapsed, period);
|
|
135
168
|
doc.scrollLeft = spos;
|
|
136
169
|
if(this.overscroll !== 0)
|
|
137
|
-
|
|
138
|
-
doc.attributeStyleMap.set("transform", new CSSTransformValue([
|
|
139
|
-
new CSSTranslate(CSS.px(-lpos), CSS.px(0), CSS.px(0))
|
|
140
|
-
]));
|
|
141
|
-
} else {
|
|
142
|
-
doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`;
|
|
143
|
-
}
|
|
170
|
+
doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`;
|
|
144
171
|
|
|
145
172
|
if (elapsed < period)
|
|
146
173
|
this.wnd.requestAnimationFrame(step);
|
|
@@ -150,7 +177,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
150
177
|
doc.scrollLeft = so;
|
|
151
178
|
if(!noprogress) this.reportProgress();
|
|
152
179
|
}
|
|
153
|
-
}
|
|
180
|
+
};
|
|
154
181
|
this.wnd.requestAnimationFrame(step);
|
|
155
182
|
} else { // Instant snapping
|
|
156
183
|
doc.style.removeProperty("transform");
|
|
@@ -200,18 +227,25 @@ export class ColumnSnapper extends Snapper {
|
|
|
200
227
|
if(this.touchState === ScrollTouchState.MOVE) {
|
|
201
228
|
// Get the horizontal drag distance
|
|
202
229
|
const dragOffset = this.dragOffset();
|
|
203
|
-
const
|
|
204
|
-
|
|
230
|
+
const rtl = isRTL(this.wnd);
|
|
231
|
+
|
|
232
|
+
// Normalize: distance from document start, forward drag is positive.
|
|
233
|
+
const norm = this.normScroll();
|
|
234
|
+
const maxScroll = this.cachedScrollWidth - this.wnd.innerWidth;
|
|
235
|
+
const normalizedDrag = rtl ? -dragOffset : dragOffset;
|
|
236
|
+
|
|
205
237
|
if(this.cachedScrollWidth <= this.wnd.innerWidth) {
|
|
206
238
|
// Only a single page, meaning any swipe triggers next/prev
|
|
207
239
|
this.reportProgress();
|
|
208
|
-
if(
|
|
209
|
-
if(
|
|
210
|
-
} else if(
|
|
240
|
+
if(normalizedDrag > 5) this.comms.send("no_more", undefined);
|
|
241
|
+
if(normalizedDrag < -5) this.comms.send("no_less", undefined);
|
|
242
|
+
} else if(norm < 5 && normalizedDrag < 5) {
|
|
243
|
+
// At document start, tried to go backward
|
|
211
244
|
this.alreadyScrollLeft = 0;
|
|
212
245
|
this.comms.send("no_less", undefined);
|
|
213
|
-
} else if((
|
|
214
|
-
|
|
246
|
+
} else if((maxScroll - norm) < 5 && normalizedDrag > 5) {
|
|
247
|
+
// At document end, tried to go forward
|
|
248
|
+
this.alreadyScrollLeft = rtl ? -maxScroll : maxScroll;
|
|
215
249
|
this.comms.send("no_more", undefined);
|
|
216
250
|
}
|
|
217
251
|
|
|
@@ -242,28 +276,22 @@ export class ColumnSnapper extends Snapper {
|
|
|
242
276
|
|
|
243
277
|
const dro = this.dragOffset();
|
|
244
278
|
const newpos = this.alreadyScrollLeft + dro;
|
|
245
|
-
|
|
279
|
+
|
|
280
|
+
// For RTL, scrollLeft is in [-(scrollWidth - innerWidth), 0].
|
|
281
|
+
// For LTR, scrollLeft is in [0, scrollWidth - innerWidth].
|
|
282
|
+
const minScrollLeft = isRTL(this.wnd) ? -(this.cachedScrollWidth - this.wnd.innerWidth) : 0;
|
|
283
|
+
const maxScrollLeft = isRTL(this.wnd) ? 0 : (this.cachedScrollWidth - this.wnd.innerWidth);
|
|
284
|
+
|
|
285
|
+
if(newpos < minScrollLeft) {
|
|
246
286
|
this.overscroll = newpos;
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0))
|
|
250
|
-
]));
|
|
251
|
-
} else {
|
|
252
|
-
this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
|
|
253
|
-
}
|
|
254
|
-
} else if((newpos + this.wnd.innerWidth) > this.cachedScrollWidth) {
|
|
287
|
+
this.doc().style.transform = `translate3d(${-this.overscroll}px, 0px, 0px)`;
|
|
288
|
+
} else if(newpos > maxScrollLeft) {
|
|
255
289
|
this.overscroll = newpos;
|
|
256
|
-
|
|
257
|
-
this.doc().attributeStyleMap.set("transform", new CSSTransformValue([
|
|
258
|
-
new CSSTranslate(CSS.px(-newpos), CSS.px(0), CSS.px(0))
|
|
259
|
-
]));
|
|
260
|
-
} else {
|
|
261
|
-
this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
|
|
262
|
-
}
|
|
290
|
+
this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
|
|
263
291
|
} else {
|
|
264
292
|
this.overscroll = 0;
|
|
265
293
|
this.doc().style.removeProperty("transform");
|
|
266
|
-
this.doc().scrollLeft =
|
|
294
|
+
this.doc().scrollLeft = newpos;
|
|
267
295
|
}
|
|
268
296
|
}
|
|
269
297
|
private readonly onTouchMover = this.onTouchMove.bind(this);
|
|
@@ -340,7 +368,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
340
368
|
* {
|
|
341
369
|
scrollbar-width: none; /* for Firefox */
|
|
342
370
|
}
|
|
343
|
-
|
|
371
|
+
|
|
344
372
|
body::-webkit-scrollbar {
|
|
345
373
|
display: none; /* for Chrome, Safari, and Opera */
|
|
346
374
|
}
|
|
@@ -365,7 +393,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
365
393
|
const oldValueTransform = oldValue?.match(transformRegex);
|
|
366
394
|
const newValueTransform = newValue?.match(transformRegex);
|
|
367
395
|
if (
|
|
368
|
-
(!oldValueTransform && !newValueTransform) ||
|
|
396
|
+
(!oldValueTransform && !newValueTransform) ||
|
|
369
397
|
(oldValueTransform && !newValueTransform) ||
|
|
370
398
|
(oldValueTransform && newValueTransform && oldValueTransform[1] !== newValueTransform[1])
|
|
371
399
|
) {
|
|
@@ -384,16 +412,25 @@ export class ColumnSnapper extends Snapper {
|
|
|
384
412
|
// For cases the resizeObserver is not able to detect cos body is not resizing despite colCount,
|
|
385
413
|
// we need to check the syle attribute on the documentElement (ReadiumCSS props)
|
|
386
414
|
this.mutationObserver.observe(wnd.document.documentElement, {attributes: true, attributeFilter: ["style"]});
|
|
387
|
-
|
|
415
|
+
|
|
388
416
|
comms.register("scroll_protection", ColumnSnapper.moduleName, (_, ack) => {
|
|
389
417
|
this.enableSnapProtection();
|
|
390
418
|
ack(true);
|
|
391
419
|
});
|
|
392
420
|
|
|
421
|
+
// LTR only — snapOffset only works with non-negative values.
|
|
393
422
|
const scrollToOffset = (offset: number): boolean => {
|
|
394
423
|
const oldScrollLeft = this.doc().scrollLeft;
|
|
395
|
-
this.doc().scrollLeft = this.snapOffset(offset);
|
|
424
|
+
this.doc().scrollLeft = this.snapOffset(offset);
|
|
425
|
+
return oldScrollLeft !== this.doc().scrollLeft;
|
|
426
|
+
}
|
|
396
427
|
|
|
428
|
+
// RTL: offset is normalized (non-negative, 0 = start, scrollable = end).
|
|
429
|
+
// Snaps to page boundary and sets scrollLeft = -snappedNorm.
|
|
430
|
+
const rtlScrollToNorm = (norm: number): boolean => {
|
|
431
|
+
const oldScrollLeft = this.doc().scrollLeft;
|
|
432
|
+
const snapped = this.snapNormOffset(Math.max(0, Math.min(this.cachedScrollWidth - wnd.innerWidth, norm)));
|
|
433
|
+
this.doc().scrollLeft = -snapped;
|
|
397
434
|
return oldScrollLeft !== this.doc().scrollLeft;
|
|
398
435
|
}
|
|
399
436
|
|
|
@@ -412,10 +449,13 @@ export class ColumnSnapper extends Snapper {
|
|
|
412
449
|
}
|
|
413
450
|
this.wnd.requestAnimationFrame(() => {
|
|
414
451
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
415
|
-
const
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
452
|
+
const scrollable = this.cachedScrollWidth - wnd.innerWidth;
|
|
453
|
+
const norm = scrollable * position;
|
|
454
|
+
if (isRTL(wnd)) {
|
|
455
|
+
this.doc().scrollLeft = -this.snapNormOffset(norm);
|
|
456
|
+
} else {
|
|
457
|
+
this.doc().scrollLeft = this.snapOffset(norm);
|
|
458
|
+
}
|
|
419
459
|
this.reportProgress();
|
|
420
460
|
deselect(this.wnd);
|
|
421
461
|
ack(true);
|
|
@@ -429,7 +469,11 @@ export class ColumnSnapper extends Snapper {
|
|
|
429
469
|
return;
|
|
430
470
|
}
|
|
431
471
|
this.wnd.requestAnimationFrame(() => {
|
|
432
|
-
|
|
472
|
+
if (isRTL(wnd)) {
|
|
473
|
+
this.doc().scrollLeft = -this.snapNormOffset(element.getBoundingClientRect().left + wnd.scrollX);
|
|
474
|
+
} else {
|
|
475
|
+
this.doc().scrollLeft = this.snapOffset(element.getBoundingClientRect().left + wnd.scrollX);
|
|
476
|
+
}
|
|
433
477
|
this.reportProgress();
|
|
434
478
|
deselect(this.wnd);
|
|
435
479
|
ack(true);
|
|
@@ -460,7 +504,11 @@ export class ColumnSnapper extends Snapper {
|
|
|
460
504
|
return;
|
|
461
505
|
}
|
|
462
506
|
this.wnd.requestAnimationFrame(() => {
|
|
463
|
-
|
|
507
|
+
if (isRTL(wnd)) {
|
|
508
|
+
this.doc().scrollLeft = -this.snapNormOffset(r.getBoundingClientRect().left + wnd.scrollX);
|
|
509
|
+
} else {
|
|
510
|
+
this.doc().scrollLeft = this.snapOffset(r.getBoundingClientRect().left + wnd.scrollX);
|
|
511
|
+
}
|
|
464
512
|
this.reportProgress();
|
|
465
513
|
deselect(this.wnd);
|
|
466
514
|
ack(true);
|
|
@@ -468,12 +516,18 @@ export class ColumnSnapper extends Snapper {
|
|
|
468
516
|
});
|
|
469
517
|
|
|
470
518
|
comms.register("go_end", ColumnSnapper.moduleName, (_, ack) => {
|
|
471
|
-
const factor = isRTL(wnd) ? -1 : 1;
|
|
472
519
|
this.wnd.requestAnimationFrame(() => {
|
|
473
520
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
474
|
-
|
|
475
|
-
if(
|
|
476
|
-
|
|
521
|
+
let snappedFinal: number;
|
|
522
|
+
if (isRTL(wnd)) {
|
|
523
|
+
// RTL: end is the leftmost position, using normalized snapping first
|
|
524
|
+
snappedFinal = -this.snapNormOffset(this.cachedScrollWidth - wnd.innerWidth);
|
|
525
|
+
} else {
|
|
526
|
+
// LTR: end is scrollWidth (browser clamps to scrollWidth - innerWidth)
|
|
527
|
+
snappedFinal = this.snapOffset(this.cachedScrollWidth);
|
|
528
|
+
}
|
|
529
|
+
if(this.doc().scrollLeft === snappedFinal) return ack(false);
|
|
530
|
+
this.doc().scrollLeft = snappedFinal;
|
|
477
531
|
this.reportProgress();
|
|
478
532
|
deselect(this.wnd);
|
|
479
533
|
ack(true);
|
|
@@ -493,11 +547,15 @@ export class ColumnSnapper extends Snapper {
|
|
|
493
547
|
comms.register("go_prev", ColumnSnapper.moduleName, (_, ack) => {
|
|
494
548
|
this.wnd.requestAnimationFrame(() => {
|
|
495
549
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
550
|
+
let change: boolean;
|
|
551
|
+
if (isRTL(wnd)) {
|
|
552
|
+
// RTL: go_prev = toward document start = decreasing norm = scrollLeft toward 0
|
|
553
|
+
change = rtlScrollToNorm(this.normScroll() - wnd.innerWidth);
|
|
554
|
+
} else {
|
|
555
|
+
change = scrollToOffset(wnd.scrollX - wnd.innerWidth);
|
|
556
|
+
}
|
|
557
|
+
this.reportProgress();
|
|
499
558
|
if(change) {
|
|
500
|
-
this.reportProgress();
|
|
501
559
|
deselect(this.wnd);
|
|
502
560
|
this.checkSuspiciousSnap("left", this.wnd.innerWidth);
|
|
503
561
|
}
|
|
@@ -508,11 +566,15 @@ export class ColumnSnapper extends Snapper {
|
|
|
508
566
|
comms.register("go_next", ColumnSnapper.moduleName, (_, ack) => {
|
|
509
567
|
this.wnd.requestAnimationFrame(() => {
|
|
510
568
|
this.cachedScrollWidth = this.doc().scrollWidth!;
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
569
|
+
let change: boolean;
|
|
570
|
+
if (isRTL(wnd)) {
|
|
571
|
+
// RTL: go_next = toward document end = increasing norm = scrollLeft more negative
|
|
572
|
+
change = rtlScrollToNorm(this.normScroll() + wnd.innerWidth);
|
|
573
|
+
} else {
|
|
574
|
+
change = scrollToOffset(wnd.scrollX + wnd.innerWidth);
|
|
575
|
+
}
|
|
576
|
+
this.reportProgress();
|
|
514
577
|
if(change) {
|
|
515
|
-
this.reportProgress();
|
|
516
578
|
deselect(this.wnd);
|
|
517
579
|
this.checkSuspiciousSnap("right", this.wnd.innerWidth);
|
|
518
580
|
}
|
|
@@ -582,4 +644,4 @@ export class ColumnSnapper extends Snapper {
|
|
|
582
644
|
comms.log("ColumnSnapper Unmounted");
|
|
583
645
|
return super.unmount(wnd, comms);
|
|
584
646
|
}
|
|
585
|
-
}
|
|
647
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Locator, LocatorLocations, LocatorText } from "@readium/shared";
|
|
2
|
-
import { Comms } from "../../comms";
|
|
3
|
-
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom";
|
|
4
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
5
|
-
import { Snapper } from "./Snapper";
|
|
6
|
-
import { rangeFromLocator } from "../../helpers/locator";
|
|
7
|
-
import { forceWebkitRecalc } from "../../helpers/document";
|
|
8
|
-
import { PatternAnalyzer } from "../../protection/PatternAnalyzer";
|
|
9
|
-
import { SCROLL_PROTECTION_CONFIG } from "../../protection/config";
|
|
10
|
-
import { BaseSuspiciousActivityEvent } from "../Peripherals";
|
|
2
|
+
import { Comms } from "../../comms/index.ts";
|
|
3
|
+
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom.ts";
|
|
4
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
|
+
import { Snapper } from "./Snapper.ts";
|
|
6
|
+
import { rangeFromLocator } from "../../helpers/locator.ts";
|
|
7
|
+
import { forceWebkitRecalc } from "../../helpers/document.ts";
|
|
8
|
+
import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts";
|
|
9
|
+
import { SCROLL_PROTECTION_CONFIG } from "../../protection/config.ts";
|
|
10
|
+
import { BaseSuspiciousActivityEvent } from "../Peripherals.ts";
|
|
11
11
|
|
|
12
12
|
const SCROLL_SNAPPER_STYLE_ID = "readium-scroll-snapper-style";
|
|
13
13
|
|
|
@@ -56,7 +56,7 @@ export class ScrollSnapper extends Snapper {
|
|
|
56
56
|
|
|
57
57
|
private handleScroll = (_e: Event) => {
|
|
58
58
|
if (!this.comms.ready) return;
|
|
59
|
-
|
|
59
|
+
|
|
60
60
|
// We have to filter scroll from resize events
|
|
61
61
|
if (this.isResizing) {
|
|
62
62
|
return;
|
|
@@ -91,9 +91,9 @@ export class ScrollSnapper extends Snapper {
|
|
|
91
91
|
timeDelta
|
|
92
92
|
);
|
|
93
93
|
if (isSuspicious) {
|
|
94
|
-
const target = _e.target && "tagName" in _e.target ?
|
|
94
|
+
const target = _e.target && "tagName" in _e.target ?
|
|
95
95
|
{ tagName: (_e.target as Element).tagName } : null;
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
this.comms?.send("content_protection", {
|
|
98
98
|
type: "suspicious_scrolling",
|
|
99
99
|
timestamp: Date.now(),
|
|
@@ -107,7 +107,7 @@ export class ScrollSnapper extends Snapper {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
this.comms.send("scroll", deltaY);
|
|
110
|
-
|
|
110
|
+
|
|
111
111
|
this.isScrolling = false;
|
|
112
112
|
});
|
|
113
113
|
}
|
|
@@ -143,7 +143,7 @@ export class ScrollSnapper extends Snapper {
|
|
|
143
143
|
* {
|
|
144
144
|
scrollbar-width: none; /* for Firefox */
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
body::-webkit-scrollbar {
|
|
148
148
|
display: none; /* for Chrome, Safari, and Opera */
|
|
149
149
|
}
|
|
@@ -157,7 +157,7 @@ export class ScrollSnapper extends Snapper {
|
|
|
157
157
|
if (this.resizeDebounce) {
|
|
158
158
|
this.wnd.clearTimeout(this.resizeDebounce);
|
|
159
159
|
}
|
|
160
|
-
|
|
160
|
+
|
|
161
161
|
this.isResizing = true;
|
|
162
162
|
this.resizeDebounce = this.wnd.setTimeout(() => {
|
|
163
163
|
this.isResizing = false;
|
|
@@ -173,8 +173,8 @@ export class ScrollSnapper extends Snapper {
|
|
|
173
173
|
forceWebkitRecalc(this.wnd);
|
|
174
174
|
|
|
175
175
|
// We absolutely must do this because overflown content
|
|
176
|
-
// won’t be rendered if we do not trigger scroll…
|
|
177
|
-
// Only the content at the start of the document,
|
|
176
|
+
// won’t be rendered if we do not trigger scroll…
|
|
177
|
+
// Only the content at the start of the document,
|
|
178
178
|
// whose height is the viewport height, will be rendered.
|
|
179
179
|
const currentScroll = this.doc().scrollTop;
|
|
180
180
|
if (currentScroll > 1) {
|
|
@@ -308,4 +308,4 @@ export class ScrollSnapper extends Snapper {
|
|
|
308
308
|
comms.log("ScrollSnapper Unmounted");
|
|
309
309
|
return true;
|
|
310
310
|
}
|
|
311
|
-
}
|
|
311
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Comms } from "../../comms";
|
|
2
|
-
import { ReadiumWindow } from "../../helpers/dom";
|
|
3
|
-
import { Module } from "../Module";
|
|
4
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
1
|
+
import { Comms } from "../../comms/index.ts";
|
|
2
|
+
import { ReadiumWindow } from "../../helpers/dom.ts";
|
|
3
|
+
import { Module } from "../Module.ts";
|
|
4
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
5
|
|
|
6
6
|
const SNAPPER_STYLE_ID = "readium-snapper-style";
|
|
7
7
|
|
|
@@ -46,4 +46,4 @@ export abstract class Snapper extends Module {
|
|
|
46
46
|
comms.log("Snapper Unmounted");
|
|
47
47
|
return true;
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Locator, LocatorLocations, LocatorText } from "@readium/shared";
|
|
2
|
-
import { Comms } from "../../comms/comms";
|
|
3
|
-
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom";
|
|
4
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
5
|
-
import { Snapper } from "./Snapper";
|
|
6
|
-
import { rangeFromLocator } from "../../helpers/locator";
|
|
7
|
-
import { forceWebkitRecalc } from "../../helpers/document";
|
|
8
|
-
import { PatternAnalyzer } from "../../protection/PatternAnalyzer";
|
|
9
|
-
import { SCROLL_PROTECTION_CONFIG } from "../../protection/config";
|
|
10
|
-
import { SuspiciousScrollingEvent } from "./ScrollSnapper";
|
|
2
|
+
import { Comms } from "../../comms/comms.ts";
|
|
3
|
+
import { ReadiumWindow, deselect, findFirstVisibleLocator } from "../../helpers/dom.ts";
|
|
4
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
|
+
import { Snapper } from "./Snapper.ts";
|
|
6
|
+
import { rangeFromLocator } from "../../helpers/locator.ts";
|
|
7
|
+
import { forceWebkitRecalc } from "../../helpers/document.ts";
|
|
8
|
+
import { PatternAnalyzer } from "../../protection/PatternAnalyzer.ts";
|
|
9
|
+
import { SCROLL_PROTECTION_CONFIG } from "../../protection/config.ts";
|
|
10
|
+
import { SuspiciousScrollingEvent } from "./ScrollSnapper.ts";
|
|
11
11
|
|
|
12
12
|
export type { SuspiciousScrollingEvent };
|
|
13
13
|
|
|
@@ -82,9 +82,9 @@ export class WebPubSnapper extends Snapper {
|
|
|
82
82
|
timeDelta
|
|
83
83
|
);
|
|
84
84
|
if (isSuspicious) {
|
|
85
|
-
const target = _e.target && "tagName" in _e.target ?
|
|
85
|
+
const target = _e.target && "tagName" in _e.target ?
|
|
86
86
|
{ tagName: (_e.target as Element).tagName } : null;
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
this.comms?.send("content_protection", {
|
|
89
89
|
type: "suspicious_scrolling",
|
|
90
90
|
timestamp: Date.now(),
|
|
@@ -145,8 +145,8 @@ export class WebPubSnapper extends Snapper {
|
|
|
145
145
|
forceWebkitRecalc(this.wnd);
|
|
146
146
|
|
|
147
147
|
// We absolutely must do this because overflown content
|
|
148
|
-
// won’t be rendered if we do not trigger scroll…
|
|
149
|
-
// Only the content at the start of the document,
|
|
148
|
+
// won’t be rendered if we do not trigger scroll…
|
|
149
|
+
// Only the content at the start of the document,
|
|
150
150
|
// whose height is the viewport height, will be rendered.
|
|
151
151
|
const currentScroll = this.doc().scrollTop;
|
|
152
152
|
if (currentScroll > 1) {
|
|
@@ -267,7 +267,7 @@ export class WebPubSnapper extends Snapper {
|
|
|
267
267
|
comms.unregisterAll(WebPubSnapper.moduleName);
|
|
268
268
|
this.resizeObserver.disconnect();
|
|
269
269
|
if (this.handleScroll) wnd.removeEventListener("scroll", this.handleScroll);
|
|
270
|
-
|
|
270
|
+
|
|
271
271
|
if (this.patternAnalyzer) {
|
|
272
272
|
this.patternAnalyzer.clear();
|
|
273
273
|
this.patternAnalyzer = null;
|
|
@@ -277,4 +277,4 @@ export class WebPubSnapper extends Snapper {
|
|
|
277
277
|
comms.log("WebPubSnapper Unmounted");
|
|
278
278
|
return true;
|
|
279
279
|
}
|
|
280
|
-
}
|
|
280
|
+
}
|