@readium/navigator-html-injectables
Version:
An embeddable solution for connecting frames of HTML publications with a Readium Navigator
160 lines (137 loc) • 5.36 kB
text/typescript
import { Comms } from "../../comms/comms";
import { ReadiumWindow, EPUBReadingSystem } from "../../helpers/dom";
import { Module } from "../Module";
import { ModuleName } from "../ModuleLibrary";
export abstract class Setup extends Module {
static readonly moduleName: ModuleName = "setup";
private comms!: Comms;
wndOnErr(event: ErrorEvent) {
this.comms?.send("error", {
message: event.message,
filename: event.filename,
lineno: event.lineno,
colno: event.colno
});
}
protected unblock(wnd: ReadiumWindow) {
wnd._readium_blockEvents = false;
while(wnd._readium_blockedEvents?.length > 0) {
const x = wnd._readium_blockedEvents.shift()!;
switch(x[0]) {
case 0:
Reflect.apply(x[1], x[2], x[3]);
break;
case 1:
const ev = x[1];
const evTarget = x[2];
wnd.removeEventListener(ev.type, wnd._readium_eventBlocker, true);
const evt = new Event(ev.type, {
bubbles: ev.bubbles,
cancelable: ev.cancelable
});
if(evTarget) evTarget.dispatchEvent(evt);
else wnd.dispatchEvent(evt);
break;
}
}
}
// <audio> & <video> playback handling
private mediaPlayingCount = 0;
private onMediaPlayEvent() {
this.mediaPlayingCount++;
this.comms?.send("media_play", this.mediaPlayingCount);
}
private onMediaPauseEvent() {
if(this.mediaPlayingCount > 0) this.mediaPlayingCount--;
this.comms?.send("media_pause", this.mediaPlayingCount);
}
private pauseAllMedia(wnd: ReadiumWindow) {
const avEls = wnd.document.querySelectorAll("audio,video");
for (let i = 0; i < avEls.length; i++) {
(avEls[i] as HTMLMediaElement).pause();
}
}
private allAnimations = new Set<Animation>();
mount(wnd: ReadiumWindow, comms: Comms): boolean {
this.comms = comms;
// Track all window errors
wnd.addEventListener(
"error",
this.wndOnErr,
false
);
// Add reading system property to navigator
Reflect.defineProperty(wnd.navigator, "epubReadingSystem", {
value: {
name: "readium-ts-toolkit",
version: import.meta.env.PACKAGE_VERSION,
hasFeature: (feature: string, _version = "") => {
switch (feature) {
case "dom-manipulation":
return true;
case "layout-changes":
return true;
case "touch-events":
return true;
case "mouse-events":
return true;
case "keyboard-events":
return true;
case "spine-scripting":
return true;
case "embedded-web-content":
return true;
default:
return false;
}
}
} as EPUBReadingSystem,
writable: false
});
// Add all currently active animations and cancel them
if("getAnimations" in wnd.document) {
wnd.document.getAnimations().forEach((a) => {
a.cancel();
this.allAnimations.add(a);
});
}
comms.register("activate", Setup.moduleName, (_, ack) => {
// Restart all animations
this.allAnimations.forEach(a => {
a.cancel();
a.play();
});
ack(true);
});
comms.register("unfocus", Setup.moduleName, (_, ack) => {
// When a document is unfocused, all media is paused
this.pauseAllMedia(wnd);
// ... and all animations are paused
// They *not* cancelled because the user can see the sudden jump back to start
this.allAnimations.forEach(a => a.pause());
ack(true);
});
// Track play/pause of all <audio> and <video> elements
const avEls = wnd.document.querySelectorAll("audio,video");
for (let i = 0; i < avEls.length; i++) {
const e = avEls[i] as HTMLAudioElement | HTMLVideoElement;
e.addEventListener("play", this.onMediaPlayEvent, {
passive: true
});
e.addEventListener("pause", this.onMediaPauseEvent, {
passive: true
});
}
comms.log("Setup Mounted");
return true;
}
unmount(wnd: ReadiumWindow, comms: Comms): boolean {
wnd.removeEventListener("error", this.wndOnErr);
wnd.removeEventListener("play", this.onMediaPlayEvent);
wnd.removeEventListener("pause", this.onMediaPauseEvent);
this.allAnimations.forEach(a => a.cancel());
this.allAnimations.clear();
comms.log("Setup Unmounted");
return true;
}
}