Skip to content
Snippets Groups Projects
Select Git revision
  • 2323ee4efe7a4e4ff7010146b78ffa69f3fa906f
  • mui5-annotation-on-video-stable default
  • get_setter_canvasSizeInformations
  • fix-error-div-into-p
  • annotation-on-video-v2
  • detached
  • annotation-on-video-r17
  • mui5
  • mui5-react-18
  • jacob-test
  • annotation-on-video protected
  • master
  • test-antoinev1
  • 20-fetch-thumbnail-on-annotation
  • add-research-field
  • Save
  • add-plugin
  • 14-wip-no-seek-to
  • 14-bug-on-video-time-control
  • 9_wip_videotests
  • _upgrade_material_ui
  • latest-tetras-16
  • v3.3.0
  • v3.2.0
  • v3.1.1
  • v3.1.0
  • v3.0.0
  • v3.0.0-rc.7
  • v3.0.0-rc.6
  • v3.0.0-rc.5
  • v3.0.0-rc.4
  • v3.0.0-rc.3
  • v3.0.0-rc.2
  • v3.0.0-rc.1
  • v3.0.0-beta.10
  • v3.0.0-beta.9
  • v3.0.0-beta.8
  • v3.0.0-beta.7
  • v3.0.0-beta.6
  • v3.0.0-beta.5
  • v3.0.0-beta.3
41 results

MiradorCanvas.js

Blame
  • user avatar
    Jack Reed authored and Chris Beer committed
    2323ee4e
    History
    MiradorCanvas.js 7.15 KiB
    import flatten from 'lodash/flatten';
    import flattenDeep from 'lodash/flattenDeep';
    import { Canvas, Utils } from 'manifesto.js';
    /**
     * 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();
      }
    
      /**
       * Implements Manifesto's canonicalImageUri algorithm to support
       * IIIF Presentation v3
       */
      canonicalImageUri(w, format = 'jpg', resourceId = undefined) {
        const resource = this.getImageResourceOrDefault(resourceId);
        const service = resource && resource.getServices()[0];
        if (!(service)) return undefined;
        const region = 'full';
        let size = w;
        const imageWidth = resource.getWidth();
        if ((!w) || w === imageWidth) size = 'full';
        const quality = Utils.getImageQuality(service.getProfile());
        const id = service.id.replace(/\/+$/, '');
        return [id, region, size, 0, `${quality}.${format}`].join('/');
      }
    
      /**
       */
      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');
      }
    
      /** */
      processAnnotations(fetchAnnotation, receiveAnnotation) {
        // IIIF v2
        this.annotationListUris.forEach((uri) => {
          fetchAnnotation(this.canvas.id, uri);
        });
        // IIIF v3
        this.canvasAnnotationPages.forEach((annotation) => {
          // If there are no items, try to retrieve the referenced resource.
          // otherwise the resource should be embedded and just add to the store.
          if (!annotation.items) {
            fetchAnnotation(this.canvas.id, annotation.id);
          } else {
            receiveAnnotation(this.canvas.id, annotation.id, annotation);
          }
        });
      }
    
      /**
       * Will negotiate a v2 or v3 type of resource
       */
      get imageResource() {
        return this.imageResources[0];
      }
    
      /** */
      get imageResources() {
        const resources = flattenDeep([
          this.canvas.getImages().map(i => i.getResource()),
          this.canvas.getContent().map(i => i.getBody()),
        ]);
    
        return flatten(resources.map((resource) => {
          switch (resource.getProperty('type')) {
            case 'oa:Choice':
              return new Canvas({ images: flatten([resource.getProperty('default'), resource.getProperty('item')]).map(r => ({ resource: r })) }, this.canvas.options).getImages().map(i => i.getResource());
            default:
              return resource;
          }
        }));
      }
    
      /** */
      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 || anno.getBody().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;
        const fragmentMatch = resourceAnnotation.getProperty('on').match(/xywh=(.*)$/);
        if (!fragmentMatch) return undefined;
        return fragmentMatch[1].split(',').map(str => parseInt(str, 10));
      }
    
      /** */
      get iiifImageResources() {
        return this.imageResources
          .filter(r => r && r.getServices()[0] && r.getServices()[0].id);
      }
    
      /** */
      get imageServiceIds() {
        return this.iiifImageResources.map(r => r.getServices()[0].id);
      }
    
      /** */
      getImageResourceOrDefault(resourceId) {
        const resources = this.imageResources;
    
        if (resourceId) return resources.find(r => r.id === resourceId);
        return resources[0];
      }
    
      /** @private */
      imageInformationUri(resourceId) {
        const image = this.getImageResourceOrDefault(resourceId);
    
        const imageId = image && image.getServices()[0] && image.getServices()[0].id;
    
        if (!imageId) return undefined;
    
        return `${
          imageId.replace(/\/$/, '')
        }/info.json`;
      }
    
      /**
       * checks whether the canvas has a valid height
       */
      get hasValidHeight() {
        return (
          typeof this.canvas.getHeight() === 'number'
          && this.canvas.getHeight() > 0
        );
      }
    
      /**
       * checks whether the canvas has a valid height
       */
      get hasValidWidth() {
        return (
          typeof this.canvas.getHeight() === 'number'
          && this.canvas.getHeight() > 0
        );
      }
    
      /**
       * checks whether the canvas has valid dimensions
       */
      get hasValidDimensions() {
        return (
          this.hasValidHeight
          && this.hasValidWidth
        );
      }
    
      /**
       * Get the canvas service
       */
      get service() {
        return this.canvas.__jsonld.service; // eslint-disable-line no-underscore-dangle
      }
    
      /**
       * Get the canvas label
       */
      getLabel() {
        return this.canvas.getLabel().length > 0
          ? this.canvas.getLabel().map(label => label.value)[0]
          : String(this.canvas.index + 1);
      }
    
      /**
       * Creates a canonical image request for a thumb
       * @param {Number} height
       */
      thumbnail(maxWidth = undefined, maxHeight = undefined, resourceId = undefined) {
        let width;
        let height;
    
        if (!this.imageInformationUri(resourceId)) {
          return undefined;
        }
    
        switch (this.thumbnailConstraints(maxWidth, maxHeight)) {
          case 'sizeByH':
            height = maxHeight;
            break;
          case 'sizeByW':
            width = maxWidth;
            break;
          default:
            height = '150';
        }
    
        // note that, although the IIIF server may support sizeByConfinedWh (e.g. !w,h)
        // this is a IIIF level 2 feature, so we're instead providing w, or h,-style requests
        // which are only level 1.
        return this.canonicalImageUri(undefined, undefined, resourceId).replace(/\/full\/.*\/0\//, `/full/${width || ''},${height || ''}/0/`);
      }
    
      /** @private */
      thumbnailConstraints(maxWidth, maxHeight) {
        if (!maxHeight && !maxWidth) return undefined;
        if (maxHeight && !maxWidth) return 'sizeByH';
        if (!maxHeight && maxWidth) return 'sizeByW';
    
        const { aspectRatio } = this;
        const desiredAspectRatio = maxWidth / maxHeight;
    
        return desiredAspectRatio < aspectRatio ? 'sizeByW' : 'sizeByH';
      }
    }