@readium/navigator-html-injectables 2.3.1-alpha.2 → 2.3.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 +344 -349
- package/dist/index.umd.cjs +12 -12
- package/package.json +8 -8
- 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 +9 -9
- 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 +11 -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/ColumnSnapper.ts +20 -39
- 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/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 +1 -1
- 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/ColumnSnapper.d.ts +5 -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,8 +1,8 @@
|
|
|
1
|
-
import { Comms } from "../../comms/comms";
|
|
2
|
-
import { Setup } from "./Setup";
|
|
3
|
-
import { removeProperty, setProperty } from "../../helpers/css";
|
|
4
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
5
|
-
import { ReadiumWindow, deselect } from "../../helpers/dom";
|
|
1
|
+
import { Comms } from "../../comms/comms.ts";
|
|
2
|
+
import { Setup } from "./Setup.ts";
|
|
3
|
+
import { removeProperty, setProperty } from "../../helpers/css.ts";
|
|
4
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
|
+
import { ReadiumWindow, deselect } from "../../helpers/dom.ts";
|
|
6
6
|
|
|
7
7
|
const FIXED_STYLE_ID = "readium-fixed-style";
|
|
8
8
|
|
|
@@ -72,4 +72,4 @@ export class FixedSetup extends Setup {
|
|
|
72
72
|
comms.log("FixedSetup Unmounted");
|
|
73
73
|
return super.unmount(wnd, comms);
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Comms } from "../../comms/comms";
|
|
2
|
-
import { Setup } from "./Setup";
|
|
3
|
-
import { getProperties, removeProperty, setProperty, updateProperties } from "../../helpers/css";
|
|
4
|
-
import { ReadiumWindow } from "../../helpers/dom";
|
|
1
|
+
import { Comms } from "../../comms/comms.ts";
|
|
2
|
+
import { Setup } from "./Setup.ts";
|
|
3
|
+
import { getProperties, removeProperty, setProperty, updateProperties } from "../../helpers/css.ts";
|
|
4
|
+
import { ReadiumWindow } from "../../helpers/dom.ts";
|
|
5
5
|
|
|
6
6
|
const VIEWPORT_META_TAG_ID = "readium-viewport";
|
|
7
7
|
|
|
@@ -40,7 +40,7 @@ export class ReflowableSetup extends Setup {
|
|
|
40
40
|
ack(true);
|
|
41
41
|
});
|
|
42
42
|
comms.register("update_properties", ReflowableSetup.moduleName, (data, ack) => {
|
|
43
|
-
// In order to keep the viewport width that is added in the setup itself,
|
|
43
|
+
// In order to keep the viewport width that is added in the setup itself,
|
|
44
44
|
// and don’t have EpubNavigator concerned with this, we need to add it there
|
|
45
45
|
(data as { [key: string]: string })["--RS__viewportWidth"] = `${ wnd.innerWidth }px`;
|
|
46
46
|
updateProperties(wnd, data as { [key: string]: string });
|
|
@@ -73,4 +73,4 @@ export class ReflowableSetup extends Setup {
|
|
|
73
73
|
comms.log("ReflowableSetup Unmounted");
|
|
74
74
|
return super.unmount(wnd, comms);
|
|
75
75
|
}
|
|
76
|
-
}
|
|
76
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Comms } from "../../comms/comms";
|
|
2
|
-
import { ReadiumWindow, EPUBReadingSystem } from "../../helpers/dom";
|
|
3
|
-
import { Module } from "../Module";
|
|
4
|
-
import { ModuleName } from "../ModuleLibrary";
|
|
1
|
+
import { Comms } from "../../comms/comms.ts";
|
|
2
|
+
import { ReadiumWindow, EPUBReadingSystem } from "../../helpers/dom.ts";
|
|
3
|
+
import { Module } from "../Module.ts";
|
|
4
|
+
import { ModuleName } from "../ModuleLibrary.ts";
|
|
5
5
|
|
|
6
6
|
export abstract class Setup extends Module {
|
|
7
7
|
static readonly moduleName: ModuleName = "setup";
|
|
@@ -157,4 +157,4 @@ export abstract class Setup extends Module {
|
|
|
157
157
|
comms.log("Setup Unmounted");
|
|
158
158
|
return true;
|
|
159
159
|
}
|
|
160
|
-
}
|
|
160
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Comms } from "../../comms/comms";
|
|
2
|
-
import { ReadiumWindow } from "../../helpers/dom";
|
|
3
|
-
import { Module } from "../Module";
|
|
4
|
-
import { getProperties, removeProperty, setProperty, updateProperties } from "../../helpers/css";
|
|
1
|
+
import { Comms } from "../../comms/comms.ts";
|
|
2
|
+
import { ReadiumWindow } from "../../helpers/dom.ts";
|
|
3
|
+
import { Module } from "../Module.ts";
|
|
4
|
+
import { getProperties, removeProperty, setProperty, updateProperties } from "../../helpers/css.ts";
|
|
5
5
|
|
|
6
6
|
export class WebPubSetup extends Module {
|
|
7
7
|
static readonly moduleName = "webpub_setup";
|
|
@@ -61,4 +61,4 @@ export class WebPubSetup extends Module {
|
|
|
61
61
|
comms.log("WebPubSetup Unmounted");
|
|
62
62
|
return true;
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|
|
@@ -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
|
|
|
@@ -84,7 +83,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
84
83
|
private alreadyScrollLeft = 0;
|
|
85
84
|
private overscroll = 0;
|
|
86
85
|
private cachedScrollWidth = 0; // We have to cache this because during overscroll (transform, or left) the width is incorrect due to browser
|
|
87
|
-
|
|
86
|
+
|
|
88
87
|
private takeOverSnap() {
|
|
89
88
|
this.snappingCancelled = true;
|
|
90
89
|
this.clearTouches();
|
|
@@ -93,7 +92,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
93
92
|
// translate3d(XXXpx, 0px, 0px) -> slice 12 -> XXXpx, 0px, 0px) -> split "px" [0] -> XXX
|
|
94
93
|
this.overscroll = doc.style.transform?.length > 12 ? parseFloat(doc.style.transform.slice(12).split("px")[0]) : 0;
|
|
95
94
|
}
|
|
96
|
-
|
|
95
|
+
|
|
97
96
|
// Snaps the current offset to the page width.
|
|
98
97
|
snapCurrentOffset(smooth=false, noprogress=false) {
|
|
99
98
|
const startX = this.wnd.scrollX > 0 ? this.wnd.scrollX : this.alreadyScrollLeft;
|
|
@@ -109,11 +108,11 @@ export class ColumnSnapper extends Snapper {
|
|
|
109
108
|
((factor * cdo) > 0 ? 2 : 1);
|
|
110
109
|
|
|
111
110
|
const so = this.snapOffset(currentOffset + hurdle);
|
|
112
|
-
|
|
111
|
+
|
|
113
112
|
const direction = so > this.scrollOffset() ? "right" : "left";
|
|
114
113
|
|
|
115
114
|
this.checkSuspiciousSnap(direction, Math.abs(so - this.scrollOffset()));
|
|
116
|
-
|
|
115
|
+
|
|
117
116
|
if(smooth && so !== this.scrollOffset()) { // Smooth snapping
|
|
118
117
|
this.snappingCancelled = false;
|
|
119
118
|
const position = (start: number, end: number, elapsed: number, period: number) => {
|
|
@@ -134,13 +133,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
134
133
|
const spos = position(startX, so, elapsed, period);
|
|
135
134
|
doc.scrollLeft = spos;
|
|
136
135
|
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
|
-
}
|
|
136
|
+
doc.style.transform = `translate3d(${-lpos}px, 0px, 0px)`;
|
|
144
137
|
|
|
145
138
|
if (elapsed < period)
|
|
146
139
|
this.wnd.requestAnimationFrame(step);
|
|
@@ -244,22 +237,10 @@ export class ColumnSnapper extends Snapper {
|
|
|
244
237
|
const newpos = this.alreadyScrollLeft + dro;
|
|
245
238
|
if(newpos < 0) {
|
|
246
239
|
this.overscroll = newpos;
|
|
247
|
-
|
|
248
|
-
this.doc().attributeStyleMap.set("transform", new CSSTransformValue([
|
|
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
|
-
}
|
|
240
|
+
this.doc().style.transform = `translate3d(${-this.overscroll}px, 0px, 0px)`;
|
|
254
241
|
} else if((newpos + this.wnd.innerWidth) > this.cachedScrollWidth) {
|
|
255
242
|
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
|
-
}
|
|
243
|
+
this.doc().style.transform = `translate3d(${-newpos}px, 0px, 0px)`;
|
|
263
244
|
} else {
|
|
264
245
|
this.overscroll = 0;
|
|
265
246
|
this.doc().style.removeProperty("transform");
|
|
@@ -340,7 +321,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
340
321
|
* {
|
|
341
322
|
scrollbar-width: none; /* for Firefox */
|
|
342
323
|
}
|
|
343
|
-
|
|
324
|
+
|
|
344
325
|
body::-webkit-scrollbar {
|
|
345
326
|
display: none; /* for Chrome, Safari, and Opera */
|
|
346
327
|
}
|
|
@@ -365,7 +346,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
365
346
|
const oldValueTransform = oldValue?.match(transformRegex);
|
|
366
347
|
const newValueTransform = newValue?.match(transformRegex);
|
|
367
348
|
if (
|
|
368
|
-
(!oldValueTransform && !newValueTransform) ||
|
|
349
|
+
(!oldValueTransform && !newValueTransform) ||
|
|
369
350
|
(oldValueTransform && !newValueTransform) ||
|
|
370
351
|
(oldValueTransform && newValueTransform && oldValueTransform[1] !== newValueTransform[1])
|
|
371
352
|
) {
|
|
@@ -384,7 +365,7 @@ export class ColumnSnapper extends Snapper {
|
|
|
384
365
|
// For cases the resizeObserver is not able to detect cos body is not resizing despite colCount,
|
|
385
366
|
// we need to check the syle attribute on the documentElement (ReadiumCSS props)
|
|
386
367
|
this.mutationObserver.observe(wnd.document.documentElement, {attributes: true, attributeFilter: ["style"]});
|
|
387
|
-
|
|
368
|
+
|
|
388
369
|
comms.register("scroll_protection", ColumnSnapper.moduleName, (_, ack) => {
|
|
389
370
|
this.enableSnapProtection();
|
|
390
371
|
ack(true);
|
|
@@ -582,4 +563,4 @@ export class ColumnSnapper extends Snapper {
|
|
|
582
563
|
comms.log("ColumnSnapper Unmounted");
|
|
583
564
|
return super.unmount(wnd, comms);
|
|
584
565
|
}
|
|
585
|
-
}
|
|
566
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReadiumWindow } from "../helpers/dom";
|
|
1
|
+
import { ReadiumWindow } from "../helpers/dom.ts";
|
|
2
2
|
|
|
3
3
|
interface CopyAttempt {
|
|
4
4
|
timestamp: number;
|
|
@@ -33,7 +33,7 @@ export class BulkCopyProtector {
|
|
|
33
33
|
this.copyHistory = this.copyHistory.filter(
|
|
34
34
|
attempt => now - attempt.timestamp < historyWindow
|
|
35
35
|
);
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
// Trim to max history size
|
|
38
38
|
if (this.copyHistory.length > this.options.historySize) {
|
|
39
39
|
this.copyHistory = this.copyHistory.slice(-this.options.historySize);
|
|
@@ -42,17 +42,17 @@ export class BulkCopyProtector {
|
|
|
42
42
|
|
|
43
43
|
private isSuspiciousPattern(now: number): boolean {
|
|
44
44
|
if (this.copyHistory.length < 3) return false;
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
// Check for rapid successive copy attempts
|
|
47
47
|
const recentAttempts = this.copyHistory.filter(
|
|
48
48
|
attempt => now - attempt.timestamp < 2000 // Last 2 seconds
|
|
49
49
|
);
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
if (recentAttempts.length >= 3) {
|
|
52
52
|
// If multiple rapid copy attempts, it's suspicious
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
// Check for increasing selection sizes (bulk copy pattern)
|
|
57
57
|
const increasingSelections = this.copyHistory
|
|
58
58
|
.slice()
|
|
@@ -61,7 +61,7 @@ export class BulkCopyProtector {
|
|
|
61
61
|
if (i === 0) return true;
|
|
62
62
|
return attempt.length > arr[i - 1].length * 1.5; // Each selection is significantly larger
|
|
63
63
|
});
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
return increasingSelections;
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -70,7 +70,7 @@ export class BulkCopyProtector {
|
|
|
70
70
|
|
|
71
71
|
const selection = this.window.getSelection();
|
|
72
72
|
if (!selection) return true;
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
const selectedText = selection.toString();
|
|
75
75
|
const selectedLength = selectedText.length;
|
|
76
76
|
const docLength = this.window.document.body.innerText.length;
|
|
@@ -90,12 +90,12 @@ export class BulkCopyProtector {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
const timeSinceLastSelection = now - this.lastSelectionTime;
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
// Check for rapid successive selections
|
|
95
|
-
const isRapidSelection =
|
|
96
|
-
timeSinceLastSelection < 100 &&
|
|
95
|
+
const isRapidSelection =
|
|
96
|
+
timeSinceLastSelection < 100 &&
|
|
97
97
|
selectedLength > this.lastSelectionLength * 1.5;
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
const maxAllowedSelection = Math.min(
|
|
100
100
|
docLength * this.options.maxSelectionPercent,
|
|
101
101
|
this.options.absoluteMaxChars
|
|
@@ -103,9 +103,9 @@ export class BulkCopyProtector {
|
|
|
103
103
|
|
|
104
104
|
// Check if this matches a suspicious pattern
|
|
105
105
|
const isSuspicious = this.isSuspiciousPattern(now);
|
|
106
|
-
|
|
107
|
-
const shouldBlock = selectedLength > maxAllowedSelection ||
|
|
108
|
-
isRapidSelection ||
|
|
106
|
+
|
|
107
|
+
const shouldBlock = selectedLength > maxAllowedSelection ||
|
|
108
|
+
isRapidSelection ||
|
|
109
109
|
isSuspicious;
|
|
110
110
|
|
|
111
111
|
// Record this attempt in history
|
|
@@ -119,7 +119,7 @@ export class BulkCopyProtector {
|
|
|
119
119
|
event?.preventDefault();
|
|
120
120
|
return false;
|
|
121
121
|
}
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
this.lastSelectionLength = selectedLength;
|
|
124
124
|
this.lastSelectionTime = now;
|
|
125
125
|
return true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { KeyCombo, KeyboardPeripheral } from "./KeyboardCombinations";
|
|
2
|
-
import { BaseKeyboardPeripheralEvent, KeyboardEventData, BasicTextSelection } from "../modules/Peripherals";
|
|
3
|
-
import { ReadiumWindow } from "../helpers/dom";
|
|
1
|
+
import { KeyCombo, KeyboardPeripheral } from "./KeyboardCombinations.ts";
|
|
2
|
+
import { BaseKeyboardPeripheralEvent, KeyboardEventData, BasicTextSelection } from "../modules/Peripherals.ts";
|
|
3
|
+
import { ReadiumWindow } from "../helpers/dom.ts";
|
|
4
4
|
|
|
5
5
|
export type KeyHandler = (event: KeyboardEvent) => void;
|
|
6
6
|
export type ActivityEventDispatcher = (event: KeyboardPeripheralEvent) => void;
|
|
@@ -24,7 +24,7 @@ export class KeyCombinationManager {
|
|
|
24
24
|
const shiftMatch = combo.shift === undefined || event.shiftKey === combo.shift;
|
|
25
25
|
const altMatch = combo.alt === undefined || event.altKey === combo.alt;
|
|
26
26
|
const metaMatch = combo.meta === undefined || event.metaKey === combo.meta;
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
if (keyMatch && ctrlMatch && shiftMatch && altMatch && metaMatch) {
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
@@ -49,7 +49,7 @@ export class KeyCombinationManager {
|
|
|
49
49
|
* Creates a standardized activity event for keyboard shortcuts
|
|
50
50
|
*/
|
|
51
51
|
private createActivityEvent(
|
|
52
|
-
event: KeyboardEvent,
|
|
52
|
+
event: KeyboardEvent,
|
|
53
53
|
type: string,
|
|
54
54
|
targetFrameSrc: string,
|
|
55
55
|
wnd?: ReadiumWindow,
|
|
@@ -61,7 +61,7 @@ export class KeyCombinationManager {
|
|
|
61
61
|
const selectedTextStr = selection?.toString() || '';
|
|
62
62
|
const domRectList = (selectedTextStr && selection?.rangeCount) ? selection.getRangeAt(0)?.getClientRects() : null;
|
|
63
63
|
const rect = domRectList?.[0];
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
if (rect && selectedTextStr) {
|
|
66
66
|
selectedText = {
|
|
67
67
|
text: selectedTextStr,
|
|
@@ -124,7 +124,7 @@ export class KeyCombinationManager {
|
|
|
124
124
|
wnd?: ReadiumWindow,
|
|
125
125
|
): (event: KeyboardEvent) => void {
|
|
126
126
|
const handlers = this.createKeyboardHandlers(targetFrameSrc, shortcuts, dispatcher, wnd);
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
return (event: KeyboardEvent) => {
|
|
129
129
|
for (const handlerConfig of handlers) {
|
|
130
130
|
if (this.match(event, [handlerConfig])) {
|
|
@@ -136,4 +136,4 @@ export class KeyCombinationManager {
|
|
|
136
136
|
}
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
-
}
|
|
139
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_PATTERN_ANALYZER_CONFIG } from "./config";
|
|
1
|
+
import { DEFAULT_PATTERN_ANALYZER_CONFIG } from "./config.ts";
|
|
2
2
|
|
|
3
3
|
type Direction = "left" | "right" | "up" | "down";
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ export interface PatternAnalyzerOptions {
|
|
|
10
10
|
maxConsistentScrolls?: number; // Maximum number of consistent scrolls before flagging
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
// TODO: Improve for scroll protection,
|
|
13
|
+
// TODO: Improve for scroll protection,
|
|
14
14
|
// currently disabled because of false positives
|
|
15
15
|
export class PatternAnalyzer {
|
|
16
16
|
private options: PatternAnalyzerOptions;
|
|
@@ -20,9 +20,9 @@ export class PatternAnalyzer {
|
|
|
20
20
|
velocity: number;
|
|
21
21
|
distance: number;
|
|
22
22
|
}> = [];
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
private consistentScrollCount = 0;
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
constructor(
|
|
27
27
|
options: Partial<PatternAnalyzerOptions> = {}
|
|
28
28
|
) {
|
|
@@ -32,18 +32,18 @@ export class PatternAnalyzer {
|
|
|
32
32
|
|
|
33
33
|
analyze(direction: Direction, distance: number, timeDelta: number): boolean {
|
|
34
34
|
if (timeDelta <= 0) return false;
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
const velocity = Math.abs(distance) / timeDelta;
|
|
37
37
|
const now = Date.now();
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
// Add new event
|
|
40
|
-
this.history.push({
|
|
41
|
-
timestamp: now,
|
|
42
|
-
direction,
|
|
40
|
+
this.history.push({
|
|
41
|
+
timestamp: now,
|
|
42
|
+
direction,
|
|
43
43
|
velocity,
|
|
44
44
|
distance: Math.abs(distance)
|
|
45
45
|
});
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
// Clean up old events (keep last 2 seconds of data or historySize, whichever is smaller)
|
|
48
48
|
this.history = this.history
|
|
49
49
|
.filter(h => now - h.timestamp < 2000)
|
|
@@ -61,19 +61,19 @@ export class PatternAnalyzer {
|
|
|
61
61
|
// 2. Check for consistent scrolling patterns
|
|
62
62
|
const velocities = this.history.map(h => h.velocity);
|
|
63
63
|
const distances = this.history.map(h => h.distance);
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
// Calculate mean and variance of velocities and distances
|
|
66
66
|
const meanVelocity = velocities.reduce((a, b) => a + b, 0) / velocities.length;
|
|
67
67
|
const meanDistance = distances.reduce((a, b) => a + b, 0) / distances.length;
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
const velocityVariance = velocities.reduce((a, b) => a + Math.pow(b - meanVelocity, 2), 0) / velocities.length;
|
|
70
70
|
const distanceVariance = distances.reduce((a, b) => a + Math.pow(b - meanDistance, 2), 0) / distances.length;
|
|
71
71
|
|
|
72
72
|
// Check for suspiciously consistent scrolling (low variance in speed and distance)
|
|
73
|
-
if (velocityVariance < this.options.minVariance &&
|
|
73
|
+
if (velocityVariance < this.options.minVariance &&
|
|
74
74
|
distanceVariance < (meanDistance * 0.1)) { // Allow 10% variance in distance
|
|
75
75
|
this.consistentScrollCount++;
|
|
76
|
-
|
|
76
|
+
|
|
77
77
|
// Only trigger if we've seen consistent scrolling for a while
|
|
78
78
|
if (this.consistentScrollCount >= (this.options.maxConsistentScrolls || 10)) {
|
|
79
79
|
this.resetAfterDetection();
|
|
@@ -87,14 +87,14 @@ export class PatternAnalyzer {
|
|
|
87
87
|
// 3. Check for rapid direction changes (unnatural back-and-forth scrolling)
|
|
88
88
|
let directionChanges = 0;
|
|
89
89
|
let lastDirection = this.history[0].direction;
|
|
90
|
-
|
|
90
|
+
|
|
91
91
|
for (let i = 1; i < this.history.length; i++) {
|
|
92
92
|
if (this.history[i].direction !== lastDirection) {
|
|
93
93
|
directionChanges++;
|
|
94
94
|
lastDirection = this.history[i].direction;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
const directionChangeRatio = directionChanges / this.history.length;
|
|
99
99
|
if (directionChangeRatio > (this.options.minDirectionChanges || 0.3)) {
|
|
100
100
|
this.resetAfterDetection();
|
|
@@ -109,9 +109,9 @@ export class PatternAnalyzer {
|
|
|
109
109
|
this.history = this.history.slice(-3);
|
|
110
110
|
this.consistentScrollCount = 0;
|
|
111
111
|
}
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
clear(): void {
|
|
114
114
|
this.history = [];
|
|
115
115
|
this.consistentScrollCount = 0;
|
|
116
116
|
}
|
|
117
|
-
}
|
|
117
|
+
}
|