all files / model/ AnnotationIndex.js

96.97% Statements 32/33
89.47% Branches 17/19
100% Functions 10/10
96.97% Lines 32/33
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93                                          725×   725× 725×       5855×       725× 725× 725×         2035× 2035× 166×   1869×   2035× 2035× 551×   2035×     2035×       2062× 2062×             12× 12×         25×         551× 103× 103× 103×   103× 100×   103×          
import { isString, isNumber, filter, map, TreeIndex } from '../util'
import DocumentIndex from './DocumentIndex'
 
/*
  Index for Annotations.
 
  @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)
*/
class AnnotationIndex extends DocumentIndex {
 
  constructor() {
    super()
 
    this.byPath = new TreeIndex()
    this.byType = new TreeIndex()
  }
 
  select(node) {
    return Boolean(node._isPropertyAnnotation)
  }
 
  reset(data) {
    this.byPath.clear()
    this.byType.clear()
    this._initialize(data)
  }
 
  // TODO: use object interface? so we can combine filters (path and type)
  get(path, start, end, type) {
    var 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))
    }
    Iif (type) {
      annotations = filter(annotations, DocumentIndex.filterByType(type))
    }
    return annotations
  }
 
  create(anno) {
    this.byType.set([anno.type, anno.id], anno)
    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])
    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)
    }
  }
}
 
AnnotationIndex.filterByRange = function(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
  }
}
 
export default AnnotationIndex