UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

227 lines (226 loc) 7.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OverlayPoint = void 0; const Point_1 = require("../rga/Point"); const clock_1 = require("../../../json-crdt-patch/clock"); const refs_1 = require("./refs"); const printTree_1 = require("tree-dump/lib/printTree"); /** * A {@link Point} which is indexed in the {@link Overlay} tree. Represents * sparse locations in the string of the places where annotation slices start, * end, or are broken down by other intersecting slices. */ class OverlayPoint extends Point_1.Point { constructor() { super(...arguments); /** * Hash of text contents until the next {@link OverlayPoint}. This field is * modified by the {@link Overlay} tree. */ this.hash = 0; // ------------------------------------------------------------------- layers /** * Sorted list of layers, contains the interval from this point to the next * one. A *layer* is a part of a slice from the current point to the next one. * This interval can contain many layers, as the slices can be overlapped. */ this.layers = []; // ------------------------------------------------------------------ markers /** * Collapsed slices - markers (block splits), which represent a single point * in the text, even if the start and end of the slice are different. */ this.markers = []; // --------------------------------------------------------------------- refs /** * Sorted list of all references to rich-text constructs. */ this.refs = []; // ------------------------------------------------------------- HeadlessNode this.p = undefined; this.l = undefined; this.r = undefined; } /** * Inserts a slice to the list of layers which contains the area from this * point until the next one. The operation is idempotent, so inserting the * same slice twice will not change the state of the point. The layers are * sorted by the slice ID. * * @param slice Slice to add to the layer list. */ addLayer(slice) { const layers = this.layers; const length = layers.length; if (!length) { layers.push(slice); return; } // We attempt to insert from the end of the list, as it is the most likely // scenario. And `.push()` is more efficient than `.unshift()`. const lastSlice = layers[length - 1]; const sliceId = slice.id; const cmp = (0, clock_1.compare)(lastSlice.id, sliceId); if (cmp < 0) { layers.push(slice); return; } else if (!cmp) return; for (let i = length - 2; i >= 0; i--) { const currSlice = layers[i]; const cmp = (0, clock_1.compare)(currSlice.id, sliceId); if (cmp < 0) { layers.splice(i + 1, 0, slice); return; } else if (!cmp) return; } layers.unshift(slice); } /** * Removes a slice from the list of layers, which start from this overlay * point. * * @param slice Slice to remove from the layer list. */ removeLayer(slice) { const layers = this.layers; const length = layers.length; for (let i = 0; i < length; i++) { if (layers[i] === slice) { layers.splice(i, 1); return; } } } /** * Inserts a slice to the list of markers which represent a single point in * the text, even if the start and end of the slice are different. The * operation is idempotent, so inserting the same slice twice will not change * the state of the point. The markers are sorted by the slice ID. * * @param slice Slice to add to the marker list. */ addMarker(slice) { const markers = this.markers; const length = markers.length; if (!length) { markers.push(slice); return; } // We attempt to insert from the end of the list, as it is the most likely // scenario. And `.push()` is more efficient than `.unshift()`. const lastSlice = markers[length - 1]; const sliceId = slice.id; const cmp = (0, clock_1.compare)(lastSlice.id, sliceId); if (cmp < 0) { markers.push(slice); return; } else if (!cmp) return; for (let i = length - 2; i >= 0; i--) { const currSlice = markers[i]; const cmp = (0, clock_1.compare)(currSlice.id, sliceId); if (cmp < 0) { markers.splice(i + 1, 0, slice); return; } else if (!cmp) return; } markers.unshift(slice); } /** * Removes a slice from the list of markers, which represent a single point in * the text, even if the start and end of the slice are different. * * @param slice Slice to remove from the marker list. */ removeMarker(slice) { const markers = this.markers; const length = markers.length; for (let i = 0; i < length; i++) { if (markers[i] === slice) { markers.splice(i, 1); return; } } } /** * Insert a reference to a marker. * * @param slice A marker (split slice). */ addMarkerRef(slice) { this.refs.push(slice); this.addMarker(slice); } /** * Insert a layer that starts at this point. * * @param slice A slice that starts at this point. */ addLayerStartRef(slice) { this.refs.push(new refs_1.OverlayRefSliceStart(slice)); this.addLayer(slice); } /** * Insert a layer that ends at this point. * * @param slice A slice that ends at this point. */ addLayerEndRef(slice) { this.refs.push(new refs_1.OverlayRefSliceEnd(slice)); } /** * Removes a reference to a marker or a slice, and remove the corresponding * layer or marker. * * @param slice A slice to remove the reference to. */ removeRef(slice) { const refs = this.refs; const length = refs.length; for (let i = 0; i < length; i++) { const ref = refs[i]; if (ref === slice) { refs.splice(i, 1); this.removeMarker(slice); return; } if ((ref instanceof refs_1.OverlayRefSliceStart && ref.slice === slice) || (ref instanceof refs_1.OverlayRefSliceEnd && ref.slice === slice)) { refs.splice(i, 1); this.removeLayer(slice); return; } } } // ---------------------------------------------------------------- Printable toStringName() { return 'OverlayPoint'; } toStringHeader(tab = '', lite) { return super.toString(tab, lite); } toString(tab = '', lite) { const refs = lite ? '' : `, refs = ${this.refs.length}`; const header = this.toStringHeader(tab, lite) + refs; if (lite) return header; const children = []; const layers = this.layers; const layerLength = layers.length; for (let i = 0; i < layerLength; i++) children.push((tab) => layers[i].toString(tab)); const markers = this.markers; const markerLength = markers.length; for (let i = 0; i < markerLength; i++) children.push((tab) => markers[i].toString(tab)); return header + (0, printTree_1.printTree)(tab, children); } } exports.OverlayPoint = OverlayPoint;