epubavocado
Version:
I am an EPUB object model aspiring to be standards compliant.
390 lines (389 loc) • 13.6 kB
JavaScript
import { Entity } from './mixins/entity.js';
import { I18n } from './package/mixins/i18n.js';
import { ID } from './package/mixins/id.js';
import { Properties } from './package/mixins/properties.js';
import { Resource } from './mixins/resource.js';
import { Value } from './package/mixins/value.js';
import { splitRelAttribute, toArray } from '../util.js';
import { prefixMap, select } from '../xpath.js';
import { ManifestItem } from './package/manifest-item.js';
import { Manifest } from './package/manifest.js';
import { SpineItem } from './package/spine-item.js';
import { Spine } from './package/spine.js';
import { idFilter, attributeFilter, anyPropertiesFilter, allPropertiesFilter, anyRelFilter, allRelFilter, } from './package/util.js';
export { ManifestItem, Manifest, SpineItem, Spine };
const nodeTypeMap = () => ({
'opf:package': Package,
'opf:metadata': Metadata,
'opf:manifest': Manifest,
'opf:spine': Spine,
'opf:meta': Meta,
'opf:item': ManifestItem,
'opf:itemref': SpineItem,
'opf:link': Link,
// 'opf:collection': Collection,
'dc:identifier': Identifier,
'dc:title': Title,
'dc:language': Language,
'dc:contributor': Contributor,
'dc:coverage': Coverage,
'dc:creator': Creator,
'dc:date': Date,
'dc:description': Description,
'dc:format': Format,
'dc:publisher': Publisher,
'dc:relation': Relation,
'dc:rights': Rights,
'dc:source': Source,
'dc:subject': Subject,
'dc:type': Type,
});
function MetaProperties(Base) {
return class MetaProperties extends Base {
_resolveMetaProperty(property, constructor = Meta) {
return toArray(this._resolveMetaPropertyList(property, constructor))[0];
}
_resolveMetaPropertyList(property, constructor = Meta) {
var _a;
const id = this.id();
if (!id) {
return [];
}
const propertyMap = (_a = this._context.metadata()) === null || _a === void 0 ? void 0 : _a._metaPropertyMap[id];
if (!propertyMap) {
return [];
}
const metaNodes = propertyMap[property];
if (!metaNodes) {
return [];
}
return metaNodes.map((node) => new constructor(node, this._context));
}
alternateScript() {
return this.alternateScripts()[0];
}
alternateScripts() {
return this._resolveMetaPropertyList('alternate-script');
}
displaySeq() {
return this._resolveMetaProperty('display-seq');
}
fileAs() {
return this._resolveMetaProperty('file-as');
}
groupPosition() {
return this._resolveMetaProperty('group-position');
}
metaAuth() {
return this._resolveMetaProperty('meta-auth');
}
};
}
function MetaAttributes(Base) {
return class MetaAttributes extends Base {
property() {
return this._resolve('./@property');
}
scheme() {
return this._resolve('./@scheme');
}
};
}
function Refines(Base) {
return class Refines extends Base {
refines() {
const refines = this._resolve('./@refines');
if (!refines) {
return null;
}
// drop the # prefix
const idRefined = refines[0] === '#' ? refines.substr(1) : refines;
const node = this._context._select(`//*[@id='${idRefined}']`);
if (!node) {
return null;
}
const name = node.localName;
const namespace = node.namespaceURI;
if (!namespace) {
return null;
}
const prefix = prefixMap[namespace];
const typeConstructor = nodeTypeMap()[`${prefix}:${name}`];
if (!typeConstructor) {
return null;
}
return new typeConstructor(node, this._context);
}
};
}
export class Meta extends MetaProperties(MetaAttributes(Refines(I18n(Value(ID(Entity)))))) {
}
export class Identifier extends Value(MetaProperties(ID(Entity))) {
identifierType() {
return this._resolveMetaProperty('identifier-type');
}
}
export class Title extends Value(I18n(MetaProperties(ID(Entity)))) {
titleType() {
return this._resolveMetaProperty('title-type');
}
}
export class Language extends Value(MetaProperties(ID(Entity))) {
}
export class Contributor extends Value(I18n(MetaProperties(ID(Entity)))) {
role() {
return this._resolveMetaProperty('role');
}
}
export class Coverage extends Value(I18n(MetaProperties(ID(Entity)))) {
}
export class Creator extends Contributor {
}
export class Date extends Value(MetaProperties(ID(Entity))) {
}
export class Description extends Value(I18n(MetaProperties(ID(Entity)))) {
}
export class Format extends Value(MetaProperties(ID(Entity))) {
}
export class Publisher extends Value(I18n(MetaProperties(ID(Entity)))) {
}
export class Relation extends Value(I18n(MetaProperties(ID(Entity)))) {
}
export class Rights extends Value(I18n(MetaProperties(ID(Entity)))) {
}
export class Source extends Identifier {
sourceOf() {
return this._resolveMetaProperty('source-of');
}
}
export class Subject extends Value(I18n(MetaProperties(ID(Entity)))) {
authority() {
return this._resolveMetaProperty('authority');
}
term() {
return this._resolveMetaProperty('term');
}
}
export class Type extends Value(MetaProperties(ID(Entity))) {
}
export class BelongsToCollection extends Value(I18n(Refines(MetaAttributes(MetaProperties(ID(Entity)))))) {
identifier() {
return this._resolveMetaProperty('dcterms:identifier');
}
collectionType() {
return this._resolveMetaProperty('collection-type');
}
belongsToCollection() {
return this.belongsToCollections()[0];
}
belongsToCollections() {
return this._resolveMetaPropertyList('belongs-to-collection', BelongsToCollection);
}
}
export class Link extends Resource(Properties(Refines(ID(Entity)))) {
rel() {
return this.rels()[0];
}
rels() {
const rel = this._resolve('./@rel');
if (rel) {
return splitRelAttribute(rel);
}
return [];
}
}
export class Metadata extends ID(Entity) {
constructor(node, context) {
super(node, context);
this._metaPropertyMap = {};
const metaRefiningSelected = toArray(this._selectAll('./opf:meta[@refines and @property]'));
metaRefiningSelected.forEach((selectedValue) => {
const node = selectedValue;
if (!node) {
return;
}
const refinesAttr = select('./@refines', node);
if (!refinesAttr) {
return;
}
const refinesValue = refinesAttr.value;
// drop the # prefix
const idRefined = refinesValue[0] === '#' ? refinesValue.substr(1) : refinesValue;
const propertyAttr = select('./@property', node);
if (!propertyAttr) {
return;
}
const property = propertyAttr.value;
if (!this._metaPropertyMap[idRefined]) {
this._metaPropertyMap[idRefined] = {};
}
if (!this._metaPropertyMap[idRefined][property]) {
this._metaPropertyMap[idRefined][property] = [];
}
this._metaPropertyMap[idRefined][property].push(node);
});
}
identifier({ id }) {
return this._resolve(`./dc:identifier${idFilter(id)}`, Identifier);
}
modified() {
const node = this._select("./opf:meta[@property='dcterms:modified' and not(@refines)]");
if (node) {
return new Meta(node, this._context);
}
}
title({ id }) {
return this.titles({ ids: id ? [id] : [] })[0];
}
titles({ ids }) {
return this._resolveAll(`./dc:title${idFilter(ids)}`, Title);
}
language({ id }) {
return this.languages({ ids: id ? [id] : [] })[0];
}
languages({ ids }) {
return this._resolveAll(`./dc:language${idFilter(ids)}`, Language);
}
contributor({ id }) {
return this.contributors({ ids: id ? [id] : [] })[0];
}
contributors({ ids }) {
return this._resolveAll(`./dc:contributor${idFilter(ids)}`, Contributor);
}
coverage({ id }) {
return this.coverages({ ids: id ? [id] : [] })[0];
}
coverages({ ids }) {
return this._resolveAll(`./dc:coverage${idFilter(ids)}`, Coverage);
}
creator({ id }) {
return this.creators({ ids: id ? [id] : [] })[0];
}
creators({ ids }) {
return this._resolveAll(`./dc:creator${idFilter(ids)}`, Creator);
}
date({ id }) {
return this._resolve(`./dc:date${idFilter(id)}`, Date);
}
description({ id }) {
return this.descriptions({ ids: id ? [id] : [] })[0];
}
descriptions({ ids }) {
return this._resolveAll(`./dc:description${idFilter(ids)}`, Description);
}
format({ id }) {
return this.formats({ ids: id ? [id] : [] })[0];
}
formats({ ids }) {
return this._resolveAll(`./dc:format${idFilter(ids)}`, Format);
}
publisher({ id }) {
return this.publishers({ ids: id ? [id] : [] })[0];
}
publishers({ ids }) {
return this._resolveAll(`./dc:publisher${idFilter(ids)}`, Publisher);
}
relation({ id }) {
return this.relations({ ids: id ? [id] : [] })[0];
}
relations({ ids }) {
return this._resolveAll(`./dc:relation${idFilter(ids)}`, Relation);
}
rights({ id }) {
return this.rightses({ ids: id ? [id] : [] })[0];
}
rightses({ ids }) {
return this._resolveAll(`./dc:rights${idFilter(ids)}`, Rights);
}
source({ id }) {
return this.sources({ ids: id ? [id] : [] })[0];
}
sources({ ids }) {
return this._resolveAll(`./dc:source${idFilter(ids)}`, Source);
}
subject({ id }) {
return this.subjects({ ids: id ? [id] : [] })[0];
}
subjects({ ids }) {
return this._resolveAll(`./dc:subject${idFilter(ids)}`, Subject);
}
type({ id }) {
return this.types({ ids: id ? [id] : [] })[0];
}
types({ ids }) {
return this._resolveAll(`./dc:type${idFilter(ids)}`, Type);
}
belongsToCollection({ id }) {
return this.belongsToCollections({ ids: id ? [id] : [] })[0];
}
belongsToCollections({ ids, } = {}) {
return this._resolveAll(`./opf:meta${idFilter(ids)}[='belongs-to-collection' and not()]`, BelongsToCollection);
}
meta({ id, property, refines, } = {}) {
return this.metas({
ids: id ? [id] : [],
property,
refines,
})[0];
}
metas({ ids, property, refines, } = {}) {
return this._resolveAll(`./opf:meta${idFilter(ids)}${attributeFilter('@property', property, 'and')}${attributeFilter('@refines', refines ? `#${refines}` : undefined, 'or')}`, Meta);
}
link({ id, href, anyProperties, allProperties, onlyProperties, anyRel, allRel, onlyRel, } = {}) {
return this.links({
ids: id ? [id] : [],
href,
anyProperties,
allProperties,
onlyProperties,
anyRel,
allRel,
onlyRel,
})[0];
}
links(args = {}) {
const { ids, href, anyProperties, allProperties, onlyProperties, anyRel, allRel, onlyRel, } = args;
if (onlyProperties) {
return this.links(Object.assign(Object.assign({}, args), { allProperties: onlyProperties, onlyProperties: undefined })).filter((item) => toArray(item.properties()).length === onlyProperties.length);
}
if (onlyRel) {
return this.links(Object.assign(Object.assign({}, args), { allRel: onlyRel, onlyRel: undefined })).filter((item) => toArray(item.rels()).length === onlyRel.length);
}
const expression = `./opf:link${idFilter(ids)}${attributeFilter('@href', href)}${anyPropertiesFilter(anyProperties)}${allPropertiesFilter(allProperties)}${anyRelFilter(anyRel)}${allRelFilter(allRel)}`;
return this._resolveAll(expression, Link);
}
}
export class Package extends I18n(ID(Entity)) {
constructor(doc) {
super(select('/opf:package', doc));
}
version() {
return this._resolve('./@version');
}
uniqueIdentifier() {
var _a;
const uniqueIdentifierIDRef = this._resolve('./@unique-identifier');
if (uniqueIdentifierIDRef) {
return (_a = this.metadata()) === null || _a === void 0 ? void 0 : _a.identifier({ id: uniqueIdentifierIDRef });
}
}
releaseIdentifier() {
var _a;
const uniqueIdentifier = this.uniqueIdentifier();
const modified = (_a = this.metadata()) === null || _a === void 0 ? void 0 : _a.modified();
if (uniqueIdentifier && modified) {
return `${uniqueIdentifier.value()}@${modified.value()}`;
}
}
metadata() {
return (this._metadata =
this._metadata || this._resolve('./opf:metadata', Metadata));
}
spine() {
return (this._spine = this._spine || this._resolve('./opf:spine', Spine));
}
manifest() {
return (this._manifest =
this._manifest || this._resolve('./opf:manifest', Manifest));
}
}