@readium/navigator-html-injectables 2.2.4 → 2.3.0

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.
Files changed (41) hide show
  1. package/dist/ar-DyHX_uy2.js +7 -0
  2. package/dist/da-Dct0PS3E.js +7 -0
  3. package/dist/fr-C5HEel98.js +7 -0
  4. package/dist/index.js +2121 -1352
  5. package/dist/index.umd.cjs +24 -10
  6. package/dist/it-DFOBoXGy.js +7 -0
  7. package/dist/pt_PT-Di3sVjze.js +7 -0
  8. package/dist/sv-BfzAFsVN.js +7 -0
  9. package/package.json +4 -2
  10. package/src/comms/keys.ts +22 -3
  11. package/src/index.ts +2 -1
  12. package/src/modules/ModuleLibrary.ts +10 -4
  13. package/src/modules/Peripherals.ts +458 -13
  14. package/src/modules/snapper/ColumnSnapper.ts +63 -2
  15. package/src/modules/snapper/ScrollSnapper.ts +59 -1
  16. package/src/modules/snapper/WebPubSnapper.ts +57 -3
  17. package/src/protection/BulkCopyProtector.ts +134 -0
  18. package/src/protection/KeyCombinationManager.ts +139 -0
  19. package/src/protection/KeyboardCombinations.ts +60 -0
  20. package/src/protection/PatternAnalyzer.ts +117 -0
  21. package/src/protection/PrintProtector.ts +87 -0
  22. package/src/protection/SelectionAnalyzer.ts +133 -0
  23. package/src/protection/config.ts +33 -0
  24. package/src/protection/index.ts +6 -0
  25. package/types/src/comms/keys.d.ts +3 -2
  26. package/types/src/index.d.ts +1 -0
  27. package/types/src/modules/ModuleLibrary.d.ts +1 -1
  28. package/types/src/modules/Peripherals.d.ts +102 -2
  29. package/types/src/modules/snapper/ColumnSnapper.d.ts +10 -0
  30. package/types/src/modules/snapper/ScrollSnapper.d.ts +13 -0
  31. package/types/src/modules/snapper/WebPubSnapper.d.ts +6 -0
  32. package/types/src/protection/BulkCopyProtector.d.ts +20 -0
  33. package/types/src/protection/KeyCombinationManager.d.ts +33 -0
  34. package/types/src/protection/KeyboardCombinations.d.ts +15 -0
  35. package/types/src/protection/PatternAnalyzer.d.ts +18 -0
  36. package/types/src/protection/PrintProtector.d.ts +20 -0
  37. package/types/src/protection/SelectionAnalyzer.d.ts +20 -0
  38. package/types/src/protection/config.d.ts +7 -0
  39. package/types/src/protection/index.d.ts +6 -0
  40. package/src/types/protection.ts +0 -16
  41. package/types/src/types/protection.d.ts +0 -7
