Giter Site home page Giter Site logo

stephenwf / iiif-redux Goto Github PK

View Code? Open in Web Editor NEW
7.0 3.0 2.0 4.71 MB

IIIF Redux Implementation

Home Page: https://iiif-redux.stephen.wf

JavaScript 99.11% HTML 0.10% CSS 0.78%
iiif iiif-presentation manifesto iiif-av iiif-image iiif-presentation-3 iiif-search

iiif-redux's Introduction


Moving to Hyperion

This project will soon be part of the Hyperion Framework and APIs finalised. The aim of this library was to standardise both the IIIF presentation 2 and 3 specifications and provide a single layer for accessing data from them. As an experiment it was a success. However, the practicalities of supporting 2 presentation versions, and more when further versions are released is unrealistic.

Where the Hyperion Framework is different is that it will always chase the specification. It is designed to internally work with latest Presentation version, and all IIIF resources requested by it will be upgraded on the fly to the latest version. It's also grounded in strong types, although does not require types to be used by consumers of the framework. This provides a better foundation for writing libraries and tools on top of the IIIF specifications, and is a consolidation of various experiments.

The original architecture and readme can still be found below. You can find some more experiments in the pull requests.


Coverage Status Build Status Codacy Badge FOSSA Status

Demo | Concepts | API Proposal | Selector API | IIIF Spec progress | Contributing


โš ๏ธ IIIF Redux current state is a request for comments from the community with a proposal for managing IIIF resources state on the frontend for both presentational and content creation purposes.

Pull requests + issues are welcomed to discuss and improve this proposal.


What is IIIF Redux?

IIIF Redux is a single source of truth for IIIF resources, split into 3 distinct packages:

  • IIIF Redux - the "to the spec" implementation, storing and normalizing collections, manifests and canvases.
  • IIIF Redux Viewer - more state, actions and selectors for building a viewer experience
  • IIIF Redux Creator - new set of actions for drafting and editing IIIF resources, and hook-able middleware for saving, for creating editor UIs for IIIF resources.

Planned projects

  • IIIF Service plugins - Community sourced set of plugins for understanding and presenting different services on IIIF resources.
  • IIIF GraphQL - built using the selector library, an interface to IIIF endpoints using GraphQL.
  • IIIF Redux AV - Redux state in the temporal dimension, with extensions focused on displaying AV content.
  • IIIF Presentation Upgrade - Presentation 2 to 3 converter, Presentation 2 or 3 normalization, fixing and validation.

Proposal

Testing

For libraries that deal with interoperable data its important to have smoke tests that can be run over large sets of manifests. These tests would have to be expect(blah).toExist() or expect(blah.length).not.toEqual(0) type tests that SHOULD pass for all manifests.

In addition unit tests of the units that make up the library (selectors, reducers, actions).

Finally running Interoperable Manifesto library through Manifesto's tests too would work to ensure parity.

Contributing

This project is currently firmly in the planning and prototyping stages at the moment. Any contributions in the form of pull requests or issues for discussion are welcome.

License

FOSSA Status

iiif-redux's People

Contributors

fossabot avatar renovate-bot avatar renovate[bot] avatar stephenwf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

fossabot tomcrane

iiif-redux's Issues

Dependency deprecation warning: @types/reselect (npm)

On registry https://registry.npmjs.org/, the "latest" version (v2.2.0) of dependency @types/reselect has the following deprecation notice:

