Skip to content
Snippets Groups Projects
Select Git revision
  • c2edb3b35a846c531c8b376db5fba65be7d7c17b
  • annotation-on-video default protected
  • demo_ci
  • 3-upstream-01022023
  • master
  • gh3538-captions
  • 16-adapt-for-images-annot
  • 15-api-for-annotations-on-video
  • 15-annotations-on-videos
  • video_for_annotations
  • wip-1-annotations-on-videos
  • 9-videoviewer-tests
  • 9_wip_videotests
  • 6-fix-tests-and-ci
  • _fix_ci
  • wip-webpack-from-git
16 results

manifests.js

Blame
  • user avatar
    Michael J. Giarlo authored and GitHub committed
    Consolidate implementations of asArray
    c2edb3b3
    History
    manifests.js 9.71 KiB
    import { createSelector } from 'reselect';
    import createCachedSelector from 're-reselect';
    import { PropertyValue } from 'manifesto.js/dist-esmodule/PropertyValue';
    import { Utils } from 'manifesto.js/dist-esmodule/Utils';
    import getThumbnail from '../../lib/ThumbnailFactory';
    import asArray from '../../lib/asArray';
    import { getCompanionWindow } from './companionWindows';
    import { getManifest } from './getters';
    import { getConfig } from './config';
    
    /** */
    function createManifestoInstance(json, locale) {
      if (!json) return undefined;
      const manifestoObject = Utils.parseManifest(json, locale ? { locale } : undefined);
      // Local patching of Manifesto so that when its a Collection, it behaves similarly
      if (typeof manifestoObject.getSequences != 'function') {
        manifestoObject.getSequences = () => [];
      }
      return manifestoObject;
    }
    
    /** */
    const getLocale = createSelector(
      [
        getCompanionWindow,
        getConfig,
      ],
      (companionWindow = {}, config = {}) => (
        companionWindow.locale || config.language
      ),
    );
    
    /** Convenience selector to get a manifest (or placeholder) */
    export const getManifestStatus = createSelector(
      [getManifest],
      manifest => manifest || { missing: true },
    );
    
    /** Convenience selector to get a manifest loading error */
    export const getManifestError = createSelector(
      [getManifest],
      manifest => manifest && manifest.error,
    );
    
    /** Instantiate a manifesto instance */
    const getContextualManifestoInstance = createCachedSelector(
      getManifest,
      getLocale,
      (manifest, locale) => manifest
        && createManifestoInstance(manifest.json, locale),
    )(
      (state, { companionWindowId, manifestId, windowId }) => [
        manifestId,
        windowId,
        getLocale(state, { companionWindowId }),
      ].join(' - '), // Cache key consisting of manifestId, windowId, and locale
    );
    
    /** Instantiate a manifesto instance */
    export const getManifestoInstance = createSelector(
      getContextualManifestoInstance,
      (state, { json }) => json,
      getLocale,
      (manifesto, manifestJson, locale) => (
        manifestJson && createManifestoInstance(manifestJson, locale)
      ) || manifesto,
    );
    
    export const getManifestLocale = createSelector(
      [getManifestoInstance],
      manifest => manifest && manifest.options && manifest.options.locale && manifest.options.locale.replace(/-.*$/, ''),
    );
    
    /** */
    function getProperty(property) {
      return createSelector(
        [getManifestoInstance],
        manifest => manifest && manifest.getProperty(property),
      );
    }
    
    /**
     * Get the logo for a manifest
     * @param {object} state
     * @param {object} props
     * @param {string} props.manifestId
     * @param {string} props.windowId
     * @return {String|null}
     */
    export const getManifestLogo = createSelector(
      [getManifestoInstance],
      manifest => manifest && manifest.getLogo(),
    );
    
    /**
    * Return the IIIF v3 provider of a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getManifestProvider = createSelector(
      [
        getProperty('provider'),
        getManifestLocale,
      ],
      (provider, locale) => provider
        && provider[0].label
        && PropertyValue.parse(provider[0].label, locale).getValue(),
    );
    
    /**
    * Return the IIIF v3 homepage of a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getManifestHomepage = createSelector(
      [
        getProperty('homepage'),
        getManifestLocale,
      ],
      (homepages, locale) => homepages
        && asArray(homepages).map(homepage => (
          {
            label: PropertyValue.parse(homepage.label, locale)
              .getValue(),
            value: homepage.id || homepage['@id'],
          }
        )),
    );
    
    /**
    * Return the IIIF v3 renderings of a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getManifestRenderings = createSelector(
      [getManifestoInstance],
      manifest => manifest
        && manifest.getRenderings().map(rendering => (
          {
            label: rendering.getLabel().getValue(),
            value: rendering.id,
          }
        )),
    );
    
    /**
    * Return the IIIF v2/v3 seeAlso data from a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getManifestRelatedContent = createSelector(
      [
        getProperty('seeAlso'),
        getManifestLocale,
      ],
      (seeAlso, locale) => seeAlso
        && asArray(seeAlso).map(related => (
          {
            format: related.format,
            label: PropertyValue.parse(related.label, locale)
              .getValue(),
            value: related.id || related['@id'],
          }
        )),
    );
    
    /**
    * Return the IIIF requiredStatement (v3) or attribution (v2) data from a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getRequiredStatement = createSelector(
      [getManifestoInstance],
      manifest => manifest
        && asArray(manifest.getRequiredStatement())
          .filter(l => l.getValues().some(v => v))
          .map(labelValuePair => ({
            label: (labelValuePair.label && labelValuePair.label.getValue()) || null,
            values: labelValuePair.getValues(),
          })),
    );
    
    /**
    * Return the IIIF v2 rights (v3) or license (v2) data from a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export const getRights = createSelector(
      [
        getProperty('rights'),
        getProperty('license'),
        getManifestLocale,
      ],
      (rights, license, locale) => {
        const data = rights || license;
        return asArray(PropertyValue.parse(data, locale).getValues());
      },
    );
    
    /**
    * Return the supplied thumbnail for a manifest or null
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String|null}
    */
    export function getManifestThumbnail(state, props) {
      const manifest = getManifestoInstance(state, props);
      const { thumbnails = {} } = getConfig(state);
    
      if (!manifest) return undefined;
    
      const thumbnail = getThumbnail(manifest, {
        maxHeight: 80, maxWidth: 120, preferredFormats: thumbnails.preferredFormats,
      });
    
      return thumbnail && thumbnail.url;
    }
    
    /**
    * Return manifest title
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String}
    */
    export const getManifestTitle = createSelector(
      [getManifestoInstance],
      manifest => manifest
        && manifest.getLabel().getValue(),
    );
    
    /**
    * Return manifest description
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String}
    */
    export const getManifestDescription = createSelector(
      [getManifestoInstance],
      manifest => manifest
        && manifest.getDescription().getValue(),
    );
    
    /**
    * Return manifest title
    * @param {object} state
    * @param {object} props
    * @param {string} props.manifestId
    * @param {string} props.windowId
    * @return {String}
    */
    export const getManifestUrl = createSelector(
      [getManifestoInstance],
      manifest => manifest && manifest.id,
    );
    
    /**
    * Return metadata in a label / value structure
    * This is a potential seam for pulling the i18n locale from
    * state and plucking out the appropriate language.
    * For now we're just getting the first.
    * @param {object} Manifesto IIIF Resource (e.g. canvas, manifest)
    * @return {Array[Object]}
    */
    export function getDestructuredMetadata(iiifResource) {
      return (iiifResource
        && iiifResource.getMetadata().map(labelValuePair => ({
          label: labelValuePair.getLabel(),
          values: labelValuePair.getValues(),
        }))
      );
    }
    
    /**
     * Return manifest metadata in a label / value structure
     * @param {object} state
     * @param {object} props
     * @param {string} props.manifestId
     * @param {string} props.windowId
     * @return {Array[Object]}
     */
    export const getManifestMetadata = createSelector(
      [getManifestoInstance],
      manifest => manifest && getDestructuredMetadata(manifest),
    );
    
    /** */
    function getLocalesForStructure(item) {
      const languages = [];
    
      if (Array.isArray(item)) {
        languages.push(...item.filter(i => (typeof i === 'object' && i['@language'])).map(i => i['@language']));
      } else if (item && typeof item === 'object') {
        if (item['@language']) languages.push(item['@language']);
      }
      return languages;
    }
    
    /** */
    function getLocales(resource) {
      if (!resource) return [];
    
      const metadata = resource.getProperty('metadata') || [];
      const languages = {};
    
      for (let i = 0; i < metadata.length; i += 1) {
        const item = metadata[i];
        getLocalesForStructure(item.label).forEach((l) => { languages[l] = true; });
        getLocalesForStructure(item.value).forEach((l) => { languages[l] = true; });
      }
      return Object.keys(languages);
    }
    
    export const getMetadataLocales = createSelector(
      [getManifestoInstance],
      manifest => getLocales(manifest),
    );
    
    /** */
    export const getManifestSearchService = createSelector(
      [getManifestoInstance],
      (manifest) => {
        if (!manifest) return null;
        const searchService = manifest.getService('http://iiif.io/api/search/0/search')
         || manifest.getService('http://iiif.io/api/search/1/search');
        if (searchService) return searchService;
        return null;
      },
    );
    
    /** */
    export const getManifestAutocompleteService = createSelector(
      [getManifestSearchService],
      (searchService) => {
        const autocompleteService = searchService && (
          searchService.getService('http://iiif.io/api/search/0/autocomplete')
          || searchService.getService('http://iiif.io/api/search/1/autocomplete')
        );
    
        return autocompleteService && autocompleteService;
      },
    );