diff --git a/__tests__/fixtures/version-2/minimumRequired.json b/__tests__/fixtures/version-2/minimumRequired.json new file mode 100644 index 0000000000..c07ca46ae4 --- /dev/null +++ b/__tests__/fixtures/version-2/minimumRequired.json @@ -0,0 +1,34 @@ +{ + "@context": "http://iiif.io/api/presentation/2/context.json", + "@id": "http://iiif.io/api/presentation/2.0/example/fixtures/1/manifest.json", + "@type": "sc:Manifest", + "label": "Test 1 Manifest: Minimum Required Fields", + "within": "http://iiif.io/api/presentation/2.0/example/fixtures/collection.json", + "sequences": [ + { + "@type": "sc:Sequence", + "canvases": [ + { + "@id": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/1/c1.json", + "@type": "sc:Canvas", + "label": "Test 1 Canvas: 1", + "height": 1800, + "width": 1200, + "images": [ + { + "@type": "oa:Annotation", + "motivation": "sc:painting", + "resource": { + "@id": "http://iiif.io/api/presentation/2.0/example/fixtures/resources/page1-full.png", + "@type": "dctypes:Image", + "height": 1800, + "width": 1200 + }, + "on": "http://iiif.io/api/presentation/2.0/example/fixtures/canvas/1/c1.json" + } + ] + } + ] + } + ] +} diff --git a/__tests__/src/components/OpenSeadragonViewer.test.js b/__tests__/src/components/OpenSeadragonViewer.test.js index 4822d4178d..141315ce35 100644 --- a/__tests__/src/components/OpenSeadragonViewer.test.js +++ b/__tests__/src/components/OpenSeadragonViewer.test.js @@ -118,7 +118,7 @@ describe('OpenSeadragonViewer', () => { }); }); - describe('addAllTileSources', () => { + describe('addAllImageSources', () => { it('calls addTileSource for every tileSources and then zoomsToWorld', () => { wrapper.instance().viewer = { close: () => {}, @@ -126,9 +126,19 @@ describe('OpenSeadragonViewer', () => { wrapper.setProps({ tileSources: [1, 2, 3, 4] }); const mockAddTileSource = jest.fn(); wrapper.instance().addTileSource = mockAddTileSource; - wrapper.instance().addAllTileSources(); + wrapper.instance().addAllImageSources(); expect(mockAddTileSource).toHaveBeenCalledTimes(4); }); + it('calls addNonTileSource for every nonTiledImage and then zoomsToWorld', () => { + wrapper.instance().viewer = { + close: () => {}, + }; + wrapper.setProps({ nonTiledImages: [1, 2, 3, 4] }); + const mockAddNonTiledImage = jest.fn(); + wrapper.instance().addNonTiledImage = mockAddNonTiledImage; + wrapper.instance().addAllImageSources(); + expect(mockAddNonTiledImage).toHaveBeenCalledTimes(4); + }); }); describe('addTileSource', () => { diff --git a/__tests__/src/selectors/canvases.test.js b/__tests__/src/selectors/canvases.test.js index a6fcf1a745..d0da4e73a2 100644 --- a/__tests__/src/selectors/canvases.test.js +++ b/__tests__/src/selectors/canvases.test.js @@ -1,5 +1,7 @@ import manifestFixture001 from '../../fixtures/version-2/001.json'; import manifestFixture019 from '../../fixtures/version-2/019.json'; +import minimumRequired from '../../fixtures/version-2/minimumRequired.json'; + import { getVisibleCanvases, getNextCanvasGrouping, @@ -9,6 +11,7 @@ import { selectCanvasAuthService, selectNextAuthService, selectInfoResponse, + getVisibleCanvasNonTiledResources, } from '../../../src/state/selectors/canvases'; describe('getVisibleCanvases', () => { @@ -391,3 +394,25 @@ describe('selectInfoResponse', () => { expect(selectInfoResponse(state, { canvasId: 'some-canvas-without-services', manifestId: 'a' })).toBe(undefined); }); }); + +describe('getVisibleCanvasNonTiledResources', () => { + it('returns canvases resources without services', () => { + const state = { + manifests: { + 'http://iiif.io/api/presentation/2.0/example/fixtures/1/manifest.json': { + id: 'http://iiif.io/api/presentation/2.0/example/fixtures/1/manifest.json', + json: minimumRequired, + }, + }, + windows: { + a: { + canvasId: 'http://iiif.io/api/presentation/2.0/example/fixtures/canvas/1/c1.json', + manifestId: 'http://iiif.io/api/presentation/2.0/example/fixtures/1/manifest.json', + }, + }, + }; + expect(getVisibleCanvasNonTiledResources( + state, { windowId: 'a' }, + )[0].id).toBe('http://iiif.io/api/presentation/2.0/example/fixtures/resources/page1-full.png'); + }); +}); diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index 94538fe2ce..1d5472cff5 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -84,7 +84,7 @@ export class OpenSeadragonViewer extends Component { this.viewer.viewport.panTo(viewer, true); this.viewer.viewport.zoomTo(viewer.zoom, viewer, true); } - this.addAllTileSources(); + this.addAllImageSources(); } /** @@ -125,7 +125,7 @@ export class OpenSeadragonViewer extends Component { if (!this.tileSourcesMatch(prevProps.tileSources)) { this.viewer.close(); - this.addAllTileSources(); + this.addAllImageSources(); } else if (viewer && !this.osdUpdating) { const { viewport } = this.viewer; @@ -197,17 +197,36 @@ export class OpenSeadragonViewer extends Component { } /** */ - addAllTileSources() { - const { tileSources } = this.props; + addAllImageSources() { + const { nonTiledImages, tileSources } = this.props; Promise.all( tileSources.map((tileSource, i) => this.addTileSource(tileSource, i)), + nonTiledImages.map((image, i) => this.addNonTiledImage(image, i)), ).then(() => { - if (tileSources[0]) { + if (tileSources[0] || nonTiledImages[0]) { this.zoomToWorld(); } }); } + /** */ + addNonTiledImage(image, i = 0) { + const { canvasWorld } = this.props; + return new Promise((resolve, reject) => { + if (!this.viewer) { + return; + } + this.viewer.addSimpleImage({ + error: event => reject(event), + fitBounds: new OpenSeadragon.Rect( + ...canvasWorld.canvasToWorldCoordinates(i), + ), + success: event => resolve(event), + url: image.id, + }); + }); + } + /** */ addTileSource(tileSource, i = 0) { @@ -321,6 +340,7 @@ OpenSeadragonViewer.defaultProps = { children: null, highlightedAnnotations: [], label: null, + nonTiledImages: [], osdConfig: {}, palette: {}, searchAnnotations: [], @@ -336,6 +356,7 @@ OpenSeadragonViewer.propTypes = { classes: PropTypes.objectOf(PropTypes.string).isRequired, highlightedAnnotations: PropTypes.arrayOf(PropTypes.object), label: PropTypes.string, + nonTiledImages: PropTypes.array, // eslint-disable-line react/forbid-prop-types osdConfig: PropTypes.object, // eslint-disable-line react/forbid-prop-types palette: PropTypes.object, // eslint-disable-line react/forbid-prop-types searchAnnotations: PropTypes.arrayOf(PropTypes.object), diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js index 196d195ff2..65408cbfd1 100644 --- a/src/containers/OpenSeadragonViewer.js +++ b/src/containers/OpenSeadragonViewer.js @@ -7,6 +7,7 @@ import { OpenSeadragonViewer } from '../components/OpenSeadragonViewer'; import * as actions from '../state/actions'; import CanvasWorld from '../lib/CanvasWorld'; import { + getVisibleCanvasNonTiledResources, getCurrentCanvas, getSelectedAnnotationsOnCanvases, getHighlightedAnnotationsOnCanvases, @@ -34,6 +35,7 @@ const mapStateToProps = (state, { companionWindowId, windowId }) => ({ canvasId: (getCurrentCanvas(state, { windowId }) || {}).id, windowId, }), + nonTiledImages: getVisibleCanvasNonTiledResources(state, { windowId }), osdConfig: state.config.osdConfig, palette: getTheme(state).palette, searchAnnotations: getSearchAnnotationsForWindow( diff --git a/src/state/selectors/canvases.js b/src/state/selectors/canvases.js index 99753107d2..d094c1db5c 100644 --- a/src/state/selectors/canvases.js +++ b/src/state/selectors/canvases.js @@ -1,5 +1,6 @@ import { createSelector } from 'reselect'; import { Utils } from 'manifesto.js/dist-esmodule/Utils'; +import flatten from 'lodash/flatten'; import CanvasGroupings from '../../lib/CanvasGroupings'; import { getManifestoInstance } from './manifests'; import { getWindow, getWindowViewType } from './windows'; @@ -170,6 +171,16 @@ export const getCanvasDescription = createSelector( /** */ export const selectInfoResponses = state => state.infoResponses; +export const getVisibleCanvasNonTiledResources = createSelector( + [ + getVisibleCanvases, + ], + canvases => canvases && flatten(canvases + .map(canvas => canvas.getImages() + .map(image => image.getResource()))) + .filter(resource => resource.getServices().length < 1), +); + export const selectInfoResponse = createSelector( [ getCanvas,