UNPKG

substance

Version:

Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing system. It is developed to power our online editing platform [Substance](http://substance.io).

98 lines (82 loc) 2.48 kB
import isString from '../util/isString' import isNumber from '../util/isNumber' import filter from '../util/filter' import map from '../util/map' import TreeIndex from '../util/TreeIndex' import DocumentIndex from './DocumentIndex' /* Index for Annotations and InlineNodes. @example Lets us look up existing annotations by path and type To get all annotations for the content of a text node var aIndex = doc.annotationIndex aIndex.get(["text_1", "content"]) You can also scope for a specific range aIndex.get(["text_1", "content"], 23, 45) */ export default class AnnotationIndex extends DocumentIndex { constructor () { super() this.byPath = new TreeIndex() this.byType = new TreeIndex() } select (node) { return node.isPropertyAnnotation() || node.isInlineNode() } clear () { this.byPath.clear() this.byType.clear() } // TODO: use object interface? so we can combine filters (path and type) get (path, start, end, type) { let annotations if (isString(path) || path.length === 1) { annotations = this.byPath.getAll(path) || {} } else { annotations = this.byPath.get(path) } annotations = map(annotations) if (isNumber(start)) { annotations = filter(annotations, AnnotationIndex.filterByRange(start, end)) } if (type) { annotations = filter(annotations, DocumentIndex.filterByType(type)) } return annotations } create (anno) { const path = anno.start.path this.byType.set([anno.type, anno.id], anno) if (path && path.length > 0) { this.byPath.set(anno.start.path.concat([anno.id]), anno) } } delete (anno) { this._delete(anno.type, anno.id, anno.start.path) } _delete (type, id, path) { this.byType.delete([type, id]) if (path && path.length > 0) { this.byPath.delete(path.concat([id])) } } update (node, path, newValue, oldValue) { // TODO: this should better be a coordinate op if (this.select(node) && path[1] === 'start' && path[2] === 'path') { this._delete(node.type, node.id, oldValue) this.create(node) } } static filterByRange (start, end) { return function (anno) { var aStart = anno.start.offset var aEnd = anno.end.offset var overlap = (aEnd >= start) // Note: it is allowed to omit the end part if (isNumber(end)) { overlap = overlap && (aStart <= end) } return overlap } } }