UNPKG

eu-eleysion-recogito-js

Version:

A JavaScript library for text annotation

211 lines (168 loc) 6.82 kB
import React from 'react'; import ReactDOM from 'react-dom'; import Emitter from 'tiny-emitter'; import { WebAnnotation, createEnvironment, setLocale, } from 'eu-eleysion-recogito-client-core'; import TextAnnotator from './TextAnnotator'; import { deflateHTML } from './utils'; import 'eu-eleysion-recogito-client-core/themes/default'; /** * The entrypoint into the application. Provides the * externally visible JavaScript API. */ export class Recogito { constructor(config) { // API calls to this instance are forwarded through a ref this._app = React.createRef(); // Event handling via tiny-emitter this._emitter = new Emitter(); // Environment settings container this._environment = createEnvironment(); // The content element (which contains the text we want to annotate) // is wrapped in a DIV ('wrapperEl'). The application container DIV, // which holds the editor popup, will be attached as a child to the // wrapper element (=a sibling to the content element). This way, // content and editor share the same CSS position reference frame. // // <wrapperEl> // <contentEl /> // <appContainerEl /> // </wrapperEl> // let contentEl = config.content.nodeType ? config.content : document.getElementById(config.content); // Deep-clone the original node, so we can easily destroy the Recogito instance this._originalContent = contentEl.cloneNode(true); // Unless this is preformatted text, remove multi spaces and // empty text nodes, so that HTML char offsets == browser offsets. if (config.mode !== 'pre') contentEl = deflateHTML(contentEl); this._wrapperEl = document.createElement('DIV'); this._wrapperEl.className = 'r6o-content-wrapper'; this._wrapperEl.style.position = 'relative'; if (contentEl instanceof HTMLBodyElement) { this._wrapperEl.append(...contentEl.childNodes); contentEl.appendChild(this._wrapperEl); } else { contentEl.parentNode.insertBefore(this._wrapperEl, contentEl); this._wrapperEl.appendChild(contentEl); } this._appContainerEl = document.createElement('DIV'); this._wrapperEl.appendChild(this._appContainerEl); setLocale(config.locale); ReactDOM.render( <TextAnnotator ref={this._app} env={this._environment} contentEl={contentEl} wrapperEl={this._wrapperEl} config={config} onAnnotationSelected={this.handleAnnotationSelected} onAnnotationCreated={this.handleAnnotationCreated} onAnnotationUpdated={this.handleAnnotationUpdated} onAnnotationDeleted={this.handleAnnotationDeleted} onCancelSelected={this.handleCancelSelected} relationVocabulary={config.relationVocabulary} />, this._appContainerEl ); } handleAnnotationSelected = (annotation, element) => this._emitter.emit('selectAnnotation', annotation.underlying, element); handleAnnotationCreated = (annotation, overrideId) => this._emitter.emit( 'createAnnotation', annotation.underlying, overrideId ); handleAnnotationUpdated = (annotation, previous) => this._emitter.emit( 'updateAnnotation', annotation.underlying, previous.underlying ); handleAnnotationDeleted = (annotation) => this._emitter.emit('deleteAnnotation', annotation.underlying); handleCancelSelected = (annotation) => this._emitter.emit('cancelSelected', annotation.underlying); /******************/ /* External API */ /******************/ // Common shorthand for handling annotationOrId args _wrap = (annotationOrId) => annotationOrId?.type === 'Annotation' ? new WebAnnotation(annotationOrId) : annotationOrId; addAnnotation = (annotation) => this._app.current.addAnnotation(new WebAnnotation(annotation)); clearAnnotations = () => this.setAnnotations(null); clearAuthInfo = () => (this._environment.user = null); destroy = () => { ReactDOM.unmountComponentAtNode(this._appContainerEl); this._wrapperEl.parentNode.insertBefore( this._originalContent, this._wrapperEl ); this._wrapperEl.parentNode.removeChild(this._wrapperEl); }; get disableEditor() { return this._app.current.disableEditor; } set disableEditor(disabled) { this._app.current.disableEditor = disabled; } get disableSelect() { return this._app.current.disableSelect; } set disableSelect(select) { this._app.current.disableSelect = select; } getAnnotations = () => { const annotations = this._app.current.getAnnotations(); return annotations.map((a) => a.underlying); }; loadAnnotations = (url, requestArgs) => fetch(url, requestArgs) .then((response) => response.json()) .then((annotations) => { return this.setAnnotations(annotations).then(() => annotations); }); off = (event, callback) => this._emitter.off(event, callback); on = (event, handler) => this._emitter.on(event, handler); get readOnly() { return this._app.current.readOnly; } set readOnly(readOnly) { this._app.current.readOnly = readOnly; } removeAnnotation = (annotation) => this._app.current.removeAnnotation(new WebAnnotation(annotation)); selectAnnotation = (annotationOrId) => { const selected = this._app.current.selectAnnotation( this._wrap(annotationOrId) ); return selected?.underlying; }; setAnnotations = (arg) => { const annotations = arg || []; const webannotations = annotations.map((a) => new WebAnnotation(a)); return this._app.current.setAnnotations(webannotations); }; setAuthInfo = (authinfo) => (this._environment.user = authinfo); /** * Activates annotation or relationship drawing mode. * @param mode a string, either ANNOTATION (default) or RELATIONS */ setMode = (mode) => this._app.current.setMode(mode); setServerTime = (timestamp) => this._environment.setServerTime(timestamp); get widgets() { return this._app.current.widgets; } set widgets(widgets) { this._app.current.widgets = widgets; } } export const init = (config) => new Recogito(config);