@tindalabs/shield 0.1.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 (118) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +357 -0
  3. package/dist/assess.d.ts +16 -0
  4. package/dist/assess.js +220 -0
  5. package/dist/config/default-extensions-config.json +103 -0
  6. package/dist/core/ContentProtector.d.ts +63 -0
  7. package/dist/core/ContentProtector.js +281 -0
  8. package/dist/core/index.d.ts +1 -0
  9. package/dist/core/index.js +2 -0
  10. package/dist/core/mediator/ContentProtectionMediator.d.ts +86 -0
  11. package/dist/core/mediator/ContentProtectionMediator.js +238 -0
  12. package/dist/core/mediator/eventDataTypes.d.ts +112 -0
  13. package/dist/core/mediator/eventDataTypes.js +23 -0
  14. package/dist/core/mediator/handlers/abstractEventHandler.d.ts +41 -0
  15. package/dist/core/mediator/handlers/abstractEventHandler.js +59 -0
  16. package/dist/core/mediator/handlers/devToolsEventHandler.d.ts +9 -0
  17. package/dist/core/mediator/handlers/devToolsEventHandler.js +95 -0
  18. package/dist/core/mediator/handlers/eventHandlerRegistry.d.ts +9 -0
  19. package/dist/core/mediator/handlers/eventHandlerRegistry.js +34 -0
  20. package/dist/core/mediator/handlers/extensionEventHandlers.d.ts +40 -0
  21. package/dist/core/mediator/handlers/extensionEventHandlers.js +140 -0
  22. package/dist/core/mediator/handlers/iFrameEventHandlers.d.ts +27 -0
  23. package/dist/core/mediator/handlers/iFrameEventHandlers.js +93 -0
  24. package/dist/core/mediator/handlers/screenShotEventHandlers.d.ts +34 -0
  25. package/dist/core/mediator/handlers/screenShotEventHandlers.js +111 -0
  26. package/dist/core/mediator/protection-event.d.ts +77 -0
  27. package/dist/core/mediator/protection-event.js +32 -0
  28. package/dist/core/mediator/types.d.ts +105 -0
  29. package/dist/core/mediator/types.js +1 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +7 -0
  32. package/dist/otel.d.ts +24 -0
  33. package/dist/otel.js +83 -0
  34. package/dist/policy.d.ts +98 -0
  35. package/dist/policy.js +97 -0
  36. package/dist/strategies/AbstractStrategy.d.ts +124 -0
  37. package/dist/strategies/AbstractStrategy.js +256 -0
  38. package/dist/strategies/ClipboardStrategy.d.ts +67 -0
  39. package/dist/strategies/ClipboardStrategy.js +291 -0
  40. package/dist/strategies/ContextMenuStrategy.d.ts +60 -0
  41. package/dist/strategies/ContextMenuStrategy.js +454 -0
  42. package/dist/strategies/DevToolsStrategy.d.ts +55 -0
  43. package/dist/strategies/DevToolsStrategy.js +314 -0
  44. package/dist/strategies/ExtensionStrategy.d.ts +66 -0
  45. package/dist/strategies/ExtensionStrategy.js +486 -0
  46. package/dist/strategies/IFrameStrategy.d.ts +49 -0
  47. package/dist/strategies/IFrameStrategy.js +255 -0
  48. package/dist/strategies/KeyboardStrategy.d.ts +35 -0
  49. package/dist/strategies/KeyboardStrategy.js +130 -0
  50. package/dist/strategies/PrintStrategy.d.ts +47 -0
  51. package/dist/strategies/PrintStrategy.js +201 -0
  52. package/dist/strategies/ScreenshotStrategy.d.ts +90 -0
  53. package/dist/strategies/ScreenshotStrategy.js +502 -0
  54. package/dist/strategies/SelectionStrategy.d.ts +49 -0
  55. package/dist/strategies/SelectionStrategy.js +216 -0
  56. package/dist/strategies/WatermarkStrategy.d.ts +56 -0
  57. package/dist/strategies/WatermarkStrategy.js +287 -0
  58. package/dist/strategies/index.d.ts +10 -0
  59. package/dist/strategies/index.js +11 -0
  60. package/dist/types/assessment.d.ts +62 -0
  61. package/dist/types/assessment.js +1 -0
  62. package/dist/types/index.d.ts +278 -0
  63. package/dist/types/index.js +17 -0
  64. package/dist/utils/DOMObserver.d.ts +68 -0
  65. package/dist/utils/DOMObserver.js +134 -0
  66. package/dist/utils/base/LoggableComponent.d.ts +44 -0
  67. package/dist/utils/base/LoggableComponent.js +56 -0
  68. package/dist/utils/detectors/AbstractDevToolsDetector.d.ts +98 -0
  69. package/dist/utils/detectors/AbstractDevToolsDetector.js +127 -0
  70. package/dist/utils/detectors/dateToStringDetector.d.ts +43 -0
  71. package/dist/utils/detectors/dateToStringDetector.js +96 -0
  72. package/dist/utils/detectors/debugLibDetector.d.ts +64 -0
  73. package/dist/utils/detectors/debugLibDetector.js +195 -0
  74. package/dist/utils/detectors/debuggerDetector.d.ts +51 -0
  75. package/dist/utils/detectors/debuggerDetector.js +211 -0
  76. package/dist/utils/detectors/defineGetterDetector.d.ts +48 -0
  77. package/dist/utils/detectors/defineGetterDetector.js +150 -0
  78. package/dist/utils/detectors/detectorInterface.d.ts +36 -0
  79. package/dist/utils/detectors/detectorInterface.js +1 -0
  80. package/dist/utils/detectors/devToolsDetectorManager.d.ts +88 -0
  81. package/dist/utils/detectors/devToolsDetectorManager.js +243 -0
  82. package/dist/utils/detectors/funcToStringDetector.d.ts +43 -0
  83. package/dist/utils/detectors/funcToStringDetector.js +90 -0
  84. package/dist/utils/detectors/regToStringDetector.d.ts +43 -0
  85. package/dist/utils/detectors/regToStringDetector.js +129 -0
  86. package/dist/utils/detectors/sizeDetector.d.ts +54 -0
  87. package/dist/utils/detectors/sizeDetector.js +134 -0
  88. package/dist/utils/detectors/timingDetector.d.ts +55 -0
  89. package/dist/utils/detectors/timingDetector.js +143 -0
  90. package/dist/utils/dom.d.ts +20 -0
  91. package/dist/utils/dom.js +83 -0
  92. package/dist/utils/environment.d.ts +29 -0
  93. package/dist/utils/environment.js +267 -0
  94. package/dist/utils/eventManager.d.ts +162 -0
  95. package/dist/utils/eventManager.js +548 -0
  96. package/dist/utils/index.d.ts +2 -0
  97. package/dist/utils/index.js +3 -0
  98. package/dist/utils/intervalManager.d.ts +91 -0
  99. package/dist/utils/intervalManager.js +221 -0
  100. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.d.ts +41 -0
  101. package/dist/utils/keyboardShortcutManager/keyboardShortcutManager.js +135 -0
  102. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.d.ts +18 -0
  103. package/dist/utils/keyboardShortcutManager/keyboardShortcuts.js +195 -0
  104. package/dist/utils/logging/simple/Loggable.d.ts +33 -0
  105. package/dist/utils/logging/simple/Loggable.js +1 -0
  106. package/dist/utils/logging/simple/LoggingDelegate.d.ts +42 -0
  107. package/dist/utils/logging/simple/LoggingDelegate.js +53 -0
  108. package/dist/utils/logging/simple/SimpleLoggingService.d.ts +39 -0
  109. package/dist/utils/logging/simple/SimpleLoggingService.js +58 -0
  110. package/dist/utils/orientation.d.ts +15 -0
  111. package/dist/utils/orientation.js +32 -0
  112. package/dist/utils/protectedContentManager.d.ts +155 -0
  113. package/dist/utils/protectedContentManager.js +424 -0
  114. package/dist/utils/securityOverlayManager.d.ts +253 -0
  115. package/dist/utils/securityOverlayManager.js +786 -0
  116. package/dist/utils/timeoutManager.d.ts +50 -0
  117. package/dist/utils/timeoutManager.js +113 -0
  118. package/package.json +61 -0
