UNPKG

json-joy

Version:

Collection of libraries for building collaborative editing apps.

133 lines (132 loc) 4.61 kB
import * as nodes from '../../../nodes'; import { ClockEncoder } from '../../../../json-crdt-patch/codec/clock/ClockEncoder'; import { Timestamp } from '../../../../json-crdt-patch/clock'; import { JsonCrdtDataType } from '../../../../json-crdt-patch/constants'; import { SESSION } from '../../../../json-crdt-patch/constants'; export class Encoder { time; clock; model; encode(model) { this.model = model; const isServerTime = model.clock.sid === SESSION.SERVER; const clock = model.clock; if (isServerTime) { this.time = clock.time; } else { this.clock = new ClockEncoder(); this.clock.reset(model.clock); } const root = model.root; const doc = [0, !root.val.time ? 0 : this.cNode(root.node())]; doc[0] = isServerTime ? this.time : this.clock.toJson(); return doc; } ts(ts) { switch (ts.sid) { case SESSION.SYSTEM: return [ts.sid, ts.time]; case SESSION.SERVER: return this.time - ts.time; default: { const relativeId = this.clock.append(ts); return [-relativeId.sessionIndex, relativeId.timeDiff]; } } } cNode(node) { // TODO: PERF: use switch with `node.constructor`. if (node instanceof nodes.ObjNode) return this.cObj(node); else if (node instanceof nodes.ArrNode) return this.cArr(node); else if (node instanceof nodes.StrNode) return this.cStr(node); else if (node instanceof nodes.ValNode) return this.cVal(node); else if (node instanceof nodes.VecNode) return this.cVec(node); else if (node instanceof nodes.ConNode) return this.cCon(node); else if (node instanceof nodes.BinNode) return this.cBin(node); throw new Error('UNKNOWN_NODE'); } cObj(obj) { const map = {}; obj.nodes((child, key) => (map[key] = this.cNode(child))); const res = [JsonCrdtDataType.obj, this.ts(obj.id), map]; return res; } cVec(vec) { const elements = vec.elements; const length = elements.length; const index = this.model.index; const map = []; for (let i = 0; i < length; i++) { const elementId = elements[i]; if (!elementId) map.push(0); else { const node = index.get(elementId); map.push(this.cNode(node)); } } const res = [JsonCrdtDataType.vec, this.ts(vec.id), map]; return res; } cArr(node) { const chunks = []; const index = this.model.index; for (let chunk = node.first(); chunk; chunk = node.next(chunk)) { const deleted = chunk.del; const span = chunk.span; const chunkIdEncoded = this.ts(chunk.id); if (deleted) { chunks.push([chunkIdEncoded, span]); } else { const nodeIds = chunk.data; const nodes = []; for (let i = 0; i < span; i++) nodes.push(this.cNode(index.get(nodeIds[i]))); chunks.push([chunkIdEncoded, nodes]); } } const res = [JsonCrdtDataType.arr, this.ts(node.id), chunks]; return res; } cStr(node) { const chunks = []; for (let chunk = node.first(); chunk; chunk = node.next(chunk)) chunks.push([this.ts(chunk.id), chunk.del ? chunk.span : chunk.data]); const res = [JsonCrdtDataType.str, this.ts(node.id), chunks]; return res; } cBin(node) { const chunks = []; for (let chunk = node.first(); chunk; chunk = node.next(chunk)) chunks.push([this.ts(chunk.id), chunk.del ? chunk.span : chunk.data]); const res = [JsonCrdtDataType.bin, this.ts(node.id), chunks]; return res; } cVal(node) { const res = [JsonCrdtDataType.val, this.ts(node.id), this.cNode(node.node())]; return res; } cCon(node) { const val = node.val; const res = [JsonCrdtDataType.con, this.ts(node.id), val]; if (val instanceof Timestamp) { res[2] = 0; const specialData = this.ts(val); res.push(specialData); } else if (val === undefined) { res[2] = 0; res.push(0); } return res; } }