diff --git a/src/Icons.js b/src/Icons.js index 71964ad..22d1f4e 100644 --- a/src/Icons.js +++ b/src/Icons.js @@ -36,7 +36,7 @@ export const CheckIcon = props => { IcoFont Icons verification-check - + ) diff --git a/src/TextAnnotator.jsx b/src/TextAnnotator.jsx index edd8f85..1baf1f9 100644 --- a/src/TextAnnotator.jsx +++ b/src/TextAnnotator.jsx @@ -82,8 +82,16 @@ export default class TextAnnotator extends Component { this.selectionHandler.clearSelection(); this.highlighter.addOrUpdateAnnotation(annotation, previous); + // A convenience method that allows the external application to + // override the autogenerated Id + const overrideId = originalId => forcedId => + this.highlighter.overrideId(originalId, forcedId); + // Call CREATE or UPDATE handler - this.props[method](annotation, previous); + if (previous) + this.props[method](annotation.clone(), previous.clone()); + else + this.props[method](annotation.clone(), overrideId(annotation.id)); } onDeleteAnnotation = annotation => { @@ -134,10 +142,15 @@ export default class TextAnnotator extends Component { // otherwise, fire 'update' const isNew = previous.annotation.bodies.length === 0; + // A convenience method that allows the external application to + // override the autogenerated Id + const overrideId = originalId => forcedId => + this.relationsLayer.overrideId(originalId, forcedId); + if (isNew) - this.props.onAnnotationCreated(relation.annotation); + this.props.onAnnotationCreated(relation.annotation.clone(), overrideId(relation.annotation.id)); else - this.props.onAnnotationUpdated(relation.annotation, previous.annotation); + this.props.onAnnotationUpdated(relation.annotation.clone(), previous.annotation.clone()); } /** 'Delete' on the relation editor popup **/ @@ -152,7 +165,7 @@ export default class TextAnnotator extends Component { /****************/ addAnnotation = annotation => { - this.highlighter.addOrUpdateAnnotation(annotation); + this.highlighter.addOrUpdateAnnotation(annotation.clone()); } removeAnnotation = annotation => { @@ -165,14 +178,15 @@ export default class TextAnnotator extends Component { } setAnnotations = annotations => { - this.highlighter.init(annotations).then(() => - this.relationsLayer.init(annotations)); + const clones = annotations.map(a => a.clone()); + this.highlighter.init(clones).then(() => + this.relationsLayer.init(clones)); } getAnnotations = () => { const annotations = this.highlighter.getAllAnnotations(); const relations = this.relationsLayer.getAllRelations(); - return annotations.concat(relations); + return annotations.concat(relations).map(a => a.clone()); } setMode = mode => { diff --git a/src/highlighter/Highlighter.js b/src/highlighter/Highlighter.js index f185b2c..7acbdb1 100644 --- a/src/highlighter/Highlighter.js +++ b/src/highlighter/Highlighter.js @@ -53,17 +53,15 @@ export default class Highlighter { } _findAnnotationSpans = annotation => { - // TODO index annotation to make this faster const allAnnotationSpans = document.querySelectorAll('.r6o-annotation'); return Array.prototype.slice.call(allAnnotationSpans) .filter(span => span.annotation.isEqual(annotation)); } getAllAnnotations = () => { - // TODO index annotation to make this faster const allAnnotationSpans = document.querySelectorAll('.r6o-annotation'); - return Array.prototype.slice.call(allAnnotationSpans) - .map(span => span.annotation); + const allAnnotations = Array.from(allAnnotationSpans).map(span => span.annotation); + return [...new Set(allAnnotations)]; } addOrUpdateAnnotation = (annotation, maybePrevious) => { @@ -90,6 +88,25 @@ export default class Highlighter { } } + /** + * Forces a new ID on the given annotation (or annotation with the given ID). + * This method handles the ID update within the Highlighter ONLY. It's up to + * the application to keep the RelationsLayer in sync! + * + * @returns the updated annotation for convenience + */ + overrideId = (annotationOrId, forcedId) => { + const id = annotationOrId.id ? annotationOrId.id : annotationOrId; + + const allSpans = document.querySelectorAll(`.r6o-annotation[data-id="${id}"]`); + const annotation = allSpans[0].annotation; + + const updatedAnnotation = annotation.clone({ id : forcedId }); + this.bindAnnotation(updatedAnnotation, allSpans); + + return updatedAnnotation; + } + _unwrapHighlightings(highlightSpans) { for (const span of highlightSpans) { const parent = span.parentNode; @@ -106,8 +123,7 @@ export default class Highlighter { bindAnnotation = (annotation, elements) => { elements.forEach(el => { el.annotation = annotation; - if (annotation.id) - el.dataset.id = annotation.id; + el.dataset.id = annotation.id; }); } diff --git a/src/relations/RelationsLayer.js b/src/relations/RelationsLayer.js index 2a20ae6..b261696 100644 --- a/src/relations/RelationsLayer.js +++ b/src/relations/RelationsLayer.js @@ -93,6 +93,16 @@ export default class RelationsLayer extends EventEmitter { } } + overrideId = (annotationOrId, forcedId) => { + const id = annotationOrId.id ? annotationOrId.id : annotationOrId; + const conn = this.connections.find(c => c.annotation.id == id); + + const updatedAnnotation = conn.annotation.clone({ id : forcedId }); + conn.annotation = updatedAnnotation; + + return conn; + } + getAllRelations = () => { return this.connections.map(c => c.annotation); }