UNPKG

mirador

Version:

An open-source, web-based 'multi-up' viewer that supports zoom-pan-rotate functionality, ability to display/compare simple images, and images with annotations.

218 lines (192 loc) 6.41 kB
import flatten from 'lodash/flatten'; import flattenDeep from 'lodash/flattenDeep'; import { Canvas, AnnotationPage, Annotation } from 'manifesto.js'; import { getIiifResourceImageService } from './iiif'; /** * MiradorCanvas - adds additional, testable logic around Manifesto's Canvas * https://iiif-commons.github.io/manifesto/classes/_canvas_.manifesto.canvas.html */ export default class MiradorCanvas { /** * @param {MiradorCanvas} canvas */ constructor(canvas) { this.canvas = canvas; } /** */ get id() { return this.canvas.id; } /** */ getWidth() { return this.canvas.getWidth(); } /** */ getHeight() { return this.canvas.getHeight(); } /** */ get aspectRatio() { return this.canvas.getWidth() / this.canvas.getHeight(); } /** * Fetches AnnotationList URIs from canvas's otherContent property * * Supported otherContent types: * - Objects having @type property of "sc:AnnotationList" and URI in @id * - Strings being the URIs */ get annotationListUris() { return flatten( new Array(this.canvas.__jsonld.otherContent), // eslint-disable-line no-underscore-dangle ) .filter(otherContent => otherContent && (typeof otherContent === 'string' || otherContent['@type'] === 'sc:AnnotationList')) .map(otherContent => (typeof otherContent === 'string' ? otherContent : otherContent['@id'])); } /** */ get canvasAnnotationPages() { return flatten( new Array(this.canvas.__jsonld.annotations), // eslint-disable-line no-underscore-dangle ) .filter(annotations => annotations && annotations.type === 'AnnotationPage'); } /** * Will negotiate a v2 or v3 type of resource */ get imageResource() { return this.imageResources[0]; } /** */ get imageResources() { // TODO Clean up the following hack as soon as manifesto.js provides any information if an annotation body is a Choice option, and if so, whether it is the preferred one. const resources = flattenDeep([ this.canvas.getImages().map(i => i.getResource()), this.canvas.getContent().map(i => (i.__jsonld.body.type === 'Choice' ? i.__jsonld.body : i.getBody())), ]); return flatten(resources.map((resource) => { const type = resource.type || resource.getProperty('type'); switch (type) { case 'Choice': { return new Canvas({ images: resource.items.map(r => ({ resource: r })) }, this.canvas.options) .getImages().map((img, index) => { const r = img.getResource(); if (r) { r.preferred = !index; } return r; }); } case 'oa:Choice': { return new Canvas({ images: flattenDeep([resource.getProperty('default'), resource.getProperty('item')]).map(r => ({ resource: r })) }, this.canvas.options).getImages() .map((img, index) => { const r = img.getResource(); if (r) { r.preferred = !index; } return r; }); } default: { const r = resource; r.preferred = true; return r; } } })); } /** */ get textResources() { const resources = flattenDeep([ this.canvas.getContent().map(i => i.getBody()), ]); return flatten(resources.filter((resource) => resource.getProperty('type') === 'Text')); } /** */ get videoResources() { const resources = flattenDeep([ this.canvas.getContent().map(i => i.getBody()), ]); return flatten(resources.filter((resource) => resource.getProperty('type') === 'Video')); } /** */ get audioResources() { const resources = flattenDeep([ this.canvas.getContent().map(i => i.getBody()), ]); return flatten(resources.filter((resource) => resource.getProperty('type') === 'Sound')); } /** */ get v2VttContent() { const resources = flattenDeep([ this.canvas.getContent().map(i => i.getBody()), ]); return flatten(resources.filter((resource) => resource.getProperty('format') === 'text/vtt')); } /** IIIF v3 captions are stored as 'supplementing' Annotations rather than in the resource content itself */ get v3VttContent() { const resources = flattenDeep(this.canvasAnnotationPages.map(annoPage => { const manifestoAnnoPage = new AnnotationPage(annoPage, this.canvas.options); return manifestoAnnoPage.getItems().map(item => { const manifestoAnnotation = new Annotation(item, this.canvas.options); return manifestoAnnotation.getBody(); }); })); return flatten(resources.filter((resource) => resource.getProperty('format') === 'text/vtt')); } /** */ get resourceAnnotations() { return flattenDeep([ this.canvas.getImages(), this.canvas.getContent(), ]); } /** * Returns a given resource Annotation, based on a contained resource or body * id */ resourceAnnotation(id) { return this.resourceAnnotations.find( anno => anno.getResource().id === id || flatten( new Array(anno.getBody()), ).some(body => body.id === id), ); } /** * Returns the fragment placement values if a resourceAnnotation is placed on * a canvas somewhere besides the full extent */ onFragment(id) { const resourceAnnotation = this.resourceAnnotation(id); if (!resourceAnnotation) return undefined; // IIIF v2 const on = resourceAnnotation.getProperty('on'); // IIIF v3 const target = resourceAnnotation.getProperty('target'); const fragmentMatch = (on || target).match(/xywh=(.*)$/); if (!fragmentMatch) return undefined; return fragmentMatch[1].split(',').map(str => parseInt(str, 10)); } /** */ get iiifImageResources() { return this.imageResources.filter(r => r && getIiifResourceImageService(r)?.id); } /** */ get imageServiceIds() { return this.iiifImageResources.map(r => r && getIiifResourceImageService(r)?.id); } /** * Get the canvas service */ get service() { return this.canvas.__jsonld.service; // eslint-disable-line no-underscore-dangle } /** * Get the canvas label */ getLabel(locale = undefined) { return this.canvas.getLabel().length > 0 ? this.canvas.getLabel().getValue(locale) : String(this.canvas.index + 1); } }