@allmaps/iiif-parser
Version:
Allmaps IIIF parser
263 lines (262 loc) • 10.3 kB
JavaScript
import { Collection2Schema, Collection3Schema, CollectionSchema } from '../schemas/iiif.js';
import { EmbeddedManifest, Manifest } from './manifest.js';
import { parseVersion2String, parseVersion3String, parseVersion2Metadata, parseVersion3Metadata, parseVersion2Attribution, parseVersion2Thumbnail, parseVersion2Rendering, parseVersion2Related } from '../lib/convert.js';
const CollectionTypeString = 'collection';
const defaulfFetchNextOptions = {
maxDepth: Number.POSITIVE_INFINITY,
fetchCollections: true,
fetchManifests: true,
fetchImages: false,
fetchFn: globalThis.fetch
};
export class EmbeddedCollection {
uri;
type = CollectionTypeString;
majorVersion;
// TODO: add description?
// TODO: add metadata?
label;
embedded = true;
constructor(parsedCollection) {
if ('@type' in parsedCollection) {
// IIIF Presentation API 2.0
this.uri = parsedCollection['@id'];
this.majorVersion = 2;
this.label = parseVersion2String(parsedCollection.label);
}
else if ('type' in parsedCollection) {
// IIIF Presentation API 3.0
this.uri = parsedCollection.id;
this.majorVersion = 3;
this.label = parseVersion3String(parsedCollection.label);
}
else {
// TODO: improve error message
throw new Error('Unsupported Collection');
}
}
/**
* Parses a IIIF Collection and returns a [Collection](#collection) containing the parsed version
* @param iiifCollection - Source data of IIIF Collection
* @param majorVersion - IIIF API version of Collection. If not provided, it will be determined automatically
* @returns Parsed IIIF Collection
* @static
*/
static parse(iiifCollection, majorVersion = null) {
let parsedCollection;
if (majorVersion === 2) {
parsedCollection = Collection2Schema.parse(iiifCollection);
}
else if (majorVersion === 3) {
parsedCollection = Collection3Schema.parse(iiifCollection);
}
else {
parsedCollection = CollectionSchema.parse(iiifCollection);
}
return new Collection(parsedCollection);
}
}
/**
* Parsed IIIF Collection
*
* @property uri - URI of Collection
* @property label - Label of Collection
* @property items - Items in Collection
* @property majorVersion - IIIF API version of Collection
* @property type - Resource type, equals 'collection'
*/
export class Collection extends EmbeddedCollection {
items = [];
embedded = false;
description;
metadata;
navDate;
navPlace;
homepage;
thumbnail;
rendering;
seeAlso;
summary;
requiredStatement;
annotations;
constructor(parsedCollection) {
super(parsedCollection);
if ('@type' in parsedCollection) {
// IIIF Presentation API 2.0
this.description = parseVersion2String(parsedCollection.description);
this.label = parseVersion2String(parsedCollection.label);
this.metadata = parseVersion2Metadata(parsedCollection.metadata);
this.navDate = parsedCollection.navDate;
this.navPlace = parsedCollection.navPlace;
this.requiredStatement = parseVersion2Attribution(parsedCollection.attribution);
this.thumbnail = parseVersion2Thumbnail(parsedCollection.thumbnail);
this.rendering = parseVersion2Rendering(parsedCollection.rendering);
this.homepage = parseVersion2Related(parsedCollection.related);
const manifests = 'manifests' in parsedCollection && parsedCollection.manifests
? parsedCollection.manifests
: [];
const collections = 'collections' in parsedCollection && parsedCollection.collections
? parsedCollection.collections
: [];
const members = 'members' in parsedCollection && parsedCollection.members
? parsedCollection.members
: [];
const items = [...manifests, ...collections, ...members];
this.items = items.map((item) => {
if (item['@type'] === 'sc:Collection') {
return new Collection(item);
}
else {
return new EmbeddedManifest(item);
}
});
}
else if ('type' in parsedCollection) {
// IIIF Presentation API 3.0
this.description = parseVersion3String(parsedCollection.description);
this.metadata = parseVersion3Metadata(parsedCollection.metadata);
this.navDate = parsedCollection.navDate;
this.navPlace = parsedCollection.navPlace;
this.homepage = parsedCollection.homepage;
this.thumbnail = parsedCollection.thumbnail;
this.rendering = parsedCollection.rendering;
this.seeAlso = parsedCollection.seeAlso;
this.summary = parsedCollection.summary;
this.requiredStatement = parsedCollection.requiredStatement;
this.annotations = parsedCollection.annotations;
if ('items' in parsedCollection) {
this.items = parsedCollection.items.map((item) => {
if (item.type === 'Collection') {
if ('items' in item) {
return new Collection(item);
}
else {
item;
return new EmbeddedCollection(item);
}
}
else {
return new EmbeddedManifest(item);
}
});
}
}
else {
// TODO: improve error message
throw new Error('Unsupported Collection');
}
}
/**
* Parses a IIIF Collection and returns a [Collection](#collection) containing the parsed version
* @param iiifCollection - Source data of IIIF Collection
* @param majorVersion - IIIF API version of Collection. If not provided, it will be determined automatically
* @returns Parsed IIIF Collection
*/
static parse(iiifCollection, majorVersion = null) {
let parsedCollection;
if (majorVersion === 2) {
parsedCollection = Collection2Schema.parse(iiifCollection);
}
else if (majorVersion === 3) {
parsedCollection = Collection3Schema.parse(iiifCollection);
}
else {
parsedCollection = CollectionSchema.parse(iiifCollection);
}
return new Collection(parsedCollection);
}
get canvases() {
const initialValue = [];
return this.items.reduce((canvases, item) => {
if (item instanceof Manifest) {
return [...canvases, ...item.canvases];
}
else if (item instanceof EmbeddedManifest) {
return canvases;
}
else if (item instanceof Collection) {
return [...canvases, ...item.canvases];
}
return canvases;
}, initialValue);
}
get images() {
return this.canvases.map((canvas) => canvas.image);
}
async fetchAll(options) {
const results = [];
for await (const next of this.fetchNext(options)) {
results.push(next);
}
return results;
}
async *fetchNext(options, depth = 0) {
options = {
...defaulfFetchNextOptions,
...options
};
if (Number.isNaN(options.maxDepth)) {
return;
}
if (options.maxDepth === undefined) {
options.maxDepth = defaulfFetchNextOptions.maxDepth;
}
if (options.fetchImages === undefined) {
options.fetchImages = defaulfFetchNextOptions.fetchImages;
}
if (options.fetchManifests === undefined) {
options.fetchManifests = defaulfFetchNextOptions.fetchManifests;
}
let fetchFn = defaulfFetchNextOptions.fetchFn;
if (options.fetchFn) {
fetchFn = options.fetchFn;
}
if (depth >= options.maxDepth) {
return;
}
for (const itemIndex in this.items) {
let item = this.items[itemIndex];
if (item instanceof Manifest) {
if (options.fetchImages) {
yield* item.fetchNext(fetchFn, depth + 1);
}
}
else if (item instanceof EmbeddedManifest && options.fetchManifests) {
const manifestUri = item.uri;
const iiifManifest = await fetchFn(manifestUri).then((response) => response.json());
const newParsedManifest = Manifest.parse(iiifManifest);
this.items[itemIndex] = newParsedManifest;
yield {
item: newParsedManifest,
depth: depth + 1,
parent: {
uri: this.uri,
type: this.type
}
};
if (depth + 1 < options.maxDepth && options.fetchImages) {
yield* newParsedManifest.fetchNext(fetchFn, depth + 2);
}
}
else if (item instanceof EmbeddedCollection &&
options.fetchCollections) {
const collectionUri = item.uri;
const iiifCollection = await fetchFn(collectionUri).then((response) => response.json());
const newParsedCollection = Collection.parse(iiifCollection);
this.items[itemIndex] = newParsedCollection;
yield {
item: newParsedCollection,
depth: depth + 1,
parent: {
uri: this.uri,
type: this.type
}
};
item = newParsedCollection;
if (depth + 1 < options.maxDepth) {
yield* newParsedCollection.fetchNext(options, depth + 2);
}
}
}
}
}