@readium/navigator 2.4.0-beta.1 → 2.4.0-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@readium/navigator",
3
- "version": "2.4.0-beta.1",
3
+ "version": "2.4.0-beta.3",
4
4
  "type": "module",
5
5
  "description": "Next generation SDK for publications in Web Apps",
6
6
  "author": "readium",
@@ -1,5 +1,5 @@
1
1
  import { Link, Locator, LocatorLocations, Publication } from "@readium/shared";
2
- import { MediaNavigator } from "../Navigator";
2
+ import { MediaNavigator, IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../Navigator";
3
3
  import { Configurable } from "../preferences";
4
4
  import { WebAudioEngine, PlaybackState } from "./engine";
5
5
  import {
@@ -11,6 +11,10 @@ import {
11
11
  IAudioDefaults
12
12
  } from "./preferences";
13
13
  import { AudioPoolManager } from "./AudioPoolManager";
14
+ import { ContextMenuEvent, KeyboardEventData, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
15
+ import { AudioNavigatorProtector } from "./protection/AudioNavigatorProtector";
16
+ import { NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT } from "../protection/NavigatorProtector";
17
+ import { KeyboardPeripherals, NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT } from "../peripherals/KeyboardPeripherals";
14
18
 
15
19
  export interface AudioNavigatorListeners {
16
20
  trackLoaded: (media: HTMLMediaElement) => void;
@@ -23,11 +27,32 @@ export interface AudioNavigatorListeners {
23
27
  stalled: (isStalled: boolean) => void;
24
28
  seeking: (isSeeking: boolean) => void;
25
29
  seekable: (seekable: TimeRanges) => void;
30
+ contentProtection: (type: string, data: SuspiciousActivityEvent) => void;
31
+ peripheral: (data: KeyboardEventData) => void;
32
+ contextMenu: (data: ContextMenuEvent) => void;
26
33
  }
27
34
 
35
+ const defaultListeners = (listeners: Partial<AudioNavigatorListeners>): AudioNavigatorListeners => ({
36
+ trackLoaded: listeners.trackLoaded ?? (() => {}),
37
+ positionChanged: listeners.positionChanged ?? (() => {}),
38
+ error: listeners.error ?? (() => {}),
39
+ trackEnded: listeners.trackEnded ?? (() => {}),
40
+ play: listeners.play ?? (() => {}),
41
+ pause: listeners.pause ?? (() => {}),
42
+ metadataLoaded: listeners.metadataLoaded ?? (() => {}),
43
+ stalled: listeners.stalled ?? (() => {}),
44
+ seeking: listeners.seeking ?? (() => {}),
45
+ seekable: listeners.seekable ?? (() => {}),
46
+ contentProtection: listeners.contentProtection ?? (() => {}),
47
+ peripheral: listeners.peripheral ?? (() => {}),
48
+ contextMenu: listeners.contextMenu ?? (() => {}),
49
+ });
50
+
28
51
  export interface AudioNavigatorConfiguration {
29
52
  preferences: IAudioPreferences;
30
53
  defaults: IAudioDefaults;
54
+ contentProtection?: IContentProtectionConfig;
55
+ keyboardPeripherals?: IKeyboardPeripheralsConfig;
31
56
  }
32
57
 
33
58
  export class AudioNavigator extends MediaNavigator implements Configurable<AudioSettings, AudioPreferences> {
@@ -42,6 +67,10 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
42
67
  private _settings: AudioSettings;
43
68
  private _preferencesEditor: AudioPreferencesEditor | null = null;
44
69
  private pool: AudioPoolManager;
70
+ private readonly _navigatorProtector: AudioNavigatorProtector | null = null;
71
+ private readonly _keyboardPeripheralsManager: KeyboardPeripherals | null = null;
72
+ private readonly _suspiciousActivityListener: ((event: Event) => void) | null = null;
73
+ private readonly _keyboardPeripheralListener: ((event: Event) => void) | null = null;
45
74
 
46
75
  constructor(publication: Publication, listeners: AudioNavigatorListeners, initialPosition?: Locator, configuration: AudioNavigatorConfiguration = {
47
76
  preferences: {},
@@ -49,11 +78,11 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
49
78
  }) {
50
79
  super();
51
80
  this.pub = publication;
81
+ this.listeners = defaultListeners(listeners);
52
82
 
53
83
  this._preferences = new AudioPreferences(configuration.preferences);
54
84
  this._defaults = new AudioDefaults(configuration.defaults);
55
85
  this._settings = new AudioSettings(this._preferences, this._defaults);
56
- this.listeners = listeners;
57
86
 
58
87
  if (initialPosition) {
59
88
  this.currentLocation = this.ensureLocatorLocations(initialPosition);
@@ -88,14 +117,49 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
88
117
  }
89
118
  });
90
119
 
91
- this.pool = new AudioPoolManager(audioEngine);
120
+ this.pool = new AudioPoolManager(audioEngine, publication);
121
+
122
+ // Initialize content protection
123
+ const contentProtection = configuration.contentProtection || {};
124
+ const keyboardPeripherals = this.mergeKeyboardPeripherals(
125
+ contentProtection,
126
+ configuration.keyboardPeripherals || []
127
+ );
128
+
129
+ if (contentProtection.disableContextMenu ||
130
+ contentProtection.checkAutomation ||
131
+ contentProtection.checkIFrameEmbedding ||
132
+ contentProtection.monitorDevTools ||
133
+ contentProtection.protectPrinting?.disable ||
134
+ contentProtection.disableDragAndDrop ||
135
+ contentProtection.protectCopy) {
136
+ this._navigatorProtector = new AudioNavigatorProtector(contentProtection);
137
+ this._suspiciousActivityListener = (event: Event) => {
138
+ const { type, ...detail } = (event as CustomEvent).detail;
139
+ if (type === "context_menu") {
140
+ this.listeners.contextMenu(detail as ContextMenuEvent);
141
+ } else {
142
+ this.listeners.contentProtection(type, detail as SuspiciousActivityEvent);
143
+ }
144
+ };
145
+ window.addEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
146
+ }
147
+
148
+ if (keyboardPeripherals.length > 0) {
149
+ this._keyboardPeripheralsManager = new KeyboardPeripherals({ keyboardPeripherals });
150
+ this._keyboardPeripheralListener = (event: Event) => {
151
+ this.listeners.peripheral((event as CustomEvent).detail);
152
+ };
153
+ window.addEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
154
+ }
155
+
92
156
  this.setupEventListeners();
93
157
 
94
158
  if (this._settings.enableMediaSession) {
95
159
  this.setupMediaSession();
96
160
  }
97
161
 
98
- this.pool.setCurrentAudio(initialHref, this.pub, trackIndex, "forward");
162
+ this.pool.setCurrentAudio(trackIndex, "forward");
99
163
 
100
164
  // Load and seek to initial position, then notify consumer.
101
165
  // No cancellation needed here — the constructor runs once.
@@ -370,7 +434,7 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
370
434
  const wasPlaying = this.isPlaying;
371
435
 
372
436
  this.stopPositionPolling();
373
- this.pool.setCurrentAudio(href, this.pub, trackIndex, direction);
437
+ this.pool.setCurrentAudio(trackIndex, direction);
374
438
  this.currentLocation = locator.copyWithLocations(locator.locations);
375
439
 
376
440
  await this.waitForLoadedAndSeeked(time, id);
@@ -399,7 +463,8 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
399
463
  cb(false);
400
464
  return;
401
465
  }
402
- const locator = this.createLocator(trackIndex, 0);
466
+ const time = link.locator.locations?.time() ?? 0;
467
+ const locator = this.createLocator(trackIndex, time);
403
468
  await this.go(locator, _animated, cb);
404
469
  }
405
470
 
@@ -492,6 +557,14 @@ export class AudioNavigator extends MediaNavigator implements Configurable<Audio
492
557
  destroy(): void {
493
558
  this.stopPositionPolling();
494
559
  this.destroyMediaSession();
560
+ if (this._suspiciousActivityListener) {
561
+ window.removeEventListener(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, this._suspiciousActivityListener);
562
+ }
563
+ if (this._keyboardPeripheralListener) {
564
+ window.removeEventListener(NAVIGATOR_KEYBOARD_PERIPHERAL_EVENT, this._keyboardPeripheralListener);
565
+ }
566
+ this._navigatorProtector?.destroy();
567
+ this._keyboardPeripheralsManager?.destroy();
495
568
  this.pool.destroy();
496
569
  }
497
570
  }
@@ -1,12 +1,46 @@
1
- import { Publication } from "@readium/shared";
1
+ import { Link, Publication } from "@readium/shared";
2
2
  import { WebAudioEngine } from "./engine/WebAudioEngine";
3
3
 
4
4
  export class AudioPoolManager {
5
5
  private preloadedElements: Map<string, HTMLAudioElement> = new Map();
6
6
  private _audioEngine: WebAudioEngine;
7
+ private readonly _publication: Publication;
8
+ private readonly _supportedAudioTypes: Map<string, "probably" | "maybe">;
7
9
 
8
- constructor(audioEngine: WebAudioEngine) {
10
+ constructor(audioEngine: WebAudioEngine, publication: Publication) {
9
11
  this._audioEngine = audioEngine;
12
+ this._publication = publication;
13
+ this._supportedAudioTypes = this.detectSupportedAudioTypes();
14
+ }
15
+
16
+ private detectSupportedAudioTypes(): Map<string, "probably" | "maybe"> {
17
+ const audio = document.createElement("audio");
18
+ const unique = new Set<string>();
19
+ for (const link of this._publication.readingOrder.items) {
20
+ if (link.type) unique.add(link.type);
21
+ for (const alt of link.alternates?.items ?? []) {
22
+ if (alt.type) unique.add(alt.type);
23
+ }
24
+ }
25
+ const supported = new Map<string, "probably" | "maybe">();
26
+ for (const type of unique) {
27
+ const result = audio.canPlayType(type);
28
+ if (result !== "") supported.set(type, result as "probably" | "maybe");
29
+ }
30
+ return supported;
31
+ }
32
+
33
+ private pickPlayableHref(link: Link): string {
34
+ const candidates = [link, ...(link.alternates?.items ?? [])];
35
+ let best: { href: string; confidence: "probably" | "maybe" } | undefined;
36
+ for (const candidate of candidates) {
37
+ if (!candidate.type) continue;
38
+ const confidence = this._supportedAudioTypes.get(candidate.type);
39
+ if (!confidence) continue;
40
+ if (confidence === "probably") return candidate.href;
41
+ if (!best) best = { href: candidate.href, confidence };
42
+ }
43
+ return best?.href ?? link.href;
10
44
  }
11
45
 
12
46
  get audioEngine(): WebAudioEngine {
@@ -21,7 +55,8 @@ export class AudioPoolManager {
21
55
  * @param currentIndex The current track index.
22
56
  * @param direction The navigation direction ('forward' or 'backward').
23
57
  */
24
- setCurrentAudio(href: string, publication: Publication, currentIndex: number, direction: 'forward' | 'backward'): void {
58
+ setCurrentAudio(currentIndex: number, direction: 'forward' | 'backward'): void {
59
+ const href = this.pickPlayableHref(this._publication.readingOrder.items[currentIndex]);
25
60
  // When Web Audio is active, preloaded elements lack crossOrigin="anonymous"
26
61
  // and cannot be connected to MediaElementAudioSourceNode, so bypass the pool.
27
62
  const preloadedElement = !this.audioEngine.isWebAudioActive ? this.get(href) : undefined;
@@ -32,7 +67,7 @@ export class AudioPoolManager {
32
67
  this.clear(href);
33
68
  this.audioEngine.loadAudio(href);
34
69
  }
35
- this.preloadAdjacent(publication, currentIndex, direction);
70
+ this.preloadAdjacent(currentIndex, direction);
36
71
  }
37
72
  preload(href: string): void {
38
73
  if (this.preloadedElements.has(href)) {
@@ -69,44 +104,38 @@ export class AudioPoolManager {
69
104
  * @param publication The publication containing the reading order.
70
105
  * @param currentIndex The current track index.
71
106
  */
72
- preloadNext(publication: Publication, currentIndex: number): void {
107
+ preloadNext(currentIndex: number): void {
73
108
  const nextIndex = currentIndex + 1;
74
- if (nextIndex < publication.readingOrder.items.length) {
75
- const nextLink = publication.readingOrder.items[nextIndex];
76
- if (nextLink.href) {
77
- this.preload(nextLink.href);
78
- }
109
+ if (nextIndex < this._publication.readingOrder.items.length) {
110
+ const nextLink = this._publication.readingOrder.items[nextIndex];
111
+ this.preload(this.pickPlayableHref(nextLink));
79
112
  }
80
113
  }
81
114
 
82
115
  /**
83
116
  * Preloads the previous track in the reading order.
84
- * @param publication The publication containing the reading order.
85
117
  * @param currentIndex The current track index.
86
118
  */
87
- preloadPrevious(publication: Publication, currentIndex: number): void {
119
+ preloadPrevious(currentIndex: number): void {
88
120
  const prevIndex = currentIndex - 1;
89
121
  if (prevIndex >= 0) {
90
- const prevLink = publication.readingOrder.items[prevIndex];
91
- if (prevLink.href) {
92
- this.preload(prevLink.href);
93
- }
122
+ const prevLink = this._publication.readingOrder.items[prevIndex];
123
+ this.preload(this.pickPlayableHref(prevLink));
94
124
  }
95
125
  }
96
126
 
97
127
  /**
98
128
  * Preloads adjacent tracks (previous and next) for smoother navigation.
99
- * @param publication The publication containing the reading order.
100
129
  * @param currentIndex The current track index.
101
130
  * @param direction The navigation direction ('forward' or 'backward').
102
131
  */
103
- preloadAdjacent(publication: Publication, currentIndex: number, direction: 'forward' | 'backward' = 'forward'): void {
132
+ preloadAdjacent(currentIndex: number, direction: 'forward' | 'backward' = 'forward'): void {
104
133
  if (direction === 'forward') {
105
- this.preloadNext(publication, currentIndex);
106
- this.preloadPrevious(publication, currentIndex);
134
+ this.preloadNext(currentIndex);
135
+ this.preloadPrevious(currentIndex);
107
136
  } else {
108
- this.preloadPrevious(publication, currentIndex);
109
- this.preloadNext(publication, currentIndex);
137
+ this.preloadPrevious(currentIndex);
138
+ this.preloadNext(currentIndex);
110
139
  }
111
140
  }
112
141
 
@@ -0,0 +1,38 @@
1
+ import { NavigatorProtector } from "../../protection/NavigatorProtector";
2
+ import { DragAndDropProtector } from "../../protection/DragAndDropProtector";
3
+ import { CopyProtector } from "../../protection/CopyProtector";
4
+ import { IContentProtectionConfig } from "../../Navigator";
5
+
6
+ export class AudioNavigatorProtector extends NavigatorProtector {
7
+ private dragAndDropProtector?: DragAndDropProtector;
8
+ private copyProtector?: CopyProtector;
9
+
10
+ constructor(config: IContentProtectionConfig = {}) {
11
+ super(config);
12
+
13
+ if (config.disableDragAndDrop) {
14
+ this.dragAndDropProtector = new DragAndDropProtector({
15
+ onDragDetected: (dataTransferTypes) => {
16
+ this.dispatchSuspiciousActivity("drag_detected", { dataTransferTypes, targetFrameSrc: "" });
17
+ },
18
+ onDropDetected: (dataTransferTypes, fileCount) => {
19
+ this.dispatchSuspiciousActivity("drop_detected", { dataTransferTypes, fileCount, targetFrameSrc: "" });
20
+ }
21
+ });
22
+ }
23
+
24
+ if (config.protectCopy) {
25
+ this.copyProtector = new CopyProtector({
26
+ onCopyBlocked: () => {
27
+ this.dispatchSuspiciousActivity("bulk_copy", { targetFrameSrc: "" });
28
+ }
29
+ });
30
+ }
31
+ }
32
+
33
+ public override destroy() {
34
+ super.destroy();
35
+ this.dragAndDropProtector?.destroy();
36
+ this.copyProtector?.destroy();
37
+ }
38
+ }
@@ -0,0 +1,22 @@
1
+ export interface CopyProtectionOptions {
2
+ onCopyBlocked?: () => void;
3
+ }
4
+
5
+ export class CopyProtector {
6
+ private copyHandler: (event: ClipboardEvent) => void;
7
+
8
+ constructor(options: CopyProtectionOptions = {}) {
9
+ this.copyHandler = (event: ClipboardEvent) => {
10
+ event.preventDefault();
11
+ event.stopPropagation();
12
+ options.onCopyBlocked?.();
13
+ };
14
+
15
+ document.addEventListener("copy", this.copyHandler, true);
16
+ window.addEventListener("unload", () => this.destroy());
17
+ }
18
+
19
+ public destroy() {
20
+ document.removeEventListener("copy", this.copyHandler, true);
21
+ }
22
+ }
@@ -0,0 +1,34 @@
1
+ export interface DragAndDropProtectionOptions {
2
+ onDragDetected?: (dataTransferTypes: readonly string[]) => void;
3
+ onDropDetected?: (dataTransferTypes: readonly string[], fileCount: number) => void;
4
+ }
5
+
6
+ export class DragAndDropProtector {
7
+ private dragstartHandler: (event: DragEvent) => void;
8
+ private dropHandler: (event: DragEvent) => void;
9
+
10
+ constructor(options: DragAndDropProtectionOptions = {}) {
11
+ this.dragstartHandler = (event: DragEvent) => {
12
+ event.preventDefault();
13
+ event.stopPropagation();
14
+ options.onDragDetected?.(Array.from(event.dataTransfer?.types ?? []));
15
+ };
16
+
17
+ this.dropHandler = (event: DragEvent) => {
18
+ event.preventDefault();
19
+ event.stopPropagation();
20
+ const types = Array.from(event.dataTransfer?.types ?? []);
21
+ const fileCount = event.dataTransfer?.files.length ?? 0;
22
+ options.onDropDetected?.(types, fileCount);
23
+ };
24
+
25
+ document.addEventListener("dragstart", this.dragstartHandler, true);
26
+ document.addEventListener("drop", this.dropHandler, true);
27
+ window.addEventListener("unload", () => this.destroy());
28
+ }
29
+
30
+ public destroy() {
31
+ document.removeEventListener("dragstart", this.dragstartHandler, true);
32
+ document.removeEventListener("drop", this.dropHandler, true);
33
+ }
34
+ }
@@ -15,7 +15,7 @@ export class NavigatorProtector {
15
15
  private printProtector?: PrintProtector;
16
16
  private contextMenuProtector?: ContextMenuProtector;
17
17
 
18
- private dispatchSuspiciousActivity(type: string, detail: Record<string, unknown>) {
18
+ protected dispatchSuspiciousActivity(type: string, detail: Record<string, unknown>) {
19
19
  const event = new CustomEvent(NAVIGATOR_SUSPICIOUS_ACTIVITY_EVENT, {
20
20
  detail: {
21
21
  type,
@@ -55,7 +55,7 @@ export class NavigatorProtector {
55
55
  }
56
56
  });
57
57
  }
58
-
58
+
59
59
  // Enable iframe embedding detection if explicitly enabled in config
60
60
  if (config.checkIFrameEmbedding) {
61
61
  this.iframeEmbeddingDetector = new IframeEmbeddingDetector({
@@ -74,7 +74,7 @@ export class NavigatorProtector {
74
74
  }
75
75
  });
76
76
  }
77
-
77
+
78
78
  // Enable context menu protection if configured
79
79
  if (config.disableContextMenu) {
80
80
  this.contextMenuProtector = new ContextMenuProtector({
@@ -1,7 +1,8 @@
1
1
  import { Link, Locator, Publication } from "@readium/shared";
2
- import { MediaNavigator } from "../Navigator";
2
+ import { MediaNavigator, IContentProtectionConfig, IKeyboardPeripheralsConfig } from "../Navigator";
3
3
  import { Configurable } from "../preferences";
4
4
  import { AudioPreferences, AudioSettings, AudioPreferencesEditor, IAudioPreferences, IAudioDefaults } from "./preferences";
5
+ import { ContextMenuEvent, KeyboardEventData, SuspiciousActivityEvent } from "@readium/navigator-html-injectables";
5
6
  export interface AudioNavigatorListeners {
6
7
  trackLoaded: (media: HTMLMediaElement) => void;
7
8
  positionChanged: (locator: Locator) => void;
@@ -13,10 +14,15 @@ export interface AudioNavigatorListeners {
13
14
  stalled: (isStalled: boolean) => void;
14
15
  seeking: (isSeeking: boolean) => void;
15
16
  seekable: (seekable: TimeRanges) => void;
17
+ contentProtection: (type: string, data: SuspiciousActivityEvent) => void;
18
+ peripheral: (data: KeyboardEventData) => void;
19
+ contextMenu: (data: ContextMenuEvent) => void;
16
20
  }
17
21
  export interface AudioNavigatorConfiguration {
18
22
  preferences: IAudioPreferences;
19
23
  defaults: IAudioDefaults;
24
+ contentProtection?: IContentProtectionConfig;
25
+ keyboardPeripherals?: IKeyboardPeripheralsConfig;
20
26
  }
21
27
  export declare class AudioNavigator extends MediaNavigator implements Configurable<AudioSettings, AudioPreferences> {
22
28
  private readonly pub;
@@ -29,6 +35,10 @@ export declare class AudioNavigator extends MediaNavigator implements Configurab
29
35
  private _settings;
30
36
  private _preferencesEditor;
31
37
  private pool;
38
+ private readonly _navigatorProtector;
39
+ private readonly _keyboardPeripheralsManager;
40
+ private readonly _suspiciousActivityListener;
41
+ private readonly _keyboardPeripheralListener;
32
42
  constructor(publication: Publication, listeners: AudioNavigatorListeners, initialPosition?: Locator, configuration?: AudioNavigatorConfiguration);
33
43
  get settings(): AudioSettings;
34
44
  get preferencesEditor(): AudioPreferencesEditor;
@@ -3,7 +3,11 @@ import { WebAudioEngine } from "./engine/WebAudioEngine";
3
3
  export declare class AudioPoolManager {
4
4
  private preloadedElements;
5
5
  private _audioEngine;
6
- constructor(audioEngine: WebAudioEngine);
6
+ private readonly _publication;
7
+ private readonly _supportedAudioTypes;
8
+ constructor(audioEngine: WebAudioEngine, publication: Publication);
9
+ private detectSupportedAudioTypes;
10
+ private pickPlayableHref;
7
11
  get audioEngine(): WebAudioEngine;
8
12
  /**
9
13
  * Sets the current audio by href, using preloaded element if available or loading otherwise,
@@ -13,7 +17,7 @@ export declare class AudioPoolManager {
13
17
  * @param currentIndex The current track index.
14
18
  * @param direction The navigation direction ('forward' or 'backward').
15
19
  */
16
- setCurrentAudio(href: string, publication: Publication, currentIndex: number, direction: 'forward' | 'backward'): void;
20
+ setCurrentAudio(currentIndex: number, direction: 'forward' | 'backward'): void;
17
21
  preload(href: string): void;
18
22
  /**
19
23
  * Retrieves a preloaded audio element by URL.
@@ -31,20 +35,18 @@ export declare class AudioPoolManager {
31
35
  * @param publication The publication containing the reading order.
32
36
  * @param currentIndex The current track index.
33
37
  */
34
- preloadNext(publication: Publication, currentIndex: number): void;
38
+ preloadNext(currentIndex: number): void;
35
39
  /**
36
40
  * Preloads the previous track in the reading order.
37
- * @param publication The publication containing the reading order.
38
41
  * @param currentIndex The current track index.
39
42
  */
40
- preloadPrevious(publication: Publication, currentIndex: number): void;
43
+ preloadPrevious(currentIndex: number): void;
41
44
  /**
42
45
  * Preloads adjacent tracks (previous and next) for smoother navigation.
43
- * @param publication The publication containing the reading order.
44
46
  * @param currentIndex The current track index.
45
47
  * @param direction The navigation direction ('forward' or 'backward').
46
48
  */
47
- preloadAdjacent(publication: Publication, currentIndex: number, direction?: 'forward' | 'backward'): void;
49
+ preloadAdjacent(currentIndex: number, direction?: 'forward' | 'backward'): void;
48
50
  /**
49
51
  * Destroys the pool by stopping the engine and clearing all preloaded elements.
50
52
  */
@@ -0,0 +1,8 @@
1
+ import { NavigatorProtector } from "../../protection/NavigatorProtector";
2
+ import { IContentProtectionConfig } from "../../Navigator";
3
+ export declare class AudioNavigatorProtector extends NavigatorProtector {
4
+ private dragAndDropProtector?;
5
+ private copyProtector?;
6
+ constructor(config?: IContentProtectionConfig);
7
+ destroy(): void;
8
+ }
@@ -0,0 +1,8 @@
1
+ export interface CopyProtectionOptions {
2
+ onCopyBlocked?: () => void;
3
+ }
4
+ export declare class CopyProtector {
5
+ private copyHandler;
6
+ constructor(options?: CopyProtectionOptions);
7
+ destroy(): void;
8
+ }
@@ -0,0 +1,10 @@
1
+ export interface DragAndDropProtectionOptions {
2
+ onDragDetected?: (dataTransferTypes: readonly string[]) => void;
3
+ onDropDetected?: (dataTransferTypes: readonly string[], fileCount: number) => void;
4
+ }
5
+ export declare class DragAndDropProtector {
6
+ private dragstartHandler;
7
+ private dropHandler;
8
+ constructor(options?: DragAndDropProtectionOptions);
9
+ destroy(): void;
10
+ }
@@ -6,7 +6,7 @@ export declare class NavigatorProtector {
6
6
  private iframeEmbeddingDetector?;
7
7
  private printProtector?;
8
8
  private contextMenuProtector?;
9
- private dispatchSuspiciousActivity;
9
+ protected dispatchSuspiciousActivity(type: string, detail: Record<string, unknown>): void;
10
10
  constructor(config?: IContentProtectionConfig);
11
11
  destroy(): void;
12
12
  }