@@ -0,0 +1,87 @@
1
+ import { Comms } from "../comms";
2
+ import { ReadiumWindow } from "../helpers/dom";
3
+ import { Module } from "../modules/Module";
4
+ import { ModuleName } from "../modules/ModuleLibrary";
5
+
6
+ export interface PrintProtectionConfig {
7
+ disable?: boolean;
8
+ watermark?: string;
9
+ }
10
+
11
+ export class PrintProtector extends Module {
12
+ static readonly moduleName: ModuleName = "print_protection";
13
+
14
+ private comms!: Comms;
15
+ private wnd!: ReadiumWindow;
16
+ private styleElement: HTMLStyleElement | null = null;
17
+ private beforePrintHandler: ((e: Event) => void) | null = null;
18
+ private configApplied = false;
19
+
20
+ private setupPrintProtection(wnd: Window, config: PrintProtectionConfig) {
21
+ if (!config.disable) return;
22
+
23
+ const style = wnd.document.createElement("style");
24
+ style.textContent = `
25
+ @media print {
26
+ body * {
27
+ display: none !important;
28
+ }
29
+ body::after {
30
+ content: "${config.watermark || 'Printing has been disabled'}";
31
+ font-size: 200%;
32
+ display: block;
33
+ text-align: center;
34
+ margin-top: 50vh;
35
+ transform: translateY(-50%);
36
+ }
37
+ }
38
+ `;
39
+ wnd.document.head.appendChild(style);
40
+ this.styleElement = style;
41
+
42
+ this.beforePrintHandler = (e: Event) => {
43
+ e.preventDefault();
44
+ return false;
45
+ };
46
+ wnd.addEventListener("beforeprint", this.beforePrintHandler);
47
+ }
48
+
49
+ private registerPrintHandlers() {
50
+ this.comms?.register("print_protection", PrintProtector.moduleName, (data: unknown) => {
51
+ const config = data as PrintProtectionConfig;
52
+
53
+ if (!this.configApplied) {
54
+ this.configApplied = true;
55
+ this.setupPrintProtection(this.wnd, config);
56
+ this.comms?.log("Print protection configuration applied");
57
+ }
58
+
59
+ return true;
60
+ });
61
+ }
62
+
63
+ mount(wnd: ReadiumWindow, comms: Comms): boolean {
64
+ this.wnd = wnd;
65
+ this.comms = comms;
66
+ this.registerPrintHandlers();
67
+ return true;
68
+ }
69
+
70
+ unmount(wnd: ReadiumWindow, _comms: Comms): boolean {
71
+ if (this.beforePrintHandler) {
72
+ wnd.removeEventListener("beforeprint", this.beforePrintHandler);
73
+ this.beforePrintHandler = null;
74
+ }
75
+
76
+ if (this.styleElement?.parentNode) {
77
+ this.styleElement.parentNode.removeChild(this.styleElement);
78
+ this.styleElement = null;
79
+ }
80
+
81
+ // Unregister all print protection handlers
82
+ this.comms?.unregisterAll(PrintProtector.moduleName);
83
+
84
+ this.configApplied = false;
85
+ return true;
86
+ }
87
+ }
@@ -0,0 +1,133 @@
1
+ interface SelectionEvent {
2
+ timestamp: number;
3
+ position: number;
4
+ length: number;
5
+ }
6
+
7
+ export interface SelectionAnalyzerOptions {
8
+ maxSelectionsPerSecond: number;
9
+ minVariance: number;
10
+ historySize: number;
11
+ }
12
+
13
+ import { SELECTION_ANALYZER_CONFIG } from './config';
14
+
15
+ export class SelectionAnalyzer {
16
+ private events: SelectionEvent[] = [];
17
+ private selectionStartTime = 0;
18
+ private lastSelectionTime = 0;
19
+ private lastSelectionPosition = 0;
20
+ private selectionPatterns: number[] = [];
21
+ private lastSelectedText = "";
22
+
23
+ constructor(
24
+ private readonly options: SelectionAnalyzerOptions = SELECTION_ANALYZER_CONFIG
25
+ ) {}
26
+
27
+ public analyze(selection: Selection | null): boolean {
28
+ if (!selection) {
29
+ this.clear();
30
+ return false;
31
+ }
32
+
33
+ const selectedText = selection.toString();
34
+ if (selectedText.length === 0) {
35
+ this.clear();
36
+ return false;
37
+ }
38
+
39
+ if (selection.type !== "Range" || !selection.rangeCount) {
40
+ return false;
41
+ }
42
+
43
+ const now = Date.now();
44
+
45
+ // Only analyze meaningful selections (not too short, not duplicates)
46
+ if (selectedText.length <= 50 || selectedText === this.lastSelectedText) {
47
+ return false;
48
+ }
49
+
50
+ // Start timing if this is a new selection
51
+ if (this.selectionStartTime === 0) {
52
+ this.selectionStartTime = now;
53
+ this.lastSelectedText = selectedText;
54
+ return false; // Don't analyze first selection, just track it
55
+ }
56
+
57
+ // Check if enough time has passed for selection to be complete
58
+ const timeSinceStart = now - this.selectionStartTime;
59
+ if (timeSinceStart < 500) {
60
+ return false; // Still selecting, wait longer
61
+ }
62
+
63
+ // Track selection frequency
64
+ if (now - this.lastSelectionTime > 1000) {
65
+ this.lastSelectionTime = now;
66
+ }
67
+
68
+ if (this.selectionStartTime === 0) {
69
+ this.selectionStartTime = now;
70
+ }
71
+
72
+ // Store current selection for comparison
73
+ this.lastSelectedText = selectedText;
74
+
75
+ // Analyze selection pattern
76
+ const isSuspicious = this.analyzeSelectionPattern(selection, now);
77
+
78
+ // Clean up old events
79
+ this.cleanup(now);
80
+
81
+ return isSuspicious;
82
+ }
83
+
84
+ private analyzeSelectionPattern(selection: Selection, now: number): boolean {
85
+ if (!selection.rangeCount) return false;
86
+
87
+ const range = selection.getRangeAt(0);
88
+ const text = range.toString();
89
+
90
+ // Calculate selection speed (characters per second)
91
+ const duration = (now - this.selectionStartTime) / 1000; // Convert to seconds
92
+ const speed = text.length / Math.max(1, duration); // Characters per second
93
+
94
+ // Check for unnaturally fast selection
95
+ if (speed > this.options.maxSelectionsPerSecond) return true;
96
+
97
+ // Analyze selection pattern (human selections have more variance)
98
+ const currentPosition = range.startOffset;
99
+ const distance = Math.abs(currentPosition - this.lastSelectionPosition);
100
+ this.selectionPatterns.push(distance);
101
+
102
+ if (this.selectionPatterns.length > this.options.historySize) {
103
+ this.selectionPatterns.shift();
104
+ const variance = this.calculateVariance(this.selectionPatterns);
105
+ if (variance < this.options.minVariance) { // Very consistent patterns suggest automation
106
+ return true;
107
+ }
108
+ }
109
+
110
+ this.lastSelectionPosition = currentPosition;
111
+ return false;
112
+ }
113
+
114
+ private calculateVariance(numbers: number[]): number {
115
+ if (numbers.length === 0) return 0;
116
+ const mean = numbers.reduce((a, b) => a + b, 0) / numbers.length;
117
+ return numbers.reduce((a, b) => a + Math.pow(b - mean, 2), 0) / numbers.length;
118
+ }
119
+
120
+ private cleanup(now: number): void {
121
+ // Keep events from the last second
122
+ this.events = this.events.filter(e => now - e.timestamp <= 1000);
123
+ }
124
+
125
+ public clear(): void {
126
+ this.events = [];
127
+ this.selectionStartTime = 0;
128
+ this.lastSelectionTime = 0;
129
+ this.lastSelectionPosition = 0;
130
+ this.selectionPatterns = [];
131
+ this.lastSelectedText = "";
132
+ }
133
+ }
@@ -0,0 +1,33 @@
1
+ import { BulkCopyProtectionOptions } from "./BulkCopyProtector";
2
+ import { PatternAnalyzerOptions } from "./PatternAnalyzer";
3
+ import { SelectionAnalyzerOptions } from "./SelectionAnalyzer";
4
+
5
+ export const DEFAULT_PATTERN_ANALYZER_CONFIG: PatternAnalyzerOptions = {
6
+ maxVelocity: 200, // Reasonable default for human-like scrolling (pixels/ms)
7
+ minVariance: 0.01, // Default variance threshold
8
+ historySize: 20, // Balanced history size for performance
9
+ minDirectionChanges: 0.2, // Reasonable default for detecting patterns
10
+ maxConsistentScrolls: 15 // Balanced threshold for flagging
11
+ };
12
+
13
+ export const SCROLL_PROTECTION_CONFIG: PatternAnalyzerOptions = {
14
+ maxVelocity: 200, // Extremely fast scrolling (pixels/ms)
15
+ minVariance: 0.00001, // Near-perfect consistency
16
+ historySize: 100, // Large history window
17
+ minDirectionChanges: 0.1, // Only trigger on near-perfect patterns
18
+ maxConsistentScrolls: 20 // Need many consistent scrolls
19
+ };
20
+
21
+ export const SELECTION_ANALYZER_CONFIG: SelectionAnalyzerOptions = {
22
+ maxSelectionsPerSecond: 500,
23
+ minVariance: 50,
24
+ historySize: 20
25
+ };
26
+
27
+ export const BULK_COPY_CONFIG: BulkCopyProtectionOptions = {
28
+ enabled: true,
29
+ maxSelectionPercent: 0.1,
30
+ minThreshold: 100,
31
+ absoluteMaxChars: 5000,
32
+ historySize: 20
33
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./BulkCopyProtector";
2
+ export * from "./KeyCombinationManager";
3
+ export * from "./KeyboardCombinations";
4
+ export * from "./PatternAnalyzer";
5
+ export * from "./SelectionAnalyzer";
6
+ export * from "./PrintProtector";
@@ -1,2 +1,3 @@
1
- export type CommsEventKey = "_pong" | "_unhandled" | "_ack" | "log" | "error" | "click" | "tap" | "tap_more" | "no_more" | "no_less" | "swipe" | "scroll" | "progress" | "first_visible_locator" | "text_selected" | "media_play" | "media_pause";
2
- export type CommsCommandKey = "_ping" | "go_prev" | "go_next" | "go_id" | "go_text" | "go_end" | "go_start" | "go_progression" | "get_properties" | "update_properties" | "set_property" | "remove_property" | "first_visible_locator" | "decorate" | "protect" | "unprotect" | "unfocus" | "focus" | "activate" | "shake" | "force_webkit_recalc";
1
+ export type CommsEventKey = "_pong" | "_unhandled" | "_ack" | "log" | "error" | "click" | "tap" | "tap_more" | "no_more" | "no_less" | "swipe" | "scroll" | "progress" | "first_visible_locator" | "text_selected" | "context_menu" | "media_play" | "media_pause" | "content_protection" | "keyboard_peripherals";
2
+ export type CommsCommandKey = "_ping" | "go_prev" | "go_next" | "go_id" | "go_text" | "go_end" | "go_start" | "go_progression" | "get_properties" | "update_properties" | "set_property" | "remove_property" | "first_visible_locator" | "decorate" | "protect" | "unprotect" | "unfocus" | "focus" | "activate" | "shake" | "force_webkit_recalc" | "peripherals_protection" | "keyboard_peripherals" | "scroll_protection" | "print_protection";
3
+ export type SuspiciousActivityType = "developer_tools" | "select_all" | "save" | "suspicious_selection" | "bulk_copy" | "suspicious_scrolling" | "suspicious_snapping" | "drag_detected" | "drop_detected" | "print";
@@ -1,3 +1,4 @@
1
1
  export * from './comms';
2
2
  export * from './modules';
3
3
  export * from './Loader';
4
+ export * from './protection';
@@ -1,5 +1,5 @@
1
1
  import { ModuleDerived } from "./Module";
2
- export type ModuleName = "setup" | "snapper" | "column_snapper" | "scroll_snapper" | "webpub_snapper" | "fixed_setup" | "decorator" | "reflowable_setup" | "peripherals" | "webpub_setup";
2
+ export type ModuleName = "setup" | "snapper" | "column_snapper" | "scroll_snapper" | "webpub_snapper" | "fixed_setup" | "decorator" | "reflowable_setup" | "peripherals" | "webpub_setup" | "print_protection";
3
3
  export declare const FXLModules: ModuleName[];
4
4
  export declare const ReflowableModules: ModuleName[];
5
5
  export declare const WebPubModules: ModuleName[];
@@ -1,6 +1,12 @@
1
1
  import { Comms } from "../comms/comms";
2
2
  import { Module } from "./Module";
3
3
  import { ReadiumWindow } from "../helpers/dom";
4
+ import { BulkCopyProtectionOptions } from "../protection/BulkCopyProtector";
5
+ import { SelectionAnalyzerOptions } from "../protection/SelectionAnalyzer";
6
+ import { SuspiciousActivityType } from "../comms";
7
+ import { KeyboardPeripheralEvent } from "../protection/KeyCombinationManager";
8
+ import { SuspiciousScrollingEvent } from "./snapper/ScrollSnapper";
9
+ import { SuspiciousSnappingEvent } from "./snapper/ColumnSnapper";
4
10
  export interface FrameClickEvent {
5
11
  defaultPrevented: boolean;
6
12
  doNotDisturb: boolean;
@@ -19,21 +25,115 @@ export interface BasicTextSelection {
19
25
  height: number;
20
26
  targetFrameSrc: string;
21
27
  }
28
+ export interface BaseSuspiciousActivityEvent {
29
+ type: SuspiciousActivityType;
30
+ timestamp: number;
31
+ targetFrameSrc: string;
32
+ }
33
+ export interface KeyboardEventData {
34
+ key: string;
35
+ code: string;
36
+ keyCode: number;
37
+ ctrlKey: boolean;
38
+ altKey: boolean;
39
+ shiftKey: boolean;
40
+ metaKey: boolean;
41
+ }
42
+ export interface BaseKeyboardPeripheralEvent {
43
+ type: string;
44
+ timestamp: number;
45
+ targetFrameSrc: string;
46
+ selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
47
+ }
48
+ export interface DeveloperToolsEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
49
+ type: "developer_tools";
50
+ }
51
+ export interface SelectAllEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
52
+ type: "select_all";
53
+ }
54
+ export interface PrintEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
55
+ type: "print";
56
+ }
57
+ export interface SaveEvent extends BaseSuspiciousActivityEvent, KeyboardEventData {
58
+ type: "save";
59
+ }
60
+ export interface BulkCopyEvent extends BaseSuspiciousActivityEvent {
61
+ type: "bulk_copy";
62
+ clipboardTypes: readonly string[];
63
+ selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
64
+ selectionLength?: number;
65
+ }
66
+ export interface SuspiciousSelectionEvent extends BaseSuspiciousActivityEvent {
67
+ type: "suspicious_selection";
68
+ selectionLength: number;
69
+ selectedText: Omit<BasicTextSelection, "targetFrameSrc">;
70
+ eventType: string;
71
+ }
72
+ export interface DragDetectedEvent extends BaseSuspiciousActivityEvent {
73
+ type: "drag_detected";
74
+ dataTransferTypes: readonly string[];
75
+ }
76
+ export interface DropDetectedEvent extends BaseSuspiciousActivityEvent {
77
+ type: "drop_detected";
78
+ dataTransferTypes: readonly string[];
79
+ fileCount: number;
80
+ }
81
+ export interface ContextMenuEvent extends Omit<BaseSuspiciousActivityEvent, "type"> {
82
+ clientX: number;
83
+ clientY: number;
84
+ selectedText?: Omit<BasicTextSelection, "targetFrameSrc">;
85
+ }
86
+ export type SuspiciousActivityEvent = DeveloperToolsEvent | SelectAllEvent | BulkCopyEvent | SuspiciousSelectionEvent | DragDetectedEvent | DropDetectedEvent | PrintEvent | SaveEvent | SuspiciousScrollingEvent | SuspiciousSnappingEvent | KeyboardPeripheralEvent;
87
+ export interface ContentProtectionConfig {
88
+ monitorSelection?: boolean | SelectionAnalyzerOptions;
89
+ protectCopy?: boolean | Omit<BulkCopyProtectionOptions, "enabled">;
90
+ disableContextMenu?: boolean;
91
+ disableDragAndDrop?: boolean;
92
+ disableSelectAll?: boolean;
93
+ disableSave?: boolean;
94
+ monitorDevTools?: boolean;
95
+ monitorScrollingExperimental?: boolean;
96
+ }
22
97
  export declare class Peripherals extends Module {
23
98
  static readonly moduleName = "peripherals";
24
99
  private wnd;
25
100
  private comms;
101
+ private configApplied;
102
+ private cleanupCallbacks;
26
103
  private pointerMoved;
104
+ private isContextMenuEnabled;
105
+ private isDragAndDropEnabled;
106
+ private isSelectionMonitoringEnabled;
107
+ private isBulkCopyProtectionEnabled;
108
+ private selectionAnalyzer;
109
+ private currentSelection;
110
+ private bulkCopyProtector;
111
+ private keyManager;
112
+ private addContextMenuPrevention;
113
+ private removeContextMenuPrevention;
114
+ private addDragAndDropPrevention;
115
+ private removeDragAndDropPrevention;
116
+ private keyDownHandler;
117
+ private enableKeyboardPeripherals;
118
+ private disableKeyboardPeripherals;
119
+ private addBulkCopyProtection;
120
+ private removeBulkCopyProtection;
121
+ private preventBulkCopy;
122
+ private handleSelection;
123
+ private addSelectionMonitoring;
124
+ private removeSelectionMonitoring;
125
+ private onDragStart;
126
+ private onDrop;
127
+ private onContext;
27
128
  onPointUp(event: PointerEvent): void;
28
129
  private readonly onPointerUp;
29
130
  onPointMove(event: PointerEvent): void;
30
131
  private readonly onPointerMove;
31
132
  onPointDown(): void;
32
133
  private readonly onPointerDown;
33
- onContext(event: MouseEvent): void;
34
- private readonly onContextMenu;
35
134
  onClick(event: MouseEvent): void;
36
135
  private readonly onClicker;
136
+ private registerProtectionHandlers;
37
137
  mount(wnd: ReadiumWindow, comms: Comms): boolean;
38
138
  unmount(wnd: ReadiumWindow, comms: Comms): boolean;
39
139
  }
