@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.
Files changed (56) hide show
  1. package/dist/index.js +344 -349
  2. package/dist/index.umd.cjs +12 -12
  3. package/package.json +8 -8
  4. package/src/comms/comms.ts +5 -5
  5. package/src/comms/index.ts +3 -3
  6. package/src/helpers/css.ts +2 -4
  7. package/src/helpers/document.ts +9 -9
  8. package/src/helpers/locator.ts +2 -2
  9. package/src/index.ts +4 -4
  10. package/src/modules/Decorator.ts +18 -18
  11. package/src/modules/Module.ts +4 -4
  12. package/src/modules/ModuleLibrary.ts +11 -11
  13. package/src/modules/Peripherals.ts +70 -61
  14. package/src/modules/index.ts +4 -4
  15. package/src/modules/setup/FixedSetup.ts +6 -6
  16. package/src/modules/setup/ReflowableSetup.ts +6 -6
  17. package/src/modules/setup/Setup.ts +5 -5
  18. package/src/modules/setup/WebPubSetup.ts +5 -5
  19. package/src/modules/snapper/ColumnSnapper.ts +20 -39
  20. package/src/modules/snapper/ScrollSnapper.ts +18 -18
  21. package/src/modules/snapper/Snapper.ts +5 -5
  22. package/src/modules/snapper/WebPubSnapper.ts +15 -15
  23. package/src/protection/BulkCopyProtector.ts +15 -15
  24. package/src/protection/KeyCombinationManager.ts +8 -8
  25. package/src/protection/PatternAnalyzer.ts +18 -18
  26. package/src/protection/PrintProtector.ts +12 -12
  27. package/src/protection/SelectionAnalyzer.ts +1 -1
  28. package/src/protection/config.ts +4 -4
  29. package/src/protection/index.ts +6 -6
  30. package/src/vendor/hypothesis/anchoring/html.ts +1 -1
  31. package/src/vendor/hypothesis/anchoring/types.ts +1 -1
  32. package/types/src/comms/comms.d.ts +2 -2
  33. package/types/src/comms/index.d.ts +3 -3
  34. package/types/src/helpers/css.d.ts +1 -2
  35. package/types/src/helpers/document.d.ts +1 -1
  36. package/types/src/index.d.ts +4 -4
  37. package/types/src/modules/Decorator.d.ts +4 -4
  38. package/types/src/modules/Module.d.ts +3 -3
  39. package/types/src/modules/ModuleLibrary.d.ts +1 -1
  40. package/types/src/modules/Peripherals.d.ts +10 -9
  41. package/types/src/modules/index.d.ts +4 -4
  42. package/types/src/modules/setup/FixedSetup.d.ts +4 -4
  43. package/types/src/modules/setup/ReflowableSetup.d.ts +3 -3
  44. package/types/src/modules/setup/Setup.d.ts +4 -4
  45. package/types/src/modules/setup/WebPubSetup.d.ts +3 -3
  46. package/types/src/modules/snapper/ColumnSnapper.d.ts +5 -5
  47. package/types/src/modules/snapper/ScrollSnapper.d.ts +5 -5
  48. package/types/src/modules/snapper/Snapper.d.ts +4 -4
  49. package/types/src/modules/snapper/WebPubSnapper.d.ts +5 -5
  50. package/types/src/protection/BulkCopyProtector.d.ts +1 -1
  51. package/types/src/protection/KeyCombinationManager.d.ts +3 -3
  52. package/types/src/protection/PrintProtector.d.ts +4 -4
  53. package/types/src/protection/config.d.ts +3 -3
  54. package/types/src/protection/index.d.ts +6 -6
  55. package/types/src/vendor/hypothesis/anchoring/html.d.ts +1 -1
  56. 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
- if(isTypedOMSupported()) {
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
- if(isTypedOMSupported()) {
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
- if(isTypedOMSupported()) {
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
+ }