@@ -0,0 +1,90 @@
1
+ import type { CustomEventHandlers, ScreenshotOptions } from "../types";
2
+ import { AbstractStrategy } from "./AbstractStrategy";
3
+ /**
4
+ * Strategy for preventing screenshots and screen captures
5
+ */
6
+ export declare class ScreenshotStrategy extends AbstractStrategy {
7
+ private keydownHandler;
8
+ private keyupHandler;
9
+ private blurHandler;
10
+ private focusHandler;
11
+ private visibilityHandler;
12
+ private printHandler;
13
+ private beforePrintHandler;
14
+ private fullscreenChangeHandler;
15
+ private lastKeyEvent;
16
+ private options;
17
+ private customHandler?;
18
+ private categories;
19
+ private targetElement;
20
+ private shortcutManager;
21
+ /**
22
+ * Create a new ScreenshotStrategy
23
+ * @param options Options for customizing the screenshot protection
24
+ * @param customHandler Optional custom handler for screenshot attempts
25
+ * @param debugMode Enable debug mode for troubleshooting
26
+ */
27
+ constructor(options?: ScreenshotOptions, targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onScreenshotAttempt"], debugMode?: boolean);
28
+ /**
29
+ * Handle keydown events to detect screenshot attempts
30
+ */
31
+ private handleKeyDown;
32
+ /**
33
+ * Handle keyup events - specifically for PrintScreen which may only trigger on keyup
34
+ */
35
+ private handleKeyUp;
36
+ /**
37
+ * Handle window blur event - might indicate screenshot tool activation
38
+ */
39
+ private handleWindowBlur;
40
+ /**
41
+ * Handle window focus event - remove overlay when focus returns
42
+ */
43
+ private handleWindowFocus;
44
+ /**
45
+ * Handle visibility change event - might indicate screenshot tool activation
46
+ */
47
+ private handleVisibilityChange;
48
+ /**
49
+ * Handle print event - might be used for screenshots
50
+ */
51
+ private handlePrint;
52
+ /**
53
+ * Handle beforeprint event - prepare for print/screenshot
54
+ */
55
+ private handleBeforePrint;
56
+ /**
57
+ * Handle fullscreen change event - detect entering fullscreen
58
+ */
59
+ private handleFullscreenChange;
60
+ /**
61
+ * Exit fullscreen mode if currently in fullscreen
62
+ */
63
+ private exitFullscreen;
64
+ /**
65
+ * Show a notification that fullscreen mode is disabled
66
+ */
67
+ private showScreenshotNotification;
68
+ /**
69
+ * Show a notification that fullscreen mode is disabled
70
+ */
71
+ private showFullscreenNotification;
72
+ /**
73
+ * Apply screenshot protection
74
+ */
75
+ apply(): void;
76
+ /**
77
+ * Remove screenshot protection
78
+ */
79
+ remove(): void;
80
+ /**
81
+ * Update screenshot options
82
+ * @param options New screenshot options
83
+ */
84
+ updateOptions(options: Record<string, unknown>): void;
85
+ /**
86
+ * Set debug mode
87
+ * @param enabled Whether debug mode should be enabled
88
+ */
89
+ setDebugMode(enabled: boolean): void;
90
+ }
@@ -0,0 +1,502 @@
1
+ import { isBrowser, isPrintSupported, isBeforePrintSupported } from "../utils/environment";
2
+ import { AbstractStrategy, StrategyErrorType } from "./AbstractStrategy";
3
+ import { ShortcutCategory } from "../utils/keyboardShortcutManager/keyboardShortcuts";
4
+ import { KeyboardShortcutManager } from "../utils/keyboardShortcutManager/keyboardShortcutManager";
5
+ import { ProtectionEventType } from "../core/mediator/protection-event";
6
+ import { timeoutManager } from "../utils/timeoutManager";
7
+ /**
8
+ * Strategy for preventing screenshots and screen captures
9
+ */
10
+ export class ScreenshotStrategy extends AbstractStrategy {
11
+ /**
12
+ * Create a new ScreenshotStrategy
13
+ * @param options Options for customizing the screenshot protection
14
+ * @param customHandler Optional custom handler for screenshot attempts
15
+ * @param debugMode Enable debug mode for troubleshooting
16
+ */
17
+ constructor(options, targetElement = null, customHandler, debugMode = false) {
18
+ super("ScreenshotStrategy", debugMode);
19
+ this.lastKeyEvent = 0;
20
+ this.categories = [ShortcutCategory.SCREENSHOT, ShortcutCategory.FULLSCREEN];
21
+ this.targetElement = null;
22
+ this.options = {
23
+ showOverlay: true,
24
+ overlayOptions: {
25
+ title: "SCREENSHOT PROTECTED",
26
+ textColor: "white",
27
+ backgroundColor: "rgba(255, 0, 0, 0.7)",
28
+ fontSize: "48px",
29
+ duration: 1000,
30
+ },
31
+ hideContent: true,
32
+ preventFullscreen: true,
33
+ fullscreenMessage: "Fullscreen mode is disabled for security reasons",
34
+ ...options,
35
+ };
36
+ this.customHandler = customHandler;
37
+ this.targetElement = targetElement;
38
+ this.keydownHandler = this.handleKeyDown.bind(this);
39
+ this.keyupHandler = this.handleKeyUp.bind(this);
40
+ this.blurHandler = this.handleWindowBlur.bind(this);
41
+ this.focusHandler = this.handleWindowFocus.bind(this);
42
+ this.visibilityHandler = this.handleVisibilityChange.bind(this);
43
+ this.printHandler = this.handlePrint.bind(this);
44
+ this.beforePrintHandler = this.handleBeforePrint.bind(this);
45
+ this.fullscreenChangeHandler = this.handleFullscreenChange.bind(this);
46
+ // Initialize shortcut manager
47
+ this.shortcutManager = KeyboardShortcutManager.getInstance();
48
+ // Set timeoutManager debug mode to match this strategy's debug mode
49
+ timeoutManager.setDebugMode(this.debugMode);
50
+ this.log("Initialized with options:", {
51
+ preventFullscreen: this.options.preventFullscreen,
52
+ duration: this.options.overlayOptions?.duration,
53
+ hideContent: this.options.hideContent,
54
+ });
55
+ }
56
+ /**
57
+ * Handle keydown events to detect screenshot attempts
58
+ */
59
+ handleKeyDown(e) {
60
+ return this.safeExecute("handleKeyDown", StrategyErrorType.EVENT_HANDLING, () => {
61
+ if (e.key === "F12") {
62
+ return; // Allow F12 to pass through
63
+ }
64
+ this.lastKeyEvent = Date.now();
65
+ // Use the shortcut manager to detect shortcuts
66
+ const shortcut = this.shortcutManager.matchesShortcut(e, this.categories);
67
+ if (shortcut) {
68
+ // Prevent the default action
69
+ e.preventDefault();
70
+ e.stopPropagation();
71
+ this.log(`Detected keyboard shortcut: ${shortcut.id} (${this.shortcutManager.getShortcutDescription(shortcut)})`);
72
+ // Handle based on category
73
+ if (shortcut.category === ShortcutCategory.SCREENSHOT) {
74
+ // Publish screenshot attempt event
75
+ if (this.mediator) {
76
+ this.showScreenshotNotification();
77
+ }
78
+ // Call custom handler if provided
79
+ if (this.customHandler) {
80
+ this.customHandler(e);
81
+ }
82
+ return false;
83
+ }
84
+ else if (shortcut.category === ShortcutCategory.FULLSCREEN && this.options.preventFullscreen) {
85
+ // Exit fullscreen if currently in fullscreen mode
86
+ this.exitFullscreen();
87
+ // Show anti-fullscreen notification
88
+ this.showFullscreenNotification();
89
+ // Call custom handler if provided
90
+ if (this.customHandler) {
91
+ this.customHandler(e);
92
+ }
93
+ return false;
94
+ }
95
+ }
96
+ });
97
+ }
98
+ /**
99
+ * Handle keyup events - specifically for PrintScreen which may only trigger on keyup
100
+ */
101
+ handleKeyUp(e) {
102
+ return this.safeExecute("handleKeyUp", StrategyErrorType.EVENT_HANDLING, () => {
103
+ // Check for PrintScreen key
104
+ const isPrintScreen = e.key === "PrintScreen" ||
105
+ e.key === "PrtScn" ||
106
+ e.key === "Print" ||
107
+ e.key === "PrtSc" ||
108
+ e.code === "PrintScreen" ||
109
+ e.keyCode === 44;
110
+ // Check for SysRq key
111
+ const isSysRq = e.key === "SysRq" || e.code === "SysRq";
112
+ if (isPrintScreen || isSysRq) {
113
+ this.log("PrintScreen/SysRq key detected on keyup");
114
+ e.preventDefault();
115
+ e.stopPropagation();
116
+ // Publish screenshot attempt event
117
+ if (this.mediator) {
118
+ this.showScreenshotNotification();
119
+ }
120
+ // Call custom handler if provided
121
+ if (this.customHandler) {
122
+ this.customHandler(e);
123
+ }
124
+ return false;
125
+ }
126
+ });
127
+ }
128
+ /**
129
+ * Handle window blur event - might indicate screenshot tool activation
130
+ */
131
+ handleWindowBlur() {
132
+ return this.safeExecute("handleWindowBlur", StrategyErrorType.EVENT_HANDLING, () => {
133
+ const timeSinceLastKey = Date.now() - this.lastKeyEvent;
134
+ // If window loses focus within 500ms of a key event, it might be a screenshot tool
135
+ if (timeSinceLastKey < 500) {
136
+ this.log("Window blur detected shortly after key event, possible screenshot attempt");
137
+ // Publish screenshot attempt event
138
+ if (this.mediator) {
139
+ this.showScreenshotNotification();
140
+ }
141
+ if (this.customHandler) {
142
+ this.customHandler(new Event("blur"));
143
+ }
144
+ }
145
+ });
146
+ }
147
+ /**
148
+ * Handle window focus event - remove overlay when focus returns
149
+ */
150
+ handleWindowFocus() {
151
+ return this.safeExecute("handleWindowFocus", StrategyErrorType.EVENT_HANDLING, () => {
152
+ // No need to do anything here - the handler will manage timeouts
153
+ });
154
+ }
155
+ /**
156
+ * Handle visibility change event - might indicate screenshot tool activation
157
+ */
158
+ handleVisibilityChange() {
159
+ return this.safeExecute("handleVisibilityChange", StrategyErrorType.EVENT_HANDLING, () => {
160
+ if (document.visibilityState === "hidden") {
161
+ const timeSinceLastKey = Date.now() - this.lastKeyEvent;
162
+ // If document becomes hidden within 500ms of a key event, it might be a screenshot tool
163
+ if (timeSinceLastKey < 500) {
164
+ this.log("Visibility change detected shortly after key event, possible screenshot attempt");
165
+ // Publish screenshot attempt event
166
+ if (this.mediator) {
167
+ this.showScreenshotNotification();
168
+ }
169
+ if (this.customHandler) {
170
+ this.customHandler(new Event("visibilitychange"));
171
+ }
172
+ }
173
+ }
174
+ });
175
+ }
176
+ /**
177
+ * Handle print event - might be used for screenshots
178
+ */
179
+ handlePrint() {
180
+ return this.safeExecute("handlePrint", StrategyErrorType.EVENT_HANDLING, () => {
181
+ this.log("Print event detected, possible screenshot attempt");
182
+ // Publish screenshot attempt event
183
+ if (this.mediator) {
184
+ this.showScreenshotNotification();
185
+ }
186
+ if (this.customHandler) {
187
+ this.customHandler(new Event("afterprint"));
188
+ }
189
+ });
190
+ }
191
+ /**
192
+ * Handle beforeprint event - prepare for print/screenshot
193
+ */
194
+ handleBeforePrint() {
195
+ return this.safeExecute("handleBeforePrint", StrategyErrorType.EVENT_HANDLING, () => {
196
+ this.log("BeforePrint event detected, possible screenshot attempt");
197
+ // Publish screenshot attempt event
198
+ if (this.mediator) {
199
+ this.showScreenshotNotification();
200
+ }
201
+ if (this.customHandler) {
202
+ this.customHandler(new Event("beforeprint"));
203
+ }
204
+ });
205
+ }
206
+ /**
207
+ * Handle fullscreen change event - detect entering fullscreen
208
+ */
209
+ handleFullscreenChange() {
210
+ return this.safeExecute("handleFullscreenChange", StrategyErrorType.EVENT_HANDLING, () => {
211
+ if (!this.options.preventFullscreen)
212
+ return;
213
+ // Check if we're in fullscreen mode
214
+ const doc = document; // eslint-disable-line @typescript-eslint/no-explicit-any
215
+ const isInFullscreen = doc.fullscreenElement ||
216
+ doc.webkitFullscreenElement ||
217
+ doc.mozFullScreenElement ||
218
+ doc.msFullscreenElement;
219
+ if (isInFullscreen) {
220
+ this.log("Fullscreen mode detected, exiting fullscreen");
221
+ // Exit fullscreen mode
222
+ this.exitFullscreen();
223
+ // Show notification
224
+ this.showFullscreenNotification();
225
+ // Call custom handler if provided
226
+ if (this.customHandler) {
227
+ this.customHandler(new Event("fullscreenchange"));
228
+ }
229
+ }
230
+ });
231
+ }
232
+ /**
233
+ * Exit fullscreen mode if currently in fullscreen
234
+ */
235
+ exitFullscreen() {
236
+ return this.safeExecute("exitFullscreen", StrategyErrorType.APPLICATION, () => {
237
+ if (!isBrowser())
238
+ return;
239
+ // Check if document is active and has a fullscreen element before attempting to exit
240
+ try {
241
+ // Only attempt to exit if we're actually in fullscreen mode
242
+ const doc = document; // eslint-disable-line @typescript-eslint/no-explicit-any
243
+ const isInFullscreen = doc.fullscreenElement ||
244
+ doc.webkitFullscreenElement ||
245
+ doc.mozFullScreenElement ||
246
+ doc.msFullscreenElement;
247
+ if (isInFullscreen) {
248
+ if (document.exitFullscreen) {
249
+ document.exitFullscreen().catch((err) => {
250
+ // Silently catch errors during exit fullscreen
251
+ this.log("Error exiting fullscreen:", err.message);
252
+ });
253
+ }
254
+ else if (doc.webkitExitFullscreen) {
255
+ doc.webkitExitFullscreen();
256
+ }
257
+ else if (doc.mozCancelFullScreen) {
258
+ doc.mozCancelFullScreen();
259
+ }
260
+ else if (doc.msExitFullscreen) {
261
+ doc.msExitFullscreen();
262
+ }
263
+ this.log("Exited fullscreen mode");
264
+ }
265
+ else {
266
+ this.log("Not in fullscreen mode, no need to exit");
267
+ }
268
+ }
269
+ catch (error) {
270
+ this.error("Error checking fullscreen state:", error);
271
+ }
272
+ });
273
+ }
274
+ /**
275
+ * Show a notification that fullscreen mode is disabled
276
+ */
277
+ showScreenshotNotification() {
278
+ return this.safeExecute("showScreenshotNotification", StrategyErrorType.APPLICATION, () => {
279
+ if (!isBrowser() || !this.mediator)
280
+ return;
281
+ // Publish overlay shown event through the mediator
282
+ this.mediator.publish({
283
+ type: ProtectionEventType.OVERLAY_SHOWN,
284
+ source: this.STRATEGY_NAME,
285
+ timestamp: Date.now(),
286
+ data: {
287
+ strategyName: this.STRATEGY_NAME,
288
+ overlayType: "screenshot_notification",
289
+ options: {
290
+ ...this.options.overlayOptions,
291
+ blockEvents: false, // Don't block events, just show a notification
292
+ autoRestore: true,
293
+ },
294
+ priority: 5,
295
+ duration: 3000, // Show for 3 seconds
296
+ },
297
+ });
298
+ this.log("Showed screenshot disabled notification");
299
+ });
300
+ }
301
+ /**
302
+ * Show a notification that fullscreen mode is disabled
303
+ */
304
+ showFullscreenNotification() {
305
+ return this.safeExecute("showFullscreenNotification", StrategyErrorType.APPLICATION, () => {
306
+ if (!isBrowser() || !this.mediator)
307
+ return;
308
+ // Publish overlay shown event through the mediator
309
+ this.mediator.publish({
310
+ type: ProtectionEventType.OVERLAY_SHOWN,
311
+ source: this.STRATEGY_NAME,
312
+ timestamp: Date.now(),
313
+ data: {
314
+ strategyName: this.STRATEGY_NAME,
315
+ overlayType: "fullscreen_notification",
316
+ options: {
317
+ ...this.options.overlayOptions,
318
+ title: "Fullscreen Disabled",
319
+ message: this.options.fullscreenMessage || "Fullscreen mode is disabled for security reasons",
320
+ blockEvents: false, // Don't block events, just show a notification
321
+ autoRestore: true,
322
+ },
323
+ priority: 5,
324
+ duration: 3000, // Show for 3 seconds
325
+ },
326
+ });
327
+ this.log("Showed fullscreen disabled notification");
328
+ });
329
+ }
330
+ /**
331
+ * Apply screenshot protection
332
+ */
333
+ apply() {
334
+ return this.safeExecute("apply", StrategyErrorType.APPLICATION, () => {
335
+ if (this.isAppliedFlag) {
336
+ this.log("Protection already applied");
337
+ return;
338
+ }
339
+ if (isBrowser()) {
340
+ // Use both keydown and keyup to ensure we catch all PrintScreen events
341
+ this.registerEvent(document, "keydown", this.keydownHandler, {
342
+ capture: true,
343
+ priority: 10,
344
+ });
345
+ this.registerEvent(document, "keyup", this.keyupHandler, {
346
+ capture: true,
347
+ priority: 10,
348
+ });
349
+ // Add window blur/focus detection
350
+ this.registerEvent(window, "blur", this.blurHandler, { priority: 10 });
351
+ this.registerEvent(window, "focus", this.focusHandler, { priority: 10 });
352
+ // Add visibility change detection
353
+ this.registerEvent(document, "visibilitychange", this.visibilityHandler, { priority: 10 });
354
+ // Add print event detection if supported
355
+ if (isPrintSupported()) {
356
+ this.registerEvent(window, "afterprint", this.printHandler, { priority: 10 });
357
+ }
358
+ // Add beforeprint event detection if supported
359
+ if (isBeforePrintSupported()) {
360
+ this.registerEvent(window, "beforeprint", this.beforePrintHandler, { priority: 10 });
361
+ }
362
+ // Add fullscreen change event listeners if preventing fullscreen
363
+ if (this.options.preventFullscreen) {
364
+ this.registerEvent(document, "fullscreenchange", this.fullscreenChangeHandler, {
365
+ priority: 10,
366
+ });
367
+ this.registerEvent(document, "webkitfullscreenchange", this.fullscreenChangeHandler, {
368
+ priority: 10,
369
+ });
370
+ this.registerEvent(document, "mozfullscreenchange", this.fullscreenChangeHandler, {
371
+ priority: 10,
372
+ });
373
+ this.registerEvent(document, "MSFullscreenChange", this.fullscreenChangeHandler, {
374
+ priority: 10,
375
+ });
376
+ // Use a longer delay to ensure the document is fully active
377
+ setTimeout(() => {
378
+ // Use setTimeout to ensure the document is ready and active
379
+ this.exitFullscreen();
380
+ }, 500); // Increased from 0 to 500ms
381
+ }
382
+ this.isAppliedFlag = true;
383
+ this.log("Screenshot protection applied", {
384
+ printSupported: isPrintSupported(),
385
+ beforePrintSupported: isBeforePrintSupported(),
386
+ preventFullscreen: this.options.preventFullscreen,
387
+ registeredEvents: this.eventIds.length,
388
+ });
389
+ }
390
+ });
391
+ }
392
+ /**
393
+ * Remove screenshot protection
394
+ */
395
+ remove() {
396
+ return this.safeExecute("remove", StrategyErrorType.REMOVAL, () => {
397
+ if (!this.isAppliedFlag) {
398
+ this.log("Protection not applied");
399
+ return;
400
+ }
401
+ if (isBrowser()) {
402
+ // First attempt - remove by owner using the parent class method
403
+ this.removeEventsByOwner();
404
+ // Second attempt - try direct DOM removal as fallback
405
+ try {
406
+ document.removeEventListener("keydown", this.keydownHandler, { capture: true });
407
+ document.removeEventListener("keyup", this.keyupHandler, { capture: true });
408
+ window.removeEventListener("blur", this.blurHandler);
409
+ window.removeEventListener("focus", this.focusHandler);
410
+ document.removeEventListener("visibilitychange", this.visibilityHandler);
411
+ if (isPrintSupported()) {
412
+ window.removeEventListener("afterprint", this.printHandler);
413
+ }
414
+ if (isBeforePrintSupported()) {
415
+ window.removeEventListener("beforeprint", this.beforePrintHandler);
416
+ }
417
+ if (this.options.preventFullscreen) {
418
+ document.removeEventListener("fullscreenchange", this.fullscreenChangeHandler);
419
+ document.removeEventListener("webkitfullscreenchange", this.fullscreenChangeHandler);
420
+ document.removeEventListener("mozfullscreenchange", this.fullscreenChangeHandler);
421
+ document.removeEventListener("MSFullscreenChange", this.fullscreenChangeHandler);
422
+ }
423
+ this.log("Removed events via direct DOM API");
424
+ }
425
+ catch (domError) {
426
+ // Ignore errors in direct DOM removal
427
+ this.error("Error in fallback DOM removal:", domError);
428
+ }
429
+ // Clear any pending timeout using timeoutManager
430
+ timeoutManager.clearTimeout(`${this.STRATEGY_NAME}-overlay`);
431
+ // Clear tracked event IDs (parent class method will do this too, but being explicit)
432
+ this.eventIds = [];
433
+ this.isAppliedFlag = false;
434
+ this.log("Screenshot protection removed");
435
+ }
436
+ });
437
+ }
438
+ /**
439
+ * Update screenshot options
440
+ * @param options New screenshot options
441
+ */
442
+ updateOptions(options) {
443
+ return this.safeExecute("updateOptions", StrategyErrorType.OPTION_UPDATE, () => {
444
+ this.log("Updating options", options);
445
+ const typedOptions = options;
446
+ const wasPreventingFullscreen = this.options.preventFullscreen;
447
+ this.options = {
448
+ ...this.options,
449
+ ...typedOptions,
450
+ };
451
+ // If fullscreen prevention was toggled, we need to update event listeners
452
+ if (this.isAppliedFlag && wasPreventingFullscreen !== this.options.preventFullscreen) {
453
+ if (this.options.preventFullscreen) {
454
+ // Add fullscreen event listeners
455
+ this.registerEvent(document, "fullscreenchange", this.fullscreenChangeHandler, {
456
+ priority: 10,
457
+ });
458
+ this.registerEvent(document, "webkitfullscreenchange", this.fullscreenChangeHandler, {
459
+ priority: 10,
460
+ });
461
+ this.registerEvent(document, "mozfullscreenchange", this.fullscreenChangeHandler, {
462
+ priority: 10,
463
+ });
464
+ this.registerEvent(document, "MSFullscreenChange", this.fullscreenChangeHandler, {
465
+ priority: 10,
466
+ });
467
+ // Exit fullscreen if already in fullscreen mode
468
+ this.exitFullscreen();
469
+ this.log("Added fullscreen protection");
470
+ }
471
+ else {
472
+ // If we're removing fullscreen prevention while protection is active,
473
+ // it's safest to remove all event listeners and reapply the ones we still need
474
+ const wasApplied = this.isAppliedFlag;
475
+ this.remove();
476
+ if (wasApplied) {
477
+ this.apply();
478
+ }
479
+ this.log("Removed fullscreen protection and reapplied other protections");
480
+ }
481
+ }
482
+ // Handle categories if present
483
+ if (options.categories !== undefined && Array.isArray(options.categories)) {
484
+ this.categories = options.categories;
485
+ this.log("Updated shortcut categories:", this.categories);
486
+ }
487
+ // Update timeoutManager debug mode if our debug mode changed
488
+ if (options.debugMode !== undefined) {
489
+ timeoutManager.setDebugMode(!!options.debugMode);
490
+ }
491
+ });
492
+ }
493
+ /**
494
+ * Set debug mode
495
+ * @param enabled Whether debug mode should be enabled
496
+ */
497
+ setDebugMode(enabled) {
498
+ super.setDebugMode(enabled);
499
+ // Update timeoutManager debug mode
500
+ timeoutManager.setDebugMode(enabled);
501
+ }
502
+ }
@@ -0,0 +1,49 @@
1
+ import type { CustomEventHandlers } from "../types";
2
+ import { AbstractStrategy } from "./AbstractStrategy";
3
+ /**
4
+ * Strategy for preventing text selection
5
+ */
6
+ export declare class SelectionStrategy extends AbstractStrategy {
7
+ private targetElement;
8
+ private styleElement;
9
+ private selectionHandler;
10
+ private dragHandler;
11
+ private customHandler?;
12
+ private preventDrag;
13
+ /**
14
+ * Create a new SelectionStrategy
15
+ * @param targetElement Element to protect (defaults to document.body)
16
+ * @param customHandler Optional custom handler for selection attempts
17
+ * @param debugMode Enable debug mode for troubleshooting
18
+ */
19
+ constructor(targetElement?: HTMLElement | null, customHandler?: CustomEventHandlers["onSelectionAttempt"], debugMode?: boolean);
20
+ /**
21
+ * Handle selection event
22
+ */
23
+ private handleSelection;
24
+ /**
25
+ * Handle drag event
26
+ */
27
+ private handleDrag;
28
+ /**
29
+ * Inject CSS to prevent selection
30
+ */
31
+ private injectSelectionStyles;
32
+ /**
33
+ * Remove selection-blocking CSS
34
+ */
35
+ private removeSelectionStyles;
36
+ /**
37
+ * Apply selection protection
38
+ */
39
+ apply(): void;
40
+ /**
41
+ * Remove selection protection
42
+ */
43
+ remove(): void;
44
+ /**
45
+ * Update selection protection options
46
+ * @param options New options for selection protection
47
+ */
48
+ updateOptions(options: Record<string, unknown>): void;
49
+ }