### Full Example Integration Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/RemotePlayback.md A comprehensive example demonstrating the setup of the AudioNavigator with Remote Playback, including device availability monitoring and handling user interactions for casting. ```APIDOC ## Full example ```js const castButton = document.getElementById('cast-button') as HTMLButtonElement; // --- Navigator setup --- const listeners: AudioNavigatorListeners = { trackLoaded: (_media) => { /* ... */ }, positionChanged: (_locator) => { /* ... */ }, // ...other required listeners... remotePlaybackStateChanged: (state) => { castButton.dataset.state = state; castButton.textContent = state === 'connected' ? 'Casting' : state === 'connecting' ? 'Connecting…' : 'Cast'; }, }; const navigator = new AudioNavigator(publication, listeners); // --- Device availability --- avigator.remotePlayback.watchAvailability((available) => { castButton.style.display = available ? 'inline-flex' : 'none'; }).catch(() => { // Availability monitoring not supported; always show the button. castButton.style.display = 'inline-flex'; }); // --- Cast button --- castButton.addEventListener('click', async () => { if (navigator.remotePlayback.state === 'connected') { // Already casting — nothing to do; the user disconnects via the browser UI. return; } try { await navigator.remotePlayback.prompt(); } catch { // User cancelled or no devices found. } }); ``` ``` -------------------------------- ### play Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `play` listener, which updates the play and pause button states when playback starts or resumes. ```APIDOC ## play Fires when audio playback starts or resumes. ### Example ```js const listeners = { play: function (locator: Locator): void { updatePlayButton(false); updatePauseButton(true); } }; ``` ``` -------------------------------- ### Full Remote Playback Integration Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/RemotePlayback.md Combines navigator setup, device availability watching, and cast button event handling for a complete remote playback implementation. It updates the button's text and display based on connection state and user interactions. ```javascript const castButton = document.getElementById('cast-button') as HTMLButtonElement; // --- Navigator setup --- const listeners: AudioNavigatorListeners = { trackLoaded: (_media) => { /* ... */ }, positionChanged: (_locator) => { /* ... */ }, // ...other required listeners... remotePlaybackStateChanged: (state) => { castButton.dataset.state = state; castButton.textContent = state === 'connected' ? 'Casting' : state === 'connecting' ? 'Connecting…' : 'Cast'; }, }; const navigator = new AudioNavigator(publication, listeners); // --- Device availability --- avigator.remotePlayback.watchAvailability((available) => { castButton.style.display = available ? 'inline-flex' : 'none'; }).catch(() => { // Availability monitoring not supported; always show the button. castButton.style.display = 'inline-flex'; }); // --- Cast button --- castButton.addEventListener('click', async () => { if (navigator.remotePlayback.state === 'connected') { // Already casting — nothing to do; the user disconnects via the browser UI. return; } try { await navigator.remotePlayback.prompt(); } catch { // User cancelled or no devices found. } }); ``` -------------------------------- ### Install Dependencies with pnpm Source: https://github.com/readium/ts-toolkit/blob/develop/README.md Installs all project dependencies after pnpm is set up. This command initializes the monorepo workspaces. ```sh pnpm install ``` -------------------------------- ### AudioNavigator Keyboard Peripherals Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/KeyboardPeripherals.md An example configuration for AudioNavigator, defining custom keyboard shortcuts for playback control, skipping, and volume adjustment. Note that these shortcuts will prevent default browser behavior. ```javascript const configuration = { preferences: {}, defaults: {}, keyboardPeripherals: [ { type: 'toggle_playback', keyCombos: [ { keyCode: 32 }, // Space { keyCode: 75 } // k ] }, { type: 'skip_forward', keyCombos: [ { keyCode: 39 }, // ArrowRight { keyCode: 76 } // l ] }, { type: 'skip_backward', keyCombos: [ { keyCode: 37 }, // ArrowLeft { keyCode: 74 } // j ] }, { type: 'volume_up', keyCombos: [ { keyCode: 38 } // ArrowUp ] }, { type: 'volume_down', keyCombos: [ { keyCode: 40 } // ArrowDown ] } ] }; const navigator = new AudioNavigator(publication, listeners, initialPosition, configuration); ``` -------------------------------- ### Install pnpm Source: https://github.com/readium/ts-toolkit/blob/develop/README.md Installs pnpm globally using npm. This is a prerequisite for managing the monorepo. ```sh npm install -g pnpm ``` -------------------------------- ### Install Linked Dependencies Source: https://github.com/readium/ts-toolkit/blob/develop/README.md Installs dependencies after linking packages using the 'link:' syntax. Ensure you are in your project directory. ```sh # In your project directory pnpm install ``` -------------------------------- ### trackLoaded Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `trackLoaded` listener, which logs the media source and updates the play button state. ```APIDOC ## trackLoaded Fires when an audio track has finished loading and is ready to be played. ### Example ```js const listeners = { trackLoaded: function (media: HTMLMediaElement): void { console.log('Track loaded:', media.src); updatePlayButton(true); } }; ``` ``` -------------------------------- ### pause Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `pause` listener, which updates the play and pause button states when playback is paused. ```APIDOC ## pause Fires when audio playback is paused. ### Example ```js const listeners = { pause: function (locator: Locator): void { updatePlayButton(true); updatePauseButton(false); } }; ``` ``` -------------------------------- ### positionChanged Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `positionChanged` listener, which updates the UI with the current playback time and progression. ```APIDOC ## positionChanged Fires when the current playback position changes. The frequency is controlled by the `pollInterval` preference. ### Example ```js const listeners = { positionChanged: function (locator: Locator): void { const currentTime = locator.locations?.time() ?? 0; const progression = locator.locations?.progression ?? 0; updateProgress(currentTime, progression); } }; ``` ``` -------------------------------- ### error Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `error` listener, which logs errors and displays an error message to the user. ```APIDOC ## error Fires when an error occurs during audio playback, such as network issues or unsupported formats. ### Example ```js const listeners = { error: function (error: any, locator: Locator): void { console.error('Audio error:', error, 'at:', locator.href); showErrorMessage('Failed to play audio track'); } }; ``` ``` -------------------------------- ### Full Audio Navigator Listener Implementation Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md An example demonstrating the implementation of various Audio Navigator listeners to manage playback controls, display information, and handle errors. ```javascript const listeners: AudioNavigatorListeners = { trackLoaded: (media) => { console.log('Audio track ready:', media.src); document.getElementById('play-button').disabled = false; }, positionChanged: (locator) => { const currentTime = locator.locations?.time() ?? 0; const progression = locator.locations?.progression ?? 0; document.getElementById('current-time').textContent = formatTime(currentTime); (document.getElementById('progress-bar') as HTMLInputElement).value = progression.toString(); }, metadataLoaded: (metadata) => { document.getElementById('total-time').textContent = formatTime(metadata.duration); // Setup subtitle options for (let i = 0; i < metadata.textTracks.length; i++) { const track = metadata.textTracks[i]; if (track.kind === 'subtitles' || track.kind === 'captions') { addSubtitleOption(track.label, track.language); } } }, play: () => { document.getElementById('play-button').style.display = 'none'; document.getElementById('pause-button').style.display = 'block'; }, pause: () => { document.getElementById('play-button').style.display = 'block'; document.getElementById('pause-button').style.display = 'none'; }, trackEnded: () => { console.log('Track finished'); }, error: (error, locator) => { console.error('Playback error:', error); alert(`Error playing ${locator.href}: ${error.message}`); }, stalled: (isStalled) => { document.getElementById('buffering-indicator').style.display = isStalled ? 'block' : 'none'; }, seeking: (isSeeking) => { document.getElementById('seeking-indicator').style.display = isSeeking ? 'block' : 'none'; }, seekable: (seekable) => { for (let i = 0; i < seekable.length; i++) { console.log(`Seekable range ${i}: ${seekable.start(i)}s – ${seekable.end(i)}s`); } }, remotePlaybackStateChanged: (state) => { document.getElementById('cast-button').dataset.state = state; }, }; const navigator = new AudioNavigator(publication, listeners); ``` -------------------------------- ### Initialize and Control AudioNavigator Source: https://context7.com/readium/ts-toolkit/llms.txt Demonstrates the complete lifecycle of AudioNavigator, from publication loading and listener setup to playback control and cleanup. It covers building the publication, defining listeners for various playback events, resuming from a saved position, configuring playback preferences, and interacting with playback controls. ```typescript import { AudioNavigator, AudioNavigatorListeners, AudioNavigatorConfiguration } from "@readium/navigator"; import { Publication, Manifest, HttpFetcher, Link, Locator, LocatorLocations } from "@readium/shared"; // 1. Build Publication const fetcher = new HttpFetcher(undefined, "https://cdn.example.com/audiobook/"); const manifestJson = await fetcher.get(new Link({ href: "manifest.json" })).readAsJSON(); const manifest = Manifest.deserialize(manifestJson)! manifest.setSelfLink("https://cdn.example.com/audiobook/manifest.json"); const publication = new Publication({ manifest, fetcher }); // 2. Listeners const listeners: Partial = { trackLoaded: (el) => console.log("Track ready, duration:", el.duration), positionChanged: (locator) => { const t = locator.locations.fragments[0]; // e.g. "t=127.4" scrubber.value = (locator.locations.progression ?? 0) * 100; localStorage.setItem("audioPos", JSON.stringify(locator.serialize())); }, timelineItemChanged: (item) => item && (chapterTitle.textContent = item.title), play: (locator) => playBtn.textContent = "⏸", pause: (locator) => playBtn.textContent = "▶", trackEnded: (locator) => console.log("Track ended"), stalled: (isStalled) => loadingSpinner.hidden = !isStalled, seeking: (isSeeking) => {}, seekable: (ranges) => {}, error: (err, locator) => console.error("Playback error:", err), metadataLoaded: (meta) => console.log("Duration:", meta.duration, "s"), remotePlaybackStateChanged: (state) => console.log("Remote:", state) }; // 3. Resume from last position const savedJson = localStorage.getItem("audioPos"); const initialPosition = savedJson ? Locator.deserialize(JSON.parse(savedJson)) : undefined; // 4. Configuration const config: AudioNavigatorConfiguration = { preferences: { volume: 0.85, playbackRate: 1.25, preservePitch: true, skipForwardInterval: 30, skipBackwardInterval: 10, enableMediaSession: true, autoPlay: true }, defaults: {} }; // 5. Instantiate (ready immediately) const nav = new AudioNavigator(publication, listeners, initialPosition, config); // 6. Playback controls nav.play(); nav.pause(); nav.seek(120.5); // seek to 2:00.5 nav.jump(30); // skip forward 30s nav.jump(-10); // skip back 10s nav.skipForward(); // +skipForwardInterval nav.skipBackward(); // -skipBackwardInterval // 7. Track navigation nav.goForward(false, (ok) => console.log("Next track:", ok)); nav.goBackward(false, (ok) => console.log("Prev track:", ok)); // 8. Go to specific track + timestamp const trackLocator = new Locator({ href: "audio/track-03.mp3", type: "audio/mpeg", locations: new LocatorLocations({ fragments: ["t=45"], progression: 0.1 }) }); await nav.go(trackLocator, false, (ok) => {}); // 9. Timeline chapter lookup const currentChapter = nav.timeline.locate(nav.currentLocator); console.log(currentChapter?.title); // "Part II: The Chase" // 10. Remote playback (AirPlay / Chromecast) const remote = nav.remotePlayback; if (remote) await remote.prompt(); // 11. State helpers console.log(nav.isPlaying); // true console.log(nav.currentTime); // e.g. 145.3 console.log(nav.duration); // e.g. 3812.0 console.log(nav.canGoForward); // true/false nav.destroy(); ``` -------------------------------- ### trackEnded Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `trackEnded` listener, which logs the track's href and updates the UI when a track finishes playing. ```APIDOC ## trackEnded Fires when an audio track finishes playing completely. ### Example ```js const listeners = { trackEnded: function (locator: Locator): void { console.log('Track ended:', locator.href); updateUIForTrackEnd(); } }; ``` ``` -------------------------------- ### Handling Playback Start/Resume Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md The play listener is invoked when audio playback begins or resumes. This example updates the state of play and pause buttons. ```typescript const listeners = { play: function (locator: Locator): void { updatePlayButton(false); updatePauseButton(true); } }; ``` -------------------------------- ### Configure AudioNavigator with Content Protection Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/ContentProtection.md Pass a `contentProtection` object to the `AudioNavigator` constructor to enable various protection features. This example shows how to enable context menu disabling, automation checking, and developer tools monitoring. ```typescript const navigator = new AudioNavigator(publication, listeners, initialPosition, { preferences: {}, defaults: {}, contentProtection: { disableContextMenu: true, checkAutomation: true, monitorDevTools: true, protectPrinting: { disable: true }, checkIFrameEmbedding: true, disableSave: true, } }); ``` -------------------------------- ### Listening to Custom Keyboard Events Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/KeyboardPeripherals.md Handles custom keyboard events dispatched by AudioNavigator through the `peripheral` listener. This example demonstrates actions for playback, skipping, and volume control based on event types. ```javascript const listeners = { // ... other listeners peripheral: (data) => { switch (data.type) { case 'toggle_playback': navigator.isPlaying ? navigator.pause() : navigator.play(); break; case 'skip_forward': navigator.skipForward(); break; case 'skip_backward': navigator.skipBackward(); break; case 'volume_up': navigator.submitPreferences({ volume: Math.min(1, navigator.settings.volume + 0.1) }); break; case 'volume_down': navigator.submitPreferences({ volume: Math.max(0, navigator.settings.volume - 0.1) }); break; } } }; ``` -------------------------------- ### Instantiate AudioNavigator Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/AudioNavigator.md Demonstrates how to create a new instance of AudioNavigator with required and optional arguments. ```APIDOC ## Instantiate AudioNavigator ### Description To use the `AudioNavigator`, you must first instantiate it by calling its constructor and passing in the required arguments. ### Constructor Signature ```javascript new AudioNavigator(publication, listeners, initialPosition?, configuration?) ``` ### Parameters - `publication` (Publication): The audio `Publication` object. - `listeners` (object): An object that contains event listeners for the audio publication. - `initialPosition` (Locator, optional): A `Locator` object that represents the initial position in the publication. - `configuration` (object, optional): An object that contains configuration options for the audio publication. It supports `contentProtection` and `keyboardPeripherals` options. ### Example ```js const navigator = new AudioNavigator( publication, listeners, initialPosition, configuration ); ``` ``` -------------------------------- ### AudioNavigator Initialization and Basic Playback Source: https://context7.com/readium/ts-toolkit/llms.txt Demonstrates how to initialize the AudioNavigator with a publication, listeners, and configuration, and perform basic playback operations. ```APIDOC ## AudioNavigator ### Description `AudioNavigator` drives audio playback through a `WebAudioEngine`, managing multi-track audiobooks with position tracking, timeline events, Media Session API integration, remote playback (AirPlay/Chromecast), and skip intervals. Ready immediately after construction — no `load()` needed. ### Initialization 1. **Build Publication**: Create a `Publication` object from a manifest and fetcher. 2. **Define Listeners**: Set up callback functions for various playback events like `trackLoaded`, `positionChanged`, `play`, `pause`, etc. 3. **Resume from Last Position**: Optionally load the last saved playback position. 4. **Configuration**: Define playback preferences such as volume, playback rate, and skip intervals. 5. **Instantiate AudioNavigator**: Create a new `AudioNavigator` instance with the publication, listeners, initial position, and configuration. ```typescript import { AudioNavigator, AudioNavigatorListeners, AudioNavigatorConfiguration } from "@readium/navigator"; import { Publication, Manifest, HttpFetcher, Link, Locator, LocatorLocations } from "@readium/shared"; // 1. Build Publication const fetcher = new HttpFetcher(undefined, "https://cdn.example.com/audiobook/"); const manifestJson = await fetcher.get(new Link({ href: "manifest.json" })).readAsJSON(); const manifest = Manifest.deserialize(manifestJson)!; manifest.setSelfLink("https://cdn.example.com/audiobook/manifest.json"); const publication = new Publication({ manifest, fetcher }); // 2. Listeners const listeners: Partial = { trackLoaded: (el) => console.log("Track ready, duration:", el.duration), positionChanged: (locator) => { const t = locator.locations.fragments[0]; // e.g. "t=127.4" scrubber.value = (locator.locations.progression ?? 0) * 100; localStorage.setItem("audioPos", JSON.stringify(locator.serialize())); }, timelineItemChanged: (item) => item && (chapterTitle.textContent = item.title), play: (locator) => playBtn.textContent = "⏸", pause: (locator) => playBtn.textContent = "▶", trackEnded: (locator) => console.log("Track ended"), stalled: (isStalled) => loadingSpinner.hidden = !isStalled, seeking: (isSeeking) => {}, seekable: (ranges) => {}, error: (err, locator) => console.error("Playback error:", err), metadataLoaded: (meta) => console.log("Duration:", meta.duration, "s"), remotePlaybackStateChanged: (state) => console.log("Remote:", state) }; // 3. Resume from last position const savedJson = localStorage.getItem("audioPos"); const initialPosition = savedJson ? Locator.deserialize(JSON.parse(savedJson)) : undefined; // 4. Configuration const config: AudioNavigatorConfiguration = { preferences: { volume: 0.85, playbackRate: 1.25, preservePitch: true, skipForwardInterval: 30, skipBackwardInterval: 10, enableMediaSession: true, autoPlay: true }, defaults: {} }; // 5. Instantiate (ready immediately) const nav = new AudioNavigator(publication, listeners, initialPosition, config); // 6. Playback controls nav.play(); nav.pause(); nav.seek(120.5); // seek to 2:00.5 nav.jump(30); // skip forward 30s nav.jump(-10); // skip back 10s nav.skipForward(); // +skipForwardInterval nav.skipBackward(); // -skipBackwardInterval // 7. Track navigation nav.goForward(false, (ok) => console.log("Next track:", ok)); nav.goBackward(false, (ok) => console.log("Prev track:", ok)); // 8. Go to specific track + timestamp const trackLocator = new Locator({ href: "audio/track-03.mp3", type: "audio/mpeg", locations: new LocatorLocations({ fragments: ["t=45"], progression: 0.1 }) }); await nav.go(trackLocator, false, (ok) => {}); // 9. Timeline chapter lookup const currentChapter = nav.timeline.locate(nav.currentLocator); console.log(currentChapter?.title); // "Part II: The Chase" // 10. Remote playback (AirPlay / Chromecast) const remote = nav.remotePlayback; if (remote) await remote.prompt(); // 11. State helpers console.log(nav.isPlaying); // true console.log(nav.currentTime); // e.g. 145.3 console.log(nav.duration); // e.g. 3812.0 console.log(nav.canGoForward); // true/false nav.destroy(); ``` ``` -------------------------------- ### Initialize and Load EpubNavigator Source: https://context7.com/readium/ts-toolkit/llms.txt Demonstrates the complete lifecycle of EpubNavigator, from building the publication and defining listeners to configuring, loading, and navigating the EPUB. Ensure all necessary imports are included. ```typescript import { EpubNavigator, EpubNavigatorListeners, EpubNavigatorConfiguration, Locator, LocatorLocations, Link } from "@readium/navigator"; import { Publication, Manifest, HttpFetcher } from "@readium/shared"; // 1. Build the Publication const fetcher = new HttpFetcher(undefined, "https://cdn.example.com/pub/"); const manifestJson = await fetcher.get(new Link({ href: "manifest.json" })).readAsJSON(); const manifest = Manifest.deserialize(manifestJson)! manifest.setSelfLink("https://cdn.example.com/pub/manifest.json"); const publication = new Publication({ manifest, fetcher }); const positions = await publication.positionsFromManifest(); // 2. Define listeners const listeners: EpubNavigatorListeners = { frameLoaded: (wnd) => console.log("Frame ready"), positionChanged: (locator) => { localStorage.setItem("lastPosition", JSON.stringify(locator.serialize())); progressBar.value = (locator.locations.totalProgression ?? 0) * 100; }, tap: (e) => false, click: (e) => false, zoom: (scale) => {}, miscPointer: (amount) => {}, scroll: (delta) => {}, customEvent: (key, data) => {}, handleLocator: (locator) => { window.open(locator.href); return true; }, textSelected: (sel) => console.log("Selected:", sel.text), contentProtection: (type, data) => console.warn("Protection:", type, data), contextMenu: (data) => {}, peripheral: (data) => {} }; // 3. Restore last saved position const savedJson = localStorage.getItem("lastPosition"); const initialPosition = savedJson ? Locator.deserialize(JSON.parse(savedJson)) : undefined; // 4. Configure navigator const config: EpubNavigatorConfiguration = { preferences: { fontSize: 1.2, lineHeight: 1.6, scroll: false, columnCount: 2 }, defaults: { optimalLineLength: 55, minimalLineLength: 20, maximalLineLength: 70 }, contentProtection: { disableContextMenu: true, protectCopy: true } }; // 5. Instantiate and load const container = document.getElementById("reader")! const nav = new EpubNavigator(container, publication, listeners, positions, initialPosition, config); await nav.load(); // 6. Navigate nav.goForward(false, (ok) => console.log("Went forward:", ok)); nav.goBackward(false, (ok) => console.log("Went back:", ok)); nav.goLeft(false, () => {}); nav.goRight(false, () => {}); const target = new Locator({ href: "EPUB/ch05.xhtml", type: "application/xhtml+xml", locations: new LocatorLocations({ progression: 0 }) }); nav.go(target, false, (ok) => console.log("Jumped to chapter 5:", ok)); // 7. Update preferences at runtime nav.submitPreferences(new EpubPreferences({ fontSize: 1.5, textColor: "#1a1a1a", backgroundColor: "#f5f0e8" })); // 8. Use preferences editor const editor = nav.preferencesEditor; editor.fontSize.increment(); editor.scroll.toggle(); await nav.submitPreferences(editor.preferences); console.log(nav.settings.columnCount); // auto-calculated from container width // 9. State helpers console.log(nav.canGoForward); // true/false console.log(nav.canGoBackward); // true/false console.log(nav.isScrollEnd); // true at last page console.log(nav.currentLocator.locations.totalProgression); // e.g. 0.42 // 10. Destroy when done await nav.destroy(); ``` -------------------------------- ### EpubNavigator Initialization and Loading Source: https://context7.com/readium/ts-toolkit/llms.txt Demonstrates how to initialize the EpubNavigator with a publication, listeners, and configuration, followed by loading the content. ```APIDOC ## EpubNavigator Initialization and Loading ### Description This section shows the complete process of setting up and loading an EPUB publication using the `EpubNavigator`. It covers building the `Publication` object, defining necessary listeners, restoring user preferences, configuring the navigator, and finally instantiating and loading the navigator. ### Steps: 1. **Build the Publication**: Create a `Publication` object from a manifest and fetcher. 2. **Define Listeners**: Set up event handlers for various navigator events like `frameLoaded`, `positionChanged`, etc. 3. **Restore Initial Position**: Load the last saved reading position from local storage, if available. 4. **Configure Navigator**: Define user preferences and default settings for the navigator. 5. **Instantiate and Load**: Create an instance of `EpubNavigator` and call its `load()` method. ### Code Example: ```typescript import { EpubNavigator, EpubNavigatorListeners, EpubNavigatorConfiguration, Locator, LocatorLocations, Link } from "@readium/navigator"; import { Publication, Manifest, HttpFetcher } from "@readium/shared"; // 1. Build the Publication const fetcher = new HttpFetcher(undefined, "https://cdn.example.com/pub/"); const manifestJson = await fetcher.get(new Link({ href: "manifest.json" })).readAsJSON(); const manifest = Manifest.deserialize(manifestJson) as Manifest; manifest.setSelfLink("https://cdn.example.com/pub/manifest.json"); const publication = new Publication({ manifest, fetcher }); const positions = await publication.positionsFromManifest(); // 2. Define listeners const listeners: EpubNavigatorListeners = { frameLoaded: (wnd) => console.log("Frame ready"), positionChanged: (locator) => { localStorage.setItem("lastPosition", JSON.stringify(locator.serialize())); // Assume progressBar is defined elsewhere // progressBar.value = (locator.locations.totalProgression ?? 0) * 100; }, tap: (e) => false, click: (e) => false, zoom: (scale) => {}, miscPointer: (amount) => {}, scroll: (delta) => {}, customEvent: (key, data) => {}, handleLocator: (locator) => { window.open(locator.href); return true; }, textSelected: (sel) => console.log("Selected:", sel.text), contentProtection: (type, data) => console.warn("Protection:", type, data), contextMenu: (data) => {}, peripheral: (data) => {} }; // 3. Restore last saved position const savedJson = localStorage.getItem("lastPosition"); const initialPosition = savedJson ? Locator.deserialize(JSON.parse(savedJson)) : undefined; // 4. Configure navigator const config: EpubNavigatorConfiguration = { preferences: { fontSize: 1.2, lineHeight: 1.6, scroll: false, columnCount: 2 }, defaults: { optimalLineLength: 55, minimalLineLength: 20, maximalLineLength: 70 }, contentProtection: { disableContextMenu: true, protectCopy: true } }; // 5. Instantiate and load const container = document.getElementById("reader") as HTMLElement; const nav = new EpubNavigator(container, publication, listeners, positions, initialPosition, config); await nav.load(); console.log("EpubNavigator loaded successfully."); ``` ``` -------------------------------- ### Instantiate AudioNavigator Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/AudioNavigator.md Instantiate the AudioNavigator with a publication and listeners. Optional arguments include initialPosition and configuration. ```javascript const navigator = new AudioNavigator( publication, listeners, initialPosition, configuration ); ``` -------------------------------- ### Instantiate EpubNavigator Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/EpubNavigator.md Instantiate EpubNavigator with required arguments. Container, publication, and listeners are mandatory; others are optional. ```javascript const navigator = new EpubNavigator( container, publication, listeners, positions, initialPosition, configuration ); ``` -------------------------------- ### Set Initial AudioNavigator Preferences and App Defaults Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/ConfiguringAudioNavigator.md Apply user preferences and application defaults when creating a new `AudioNavigator` instance. ```javascript const navigator = new AudioNavigator( publication, listeners, initialPosition, { preferences: { volume: 0.8, playbackRate: 1.25, preservePitch: true, skipForwardInterval: 15, skipBackwardInterval: 15, autoPlay: true, enableMediaSession: true }, defaults: { volume: 1.0, playbackRate: 1.0, preservePitch: true, skipForwardInterval: 30, skipBackwardInterval: 30, pollInterval: 1000, autoPlay: true, enableMediaSession: true } } ); ``` -------------------------------- ### Locale Object Structure Example Source: https://github.com/readium/ts-toolkit/blob/develop/shared/src/publication/accessibility/Localization.md Example of a valid locale object structure, which can contain strings or nested objects with 'compact' and 'descriptive' properties. ```typescript const locale = { conformance: { aaa: "This publication conforms to WCAG 2.1 Level AAA", hazards: { none: { compact: "No hazards", descriptive: "This content is known to be free of hazards." } } } }; ``` -------------------------------- ### timelineItemChanged Listener Example Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md Example implementation for the `timelineItemChanged` listener, used to update chapter titles and navigation buttons when the active timeline item changes. ```APIDOC ## timelineItemChanged Fires when the active `TimelineItem` changes — not on every position tick, only when the item actually changes. Receives `undefined` when no item is active. This listener is optional; omitting it is equivalent to a no-op. Use this to keep chapter titles, breadcrumbs, or previous/next navigation in sync without polling. See [Timeline](./Timeline.md) for the full API. ### Example ```js const listeners = { timelineItemChanged: function (item: TimelineItem | undefined): void { chapterTitle.textContent = item?.title ?? ''; if (item) { const { previous, next } = publication.timeline.adjacentTo(item); prevButton.disabled = !previous; nextButton.disabled = !next; } } }; ``` ``` -------------------------------- ### Build Navigator Project Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/README.MD Run this command to build the navigator project. It will output index.js and index.umd.cjs files in the dist directory. ```sh pnpm run build ``` -------------------------------- ### Example TypeScript Locale Object Source: https://github.com/readium/ts-toolkit/blob/develop/shared/src/publication/accessibility/Localization.md Demonstrates how to create a custom locale object using the defined TypeScript types. This example shows nested structures for conformance and hazards, utilizing both string and object formats for localized values. ```typescript // Example usage: const customLocale = { conformance: { aaa: "This publication conforms to WCAG 2.1 Level AAA", ... }, hazards: { none: { compact: "No hazards", descriptive: "This content is known to be free of hazards." }, ... } }; ``` -------------------------------- ### Linking Item to Source Link Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/Timeline.md Get the source `Link` object from which a given TimelineItem was built. ```javascript const link = publication.timeline.linkFor(item); ``` -------------------------------- ### Set Initial Navigator Preferences and App Defaults Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/ConfiguringEpubNavigator.md Apply user preferences and application defaults when creating a new EpubNavigator instance. Defaults serve as fallback values. ```javascript const navigator = new EpubNavigator( myHTMLElement, publication, ... configuration: { preferences: { font-size: 1.5, line-height: 1.75, letter-spacing: 0.5, word-spacing: 1 }, defaults: { minimalLineLength: 20, optimalLineLength: 55, maximalLineLength: 65 } } ); ``` -------------------------------- ### Get Available and Current Locales Source: https://github.com/readium/ts-toolkit/blob/develop/shared/src/publication/accessibility/Localization.md Retrieve a list of all registered locale codes and the currently active locale code. ```typescript // Get list of available locale codes const availableLocales = Localization.getAvailableLocales(); // Returns: ['en', 'fr', 'es', ...] // Get current locale code const currentLocale = Localization.getCurrentLocale(); // Returns: 'es' ``` -------------------------------- ### EpubNavigator Constructor Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/EpubNavigator.md Instantiate the EpubNavigator by providing a container element, publication object, and event listeners. Optional arguments include positions, initial position, and configuration. ```APIDOC ## EpubNavigator Constructor ### Description Instantiates the EpubNavigator with essential parameters and optional configurations. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **container** (HTMLElement) - Required - The HTML element that will contain the EPUB publication. - **publication** (Publication) - Required - The EPUB Publication object. - **listeners** (object) - Required - An object containing event listeners for the EPUB publication. - **positions** (Array) - Optional - An array of Locator objects representing positions in the publication. - **initialPosition** (Locator) - Optional - A Locator object representing the initial position to load. - **configuration** (object) - Optional - An object containing configuration options for the publication. ### Request Example ```js const navigator = new EpubNavigator( container, publication, listeners, positions, initialPosition, configuration ); ``` ### Response None ``` -------------------------------- ### Finding Adjacent Timeline Items Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/Timeline.md Get the previous and next TimelineItems relative to a given item in the flat timeline structure. ```javascript const { previous, next } = publication.timeline.adjacentTo(current); ``` -------------------------------- ### Fetch Manifest Resource Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/HandlingPublications.md Get the manifest resource using the fetcher and a Link object. Errors are handled at the Resource level. ```javascript const manifestLink = new Link({ href: "manifest.json" }); const resource = fetcher.get(manifestLink) ``` -------------------------------- ### Create HttpFetcher Instance Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/HandlingPublications.md Instantiate HttpFetcher with a FetchImplementation and baseURL. The default FetchImplementation is window.fetch. ```javascript const fetcher = new HttpFetcher(undefined, publicationURL); ``` -------------------------------- ### Navigate Audio Publication with AudioNavigator Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/AudioNavigator.md Use go, goLink, goForward, and goBackward to navigate through audio tracks using Locators or Links. Callbacks indicate success. ```javascript const locator = new Locator({ href: 'audio/track-1.mp3', locations: new LocatorLocations({ progression: 0.5, fragments: ['t=30'] }) }); navigator.go(locator, false, (success) => { if (success) { console.log('Navigated to Track 1 at 30 seconds'); } }); const link = new Link({ href: 'audio/track-2.mp3' }); avigator.goLink(link, false, (success) => { if (success) { console.log('Navigated to Track 2'); } }); avigator.goForward(false, (success) => { if (success) { console.log('Navigated to next track'); } }); avigator.goBackward(false, (success) => { if (success) { console.log('Navigated to previous track'); } }); ``` -------------------------------- ### Define Blob-Based Injectable Resource Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/ResourceInjection.md Example of an inline stylesheet injectable using a Blob. The injector manages the blob URL lifecycle. ```typescript const blobInjectable: IBlobInjectable = { id: "inline-styles", as: "link", blob: new Blob(["body { color: red; }"], { type: "text/css" }), rel: "stylesheet", target: "head" }; ``` -------------------------------- ### Access RemotePlayback Object Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/RemotePlayback.md Get the RemotePlayback object directly from the navigator. This object is a persistent reference throughout the navigator's lifetime. ```javascript const remote = navigator.remotePlayback; ``` -------------------------------- ### Configure Content Protection and Keyboard Peripherals Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/ConfiguringAudioNavigator.md Provide `contentProtection` and `keyboardPeripherals` options during `AudioNavigator` construction. ```javascript const navigator = new AudioNavigator( publication, listeners, initialPosition, { preferences: { volume: 0.8 }, defaults: {}, contentProtection: { disableContextMenu: true, monitorDevTools: true, protectPrinting: { disable: true }, }, keyboardPeripherals: [ { type: 'toggle_playback', keyCombos: [{ keyCode: 32 }] // Space } ] } ); ``` -------------------------------- ### Handling Playback Pause Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/CustomizingListeners.md The pause listener is triggered when audio playback is intentionally paused. This example updates the state of play and pause buttons. ```typescript const listeners = { pause: function (locator: Locator): void { updatePlayButton(true); updatePauseButton(false); } }; ``` -------------------------------- ### Define URL-Based Injectable Resource Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/ResourceInjection.md Example of an external script injectable using a URL. Ensure the URL is HTTPS and listed in allowedDomains if external. ```typescript const urlInjectable: IUrlInjectable = { id: "external-script", as: "script", url: "https://cdn.example.com/script.js", target: "head" }; ``` -------------------------------- ### Get Resource Link and Convert to URL Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/HandlingPublications.md Obtain the Link object for a resource and convert its href to a URL, useful for storing publication progression. ```javascript const resourceLink = await resource.link(); const selfHref = resourceLink.toURL(publicationURL); ``` -------------------------------- ### EPUB Navigation Methods Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/EpubNavigator.md Demonstrates how to use various navigation methods of the EpubNavigator class to move through an EPUB publication. These methods include navigating to a specific locator, link, forward, backward, left, or right, with options for animation and completion callbacks. ```javascript const locator = new Locator({ href: 'epub/chapter-1.xhtml', locations: { position: 10, progression: 0.1, totalProgression: 0.032 } }); navigator.go(locator, true, () => { console.log('Navigated to Chapter 1 at position 10'); }); ``` ```javascript const link = new Link({ href: 'epub/chapter-2.xhtml' }); avigator.goLink(link, true, () => { console.log('Navigated to Chapter 2'); }); ``` ```javascript navigator.goForward(true, () => { console.log('Navigated forwards'); }); ``` ```javascript navigator.goBackward(true, () => { console.log('Navigated backwards'); }); ``` ```javascript navigator.goLeft(true, () => { console.log('Navigated to the left'); }); ``` ```javascript navigator.goRight(true, () => { console.log('Navigated to the right'); }); ``` -------------------------------- ### Get Localized String Source: https://github.com/readium/ts-toolkit/blob/develop/shared/src/publication/accessibility/Localization.md Retrieve a localized string for accessibility metadata using its key. The system will fall back to English if the string is not found in the current locale. ```typescript // Get a localized string const text = Localization.getString('conformance.aaa'); // Returns: { compact: "WCAG 2.1 Nivel AAA", descriptive: "..." } ``` -------------------------------- ### Prompting User to Connect Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/RemotePlayback.md Initiate the browser's device picker to allow the user to select a remote playback device. This method must be called in response to a user gesture. ```APIDOC ## Prompting the user to connect Call `prompt()` to show the browser's built-in device picker. It resolves when the user connects to a device, or rejects if they dismiss the dialog or no devices are available. ```js castButton.addEventListener('click', async () => { try { await navigator.remotePlayback.prompt(); // The remotePlaybackStateChanged listener handles the resulting state change. } catch { // User dismissed the picker or no devices are available. } }); ``` > `prompt()` must be called from a user gesture (click, keypress, etc.). ``` -------------------------------- ### Getting Ancestors of an Item Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/Timeline.md Retrieve the ordered ancestor path from the root to the immediate parent of a given TimelineItem. Returns an empty array if the item is at the top level. ```javascript const path = publication.timeline.ancestors(current); breadcrumb.textContent = path.map(a => a.title).join(' › '); ``` -------------------------------- ### Configure EPUB Display Settings with EpubPreferences Source: https://context7.com/readium/ts-toolkit/llms.txt Manage EPUB display settings like font size, line height, and colors using EpubPreferences. Preferences can be serialized for persistence and restored. ```typescript import { EpubNavigator, EpubPreferences } from "@readium/navigator"; // Construct explicit preferences const prefs = new EpubPreferences({ fontSize: 1.3, lineHeight: 1.7, fontFamily: "Georgia", textColor: "#2c2c2c", backgroundColor: "#faf8f2", letterSpacing: 0.05, wordSpacing: 0.1, textAlign: "justify", columnCount: 1, scroll: false, pageGutter: 16, hyphens: true, noRuby: false }); // Apply to navigator await nav.submitPreferences(prefs); // Serialize to string for persistence const saved = EpubPreferences.serialize(prefs); localStorage.setItem("epubPrefs", saved); // Restore from storage const restored = EpubPreferences.deserialize(localStorage.getItem("epubPrefs")!)!; // Use the preferences editor for a settings UI const editor = nav.preferencesEditor; // Increment/decrement ranged preferences editor.fontSize.increment(); // increases by step editor.lineHeight.decrement(); editor.wordSpacing.increment(); // Toggle boolean preferences editor.scroll.toggle(); // switch paginated ↔ scroll editor.hyphens.toggle(); // Set specific values editor.backgroundColor.value = "#000000"; editor.textColor.value = "#ffffff"; editor.textAlign.value = "left"; // Check if a preference is active (e.g., noRuby is only effective for Japanese) console.log(editor.noRuby.isEffective); // false for English publications // Merge and submit const merged = nav.settings; // current effective settings after merging prefs + defaults await nav.submitPreferences(editor.preferences); ``` -------------------------------- ### Navigation Helpers Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/epub/EpubNavigator.md Helper properties to check the current navigation status, such as whether it's possible to move forward or backward, or if the viewport is at the start or end of the content. ```APIDOC ## Helpers Finally, `EpubNavigator` provides a few helpers to help derive information about navigation: - `canGoForward`: Returns `true` if the navigator can go forward in the publication. - `canGoBackward`: Returns `true` if the navigator can go backward in the publication. - `isScrollStart`: Returns `true` if the navigator is at the start of the resources in the viewport. - `isScrollEnd`: Returns `true` if the navigator is at the end of the resources in the viewport. These can come in handy if you want to disable navigation buttons when the user is at the start or end of the publication, or show the UI if the user is scrolling to the end of the resources in the viewport. ``` -------------------------------- ### Check Navigation State with AudioNavigator Helpers Source: https://github.com/readium/ts-toolkit/blob/develop/navigator/docs/audio/AudioNavigator.md Utilize canGoForward, canGoBackward, isTrackStart, and isTrackEnd to determine navigation capabilities and current track position for UI updates. ```javascript // Update UI based on navigation state const nextButton = document.getElementById('next-button'); nextButton.disabled = !navigator.canGoForward; const prevButton = document.getElementById('prev-button'); prevButton.disabled = !navigator.canGoBackward; // Update UI based on track position if (navigator.isTrackStart) { console.log('At the beginning of the track'); } if (navigator.isTrackEnd) { console.log('At the end of the track'); } ```