stephenwf / iiif-redux Goto Github PK
View Code? Open in Web Editor NEWIIIF Redux Implementation
Home Page: https://iiif-redux.stephen.wf
IIIF Redux Implementation
Home Page: https://iiif-redux.stephen.wf
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.
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.
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:
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.
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)
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.
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 |
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:
getViewingHint
on Presentation 2 document will return document.viewingHint
(deprecation notice logged in development)getBehavior
on Presentation 2 document will return document.viewingHint
getViewingHint
on Presentation 3 document will return null
(deprecation notice logged in development)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
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 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.
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'],
});
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.
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.
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
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.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.
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.
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:
Need a name. Names are hard.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.