@readium/shared
Version:
Shared models to be used across other Readium projects and implementations in Typescript
1,526 lines • 116 kB
JavaScript
class Ce {
close() {
}
links() {
return [];
}
get(t) {
throw Error("This is an empty fetcher");
}
}
class wr {
constructor(t, e) {
this.client = t || window.fetch.bind(window), this.baseUrl = e;
}
links() {
return [];
}
get(t) {
const e = t.toURL(this.baseUrl);
if (e === void 0)
throw Error(`Invalid HREF: ${t.href}`);
return new Re(this.client, t, e);
}
close() {
}
}
class Re {
constructor(t, e, r) {
this.client = t || window.fetch.bind(window), this._link = e, this.url = r;
}
/** Cached HEAD response to get the expected content length and other metadata. */
async headResponse() {
if (this._headResponse)
return this._headResponse;
const t = await this.client(this.url, {
method: "HEAD"
});
if (!t.ok)
throw new Error(
`http HEAD request for ${this.url} failed with HTTP status code ${t.status}`
);
return this._headResponse = t, t;
}
close() {
}
async link() {
return this._link;
}
async read(t) {
if (t)
throw new Error("http read range not implemented!");
const e = await this.client(this.url);
if (!e.ok)
throw new Error(
`http GET request for ${this.url} failed with HTTP status code ${e.status}`
);
return new Uint8Array(await e.arrayBuffer());
}
async length() {
const e = (await this.headResponse()).headers.get("content-length");
if (e === null || e === "")
throw new Error("length for resource unavailable");
return parseInt(e);
}
async readAsJSON() {
const t = await this.client(this.url);
if (!t.ok)
throw new Error(
`http GET request for ${this.url} failed with HTTP status code ${t.status}`
);
return await t.json();
}
async readAsString() {
const t = await this.client(this.url);
if (!t.ok)
throw new Error(
`http GET request for ${this.url} failed with HTTP status code ${t.status}`
);
return await t.text();
}
async readAsXML() {
const t = await this.client(this.url);
if (!t.ok)
throw new Error(
`http GET request for ${this.url} failed with HTTP status code ${t.status}`
);
return new DOMParser().parseFromString(
await t.text(),
"application/xml"
);
}
}
class Ar {
constructor(t, e) {
this.start = t, this.endInclusive = e;
}
}
class Ir {
readAsString() {
return this.read().then((t) => t === void 0 ? t : new TextDecoder().decode(t));
}
readAsJSON() {
return this.readAsString().then((t) => t === void 0 ? t : JSON.parse(t));
}
readAsXML() {
return this.readAsString().then((t) => t === void 0 ? t : new DOMParser().parseFromString(t, "text/xml"));
}
}
class pt {
/**
* Creates a [Encryption].
*/
constructor(t) {
this.algorithm = t.algorithm, this.compression = t.compression, this.originalLength = t.originalLength, this.profile = t.profile, this.scheme = t.scheme;
}
/**
* Parses a [Encryption] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t && t.algorithm)
return new pt({
algorithm: t.algorithm,
compression: t.compression,
originalLength: t.originalLength,
profile: t.profile,
scheme: t.scheme
});
}
/**
* Serializes a [Encryption] to its RWPM JSON representation.
*/
serialize() {
const t = { algorithm: this.algorithm };
return this.compression !== void 0 && (t.compression = this.compression), this.originalLength !== void 0 && (t.originalLength = this.originalLength), this.profile !== void 0 && (t.profile = this.profile), this.scheme !== void 0 && (t.scheme = this.scheme), t;
}
}
var at = /* @__PURE__ */ ((i) => (i.left = "left", i.right = "right", i.center = "center", i))(at || {});
class A {
constructor(t) {
this.otherProperties = t;
}
get page() {
return this.otherProperties.page;
}
/**
* Creates a [Properties] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t)
return new A(t);
}
/**
* 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(t) {
const e = Object.assign({}, this.otherProperties);
for (const r in t)
e[r] = t[r];
return new A(e);
}
}
Object.defineProperty(A.prototype, "encryption", {
get: function() {
return pt.deserialize(this.otherProperties.encrypted);
}
});
class gt {
/** Creates a MediaOverlay object */
constructor(t) {
this.activeClass = t.activeClass, this.playbackActiveClass = t.playbackActiveClass;
}
/**
* Parses a MediaOverlay from its RWPM JSON representation.
*/
static deserialize(t) {
if (t)
return new gt({
activeClass: t.activeClass,
playbackActiveClass: t.playbackActiveClass
});
}
/**
* Serializes a MediaOverlay to its RWPM JSON representation.
*/
serialize() {
const t = {};
return this.activeClass && (t.activeClass = this.activeClass), this.playbackActiveClass && (t.playbackActiveClass = this.playbackActiveClass), t;
}
}
function Pe(i) {
return i && i instanceof Array ? i : void 0;
}
function N(i) {
return i && typeof i == "string" ? [i] : Pe(i);
}
function K(i) {
return typeof i == "string" ? new Date(i) : void 0;
}
function L(i) {
return isNaN(i) ? void 0 : i;
}
function b(i) {
return L(i) !== void 0 && Math.sign(i) >= 0 ? i : void 0;
}
function q(i) {
const t = new Array();
return i.forEach((e) => t.push(e)), t;
}
class k {
/** Creates an AltIdentifier object */
constructor(t) {
this.value = t.value, this.scheme = t.scheme;
}
/**
* Parses an AltIdentifier from its RWPM JSON representation.
*/
static deserialize(t) {
if (t) {
if (typeof t == "string")
return new k({ value: t });
if (typeof t == "object" && t.value)
return new k({
value: t.value,
scheme: t.scheme
});
}
}
/**
* Serializes an AltIdentifier to its RWPM JSON representation.
*/
serialize() {
return this.scheme ? {
value: this.value,
scheme: this.scheme
} : this.value;
}
}
class a {
/** Creates a MediaType object. */
constructor(t) {
let e, r, s = t.mediaType.replace(/\s/g, "").split(";");
const n = s[0].split("/");
if (n.length === 2) {
if (e = n[0].toLowerCase().trim(), r = n[1].toLowerCase().trim(), e.length === 0 || r.length === 0)
throw new Error("Invalid media type");
} else
throw new Error("Invalid media type");
const o = {};
for (let f = 1; f < s.length; f++) {
const p = s[f].split("=");
if (p.length === 2) {
const S = p[0].toLocaleLowerCase(), D = S === "charset" ? p[1].toUpperCase() : p[1];
o[S] = D;
}
}
const l = {}, c = Object.keys(o);
c.sort((f, p) => f.localeCompare(p)), c.forEach((f) => l[f] = o[f]);
let h = "";
for (const f in l) {
const p = l[f];
h += `;${f}=${p}`;
}
const u = `${e}/${r}${h}`, d = l.encoding;
this.string = u, this.type = e, this.subtype = r, this.parameters = l, this.encoding = d, this.name = t.name, this.fileExtension = t.fileExtension;
}
static parse(t) {
return new a(t);
}
/** 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 t = this.subtype.split("+");
return t.length > 1 ? `+${t[t.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(t) {
const e = typeof t == "string" ? a.parse({ mediaType: t }) : t;
if (!((this.type === "*" || this.type === e.type) && (this.subtype === "*" || this.subtype === e.subtype)))
return !1;
const r = new Set(
Object.entries(this.parameters).map(([n, o]) => `${n}=${o}`)
), s = new Set(
Object.entries(e.parameters).map(([n, o]) => `${n}=${o}`)
);
for (const n of Array.from(r.values()))
if (!s.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(t) {
const e = typeof t == "string" ? a.parse({ mediaType: t }) : t;
return this.contains(e) || e.contains(this);
}
/**
* Returns whether this media type matches any of the [others] media types.
*/
matchesAny(...t) {
for (const e of t)
if (this.matches(e))
return !0;
return !1;
}
/** Checks the MediaType equals another one (comparing their string) */
equals(t) {
return this.string === t.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 Z {
constructor(t) {
this.uri = t, this.parameters = this.getParameters(t);
}
/**
* List of URI template parameter keys, if the [Link] is templated.
*/
getParameters(t) {
const e = /\{\??([^}]+)\}/g, r = t.match(e);
return r ? new Set(
r.join(",").replace(e, "$1").split(",").map((s) => s.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(t) {
const e = (s) => s.split(",").map((n) => {
const o = t[n];
return o ? encodeURIComponent(o) : "";
}).join(","), r = (s) => "?" + s.split(",").map((n) => {
const o = n.split("=")[0], l = t[o];
return l ? `${o}=${encodeURIComponent(l)}` : "";
}).join("&");
return this.uri.replace(/\{(\??)([^}]+)\}/g, (...s) => s[1] ? r(s[2]) : e(s[2]));
}
}
class m {
/**
* Creates a [Locations].
*/
constructor(t) {
this.fragments = t.fragments ? t.fragments : new Array(), this.progression = t.progression, this.totalProgression = t.totalProgression, this.position = t.position, this.otherLocations = t.otherLocations;
}
/**
* Parses a [Locations] from its RWPM JSON representation.
*/
static deserialize(t) {
if (!t)
return;
const e = L(t.progression), r = L(t.totalProgression), s = L(t.position), n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set([
"fragment",
"fragments",
"progression",
"totalProgression",
"position"
]);
return Object.entries(t).forEach(([l, c]) => {
o.has(l) || n.set(l, c);
}), new m({
fragments: N(t.fragments || t.fragment),
progression: e !== void 0 && e >= 0 && e <= 1 ? e : void 0,
totalProgression: r !== void 0 && r >= 0 && r <= 1 ? r : void 0,
position: s !== void 0 && s > 0 ? s : void 0,
otherLocations: n.size === 0 ? void 0 : n
});
}
/**
* Serializes a [Locations] to its RWPM JSON representation.
*/
serialize() {
const t = {};
return this.fragments && (t.fragments = this.fragments), this.progression !== void 0 && (t.progression = this.progression), this.totalProgression !== void 0 && (t.totalProgression = this.totalProgression), this.position !== void 0 && (t.position = this.position), this.otherLocations && this.otherLocations.forEach((e, r) => t[r] = e), t;
}
}
class H {
/**
* Creates a [Text].
*/
constructor(t) {
this.after = t.after, this.before = t.before, this.highlight = t.highlight;
}
/**
* Parses a [Locations] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t)
return new H({
after: t.after,
before: t.before,
highlight: t.highlight
});
}
/**
* Serializes a [Locations] to its RWPM JSON representation.
*/
serialize() {
const t = {};
return this.after !== void 0 && (t.after = this.after), this.before !== void 0 && (t.before = this.before), this.highlight !== void 0 && (t.highlight = this.highlight), t;
}
}
class w {
/**
* Creates a [Locator].
*/
constructor(t) {
this.href = t.href, this.type = t.type, this.title = t.title, this.locations = t.locations ? t.locations : new m({}), this.text = t.text;
}
/**
* Parses a [Link] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t && t.href && t.type)
return new w({
href: t.href,
type: t.type,
title: t.title,
locations: m.deserialize(t.locations),
text: H.deserialize(t.text)
});
}
/**
* Serializes a [Link] to its RWPM JSON representation.
*/
serialize() {
const t = { href: this.href, type: this.type };
return this.title !== void 0 && (t.title = this.title), this.locations && (t.locations = this.locations.serialize()), this.text && (t.text = this.text.serialize()), t;
}
/**
* Shortcut to get a copy of the [Locator] with different [Locations] sub-properties.
*/
copyWithLocations(t) {
return new w({
href: this.href,
type: this.type,
title: this.title,
text: this.text,
locations: new m({ ...this.locations, ...t })
});
}
}
class I {
/**
* Creates a [Link].
*/
constructor(t) {
this.href = t.href, this.templated = t.templated, this.type = t.type, this.title = t.title, this.rels = t.rels, this.properties = t.properties, this.height = t.height, this.width = t.width, this.size = t.size, this.duration = t.duration, this.bitrate = t.bitrate, this.languages = t.languages, this.alternates = t.alternates, this.children = t.children;
}
/**
* Parses a [Link] from its RWPM JSON representation.
*/
static deserialize(t) {
if (!(!t || typeof t.href != "string"))
return new I({
href: t.href,
templated: t.templated,
type: t.type,
title: t.title,
rels: t.rel ? t.rel instanceof Array ? new Set(t.rel) : /* @__PURE__ */ new Set([t.rel]) : void 0,
properties: A.deserialize(t.properties),
height: b(t.height),
width: b(t.width),
size: b(t.size),
duration: b(t.duration),
bitrate: b(t.bitrate),
languages: N(t.language),
alternates: x.deserialize(t.alternate),
children: x.deserialize(t.children)
});
}
/**
* Serializes a [Link] to its RWPM JSON representation.
*/
serialize() {
const t = { href: this.href };
return this.templated !== void 0 && (t.templated = this.templated), this.type !== void 0 && (t.type = this.type), this.title !== void 0 && (t.title = this.title), this.rels && (t.rel = q(this.rels)), this.properties && (t.properties = this.properties.serialize()), this.height !== void 0 && (t.height = this.height), this.width !== void 0 && (t.width = this.width), this.size !== void 0 && (t.size = this.size), this.duration !== void 0 && (t.duration = this.duration), this.bitrate !== void 0 && (t.bitrate = this.bitrate), this.languages && (t.language = this.languages), this.alternates && (t.alternate = this.alternates.serialize()), this.children && (t.children = this.children.serialize()), t;
}
/** 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(t) {
const e = this.href.replace(/^(\/)/, "");
if (e.length === 0)
return;
let r = t || "/";
return r.startsWith("/") && (r = "file://" + r), new URL(e, r).href.replace(/^(file:\/\/)/, "");
}
/** List of URI template parameter keys, if the `Link` is templated. */
get templateParameters() {
return this.templated ? new Z(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(t) {
return new I({
href: new Z(this.href).expand(t),
templated: !1
});
}
/**
* Makes a copy of this [Link] after merging in the given additional other [properties].
*/
addProperties(t) {
var r;
const e = I.deserialize(this.serialize());
return e.properties = e.properties ? (r = e.properties) == null ? void 0 : r.add(t) : new A(t), e;
}
/**
* Creates a [Locator] from a reading order [Link].
*/
get locator() {
let t = this.href.split("#");
return new w({
href: t.length > 0 && t[0] !== void 0 ? t[0] : this.href,
type: this.type ?? "",
title: this.title,
locations: new m({
fragments: t.length > 1 && t[1] !== void 0 ? [t[1]] : []
})
});
}
}
class x {
/**
* Creates a [Links].
*/
constructor(t) {
this.items = t;
}
/**
* Creates a list of [Link] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t && t instanceof Array)
return new x(
t.map((e) => I.deserialize(e)).filter((e) => e !== void 0)
);
}
/**
* Serializes an array of [Link] to its RWPM JSON representation.
*/
serialize() {
return this.items.map((t) => t.serialize());
}
/** Finds the first link with the given relation. */
findWithRel(t) {
const e = (r) => r.rels && r.rels.has(t);
return this.items.find(e);
}
/** Finds all the links with the given relation. */
filterByRel(t) {
const e = (r) => r.rels && r.rels.has(t);
return this.items.filter(e);
}
/** Finds the first link matching the given HREF. */
findWithHref(t) {
const e = (r) => r.href === t;
return this.items.find(e);
}
/** Finds the index of the first link matching the given HREF. */
findIndexWithHref(t) {
const e = (r) => r.href === t;
return this.items.findIndex(e);
}
/** Finds the first link matching the given media type. */
findWithMediaType(t) {
const e = (r) => r.mediaType.matches(t);
return this.items.find(e);
}
/** Finds all the links matching the given media type. */
filterByMediaType(t) {
const e = (r) => r.mediaType.matches(t);
return this.items.filter(e);
}
/** Finds all the links matching any of the given media types. */
filterByMediaTypes(t) {
const e = (r) => {
for (const s of t)
if (r.mediaType.matches(s))
return !0;
return !1;
};
return this.items.filter(e);
}
/** Returns whether all the resources in the collection are audio clips. */
everyIsAudio() {
const t = (e) => e.mediaType.isAudio;
return this.items.length > 0 && this.items.every(t);
}
/** Returns whether all the resources in the collection are bitmaps. */
everyIsBitmap() {
const t = (e) => e.mediaType.isBitmap;
return this.items.length > 0 && this.items.every(t);
}
/** Returns whether all the resources in the collection are HTML documents. */
everyIsHTML() {
const t = (e) => e.mediaType.isHTML;
return this.items.length > 0 && this.items.every(t);
}
/** Returns whether all the resources in the collection are video clips. */
everyIsVideo() {
const t = (e) => e.mediaType.isVideo;
return this.items.length > 0 && this.items.every(t);
}
/** Returns whether all the resources in the collection are matching any of the given media types. */
everyMatchesMediaType(t) {
return Array.isArray(t) ? this.items.length > 0 && this.items.every((e) => {
for (const r of t)
return e.mediaType.matches(r);
return !1;
}) : this.items.length > 0 && this.items.every((e) => e.mediaType.matches(t));
}
filterLinksHasType() {
return this.items.filter((t) => t.type);
}
}
const mt = class R {
/**
* translations can be a string or a collection of language/translation pairs,
* for a single string the language is assumed to be undefined
*/
constructor(t) {
this.translations = typeof t == "string" ? { [R.UNDEFINED_LANGUAGE]: t } : t;
}
/**
* Parses a [LocalizedString] from its RWPM JSON representation.
*
* "anyOf": [
* {
* "type": "string"
* },
* {
* "description": "The language in a language map must be a valid BCP 47 tag.",
* "type": "object",
* "patternProperties": {
* "^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-(?<extension>[0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(?<privateUse>x(-[A-Za-z0-9]{1,8})+))?)|(?<privateUse2>x(-[A-Za-z0-9]{1,8})+))$": {
* "type": "string"
* }
* },
* "additionalProperties": false,
* "minProperties": 1
* }
* ]
*/
static deserialize(t) {
if (t && (typeof t == "string" || t.constructor === Object))
return new R(t);
}
serialize() {
return this.translations;
}
/**
* Returns the first translation for the given [language] BCP–47 tag.
* If not found, then fallback:
* 1. on the undefined language
* 2. on the English language
* 3. the first translation found
* 4. returns empty string
*/
getTranslation(t) {
return this.translations[t || R.UNDEFINED_LANGUAGE] || this.translations[R.UNDEFINED_LANGUAGE] || this.translations[R.LANGUAGE_EN] || (Object.values(this.translations).length === 0 ? "" : Object.values(this.translations)[0]);
}
};
mt.UNDEFINED_LANGUAGE = "undefined";
mt.LANGUAGE_EN = "en";
let T = mt;
class X {
/**
* Creates a [Contributor] object.
*/
constructor(t) {
this.name = t.name, this.sortAs = t.sortAs, this.identifier = t.identifier, this.altIdentifiers = t.altIdentifiers, this.roles = t.roles, this.links = t.links, this.position = t.position;
}
/**
* Parses a [Contributor] from its RWPM JSON representation.
*
* A contributor can be parsed from a single string, or a full-fledged object.
*/
static deserialize(t) {
if (t)
return typeof t == "string" ? new X({
name: T.deserialize(t)
}) : t.name ? new X({
name: T.deserialize(t.name),
sortAs: t.sortAs,
identifier: t.identifier,
altIdentifiers: t.altIdentifier ? t.altIdentifier instanceof Array ? new Set(t.altIdentifier.map((e) => k.deserialize(e)).filter((e) => e !== void 0)) : new Set([k.deserialize(t.altIdentifier)].filter((e) => e !== void 0)) : void 0,
roles: t.role ? new Set(N(t.role)) : void 0,
links: x.deserialize(t.links),
position: L(t.position)
}) : void 0;
}
/**
* Serializes a [Contributor] to its RWPM JSON representation.
*/
serialize() {
const t = { name: this.name.serialize() };
return this.sortAs !== void 0 && (t.sortAs = this.sortAs), this.identifier !== void 0 && (t.identifier = this.identifier), this.altIdentifiers && (t.altIdentifier = q(this.altIdentifiers).map((e) => e.serialize())), this.roles && (t.role = q(this.roles)), this.links && (t.links = this.links.serialize()), this.position !== void 0 && (t.position = this.position), t;
}
}
class E {
/**
* Creates a [Contributors] object.
*/
constructor(t) {
this.items = t;
}
/**
* Parses a [Contributors] from its RWPM JSON representation.
*
*/
static deserialize(t) {
if (!t)
return;
const e = t instanceof Array ? t : [t];
return new E(
e.map((r) => X.deserialize(r)).filter((r) => r !== void 0)
);
}
/**
* Serializes a [Contributors] to its RWPM JSON representation.
*/
serialize() {
return this.items.map((t) => t.serialize());
}
}
class F {
/** Creates an Array of [Subject]. */
constructor(t) {
this.items = t && t.items ? t.items : /* @__PURE__ */ new Map();
}
/**
* Parses a [BelongsTo] from its RWPM JSON representation.
*/
static deserialize(t) {
if (!(t && t instanceof Object))
return;
const e = /* @__PURE__ */ new Map();
return Object.entries(t).forEach(([r, s]) => {
const n = E.deserialize(s);
n && n.items.length > 0 && e.set(r, n);
}), new F({ items: e });
}
/**
* Serializes a [BelongsTo] to its RWPM JSON representation.
*/
serialize() {
const t = {};
return this.items.forEach(
(e, r) => t[r] = e.serialize()
), t;
}
}
var U = /* @__PURE__ */ ((i) => (i.reflowable = "reflowable", i.fixed = "fixed", i.scrolled = "scrolled", i))(U || {}), B = /* @__PURE__ */ ((i) => (i.EPUB = "https://readium.org/webpub-manifest/profiles/epub", i.AUDIOBOOK = "https://readium.org/webpub-manifest/profiles/audiobook", i.DIVINA = "https://readium.org/webpub-manifest/profiles/divina", i.PDF = "https://readium.org/webpub-manifest/profiles/pdf", i))(B || {}), O = /* @__PURE__ */ ((i) => (i.ltr = "ltr", i.rtl = "rtl", i))(O || {});
function Sr(i) {
return i === "rtl" ? at.right : at.left;
}
class Q {
/** Creates a [Subject]. */
constructor(t) {
this.name = t.name, this.sortAs = t.sortAs, this.code = t.code, this.scheme = t.scheme, this.links = t.links;
}
/**
* Parses a [Subject] from its RWPM JSON representation.
*
* A [Subject] can be parsed from a single string, or a full-fledged object.
*/
static deserialize(t) {
if (t)
return typeof t == "string" ? new Q({
name: T.deserialize(t)
}) : t.name ? new Q({
name: T.deserialize(t.name),
sortAs: t.sortAs,
code: t.code,
scheme: t.scheme,
links: x.deserialize(t.links)
}) : void 0;
}
/**
* Serializes a [Subject] to its RWPM JSON representation.
*/
serialize() {
const t = { name: this.name.serialize() };
return this.sortAs !== void 0 && (t.sortAs = this.sortAs), this.code !== void 0 && (t.code = this.code), this.scheme !== void 0 && (t.scheme = this.scheme), this.links && (t.links = this.links.serialize()), t;
}
}
class Et {
/** Creates an Array of [Subject]. */
constructor(t) {
this.items = t;
}
/**
* Parses a [Subjects] from its RWPM JSON representation.
*/
static deserialize(t) {
if (!t)
return;
const e = t instanceof Array ? t : [t];
return new Et(
e.map((r) => Q.deserialize(r)).filter((r) => r !== void 0)
);
}
/**
* Serializes a [Subjects] to its RWPM JSON representation.
*/
serialize() {
return this.items.map((t) => t.serialize());
}
}
var Le = /* @__PURE__ */ ((i) => (i.all = "all", i.none = "none", i))(Le || {});
class bt {
/** Creates a [TDM] object */
constructor(t) {
this.reservation = t.reservation, this.policy = t.policy;
}
/**
* Parses a [TDM] from its RWPM JSON representation.
*/
static deserialize(t) {
if (t)
return new bt({
reservation: t.reservation,
policy: t.policy
});
}
/**
* Serializes a [TDM] to its RWPM JSON representation.
*/
serialize() {
const t = {};
return this.reservation !== void 0 && (t.reservation = this.reservation), this.policy !== void 0 && (t.policy = this.policy), t;
}
}
const Bt = class lt {
/** Creates [Metadata] object */
constructor(t) {
this.title = t.title, this.typeUri = t.typeUri, this.conformsTo = t.conformsTo, this.identifier = t.identifier, this.altIdentifier = t.altIdentifier, this.subtitle = t.subtitle, this.sortAs = t.sortAs, this.artists = t.artists, this.authors = t.authors, this.colorists = t.colorists, this.contributors = t.contributors, this.editors = t.editors, this.illustrators = t.illustrators, this.inkers = t.inkers, this.letterers = t.letterers, this.narrators = t.narrators, this.pencilers = t.pencilers, this.translators = t.translators, this.languages = t.languages, this.description = t.description, this.publishers = t.publishers, this.imprints = t.imprints, this.published = t.published, this.modified = t.modified, this.subjects = t.subjects, this.belongsTo = t.belongsTo, this.belongsToCollections = t.belongsToCollections, this.belongsToSeries = t.belongsToSeries, this.belongsToCollections && this.belongsToCollections.items.length > 0 && (this.belongsTo || (this.belongsTo = new F()), this.belongsTo.items.set("collection", this.belongsToCollections)), this.belongsToSeries && this.belongsToSeries.items.length > 0 && (this.belongsTo || (this.belongsTo = new F()), this.belongsTo.items.set("series", this.belongsToSeries)), this.layout = t.layout, this.readingProgression = t.readingProgression, this.duration = t.duration, this.numberOfPages = t.numberOfPages, this.tdm = t.tdm, this.otherMetadata = t.otherMetadata;
}
/**
* Parses a [Metadata] from its RWPM JSON representation.
*
* If the metadata can't be parsed, a warning will be logged with [warnings].
*/
static deserialize(t) {
if (!(t && t.title))
return;
const e = T.deserialize(t.title), r = t["@type"], s = N(t.conformsTo), n = t.identifier, o = k.deserialize(t.altIdentifier), l = T.deserialize(t.subtitle), c = T.deserialize(t.sortAs), h = E.deserialize(t.artist), u = E.deserialize(t.author), d = E.deserialize(t.colorist), f = E.deserialize(t.contributor), p = E.deserialize(t.editor), S = E.deserialize(t.illustrator), D = E.deserialize(t.inker), fe = E.deserialize(t.letterer), de = E.deserialize(t.narrator), pe = E.deserialize(t.penciler), ge = E.deserialize(t.translator), me = N(t.language), Ee = t.description, be = E.deserialize(t.publisher), xe = E.deserialize(t.imprint), ye = K(t.published), we = K(t.modified), Ae = Et.deserialize(t.subject), Ie = F.deserialize(t.belongsTo), Se = t.layout, ve = t.readingProgression, ze = b(t.duration), Te = b(t.numberOfPages), Oe = bt.deserialize(t.tdm);
let J = Object.assign({}, t);
return lt.mappedProperties.forEach((Ne) => delete J[Ne]), Object.keys(J).length === 0 && (J = void 0), new lt({
title: e,
typeUri: r,
conformsTo: s,
identifier: n,
altIdentifier: o,
subtitle: l,
sortAs: c,
artists: h,
authors: u,
colorists: d,
contributors: f,
editors: p,
illustrators: S,
inkers: D,
letterers: fe,
narrators: de,
pencilers: pe,
translators: ge,
languages: me,
description: Ee,
publishers: be,
imprints: xe,
published: ye,
modified: we,
subjects: Ae,
belongsTo: Ie,
layout: Se,
readingProgression: ve,
duration: ze,
numberOfPages: Te,
tdm: Oe,
otherMetadata: J
});
}
/**
* Serializes a [Metadata] to its RWPM JSON representation.
*/
serialize() {
const t = { title: this.title.serialize() };
if (this.typeUri !== void 0 && (t["@type"] = this.typeUri), this.conformsTo && (t.conformsTo = this.conformsTo), this.identifier !== void 0 && (t.identifier = this.identifier), this.altIdentifier && (t.altIdentifier = this.altIdentifier.serialize()), this.subtitle && (t.subtitle = this.subtitle.serialize()), this.sortAs && (t.sortAs = this.sortAs.serialize()), this.editors && (t.editor = this.editors.serialize()), this.artists && (t.artist = this.artists.serialize()), this.authors && (t.author = this.authors.serialize()), this.colorists && (t.colorist = this.colorists.serialize()), this.contributors && (t.contributor = this.contributors.serialize()), this.illustrators && (t.illustrator = this.illustrators.serialize()), this.letterers && (t.letterer = this.letterers.serialize()), this.narrators && (t.narrator = this.narrators.serialize()), this.pencilers && (t.penciler = this.pencilers.serialize()), this.translators && (t.translator = this.translators.serialize()), this.inkers && (t.inker = this.inkers.serialize()), this.languages && (t.language = this.languages), this.description !== void 0 && (t.description = this.description), this.publishers && (t.publisher = this.publishers.serialize()), this.imprints && (t.imprint = this.imprints.serialize()), this.published !== void 0 && (t.published = this.published.toISOString()), this.modified !== void 0 && (t.modified = this.modified.toISOString()), this.subjects && (t.subject = this.subjects.serialize()), this.belongsTo && (t.belongsTo = this.belongsTo.serialize()), this.layout && (t.layout = this.layout), this.readingProgression && (t.readingProgression = this.readingProgression), this.duration !== void 0 && (t.duration = this.duration), this.numberOfPages !== void 0 && (t.numberOfPages = this.numberOfPages), this.tdm && (t.tdm = this.tdm.serialize()), this.otherMetadata) {
const e = this.otherMetadata;
Object.keys(e).forEach((r) => t[r] = e[r]);
}
return t;
}
/**
* Computes a [Layout] using the [conformsTo] profile and layout property.
*
* Special cases:
* - EPUB profile defaults to reflowable if layout is not present
* - Divina profile defaults to fixed if layout is not present
* - Layout is ignored for audiobook and PDF profiles
* - Layout is ignored if set to reflowable on a Divina profile
*
* Note: Stops at the first matching profile.
*/
get effectiveLayout() {
if (!this.conformsTo)
return null;
for (const t of this.conformsTo)
switch (t) {
case B.EPUB:
return this.layout || U.reflowable;
case B.DIVINA:
return this.layout === U.reflowable ? U.fixed : this.layout || U.fixed;
case B.AUDIOBOOK:
case B.PDF:
return null;
}
return null;
}
/**
* Computes a [ReadingProgression] when the value of [readingProgression] is undefined, using the publication language.
*
* See this issue for more details: https://github.com/readium/architecture/issues/113
*/
get effectiveReadingProgression() {
var r;
if (this.readingProgression)
return this.readingProgression;
if (((r = this.languages) == null ? void 0 : r.length) !== 1)
return O.ltr;
const t = this.languages[0].toLowerCase();
if (t === "zh-hant" || t === "zh-tw")
return O.rtl;
switch (t.split("-")[0]) {
case "ar":
return O.rtl;
case "fa":
return O.rtl;
case "he":
return O.rtl;
default:
return O.ltr;
}
}
};
Bt.mappedProperties = [
"title",
"@type",
"conformsTo",
"identifier",
"altIdentifier",
"subtitle",
"sortAs",
"artist",
"author",
"colorist",
"contributor",
"editor",
"illustrator",
"inker",
"letterer",
"narrator",
"penciler",
"translator",
"language",
"description",
"publisher",
"imprint",
"published",
"modified",
"subject",
"belongsTo",
"layout",
"readingProgression",
"duration",
"numberOfPages",
"tdm"
];
let Ft = Bt;
Ft.prototype.getMediaOverlay = function() {
var t;
const i = (t = this.otherMetadata) == null ? void 0 : t.mediaOverlay;
if (i)
return gt.deserialize(i);
};
A.prototype.getContains = function() {
return new Set(this.otherProperties.contains || []);
};
class xt {
constructor(t) {
this.links = t.links, this.guided = t.guided;
}
static deserialize(t) {
if (t)
return new xt({
links: x.deserialize(t.links),
guided: P.deserializeArray(t.guided)
});
}
serialize() {
const t = {};
return this.links !== void 0 && (t.links = this.links.serialize()), this.guided !== void 0 && (t.guided = this.guided.map((e) => e.serialize())), t;
}
}
class _ {
constructor(t) {
this.plain = t.plain, this.ssml = t.ssml, this.language = t.language;
}
/**
* Deserializes a GuidedNavigationText from JSON
*/
static deserialize(t) {
if (t != null) {
if (typeof t == "string")
return new _({ plain: t });
if (t.plain || t.ssml || t.language)
return new _({
plain: t.plain,
ssml: t.ssml,
language: t.language
});
}
}
/**
* Serializes the text to a plain object
*/
serialize() {
const t = {};
return this.plain !== void 0 && (t.plain = this.plain), this.ssml !== void 0 && (t.ssml = this.ssml), this.language !== void 0 && (t.language = this.language), Object.keys(t).length > 0 ? t : void 0;
}
}
class P {
/**
* Creates a [GuidedNavigation] object.
*/
constructor(t) {
this.audioref = t.audioref, this.children = t.children, this.imgref = t.imgref, this.role = t.role, this.level = t.level !== void 0 ? Math.min(6, Math.max(1, t.level)) : void 0, this.text = t.text, this.textref = t.textref, this.description = t.description;
}
/**
* Gets the plain text content.
* Returns undefined if no text is available.
*/
get plainText() {
var t;
return (t = this.text) == null ? void 0 : t.plain;
}
/**
* Gets the SSML content if available.
*/
get ssmlText() {
var t;
return (t = this.text) == null ? void 0 : t.ssml;
}
/**
* Gets the language of the text if available.
*/
get textLanguage() {
var t;
return (t = this.text) == null ? void 0 : t.language;
}
/**
* Deserializes a GuidedNavigationObject from JSON
*/
static deserialize(t) {
if (t)
return new P({
audioref: t.audioref,
children: P.deserializeArray(t.children),
imgref: t.imgref,
role: t.role ? new Set(N(t.role)) : void 0,
level: typeof t.level == "number" ? t.level : void 0,
text: _.deserialize(t.text),
textref: t.textref,
description: P.deserialize(t.description)
});
}
/**
* Parses a [GuidedNavigationObject] array from its RWPM JSON representation.
*/
static deserializeArray(t) {
if (t instanceof Array)
return t.map((e) => P.deserialize(e)).filter((e) => e !== void 0);
}
/**
* Serializes a [GuidedNavigationObject] to its RWPM JSON representation.
*/
serialize() {
const t = {};
if (this.audioref !== void 0 && (t.audioref = this.audioref), this.children !== void 0 && (t.children = this.children.map((e) => e.serialize())), this.imgref !== void 0 && (t.imgref = this.imgref), this.role !== void 0 && (t.role = q(this.role)), this.level !== void 0 && (t.level = this.level), this.text !== void 0) {
const e = this.text.serialize();
e !== void 0 && (t.text = e);
}
return this.textref !== void 0 && (t.textref = this.textref), this.description && (t.description = this.description.serialize()), t;
}
get audioFile() {
var t;
return (t = this.audioref) == null ? void 0 : t.split("#")[0];
}
get audioTime() {
var t;
if ((t = this.audioref) != null && t.includes("#"))
return this.audioref.split("#", 2)[1];
}
get textFile() {
var t;
return (t = this.textref) == null ? void 0 : t.split("#")[0];
}
get fragmentId() {
var t;
if ((t = this.textref) != null && t.includes("#"))
return this.textref.split("#", 2)[1];
}
get clip() {
const t = this.audioFile;
if (!t)
return;
const e = this.audioTime, r = {
audioResource: t,
fragmentId: this.fragmentId
};
if (!e)
return r;
const s = this.parseTimer(e);
return r.start = s[0], r.end = s[1], r;
}
parseTimer(t) {
if (!t || !t.startsWith("t="))
return [void 0, void 0];
const e = t.substring(2).split(",").map((r) => parseFloat(r));
return e.length