This is a stub types definition for reselect (https://github.com/rackt/reselect). reselect provides its own type definitions, so you don't need @types/reselect installed!

Marking the latest version of an npm package as deprecated results in the entire package being considered deprecated, so contact the package author you think this is a mistake.

Please take the actions necessary to rename or substitute this deprecated package and commit to your base branch. If you wish to ignore this deprecation warning and continue using @types/reselect as-is, please add it to your ignoreDeps array in Renovate config before closing this issue, otherwise another issue will be recreated the next time Renovate runs.

Supporting services through extensions

An idea for supporting services.

At some point in the flow, when a service is available (either automatic or user-driven, not sure). A redux event announcing a service has been found is raised:

{
  type: 'SERVICE_ANNOUNCE',
  payload: {
    resource: { schema: 'manifest', id: 'http://.../' },
    service: 'http://serviceid/'
  }
}

If an extension is installed (via saga or middleware) it could listen for these events and respond if it implements the service.

function* imageServiceSaga() {
  yield takeEvery('SERVICE_ANNOUNCE', function *({ payload }) {
     if (isImageService(payload)) {
         yield put({ type: 'IMAGE_SERVICE_IMPORT', payload });
         // other instantiation logic..
         
         // Finally let redux know its accepted
         yield put({ type: 'SERVICE_ACTIVATED', {
          payload: { 
            service: payload.service, 
            id: 'my-image-service',
            label: 'My great image service',
          }
        });
     }
  });
}

This will go back to redux, and allow it to keep track of available extensions, and if they loaded successfully. It also allows redux to expose some methods:

  • isServiceActive
  • isServiceExtensionEnabled
  • enableExtensionService
  • disabledExtensionService
  • getServiceExtensions (on resources)

Which will provide a standard model for enabling/disabling them. The extensions can also listen for these events as they get fired and react as needed.

It would be likely that services would expose UI components of some form, or at very least state selectors that can power custom components. This should all fit into this model to allow any custom service to be bootstrapped, but still known to IIIF Redux.

As for end-users creating UIs that use image services:

How the API could work

class MyComponent extends Component {

  componentWillMount() {
    const { id, imageServiceExists, dispatch } = this.props;
  }
  
  render() {
    const { id, imageService, imageServiceActive, imageServiceExists } = this.props;
    
    if (!imageServiceExists) {
      return <div>No image available.</div>;
    }
    
    
    if (!imageServiceActive) {
      return <div>Loading...</div>;
    }
    
    return (
      <div>
        <h2>Image:</h2>
        <ImageServiceComponent id={imageService} target={id} />
      </div>
    );
  }

}

export default connect(
  canvasByIdSelector(api => ({
    imageServiceActive: api.isServiceActive('image-service'),
    imageServiceExists: api.isServiceExtensionEnabled('image-service'),
    imageService: api.getServiceForExtension('image-service'),
  })
))(MyComponent)

With image service maybe looking like this, using the custom selectors:

class ImageServiceComponent extends Component {
  render() {
    const { tileSource, thumbnail } = this.props;
    <div>
     <img src={thumbnail} width={200} />
     {/* Maybe do something with tile source, pass to OSD? */}
    </div>
  }
}

export default connect(
  imageServiceByIdSelector((api, props) => ({
    tileSource: api.getTileSource,
    thumbnail: api.getThumbnailAtSize(props.thumbnailSize || 200).
  }))
)

Although it would be likely that these services would have components themselves that you can simply drop in that provide this functionality.

Implement Presentation 3

IIIF Presentation 3

Implementation of IIIF Presentation 3 alpha specification. (https://iiif.io/api/presentation/3.0/)

This library aims to give full coverage for both presentation 2 and presentation 3 APIs both for importing and selecting content.

When you import Presentation 2 or 3 content it will automatically be detected and imported. Mixed Presentation 2 and 3 content in a single document is not supported. When importing presentation 2 content, due to inconsistencies you may find the original documents have been modified. This is intentional to provide a predictable and normalized structure. Descriptive properties also get expanded out to the international specification, defaulting to english, for these properties. Future exporting tools may revert this during an export. Future exporting will also offer an option to upgrade presentation 2 documents to the latest stable presentation version.

Selector API

Technical Descriptive Linking Structural
Collection ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Manifest ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Canvas ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Annotation ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Annotation Page ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Range ๐Ÿ”ด ๐Ÿ”ด ๐Ÿ”ด ๐Ÿ”ด
Annotation Collection ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต ๐Ÿ”ต
Content Resources ๐Ÿ”ด ๐Ÿ”ด ๐Ÿ”ด ๐Ÿ”ด
Choices โšช n/a n/a โšช
Services โšช n/a n/a n/a
Icon Meaning
โšช Not started
๐Ÿ”ด Started, not tested
๐Ÿ”ต Finished, tested

API Changes

When presentation updates happen, some parts of the API are deprecated and finally removed, and as such this library does not support calling deprecated APIs on sources that are not the latest version. For example, between Presentation 2.1 and 3.0 the property viewingHint was changed to behavior. Along with this change the scope of the property changed to expand what behavior may contain. Because of this, the following behavior applies:

  • Calling getViewingHint on Presentation 2 document will return document.viewingHint (deprecation notice logged in development)
  • Calling getBehavior on Presentation 2 document will return document.viewingHint
  • Calling getViewingHint on Presentation 3 document will return null (deprecation notice logged in development)
  • Calling getBehavior on Presentation 3 document will return document.behavior

Although this library does aim to have full coverage of all presentation versions, it also provides an upgrade path where possible from lower versions of the specification in your application. If remove all deprecation notices from your application, your application will be compatible with the latest versions of the specification.

Note: This compatibility only extends to structural differences, and not semantics that may have changed

Opting for strict selectors

If you know all of your content conforms strictly to one version of the specification, you can access the selectors directly:

import { canvasByIdSelector } from 'iiif-redux/api/2.x/canvas';
// or
import { canvasByIdSelector } from 'iiif-redux/api/3.x/canvas';

// instead of
import { canvasByIdSelector } from 'iiif-redux/api/canvas';

Similarly, if you want to access the IIIF specification compliant selectors (maybe for your own library) you can access them here:

import * as descriptive from 'iiif-redux/api/2.x/iiif/descriptive';

Its possible choosing a strict version implementation may improve performance on large documents. This will have undesired effects on the wrong
presentation versions though, so it should only be used on data-sets you control and not driven by user-input like traditional viewers.

Sequences between 2.x and 3.x

Sequences are a special case in the change between versions 2 and 3. In Presentation 3 you can still call getSequences and getDefaultSequence on a manifest, but instead of this being defined in the structure, its a special range that's returned. This acts in the same way as the sequence in presentation 2 and can be used to reflect the same structure. However, its more common to instead just use the canvases directly off of the manifest. Because of this, presentation 2 has been retro-fitted with a getCanvases selector that will skip the sequence. This is common practice for viewers.

Language properties

In Presentation 2 and 3 descriptive (human-readable) strings are stored native to their presentation version. When you select, say, a label, you get returned one of 2 structures representing the string:

// Presentation 2
const p2Label = [{ '@language': 'en', '@value': ['some label'] }];

// Presentation 3
const p3Label = {
  en: ['some label'],
};

Its up to the end implementation to support these 2 language implementations. IIIF Redux does offer a function for rendering these strings.

import { intlString } from 'iiif-redux/utility';

const englishString = intlString('en');

// Both return 'some label'
englishString([{ '@language': 'en', '@value': ['some label'] }]);

englishString({
  en: ['some label'],
});

Annotation changes

Annotations have changed a lot between Presentation 2 and 3. It is still unknown how IIIF Redux will differ between implementations. There is a 1-to-1 parity between structural components, which should help to align the changes.

Paging

Annotation paging defined in W3C remains, but is not supported in Presentation 2 or 3 in this library due to its relatively low usage. This may change if demand changes.

Implementation details

When you import a document either manually specifying a presentation version, or if you choose it to be auto-detected, for every resource imported an index will be created that contains the schema version.

const state = {
  schemaVersions: {
    'https://../1': 3,
    'https://../2': 3,
    'https://../3': 2,
  },
};

Although this does add an overhead to the redux store, it ensures that any resource that is loaded is identified correctly when you apply a selector.

You can opt-out of this extra overhead by creating a store targeting a specific version

Presentation 3 questions

  • For the field seeAlso and partOf, the specification does not provide a list of valid IIIF resources that can be found here, implying all IIIF resources are valid.

Changes to selector framework

Previously if you wanted to get just the label selector, you could do something like this:

import collection from 'iiif-redux/api/collection';

const state = {
  /* ... */
};

const getFirstCollection = () => /***/ null;

const { getLabel } = collection(getFirstCollection);

const label = getLabel(state);

console.log(label);

So you could effectively have access to the individual selectors. Because of the uncertainty using multiple presentation versions, this model has now changed to match the select by ID and collection selectors.

import collection from 'iiif-redux/api/collection';

const state = {
  /* ... */
};

const getFirstCollection = () => /***/ null;

const firstCollection = collection(getFirstCollection);

const selector = firstCollection(api => ({
  label: api.getLabel,
}));

console.log(selector(state).label);

// Alternatively
const getLabel = firstCollection(api => api.getLabel);

const label = getLabel(state);

console.log(label);

This unifies how the other selectors work too. If you still want access to individual selectors, say to build up your own library, you can still access each selector library for each presentation version:

import presentation2Collection from 'iiif-redux/api/2.x/collection';
import presentation3Collection from 'iiif-redux/api/3.x/collection';

const getFirstCollection = () => /***/ null;

const { getLabel, getId } = presentation2Collection(getFirstCollection);

const myCustomP2Selector = createStructuredSelector({
  label: getLabel,
  id: getId,
});

This is only recommended if you know what version of the specification you are using as you will have to manage the different versions yourself at this point.

If there is demand for a universal API that exposes single selectors, it may be added and work like this:

import collection from 'iiif-redux/api/2.x-3.x/collection';

const getFirstCollection = () => /***/ null;

const { getLabel, getId } = collection(getFirstCollection);

But this is likely to have a negative impact on performance as the version look up will happen for each individual field, instead of once per selector.

Manifest start canvas

Extra compatibility for Presentation 2 to 3, need an api for manifest.getStartCanvasId and manifest.getStartCanvas that can map to presentation 3s equivelent: manifest.getStart and manifest.getStartId

In presentation 2 this is an API on the sequence. To build Presentation 2/3 compatible APIs we need to be able to treat the sequences both as a range and also as part of the manifest. So the manifest API would have to reach into the first sequence to get the start canvas. A limitation of presentation 2 and 3 compatible apps, we won't support multiple sequences with multiple start properties, unless they are accessed from ranges.

Will probably cause other compatibility issues, since the resource types may be different, so selectors may be incompatible or.. funky. Shouldn't affect the GraphQL implementation (provided @type exists on resources)

Change name of library

I want to change the name of this library away from IIIF-Redux to something more general as its really a collection of libraries for interfacing IIIF content. That could cover:

  • IIIF Redux - redux compatible bindings for building applications
  • IIIF creation tools - minting new or editing existing IIIF content
  • IIIF GraphQL - server or client side GraphQL interface over IIIF content
  • IIIF reference UI - UI reference built with one set of CSS but written up in multiple frontend libraries hooked up to IIIF-Redux selectors and GraphQL queries for building up IIIF content viewers.

Need a name. Names are hard.

Normalize "within" properties

When resource come in, they may have a "within" property. That property might related to a layer, collection or manifests inside of the resource, or an external resource. This should be calculated and normalized from:

{
    "within": "http://collection.json"
}

to:

{
    "within": {
        "@id": "http://collection.json",
        "@type": "sc:Collection"
     }
}

If it is known what type of resource it is use that, or else assume its an external resource. When an external resource is loaded, we can pro-actively go back and update its definition.

Add support for textual content of canvases

It would be great if, once you get to the level of the canvas, you can continue using the API to extract and render the textual content.

There's a very crude idea of what this might look like here:
http://tomcrane.github.io/wellcome-today/annodump.html?manifest=https://wellcomelibrary.org/iiif/b28047345/manifest

(tick the "lines of text" box and navigate through the work to some printed content).

Having helpers like getTextLines(..), getFullText(..) and so on would help application developers pull out the text content for reuse.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.