@readium/navigator-html-injectables
Version:
An embeddable solution for connecting frames of HTML publications with a Readium Navigator
1,680 lines • 102 kB
JavaScript
const nt = Math.pow(2, 32), ge = () => Math.round(Math.random() * nt).toString(36), ot = () => `${Math.round(performance.now())}-${ge()}-${ge()}`, k = 1;
class at {
constructor(e) {
this.destination = null, this.registrar = /* @__PURE__ */ new Map(), this.origin = "", this.channelId = "", this.receiver = this.receive.bind(this), this.preLog = [], this.wnd = e, e.addEventListener("message", this.receiver);
}
receive(e) {
if (e.source === null)
throw Error("Event source is null");
if (typeof e.data != "object")
return;
const t = e.data;
if (!(!("_readium" in t) || !t._readium || t._readium <= 0)) {
if (t.key === "_ping") {
if (!this.destination) {
if (this.destination = e.source, this.origin = e.origin, this.channelId = t._channel, t._readium !== k) {
t._readium > k ? this.send("error", `received comms version ${t._readium} higher than ${k}`) : this.send("error", `received comms version ${t._readium} lower than ${k}`), this.destination = null, this.origin = "", this.channelId = "";
return;
}
this.send("_pong", void 0), this.preLog.forEach((i) => this.send("log", i)), this.preLog = [];
}
return;
} else if (this.channelId) {
if (t._channel !== this.channelId || e.origin !== this.origin)
return;
} else
return;
this.handle(t);
}
}
handle(e) {
const t = this.registrar.get(e.key);
if (!t || t.length === 0) {
e.strict && this.send("_unhandled", e);
return;
}
t.forEach((i) => i.cb(e.data, (r) => {
this.send("_ack", r, e.id);
}));
}
register(e, t, i) {
Array.isArray(e) || (e = [e]), e.forEach((r) => {
const n = this.registrar.get(r);
if (n && n.length >= 0) {
if (n.find((l) => l.module === t))
throw new Error(`Trying to register another callback for combination of event ${r} and module ${t}`);
n.push({
cb: i,
module: t
}), this.registrar.set(r, n);
} else
this.registrar.set(r, [{
cb: i,
module: t
}]);
});
}
unregister(e, t) {
Array.isArray(e) || (e = [e]), e.forEach((i) => {
const r = this.registrar.get(i);
!r || r.length === 0 || r.splice(r.findIndex((n) => n.module === t), 1);
});
}
unregisterAll(e) {
this.registrar.forEach((t, i) => this.registrar.set(i, t.filter((r) => r.module !== e)));
}
log(...e) {
this.destination ? this.send("log", e) : this.preLog.push(e);
}
get ready() {
return !!this.destination;
}
destroy() {
this.destination = null, this.channelId = "", this.preLog = [], this.registrar.clear(), this.wnd.removeEventListener("message", this.receiver);
}
send(e, t, i = void 0, r = []) {
if (!this.destination)
throw Error("Attempted to send comms message before destination has been initialized");
const n = {
_readium: k,
_channel: this.channelId,
id: i ?? ot(),
// scrict,
key: e,
data: t
};
try {
this.destination.postMessage(n, {
targetOrigin: this.origin,
transfer: r
});
} catch (o) {
if (r.length > 0)
throw o;
this.destination.postMessage(n, this.origin, r);
}
}
}
class ie {
/**
* Creates a [Encryption].
*/
constructor(e) {
this.algorithm = e.algorithm, this.compression = e.compression, this.originalLength = e.originalLength, this.profile = e.profile, this.scheme = e.scheme;
}
/**
* Parses a [Encryption] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e && e.algorithm)
return new ie({
algorithm: e.algorithm,
compression: e.compression,
originalLength: e.originalLength,
profile: e.profile,
scheme: e.scheme
});
}
/**
* Serializes a [Encryption] to its RWPM JSON representation.
*/
serialize() {
const e = { algorithm: this.algorithm };
return this.compression !== void 0 && (e.compression = this.compression), this.originalLength !== void 0 && (e.originalLength = this.originalLength), this.profile !== void 0 && (e.profile = this.profile), this.scheme !== void 0 && (e.scheme = this.scheme), e;
}
}
class w {
constructor(e) {
this.otherProperties = e;
}
get page() {
return this.otherProperties.page;
}
/**
* Creates a [Properties] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e)
return new w(e);
}
/**
* Serializes a [Properties] to its RWPM JSON representation.
*/
serialize() {
return this.otherProperties;
}
/**
* Makes a copy of this [Properties] after merging in the given additional other [properties].
*/
add(e) {
const t = Object.assign({}, this.otherProperties);
for (const i in e)
t[i] = e[i];
return new w(t);
}
}
Object.defineProperty(w.prototype, "encryption", {
get: function() {
return ie.deserialize(this.otherProperties.encrypted);
}
});
function lt(s) {
return s && s instanceof Array ? s : void 0;
}
function We(s) {
return s && typeof s == "string" ? [s] : lt(s);
}
function ye(s) {
return typeof s == "string" ? new Date(s) : void 0;
}
function j(s) {
return isNaN(s) ? void 0 : s;
}
function T(s) {
return j(s) !== void 0 && Math.sign(s) >= 0 ? s : void 0;
}
function ht(s) {
const e = new Array();
return s.forEach((t) => e.push(t)), e;
}
class a {
/** Creates a MediaType object. */
constructor(e) {
let t, i, r = e.mediaType.replace(/\s/g, "").split(";");
const n = r[0].split("/");
if (n.length === 2) {
if (t = n[0].toLowerCase().trim(), i = n[1].toLowerCase().trim(), t.length === 0 || i.length === 0)
throw new Error("Invalid media type");
} else
throw new Error("Invalid media type");
const o = {};
for (let d = 1; d < r.length; d++) {
const f = r[d].split("=");
if (f.length === 2) {
const g = f[0].toLocaleLowerCase(), m = g === "charset" ? f[1].toUpperCase() : f[1];
o[g] = m;
}
}
const l = {}, c = Object.keys(o);
c.sort((d, f) => d.localeCompare(f)), c.forEach((d) => l[d] = o[d]);
let h = "";
for (const d in l) {
const f = l[d];
h += `;${d}=${f}`;
}
const u = `${t}/${i}${h}`, p = l.encoding;
this.string = u, this.type = t, this.subtype = i, this.parameters = l, this.encoding = p, this.name = e.name, this.fileExtension = e.fileExtension;
}
static parse(e) {
return new a(e);
}
/** Structured syntax suffix, e.g. `+zip` in `application/epub+zip`.
* Gives a hint on the underlying structure of this media type.
* See. https://tools.ietf.org/html/rfc6838#section-4.2.8
*/
get structuredSyntaxSuffix() {
const e = this.subtype.split("+");
return e.length > 1 ? `+${e[e.length - 1]}` : void 0;
}
/** Parameter values might or might not be case-sensitive, depending on the semantics of
* the parameter name.
* https://tools.ietf.org/html/rfc2616#section-3.7
*
* The character set names may be up to 40 characters taken from the printable characters
* of US-ASCII. However, no distinction is made between use of upper and lower case
* letters.
* https://www.iana.org/assignments/character-sets/character-sets.xhtml
*/
get charset() {
return this.parameters.charset;
}
/** Returns whether the given `other` media type is included in this media type.
* For example, `text/html` contains `text/html;charset=utf-8`.
* - `other` must match the parameters in the `parameters` property, but extra parameters
* are ignored.
* - Order of parameters is ignored.
* - Wildcards are supported, meaning that `image/*` contains `image/png`
*/
contains(e) {
const t = typeof e == "string" ? a.parse({ mediaType: e }) : e;
if (!((this.type === "*" || this.type === t.type) && (this.subtype === "*" || this.subtype === t.subtype)))
return !1;
const i = new Set(
Object.entries(this.parameters).map(([n, o]) => `${n}=${o}`)
), r = new Set(
Object.entries(t.parameters).map(([n, o]) => `${n}=${o}`)
);
for (const n of Array.from(i.values()))
if (!r.has(n))
return !1;
return !0;
}
/** Returns whether this media type and `other` are the same, ignoring parameters that
* are not in both media types.
* For example, `text/html` matches `text/html;charset=utf-8`, but `text/html;charset=ascii`
* doesn't. This is basically like `contains`, but working in both direction.
*/
matches(e) {
const t = typeof e == "string" ? a.parse({ mediaType: e }) : e;
return this.contains(t) || t.contains(this);
}
/**
* Returns whether this media type matches any of the [others] media types.
*/
matchesAny(...e) {
for (const t of e)
if (this.matches(t))
return !0;
return !1;
}
/** Checks the MediaType equals another one (comparing their string) */
equals(e) {
return this.string === e.string;
}
/** Returns whether this media type is structured as a ZIP archive. */
get isZIP() {
return this.matchesAny(
a.ZIP,
a.LCP_PROTECTED_AUDIOBOOK,
a.LCP_PROTECTED_PDF
) || this.structuredSyntaxSuffix === "+zip";
}
/** Returns whether this media type is structured as a JSON file. */
get isJSON() {
return this.matchesAny(a.JSON) || this.structuredSyntaxSuffix === "+json";
}
/** Returns whether this media type is of an OPDS feed. */
get isOPDS() {
return this.matchesAny(
a.OPDS1,
a.OPDS1_ENTRY,
a.OPDS2,
a.OPDS2_PUBLICATION,
a.OPDS_AUTHENTICATION
) || this.structuredSyntaxSuffix === "+json";
}
/** Returns whether this media type is of an HTML document. */
get isHTML() {
return this.matchesAny(a.HTML, a.XHTML);
}
/** Returns whether this media type is of a bitmap image, so excluding vectorial formats. */
get isBitmap() {
return this.matchesAny(
a.BMP,
a.GIF,
a.JPEG,
a.PNG,
a.TIFF,
a.WEBP
);
}
/** Returns whether this media type is of an audio clip. */
get isAudio() {
return this.type === "audio";
}
/** Returns whether this media type is of a video clip. */
get isVideo() {
return this.type === "video";
}
/** Returns whether this media type is of a Readium Web Publication Manifest. */
get isRWPM() {
return this.matchesAny(
a.READIUM_AUDIOBOOK_MANIFEST,
a.DIVINA_MANIFEST,
a.READIUM_WEBPUB_MANIFEST
);
}
/** Returns whether this media type is of a publication file. */
get isPublication() {
return this.matchesAny(
a.READIUM_AUDIOBOOK,
a.READIUM_AUDIOBOOK_MANIFEST,
a.CBZ,
a.DIVINA,
a.DIVINA_MANIFEST,
a.EPUB,
a.LCP_PROTECTED_AUDIOBOOK,
a.LCP_PROTECTED_PDF,
a.LPF,
a.PDF,
a.W3C_WPUB_MANIFEST,
a.READIUM_WEBPUB,
a.READIUM_WEBPUB_MANIFEST,
a.ZAB
);
}
// Known Media Types
static get AAC() {
return a.parse({ mediaType: "audio/aac", fileExtension: "aac" });
}
static get ACSM() {
return a.parse({
mediaType: "application/vnd.adobe.adept+xml",
name: "Adobe Content Server Message",
fileExtension: "acsm"
});
}
static get AIFF() {
return a.parse({ mediaType: "audio/aiff", fileExtension: "aiff" });
}
static get AVI() {
return a.parse({
mediaType: "video/x-msvideo",
fileExtension: "avi"
});
}
static get BINARY() {
return a.parse({ mediaType: "application/octet-stream" });
}
static get BMP() {
return a.parse({ mediaType: "image/bmp", fileExtension: "bmp" });
}
static get CBZ() {
return a.parse({
mediaType: "application/vnd.comicbook+zip",
name: "Comic Book Archive",
fileExtension: "cbz"
});
}
static get CSS() {
return a.parse({ mediaType: "text/css", fileExtension: "css" });
}
static get DIVINA() {
return a.parse({
mediaType: "application/divina+zip",
name: "Digital Visual Narratives",
fileExtension: "divina"
});
}
static get DIVINA_MANIFEST() {
return a.parse({
mediaType: "application/divina+json",
name: "Digital Visual Narratives",
fileExtension: "json"
});
}
static get EPUB() {
return a.parse({
mediaType: "application/epub+zip",
name: "EPUB",
fileExtension: "epub"
});
}
static get GIF() {
return a.parse({ mediaType: "image/gif", fileExtension: "gif" });
}
static get GZ() {
return a.parse({
mediaType: "application/gzip",
fileExtension: "gz"
});
}
static get HTML() {
return a.parse({ mediaType: "text/html", fileExtension: "html" });
}
static get JAVASCRIPT() {
return a.parse({
mediaType: "text/javascript",
fileExtension: "js"
});
}
static get JPEG() {
return a.parse({ mediaType: "image/jpeg", fileExtension: "jpeg" });
}
static get JSON() {
return a.parse({ mediaType: "application/json" });
}
static get LCP_LICENSE_DOCUMENT() {
return a.parse({
mediaType: "application/vnd.readium.lcp.license.v1.0+json",
name: "LCP License",
fileExtension: "lcpl"
});
}
static get LCP_PROTECTED_AUDIOBOOK() {
return a.parse({
mediaType: "application/audiobook+lcp",
name: "LCP Protected Audiobook",
fileExtension: "lcpa"
});
}
static get LCP_PROTECTED_PDF() {
return a.parse({
mediaType: "application/pdf+lcp",
name: "LCP Protected PDF",
fileExtension: "lcpdf"
});
}
static get LCP_STATUS_DOCUMENT() {
return a.parse({
mediaType: "application/vnd.readium.license.status.v1.0+json"
});
}
static get LPF() {
return a.parse({
mediaType: "application/lpf+zip",
fileExtension: "lpf"
});
}
static get MP3() {
return a.parse({ mediaType: "audio/mpeg", fileExtension: "mp3" });
}
static get MPEG() {
return a.parse({ mediaType: "video/mpeg", fileExtension: "mpeg" });
}
static get NCX() {
return a.parse({
mediaType: "application/x-dtbncx+xml",
fileExtension: "ncx"
});
}
static get OGG() {
return a.parse({ mediaType: "audio/ogg", fileExtension: "oga" });
}
static get OGV() {
return a.parse({ mediaType: "video/ogg", fileExtension: "ogv" });
}
static get OPDS1() {
return a.parse({
mediaType: "application/atom+xml;profile=opds-catalog"
});
}
static get OPDS1_ENTRY() {
return a.parse({
mediaType: "application/atom+xml;type=entry;profile=opds-catalog"
});
}
static get OPDS2() {
return a.parse({ mediaType: "application/opds+json" });
}
static get OPDS2_PUBLICATION() {
return a.parse({ mediaType: "application/opds-publication+json" });
}
static get OPDS_AUTHENTICATION() {
return a.parse({
mediaType: "application/opds-authentication+json"
});
}
static get OPUS() {
return a.parse({ mediaType: "audio/opus", fileExtension: "opus" });
}
static get OTF() {
return a.parse({ mediaType: "font/otf", fileExtension: "otf" });
}
static get PDF() {
return a.parse({
mediaType: "application/pdf",
name: "PDF",
fileExtension: "pdf"
});
}
static get PNG() {
return a.parse({ mediaType: "image/png", fileExtension: "png" });
}
static get READIUM_AUDIOBOOK() {
return a.parse({
mediaType: "application/audiobook+zip",
name: "Readium Audiobook",
fileExtension: "audiobook"
});
}
static get READIUM_AUDIOBOOK_MANIFEST() {
return a.parse({
mediaType: "application/audiobook+json",
name: "Readium Audiobook",
fileExtension: "json"
});
}
static get READIUM_CONTENT_DOCUMENT() {
return a.parse({
mediaType: "application/vnd.readium.content+json",
name: "Readium Content Document",
fileExtension: "json"
});
}
static get READIUM_GUIDED_NAVIGATION_DOCUMENT() {
return a.parse({
mediaType: "application/guided-navigation+json",
name: "Readium Guided Navigation Document",
fileExtension: "json"
});
}
static get READIUM_POSITION_LIST() {
return a.parse({
mediaType: "application/vnd.readium.position-list+json",
name: "Readium Position List",
fileExtension: "json"
});
}
static get READIUM_WEBPUB() {
return a.parse({
mediaType: "application/webpub+zip",
name: "Readium Web Publication",
fileExtension: "webpub"
});
}
static get READIUM_WEBPUB_MANIFEST() {
return a.parse({
mediaType: "application/webpub+json",
name: "Readium Web Publication",
fileExtension: "json"
});
}
static get SMIL() {
return a.parse({
mediaType: "application/smil+xml",
fileExtension: "smil"
});
}
static get SVG() {
return a.parse({
mediaType: "image/svg+xml",
fileExtension: "svg"
});
}
static get TEXT() {
return a.parse({ mediaType: "text/plain", fileExtension: "txt" });
}
static get TIFF() {
return a.parse({ mediaType: "image/tiff", fileExtension: "tiff" });
}
static get TTF() {
return a.parse({ mediaType: "font/ttf", fileExtension: "ttf" });
}
static get W3C_WPUB_MANIFEST() {
return a.parse({
mediaType: "application/x.readium.w3c.wpub+json",
name: "Web Publication",
fileExtension: "json"
});
}
static get WAV() {
return a.parse({ mediaType: "audio/wav", fileExtension: "wav" });
}
static get WEBM_AUDIO() {
return a.parse({ mediaType: "audio/webm", fileExtension: "webm" });
}
static get WEBM_VIDEO() {
return a.parse({ mediaType: "video/webm", fileExtension: "webm" });
}
static get WEBP() {
return a.parse({ mediaType: "image/webp", fileExtension: "webp" });
}
static get WOFF() {
return a.parse({ mediaType: "font/woff", fileExtension: "woff" });
}
static get WOFF2() {
return a.parse({ mediaType: "font/woff2", fileExtension: "woff2" });
}
static get XHTML() {
return a.parse({
mediaType: "application/xhtml+xml",
fileExtension: "xhtml"
});
}
static get XML() {
return a.parse({
mediaType: "application/xml",
fileExtension: "xml"
});
}
static get ZAB() {
return a.parse({
mediaType: "application/x.readium.zab+zip",
name: "Zipped Audio Book",
fileExtension: "zab"
});
}
static get ZIP() {
return a.parse({
mediaType: "application/zip",
fileExtension: "zip"
});
}
}
class ve {
constructor(e) {
this.uri = e, this.parameters = this.getParameters(e);
}
/**
* List of URI template parameter keys, if the [Link] is templated.
*/
getParameters(e) {
const t = /\{\??([^}]+)\}/g, i = e.match(t);
return i ? new Set(
i.join(",").replace(t, "$1").split(",").map((r) => r.trim())
) : /* @__PURE__ */ new Set();
}
/** Expands the URI by replacing the template variables by the given parameters.
* Any extra parameter is appended as query parameters.
* See RFC 6570 on URI template: https://tools.ietf.org/html/rfc6570
*/
expand(e) {
const t = (r) => r.split(",").map((n) => {
const o = e[n];
return o ? encodeURIComponent(o) : "";
}).join(","), i = (r) => "?" + r.split(",").map((n) => {
const o = n.split("=")[0], l = e[o];
return l ? `${o}=${encodeURIComponent(l)}` : "";
}).join("&");
return this.uri.replace(/\{(\??)([^}]+)\}/g, (...r) => r[1] ? i(r[2]) : t(r[2]));
}
}
class E {
/**
* Creates a [Locations].
*/
constructor(e) {
this.fragments = e.fragments ? e.fragments : new Array(), this.progression = e.progression, this.totalProgression = e.totalProgression, this.position = e.position, this.otherLocations = e.otherLocations;
}
/**
* Parses a [Locations] from its RWPM JSON representation.
*/
static deserialize(e) {
if (!e)
return;
const t = j(e.progression), i = j(e.totalProgression), r = j(e.position), n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set([
"fragment",
"fragments",
"progression",
"totalProgression",
"position"
]);
return Object.entries(e).forEach(([l, c]) => {
o.has(l) || n.set(l, c);
}), new E({
fragments: We(e.fragments || e.fragment),
progression: t !== void 0 && t >= 0 && t <= 1 ? t : void 0,
totalProgression: i !== void 0 && i >= 0 && i <= 1 ? i : void 0,
position: r !== void 0 && r > 0 ? r : void 0,
otherLocations: n.size === 0 ? void 0 : n
});
}
/**
* Serializes a [Locations] to its RWPM JSON representation.
*/
serialize() {
const e = {};
return this.fragments && (e.fragments = this.fragments), this.progression !== void 0 && (e.progression = this.progression), this.totalProgression !== void 0 && (e.totalProgression = this.totalProgression), this.position !== void 0 && (e.position = this.position), this.otherLocations && this.otherLocations.forEach((t, i) => e[i] = t), e;
}
}
class U {
/**
* Creates a [Text].
*/
constructor(e) {
this.after = e.after, this.before = e.before, this.highlight = e.highlight;
}
/**
* Parses a [Locations] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e)
return new U({
after: e.after,
before: e.before,
highlight: e.highlight
});
}
/**
* Serializes a [Locations] to its RWPM JSON representation.
*/
serialize() {
const e = {};
return this.after !== void 0 && (e.after = this.after), this.before !== void 0 && (e.before = this.before), this.highlight !== void 0 && (e.highlight = this.highlight), e;
}
}
class D {
/**
* Creates a [Locator].
*/
constructor(e) {
this.href = e.href, this.type = e.type, this.title = e.title, this.locations = e.locations ? e.locations : new E({}), this.text = e.text;
}
/**
* Parses a [Link] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e && e.href && e.type)
return new D({
href: e.href,
type: e.type,
title: e.title,
locations: E.deserialize(e.locations),
text: U.deserialize(e.text)
});
}
/**
* Serializes a [Link] to its RWPM JSON representation.
*/
serialize() {
const e = { href: this.href, type: this.type };
return this.title !== void 0 && (e.title = this.title), this.locations && (e.locations = this.locations.serialize()), this.text && (e.text = this.text.serialize()), e;
}
/**
* Shortcut to get a copy of the [Locator] with different [Locations] sub-properties.
*/
copyWithLocations(e) {
return new D({
href: this.href,
type: this.type,
title: this.title,
text: this.text,
locations: new E({ ...this.locations, ...e })
});
}
}
class B {
/**
* Creates a [Link].
*/
constructor(e) {
this.href = e.href, this.templated = e.templated, this.type = e.type, this.title = e.title, this.rels = e.rels, this.properties = e.properties, this.height = e.height, this.width = e.width, this.size = e.size, this.duration = e.duration, this.bitrate = e.bitrate, this.languages = e.languages, this.alternates = e.alternates, this.children = e.children;
}
/**
* Parses a [Link] from its RWPM JSON representation.
*/
static deserialize(e) {
if (!(!e || typeof e.href != "string"))
return new B({
href: e.href,
templated: e.templated,
type: e.type,
title: e.title,
rels: e.rel ? e.rel instanceof Array ? new Set(e.rel) : /* @__PURE__ */ new Set([e.rel]) : void 0,
properties: w.deserialize(e.properties),
height: T(e.height),
width: T(e.width),
size: T(e.size),
duration: T(e.duration),
bitrate: T(e.bitrate),
languages: We(e.language),
alternates: V.deserialize(e.alternate),
children: V.deserialize(e.children)
});
}
/**
* Serializes a [Link] to its RWPM JSON representation.
*/
serialize() {
const e = { href: this.href };
return this.templated !== void 0 && (e.templated = this.templated), this.type !== void 0 && (e.type = this.type), this.title !== void 0 && (e.title = this.title), this.rels && (e.rel = ht(this.rels)), this.properties && (e.properties = this.properties.serialize()), this.height !== void 0 && (e.height = this.height), this.width !== void 0 && (e.width = this.width), this.size !== void 0 && (e.size = this.size), this.duration !== void 0 && (e.duration = this.duration), this.bitrate !== void 0 && (e.bitrate = this.bitrate), this.languages && (e.language = this.languages), this.alternates && (e.alternate = this.alternates.serialize()), this.children && (e.children = this.children.serialize()), e;
}
/** MediaType of the linked resource. */
get mediaType() {
return this.type !== void 0 ? a.parse({ mediaType: this.type }) : a.BINARY;
}
/** Computes an absolute URL to the link, relative to the given `baseURL`.
* If the link's `href` is already absolute, the `baseURL` is ignored.
*/
toURL(e) {
const t = this.href.replace(/^(\/)/, "");
if (t.length === 0)
return;
let i = e || "/";
return i.startsWith("/") && (i = "file://" + i), new URL(t, i).href.replace(/^(file:\/\/)/, "");
}
/** List of URI template parameter keys, if the `Link` is templated. */
get templateParameters() {
return this.templated ? new ve(this.href).parameters : /* @__PURE__ */ new Set();
}
/** Expands the `Link`'s HREF by replacing URI template variables by the given parameters.
* See RFC 6570 on URI template: https://tools.ietf.org/html/rfc6570
*/
expandTemplate(e) {
return new B({
href: new ve(this.href).expand(e),
templated: !1
});
}
/**
* Makes a copy of this [Link] after merging in the given additional other [properties].
*/
addProperties(e) {
var t;
const i = B.deserialize(this.serialize());
return i.properties = i.properties ? (t = i.properties) == null ? void 0 : t.add(e) : new w(e), i;
}
/**
* Creates a [Locator] from a reading order [Link].
*/
get locator() {
let e = this.href.split("#");
return new D({
href: e.length > 0 && e[0] !== void 0 ? e[0] : this.href,
type: this.type ?? "",
title: this.title,
locations: new E({
fragments: e.length > 1 && e[1] !== void 0 ? [e[1]] : []
})
});
}
}
class V {
/**
* Creates a [Links].
*/
constructor(e) {
this.items = e;
}
/**
* Creates a list of [Link] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e && e instanceof Array)
return new V(
e.map((t) => B.deserialize(t)).filter((t) => t !== void 0)
);
}
/**
* Serializes an array of [Link] to its RWPM JSON representation.
*/
serialize() {
return this.items.map((e) => e.serialize());
}
/** Finds the first link with the given relation. */
findWithRel(e) {
const t = (i) => i.rels && i.rels.has(e);
return this.items.find(t);
}
/** Finds all the links with the given relation. */
filterByRel(e) {
const t = (i) => i.rels && i.rels.has(e);
return this.items.filter(t);
}
/** Finds the first link matching the given HREF. */
findWithHref(e) {
const t = (i) => i.href === e;
return this.items.find(t);
}
/** Finds the index of the first link matching the given HREF. */
findIndexWithHref(e) {
const t = (i) => i.href === e;
return this.items.findIndex(t);
}
/** Finds the first link matching the given media type. */
findWithMediaType(e) {
const t = (i) => i.mediaType.matches(e);
return this.items.find(t);
}
/** Finds all the links matching the given media type. */
filterByMediaType(e) {
const t = (i) => i.mediaType.matches(e);
return this.items.filter(t);
}
/** Finds all the links matching any of the given media types. */
filterByMediaTypes(e) {
const t = (i) => {
for (const r of e)
if (i.mediaType.matches(r))
return !0;
return !1;
};
return this.items.filter(t);
}
/** Returns whether all the resources in the collection are audio clips. */
everyIsAudio() {
const e = (t) => t.mediaType.isAudio;
return this.items.length > 0 && this.items.every(e);
}
/** Returns whether all the resources in the collection are bitmaps. */
everyIsBitmap() {
const e = (t) => t.mediaType.isBitmap;
return this.items.length > 0 && this.items.every(e);
}
/** Returns whether all the resources in the collection are HTML documents. */
everyIsHTML() {
const e = (t) => t.mediaType.isHTML;
return this.items.length > 0 && this.items.every(e);
}
/** Returns whether all the resources in the collection are video clips. */
everyIsVideo() {
const e = (t) => t.mediaType.isVideo;
return this.items.length > 0 && this.items.every(e);
}
/** Returns whether all the resources in the collection are matching any of the given media types. */
everyMatchesMediaType(e) {
return Array.isArray(e) ? this.items.length > 0 && this.items.every((t) => {
for (const i of e)
return t.mediaType.matches(i);
return !1;
}) : this.items.length > 0 && this.items.every((t) => t.mediaType.matches(e));
}
filterLinksHasType() {
return this.items.filter((e) => e.type);
}
}
w.prototype.getContains = function() {
return new Set(this.otherProperties.contains || []);
};
class q {
/**
* Creates a [DomRange].
*/
constructor(e) {
this.cssSelector = e.cssSelector, this.textNodeIndex = e.textNodeIndex, this.charOffset = e.charOffset;
}
/**
* Parses a [DomRangePoint] from its RWPM JSON representation.
*/
static deserialize(e) {
if (!(e && e.cssSelector))
return;
let t = T(e.textNodeIndex);
if (t === void 0)
return;
let i = T(e.charOffset);
return i === void 0 && (i = T(e.offset)), new q({
cssSelector: e.cssSelector,
textNodeIndex: t,
charOffset: i
});
}
/**
* Serializes a [DomRangePoint] to its RWPM JSON representation.
*/
serialize() {
const e = {
cssSelector: this.cssSelector,
textNodeIndex: this.textNodeIndex
};
return this.charOffset !== void 0 && (e.charOffset = this.charOffset), e;
}
}
class se {
/**
* Creates a [DomRange].
*/
constructor(e) {
this.start = e.start, this.end = e.end;
}
/**
* Parses a [DomRange] from its RWPM JSON representation.
*/
static deserialize(e) {
if (!e)
return;
let t = q.deserialize(e.start);
if (t)
return new se({
start: t,
end: q.deserialize(e.end)
});
}
/**
* Serializes a [DomRange] to its RWPM JSON representation.
*/
serialize() {
const e = { start: this.start.serialize() };
return this.end && (e.end = this.end.serialize()), e;
}
}
E.prototype.getCssSelector = function() {
var s;
return (s = this.otherLocations) == null ? void 0 : s.get("cssSelector");
};
E.prototype.getPartialCfi = function() {
var s;
return (s = this.otherLocations) == null ? void 0 : s.get("partialCfi");
};
E.prototype.getDomRange = function() {
var s;
return se.deserialize((s = this.otherLocations) == null ? void 0 : s.get("domRange"));
};
E.prototype.fragmentParameters = function() {
return new Map(
this.fragments.map((s) => s.startsWith("#") ? s.slice(1) : s).join("&").split("&").filter((s) => !s.startsWith("#")).map((s) => s.split("=")).filter((s) => s.length === 2).map((s) => [
s[0].trim().toLowerCase(),
s[1].trim()
])
);
};
E.prototype.htmlId = function() {
if (!this.fragments.length)
return;
let s = this.fragments.find((e) => e.length && !e.includes("="));
if (!s) {
const e = this.fragmentParameters();
e.has("id") ? s = e.get("id") : e.has("name") && (s = e.get("name"));
}
return s != null && s.startsWith("#") ? s.slice(1) : s;
};
E.prototype.page = function() {
const s = parseInt(this.fragmentParameters().get("page"));
if (!isNaN(s) && s >= 0)
return s;
};
E.prototype.time = function() {
const s = parseInt(this.fragmentParameters().get("t"));
if (!isNaN(s))
return s;
};
E.prototype.space = function() {
const s = this.fragmentParameters();
if (!s.has("xywh"))
return;
const e = s.get("xywh").split(",").map((t) => parseInt(t));
if (e.length === 4 && !e.some(isNaN))
return e;
};
class re {
/** Creates a [Price]. */
constructor(e) {
this.currency = e.currency, this.value = e.value;
}
/**
* Parses a [Price] from its RWPM JSON representation.
*/
static deserialize(e) {
if (!e)
return;
let t = e.currency;
if (!(t && typeof t == "string" && t.length > 0))
return;
let i = T(e.value);
if (i !== void 0)
return new re({ currency: t, value: i });
}
/**
* Serializes a [Price] to its RWPM JSON representation.
*/
serialize() {
return { currency: this.currency, value: this.value };
}
}
class $ {
/** Creates a [Acquisition]. */
constructor(e) {
this.type = e.type, this.children = e.children;
}
/**
* Parses a [Acquisition] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e && e.type)
return new $({
type: e.type,
children: $.deserializeArray(e.children)
});
}
static deserializeArray(e) {
if (e instanceof Array)
return e.map((t) => $.deserialize(t)).filter((t) => t !== void 0);
}
/**
* Serializes a [Acquisition] to its RWPM JSON representation.
*/
serialize() {
const e = { type: this.type };
return this.children && (e.children = this.children.map((t) => t.serialize())), e;
}
}
class ne {
/** Creates a [Price]. */
constructor(e) {
this.total = e.total, this.position = e.position;
}
/**
* Parses a [Holds] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e)
return new ne({
total: T(e.total),
position: T(e.position)
});
}
/**
* Serializes a [Holds] to its RWPM JSON representation.
*/
serialize() {
const e = {};
return this.total !== void 0 && (e.total = this.total), this.position !== void 0 && (e.position = this.position), e;
}
}
class oe {
/** Creates a [Copies]. */
constructor(e) {
this.total = e.total, this.available = e.available;
}
/**
* Parses a [Copies] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e)
return new oe({
total: T(e.total),
available: T(e.available)
});
}
/**
* Serializes a [Copies] to its RWPM JSON representation.
*/
serialize() {
const e = {};
return this.total !== void 0 && (e.total = this.total), this.available !== void 0 && (e.available = this.available), e;
}
}
class ae {
/** Creates a [Availability]. */
constructor(e) {
this.state = e.state, this.since = e.since, this.until = e.until;
}
/**
* Parses a [Availability] from its RWPM JSON representation.
*/
static deserialize(e) {
if (e && e.state)
return new ae({
state: e.state,
since: ye(e.since),
until: ye(e.until)
});
}
/**
* Serializes a [Availability] to its RWPM JSON representation.
*/
serialize() {
const e = { state: this.state };
return this.since !== void 0 && (e.since = this.since.toISOString()), this.until !== void 0 && (e.until = this.until.toISOString()), e;
}
}
w.prototype.getNumberOfItems = function() {
return T(this.otherProperties.numberOfItems);
};
w.prototype.getPrice = function() {
return re.deserialize(this.otherProperties.price);
};
w.prototype.getIndirectAcquisitions = function() {
const s = this.otherProperties.indirectAcquisition;
if (s && s instanceof Array)
return s.map((e) => $.deserialize(e)).filter((e) => e !== void 0);
};
w.prototype.getHolds = function() {
return ne.deserialize(this.otherProperties.holds);
};
w.prototype.getCopies = function() {
return oe.deserialize(this.otherProperties.copies);
};
w.prototype.getAvailability = function() {
return ae.deserialize(this.otherProperties.availability);
};
w.prototype.getAuthenticate = function() {
return B.deserialize(this.otherProperties.authenticate);
};
var R;
(function(s) {
s.NONE = "none", s.DESCENDANT = "descendant", s.CHILD = "child";
})(R || (R = {}));
var N;
(function(s) {
s.id = "id", s.class = "class", s.tag = "tag", s.attribute = "attribute", s.nthchild = "nthchild", s.nthoftype = "nthoftype";
})(N || (N = {}));
const ct = "CssSelectorGenerator";
function be(s = "unknown problem", ...e) {
console.warn(`${ct}: ${s}`, ...e);
}
N.id, N.class, N.tag, N.attribute, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY;
function ut(s) {
return s instanceof RegExp;
}
function dt(s) {
return s.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".+");
}
function pt(s) {
const e = s.map((t) => {
if (ut(t))
return (i) => t.test(i);
if (typeof t == "function")
return (i) => {
const r = t(i);
return typeof r != "boolean" ? (be("pattern matcher function invalid", "Provided pattern matching function does not return boolean. It's result will be ignored.", t), !1) : r;
};
if (typeof t == "string") {
const i = new RegExp("^" + dt(t) + "$");
return (r) => i.test(r);
}
return be("pattern matcher invalid", "Pattern matching only accepts strings, regular expressions and/or functions. This item is invalid and will be ignored.", t), () => !1;
});
return (t) => e.some((i) => i(t));
}
const ft = "", mt = " > ", gt = " ";
R.NONE + "", R.NONE, R.DESCENDANT + "", R.DESCENDANT, R.CHILD + "", R.CHILD;
N.nthoftype, N.tag, N.id, N.class, N.attribute, N.nthchild;
pt([
"class",
"id",
// Angular attributes
"ng-*"
]);
class Y {
}
function Ee(s) {
return s.split("").reverse().join("");
}
function yt(s, e, t) {
const i = Ee(e);
return t.map((r) => {
const n = Math.max(0, r.end - e.length - r.errors), o = Ee(s.slice(n, r.end));
return {
start: Fe(o, i, r.errors).reduce((c, h) => r.end - h.end < c ? r.end - h.end : c, r.end),
end: r.end,
errors: r.errors
};
});
}
function Z(s) {
return (s | -s) >> 31 & 1;
}
function xe(s, e, t, i) {
let r = s.P[t], n = s.M[t];
const o = i >>> 31, l = e[t] | o, c = l | n, h = (l & r) + r ^ r | l;
let u = n | ~(h | r), p = r & h;
const d = Z(u & s.lastRowMask[t]) - Z(p & s.lastRowMask[t]);
return u <<= 1, p <<= 1, p |= o, u |= Z(i) - o, r = p | ~(c | u), n = u & c, s.P[t] = r, s.M[t] = n, d;
}
function Fe(s, e, t) {
if (e.length === 0)
return [];
t = Math.min(t, e.length);
const i = [], r = 32, n = Math.ceil(e.length / r) - 1, o = {
P: new Uint32Array(n + 1),
M: new Uint32Array(n + 1),
lastRowMask: new Uint32Array(n + 1)
};
o.lastRowMask.fill(1 << 31), o.lastRowMask[n] = 1 << (e.length - 1) % r;
const l = new Uint32Array(n + 1), c = /* @__PURE__ */ new Map(), h = [];
for (let d = 0; d < 256; d++)
h.push(l);
for (let d = 0; d < e.length; d += 1) {
const f = e.charCodeAt(d);
if (c.has(f))
continue;
const g = new Uint32Array(n + 1);
c.set(f, g), f < h.length && (h[f] = g);
for (let m = 0; m <= n; m += 1) {
g[m] = 0;
for (let v = 0; v < r; v += 1) {
const x = m * r + v;
if (x >= e.length)
continue;
e.charCodeAt(x) === f && (g[m] |= 1 << v);
}
}
}
let u = Math.max(0, Math.ceil(t / r) - 1);
const p = new Uint32Array(n + 1);
for (let d = 0; d <= u; d += 1)
p[d] = (d + 1) * r;
p[n] = e.length;
for (let d = 0; d <= u; d += 1)
o.P[d] = -1, o.M[d] = 0;
for (let d = 0; d < s.length; d += 1) {
const f = s.charCodeAt(d);
let g;
f < h.length ? g = h[f] : (g = c.get(f), typeof g > "u" && (g = l));
let m = 0;
for (let v = 0; v <= u; v += 1)
m = xe(o, g, v, m), p[v] += m;
if (p[u] - m <= t && u < n && (g[u + 1] & 1 || m < 0)) {
u += 1, o.P[u] = -1, o.M[u] = 0;
let v;
if (u === n) {
const x = e.length % r;
v = x === 0 ? r : x;
} else
v = r;
p[u] = p[u - 1] + v - m + xe(o, g, u, m);
} else
for (; u > 0 && p[u] >= t + r; )
u -= 1;
u === n && p[u] <= t && (p[u] < t && i.splice(0, i.length), i.push({
start: -1,
end: d + 1,
errors: p[u]
}), t = p[u]);
}
return i;
}
function vt(s, e, t) {
const i = Fe(s, e, t);
return yt(s, e, i);
}
function Be(s, e, t) {
let i = 0;
const r = [];
for (; i !== -1; )
i = s.indexOf(e, i), i !== -1 && (r.push({
start: i,
end: i + e.length,
errors: 0
}), i += 1);
return r.length > 0 ? r : vt(s, e, t);
}
function Se(s, e) {
return e.length === 0 || s.length === 0 ? 0 : 1 - Be(s, e, e.length)[0].errors / e.length;
}
function bt(s, e, t = {}) {
if (e.length === 0)
return null;
const i = Math.min(256, e.length / 2), r = Be(s, e, i);
if (r.length === 0)
return null;
const n = (l) => {
const d = 1 - l.errors / e.length, f = t.prefix ? Se(
s.slice(
Math.max(0, l.start - t.prefix.length),
l.start
),
t.prefix
) : 1, g = t.suffix ? Se(
s.slice(l.end, l.end + t.suffix.length),
t.suffix
) : 1;
let m = 1;
typeof t.hint == "number" && (m = 1 - Math.abs(l.start - t.hint) / s.length);
const v = 50 * d + 20 * f + 20 * g + 2 * m, x = 50 + 20 + 20 + 2;
return v / x;
}, o = r.map((l) => ({
start: l.start,
end: l.end,
score: n(l)
}));
return o.sort((l, c) => c.score - l.score), o[0];
}
function J(s, e, t) {
const i = t === 1 ? e : e - 1;
if (s.charAt(i).trim() !== "")
return e;
let r, n;
if (t === 2 ? (r = s.substring(0, e), n = r.trimEnd()) : (r = s.substring(e), n = r.trimStart()), !n.length)
return -1;
const o = r.length - n.length;
return t === 2 ? e - o : e + o;
}
function Te(s, e) {
const t = s.commonAncestorContainer.ownerDocument.createNodeIterator(
s.commonAncestorContainer,
NodeFilter.SHOW_TEXT
), i = e === 1 ? s.startContainer : s.endContainer, r = e === 1 ? s.endContainer : s.startContainer;
let n = t.nextNode();
for (; n && n !== i; )
n = t.nextNode();
e === 2 && (n = t.previousNode());
let o = -1;
const l = () => {
if (n = e === 1 ? t.nextNode() : t.previousNode(), n) {
const c = n.textContent, h = e === 1 ? 0 : c.length;
o = J(c, h, e);
}
};
for (; n && o === -1 && n !== r; )
l();
if (n && o >= 0)
return { node: n, offset: o };
throw new RangeError("No text nodes with non-whitespace text found in range");
}
function Et(s) {
if (!s.toString().trim().length)
throw new RangeError("Range contains no non-whitespace text");
if (s.startContainer.nodeType !== Node.TEXT_NODE)
throw new RangeError("Range startContainer is not a text node");
if (s.endContainer.nodeType !== Node.TEXT_NODE)
throw new RangeError("Range endContainer is not a text node");
const e = s.cloneRange();
let t = !1, i = !1;
const r = {
start: J(
s.startContainer.textContent,
s.startOffset,
1
/* Forwards */
),
end: J(
s.endContainer.textContent,
s.endOffset,
2
/* Backwards */
)
};
if (r.start >= 0 && (e.setStart(s.startContainer, r.start), t = !0), r.end > 0 && (e.setEnd(s.endContainer, r.end), i = !0), t && i)
return e;
if (!t) {
const { node: n, offset: o } = Te(
e,
1
/* Forwards */
);
n && o >= 0 && e.setStart(n, o);
}
if (!i) {
const { node: n, offset: o } = Te(
e,
2
/* Backwards */
);
n && o > 0 && e.setEnd(n, o);
}
return e;
}
function Ue(s) {
var e;
switch (s.nodeType) {
case Node.ELEMENT_NODE:
case Node.TEXT_NODE:
return ((e = s.textContent) == null ? void 0 : e.length) ?? 0;
default:
return 0;
}
}
function we(s) {
let e = s.previousSibling, t = 0;
for (; e; )
t += Ue(e), e = e.previousSibling;
return t;
}
function ke(s, ...e) {
let t = e.shift();
const i = s.ownerDocument.createNodeIterator(
s,
NodeFilter.SHOW_TEXT
), r = [];
let n = i.nextNode(), o, l = 0;
for (; t !== void 0 && n; )
o = n, l + o.data.length > t ? (r.push({ node: o, offset: t - l }), t = e.shift()) : (n = i.nextNode(), l += o.data.length);
for (; t !== void 0 && o && l === t; )
r.push({ node: o, offset: o.data.length }), t = e.shift();
if (t !== void 0)
throw new RangeError("Offset exceeds text length");
return r;
}
class A {
constructor(e, t) {
if (t < 0)
throw new Error("Offset is invalid");
this.element = e, this.offset = t;
}
/**
* Return a copy of this position with offset relative to a given ancestor
* element.
*
* @param parent - Ancestor of `this.element`
*/
relativeTo(e) {
if (!e.contains(this.element))
throw new Error("Parent is not an ancestor of current element");
let t = this.element, i = this.offset;
for (; t !== e; )
i += we(t), t = t.parentElement;
return new A(t, i);
}
/**
* Resolve the position to a specific text node and offset within that node.
*
* Throws if `this.offset` exceeds the length of the element's text. In the
* case where the element has no text and `this.offset` is 0, the `direction`
* option determines what happens.
*
* Offsets at the boundary between two nodes are resolved to the start of the
* node that begins at the boundary.
*
* @param options.direction - Specifies in which direction to search for the
* nearest text node if `this.offset` is `0` and
* `this.element` has no text. If not specified an
* error is thrown.
*
* @throws {RangeError}
*/
resolve(e = {}) {
try {
return ke(this.element, this.offset)[0];
} catch (t) {
if (this.offset === 0 && e.direction !== void 0) {
const i = document.createTreeWalker(
this.element.getRootNode(),
NodeFilter.SHOW_TEXT
);
i.currentNode = this.element;
const r = e.direction === 1, n = r ? i.nextNode() : i.previousNode();
if (!n)
throw t;
return { node: n, offset: r ? 0 : n.data.length };
} else
throw t;
}
}
/**
* Construct a `TextPosition` that refers to the `offset`th character within
* `node`.
*/
static fromCharOffset(e, t) {
switch (e.nodeType) {
case Node.TEXT_NODE:
return A.fromPoint(e, t);
case Node.ELEMENT_NODE:
return new A(e, t);
default:
throw new Error("Node is not an element or text node");
}
}
/**
* Construct a `TextPosition` representing the range start or end point (node, offset).
*
* @param node
* @param offset - Offset within the node
*/
static fromPoint(e, t) {
switch (e.nodeType) {
case Node.TEXT_NODE: {
if (t < 0 || t > e.data.length)
throw new Error("Text node offset is out of range");
if (!e.parentElement)
throw new Error("Text node has no parent");
const i = we(e) + t;
return new A(e.parentElement, i);
}
case Node.ELEMENT_NODE: {
if (t < 0 || t > e.childNodes.length)
throw new Error("Child node offset is out of range");
let i = 0;
for (let r = 0; r < t; r++)
i += Ue(e.childNodes[r]);
return new A(e, i);
}
default:
throw new Error("Point is not in an element or text node");
}
}
}
class z {
constructor(e, t) {
this.start = e, this.end = t;
}
/**
* Create a new TextRange whose `start` and `end` are computed relative to
* `element`. `element` must be an ancestor of both `start.element` and
* `end.element`.
*/
relativeTo(e) {
return new z(
this.start.relativeTo(e),
this.end.relativeTo(e)
);
}
/**
* Resolve this TextRange to a (DOM) Range.
*
* The resulting DOM Range will always start and end in a `Text` node.
* Hence `TextRange.fromRange(range).toRange()` can be used to "shrink" a
* range to the text it contains.
*
* May throw if the `start` or `end` positions cannot be resolved to a range.
*/
toRange() {
let e, t;
this.start.element === this.end.element && this.start.offset <= this.end.offset ? [e, t] = ke(
this.start.element,
this.start.offset,
this.end.offset
) : (e = this.start.resolve({
direction: 1
/* FORWARDS */
}), t = this.end.resolve({
direction: 2
/* BACKWARDS */
}));
const i = new Range();
return i.setStart(e.node, e.offset), i.setEnd(t.node, t.offset), i;
}
/**
* Create a TextRange from a (DOM) Range
*/
static fromRange(e) {
const t = A.fromPoint(
e.startContainer,
e.startOffset
), i = A.fromPoint(e.endContainer, e.endOffset);
return new z(t, i);
}
/**
* Create a TextRange representing the `start`th to `end`th characters in
* `root`
*/
static fromOffsets(e, t, i) {
return new z(
new A(e, t),
new A(e, i)
);
}
/**
* Return a new Range representing `range` trimmed of any leading or trailing
* whitespace
*/
static trimmedRange(e) {
return Et(z.fromRange(e).toRange());
}
}
class _ {
constructor(e, t, i) {
this.root = e, this.start = t, this.end = i;
}
static fromRange(e, t) {
const i = z.fromRange(t).relativeTo(e);
return new _(
e,
i.start.offset,
i.end.offset
);
}
static fromSelector(e, t) {
return new _(e, t.start, t.end);
}
toSelector() {
return {
type: "TextPositionSelector",
start: this.start,
end: this.end
};
}
toRange() {
return z.fromOffsets(this.root, this.start, thi