@@ -2,13 +2,21 @@ import { Comms } from "../../comms";
2
2
  import { Snapper } from "./Snapper";
3
3
  import { ModuleName } from "../ModuleLibrary";
4
4
  import { ReadiumWindow } from "../../helpers/dom";
5
+ import { BaseSuspiciousActivityEvent } from "../Peripherals";
6
+ export interface SuspiciousSnappingEvent extends BaseSuspiciousActivityEvent {
7
+ type: "suspicious_snapping";
8
+ event: null;
9
+ }
5
10
  /**
6
11
  * A {Snapper} for reflowable resources using a column-based layout
7
12
  */
8
13
  export declare class ColumnSnapper extends Snapper {
9
14
  static readonly moduleName: ModuleName;
15
+ private isSnapProtectionEnabled;
10
16
  private resizeObserver;
11
17
  private mutationObserver;
18
+ private patternAnalyzer;
19
+ private lastTurnTime;
12
20
  private wnd;
13
21
  private comms;
14
22
  private doc;
@@ -36,6 +44,8 @@ export declare class ColumnSnapper extends Snapper {
36
44
  private readonly onWidthChanger;
37
45
  onTouchMove(e: TouchEvent): void;
38
46
  private readonly onTouchMover;
47
+ private enableSnapProtection;
48
+ private checkSuspiciousSnap;
39
49
  mount(wnd: ReadiumWindow, comms: Comms): boolean;
40
50
  unmount(wnd: ReadiumWindow, comms: Comms): boolean;
41
51
  }
@@ -2,11 +2,23 @@ import { Comms } from "../../comms";
2
2
  import { ReadiumWindow } from "../../helpers/dom";
3
3
  import { ModuleName } from "../ModuleLibrary";
4
4
  import { Snapper } from "./Snapper";
5
+ import { BaseSuspiciousActivityEvent } from "../Peripherals";
6
+ export interface SuspiciousScrollingEvent extends BaseSuspiciousActivityEvent {
7
+ type: "suspicious_scrolling";
8
+ scrollDelta: number;
9
+ scrollDirection: "up" | "down";
10
+ targetElement: {
11
+ tagName: string;
12
+ } | null;
13
+ }
5
14
  export declare class ScrollSnapper extends Snapper {
6
15
  static readonly moduleName: ModuleName;
7
16
  private wnd;
8
17
  private comms;
9
18
  private resizeObserver;
19
+ private patternAnalyzer;
20
+ private lastScrollTime;
21
+ private isScrollProtectionEnabled;
10
22
  private initialScrollHandled;
11
23
  private isScrolling;
12
24
  private lastScrollTop;
@@ -15,6 +27,7 @@ export declare class ScrollSnapper extends Snapper {
15
27
  private doc;
16
28
  private reportProgress;
17
29
  private handleScroll;
30
+ private enableScrollProtection;
18
31
  mount(wnd: ReadiumWindow, comms: Comms): boolean;
19
32
  unmount(wnd: ReadiumWindow, comms: Comms): boolean;
20
33
  }
@@ -2,11 +2,16 @@ import { Comms } from "../../comms/comms";
2
2
  import { ReadiumWindow } from "../../helpers/dom";
3
3
  import { ModuleName } from "../ModuleLibrary";
4
4
  import { Snapper } from "./Snapper";
5
+ import { SuspiciousScrollingEvent } from "./ScrollSnapper";
6
+ export type { SuspiciousScrollingEvent };
5
7
  export declare class WebPubSnapper extends Snapper {
6
8
  static readonly moduleName: ModuleName;
7
9
  private wnd;
8
10
  private comms;
9
11
  private resizeObserver;
12
+ private patternAnalyzer;
13
+ private lastScrollTime;
14
+ private isScrollProtectionEnabled;
10
15
  private initialScrollHandled;
11
16
  private isScrolling;
12
17
  private lastScrollTop;
@@ -15,6 +20,7 @@ export declare class WebPubSnapper extends Snapper {
15
20
  private doc;
16
21
  private reportProgress;
17
22
  private handleScroll;
23
+ private enableScrollProtection;
18
24
  mount(wnd: ReadiumWindow, comms: Comms): boolean;
19
25
  unmount(wnd: ReadiumWindow, comms: Comms): boolean;
20
26
  }
@@ -0,0 +1,20 @@
1
+ import { ReadiumWindow } from "../helpers/dom";
2
+ export interface BulkCopyProtectionOptions {
3
+ enabled: boolean;
4
+ maxSelectionPercent: number;
5
+ absoluteMaxChars: number;
6
+ minThreshold: number;
7
+ historySize: number;
8
+ }
9
+ export declare class BulkCopyProtector {
10
+ private readonly window;
11
+ private options;
12
+ private copyHistory;
13
+ private lastSelectionLength;
14
+ private lastSelectionTime;
15
+ constructor(window: ReadiumWindow, options: BulkCopyProtectionOptions);
16
+ private cleanupOldHistory;
17
+ private isSuspiciousPattern;
18
+ shouldAllowCopy(event: ClipboardEvent): boolean;
19
+ destroy(): void;
20
+ }
@@ -0,0 +1,33 @@
1
+ import { KeyCombo, KeyboardPeripheral } from "./KeyboardCombinations";
2
+ import { BaseKeyboardPeripheralEvent, KeyboardEventData } from "../modules/Peripherals";
3
+ import { ReadiumWindow } from "../helpers/dom";
4
+ export type KeyHandler = (event: KeyboardEvent) => void;
5
+ export type ActivityEventDispatcher = (event: KeyboardPeripheralEvent) => void;
6
+ export interface KeyboardPeripheralEvent extends BaseKeyboardPeripheralEvent, KeyboardEventData {
7
+ type: string;
8
+ }
9
+ export interface KeyComboWithHandler extends KeyCombo {
10
+ handler?: (event: KeyboardEvent) => void;
11
+ }
12
+ export declare class KeyCombinationManager {
13
+ /**
14
+ * Checks if the given keyboard event matches any of the provided key combinations
15
+ */
16
+ match(event: KeyboardEvent, combos: KeyCombo[]): boolean;
17
+ /**
18
+ * Creates an event handler that will call the provided handler when any of the key combinations match
19
+ */
20
+ createKeyHandler(combos: KeyCombo[], handler: KeyHandler): (event: KeyboardEvent) => void;
21
+ /**
22
+ * Creates a standardized activity event for keyboard shortcuts
23
+ */
24
+ private createActivityEvent;
25
+ /**
26
+ * Creates handlers for keyboard shortcuts with centralized activity event dispatch
27
+ */
28
+ createKeyboardHandlers(targetFrameSrc: string, shortcuts: KeyboardPeripheral[], dispatcher: ActivityEventDispatcher, wnd?: ReadiumWindow): KeyComboWithHandler[];
29
+ /**
30
+ * Creates a unified keyboard event handler that processes all shortcuts
31
+ */
32
+ createUnifiedHandler(targetFrameSrc: string, shortcuts: KeyboardPeripheral[], dispatcher: ActivityEventDispatcher, wnd?: ReadiumWindow): (event: KeyboardEvent) => void;
33
+ }
@@ -0,0 +1,15 @@
1
+ export interface KeyCombo {
2
+ keyCode: number;
3
+ ctrl?: boolean;
4
+ shift?: boolean;
5
+ alt?: boolean;
6
+ meta?: boolean;
7
+ }
8
+ export interface KeyboardPeripheral {
9
+ type: string;
10
+ keyCombos: KeyCombo[];
11
+ }
12
+ export declare const DEV_TOOLS: KeyboardPeripheral;
13
+ export declare const SELECT_ALL: KeyboardPeripheral;
14
+ export declare const PRINT: KeyboardPeripheral;
15
+ export declare const SAVE: KeyboardPeripheral;
@@ -0,0 +1,18 @@
1
+ type Direction = "left" | "right" | "up" | "down";
2
+ export interface PatternAnalyzerOptions {
3
+ maxVelocity: number;
4
+ minVariance: number;
5
+ historySize?: number;
6
+ minDirectionChanges?: number;
7
+ maxConsistentScrolls?: number;
8
+ }
9
+ export declare class PatternAnalyzer {
10
+ private options;
11
+ private history;
12
+ private consistentScrollCount;
13
+ constructor(options?: Partial<PatternAnalyzerOptions>);
14
+ analyze(direction: Direction, distance: number, timeDelta: number): boolean;
15
+ private resetAfterDetection;
16
+ clear(): void;
17
+ }
18
+ export {};
@@ -0,0 +1,20 @@
1
+ import { Comms } from "../comms";
2
+ import { ReadiumWindow } from "../helpers/dom";
3
+ import { Module } from "../modules/Module";
4
+ import { ModuleName } from "../modules/ModuleLibrary";
5
+ export interface PrintProtectionConfig {
6
+ disable?: boolean;
7
+ watermark?: string;
8
+ }
9
+ export declare class PrintProtector extends Module {
10
+ static readonly moduleName: ModuleName;
11
+ private comms;
12
+ private wnd;
13
+ private styleElement;
14
+ private beforePrintHandler;
15
+ private configApplied;
16
+ private setupPrintProtection;
17
+ private registerPrintHandlers;
18
+ mount(wnd: ReadiumWindow, comms: Comms): boolean;
19
+ unmount(wnd: ReadiumWindow, _comms: Comms): boolean;
20
+ }
@@ -0,0 +1,20 @@
1
+ export interface SelectionAnalyzerOptions {
2
+ maxSelectionsPerSecond: number;
3
+ minVariance: number;
4
+ historySize: number;
5
+ }
6
+ export declare class SelectionAnalyzer {
7
+ private readonly options;
8
+ private events;
9
+ private selectionStartTime;
10
+ private lastSelectionTime;
11
+ private lastSelectionPosition;
12
+ private selectionPatterns;
13
+ private lastSelectedText;
14
+ constructor(options?: SelectionAnalyzerOptions);
15
+ analyze(selection: Selection | null): boolean;
16
+ private analyzeSelectionPattern;
17
+ private calculateVariance;
18
+ private cleanup;
19
+ clear(): void;
20
+ }
@@ -0,0 +1,7 @@
1
+ import { BulkCopyProtectionOptions } from "./BulkCopyProtector";
2
+ import { PatternAnalyzerOptions } from "./PatternAnalyzer";
3
+ import { SelectionAnalyzerOptions } from "./SelectionAnalyzer";
4
+ export declare const DEFAULT_PATTERN_ANALYZER_CONFIG: PatternAnalyzerOptions;
5
+ export declare const SCROLL_PROTECTION_CONFIG: PatternAnalyzerOptions;
6
+ export declare const SELECTION_ANALYZER_CONFIG: SelectionAnalyzerOptions;
7
+ export declare const BULK_COPY_CONFIG: BulkCopyProtectionOptions;
@@ -0,0 +1,6 @@
1
+ export * from "./BulkCopyProtector";
2
+ export * from "./KeyCombinationManager";
3
+ export * from "./KeyboardCombinations";
4
+ export * from "./PatternAnalyzer";
5
+ export * from "./SelectionAnalyzer";
6
+ export * from "./PrintProtector";
@@ -1,16 +0,0 @@
1
- type SuspiciousActivityType =
2
- | "developer_tools_attempt"
3
- | "select_all_attempt"
4
- | "suspicious_selection"
5
- | "suspicious_copy"
6
- | "suspicious_scrolling"
7
- | "suspicious_snapping"
8
- | "drag_detected"
9
- | "drop_detected"
10
- | "context_menu";
11
-
12
- export interface SuspiciousActivityEvent {
13
- timestamp: number;
14
- type: SuspiciousActivityType;
15
- event?: Event;
16
- }