UNPKG

@readium/shared

Version:

Shared models to be used across other Readium projects and implementations in Typescript

1,526 lines 116 kB
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