UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

200 lines (199 loc) 6.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Block = void 0; const printTree_1 = require("tree-dump/lib/printTree"); const json_hash_1 = require("../../../json-hash"); const MarkerOverlayPoint_1 = require("../overlay/MarkerOverlayPoint"); const iterator_1 = require("../../../util/iterator"); const Inline_1 = require("./Inline"); const util_1 = require("../slice/util"); const Range_1 = require("../rga/Range"); class Block extends Range_1.Range { constructor(txt, path, marker, start, end) { super(txt.str, start, end); this.txt = txt; this.path = path; this.marker = marker; this.start = start; this.end = end; this.parent = null; this.children = []; // ----------------------------------------------------------------- Stateful this.hash = 0; } /** * @returns Stable unique identifier within a list of blocks. Used for React * or other rendering library keys. */ key() { if (!this.marker) return this.tag(); const id = this.marker.id; return id.sid.toString(36) + id.time.toString(36); } tag() { const path = this.path; const length = path.length; if (!length) return ''; const step = path[length - 1]; return Array.isArray(step) ? step[0] : step; } attr() { return this.marker?.data(); } isLeaf() { return false; } /** * Iterate through all overlay points of this block, until the next marker * (regardless if that marker is a child or not). */ points0(withMarker = false) { const txt = this.txt; const overlay = txt.overlay; const iterator = overlay.points0(this.marker); let closed = false; return () => { if (withMarker) { withMarker = false; return this.marker ?? overlay.START; } if (closed) return; const point = iterator(); if (!point) return; if (point instanceof MarkerOverlayPoint_1.MarkerOverlayPoint) { closed = true; return; } return point; }; } points(withMarker) { return new iterator_1.UndefEndIter(this.points0(withMarker)); } tuples0() { const overlay = this.txt.overlay; const marker = this.marker; const iterator = overlay.tuples0(marker); let closed = false; return () => { if (closed) return; let pair = iterator(); while (!marker && pair && pair[1] && pair[1].cmpSpatial(this.start) < 0) pair = iterator(); if (!pair) return (closed = true), void 0; if (!pair[1] || pair[1] instanceof MarkerOverlayPoint_1.MarkerOverlayPoint) closed = true; return pair; }; } /** * @todo Consider moving inline-related methods to {@link LeafBlock}. */ texts0() { const txt = this.txt; const iterator = this.tuples0(); const start = this.start; const end = this.end; const startIsMarker = txt.overlay.isMarker(start.id); const endIsMarker = txt.overlay.isMarker(end.id); let isFirst = true; let next = iterator(); let closed = false; return () => { if (closed) return; const pair = next; next = iterator(); if (!pair) return; const [overlayPoint1, overlayPoint2] = pair; let point1 = overlayPoint1; let point2 = overlayPoint2; if (isFirst) { isFirst = false; if (start.cmpSpatial(overlayPoint1) > 0) point1 = start; if (startIsMarker) { point1 = point1.clone(); point1.step(1); } } if (!endIsMarker && end.cmpSpatial(overlayPoint2) < 0) { closed = true; point2 = end; } return new Inline_1.Inline(txt, overlayPoint1, overlayPoint2, point1, point2); }; } /** * @todo Consider moving inline-related methods to {@link LeafBlock}. */ texts() { return new iterator_1.UndefEndIter(this.texts0()); } text() { let str = ''; const children = this.children; const length = children.length; for (let i = 0; i < length; i++) str += children[i].text(); return str; } // ------------------------------------------------------------------- export toJson() { const data = this.attr(); const attr = data !== void 0 ? { data } : null; const node = [this.tag(), attr]; const children = this.children; const length = children.length; for (let i = 0; i < length; i++) node.push(children[i].toJson()); return node; } refresh() { const { path, children } = this; let state = json_hash_1.CONST.START_STATE; state = (0, json_hash_1.updateJson)(state, path); const marker = this.marker; if (marker) { state = (0, json_hash_1.updateNum)(state, marker.marker.refresh()); state = (0, json_hash_1.updateNum)(state, marker.textHash); } else { state = (0, json_hash_1.updateNum)(state, this.txt.overlay.leadingTextHash); } for (let i = 0; i < children.length; i++) state = (0, json_hash_1.updateNum)(state, children[i].refresh()); return (this.hash = state); } // ---------------------------------------------------------------- Printable toStringName() { return 'Block'; } toStringHeader() { const hash = `#${this.hash.toString(36).slice(-4)}`; const tag = this.path.map((step) => (0, util_1.formatType)(step)).join('.'); const header = `${super.toString('', true)} ${hash} ${tag} `; return header; } toString(tab = '') { const header = this.toStringHeader(); const hasChildren = !!this.children.length; return (header + (0, printTree_1.printTree)(tab, [ this.marker ? (tab) => this.marker.toString(tab) : null, this.marker && hasChildren ? () => '' : null, hasChildren ? (tab) => 'children' + (0, printTree_1.printTree)(tab, this.children.map((child, i) => (tab) => `${i + 1}. ` + child.toString(tab + ' ' + ' '.repeat(String(i + 1).length)))) : null, ])); } } exports.Block = Block;