/* global Y */ import { combineErasureIntervals } from "./erasure.js" export const Union = { create: function(id) { return { id: id, union: -1, struct: "Union", } }, encode: function(op) { const e = { struct: "Union", type: op.type, id: op.id, union: -1, } if (op.requires != null) { e.requires = op.requires } if (op.info != null) { e.info = op.info } return e }, requiredOps: function() { return [] }, execute: function*() {}, } export default function extendYUnion(Y) { class YUnion extends Y.utils.CustomType { constructor(os, model, contents) { super() this._model = model.id this._parent = null this._deepEventHandler = new Y.utils.EventListenerHandler() this.os = os this.union = Y.utils.copyObject(model.union) this.contents = contents this.eventHandler = new Y.utils.EventHandler((op) => { // compute op event if (op.struct === "Insert") { if (!Y.utils.compareIds(op.id, this.union)) { const mergedContents = this._merge(JSON.parse(op.content[0])) this.union = op.id if (this.contents == mergedContents) { return } this.contents = mergedContents Y.utils.bubbleEvent(this, { object: this, type: "merge", }) } } else { throw new Error("Unexpected Operation!") } }) } _getPathToChild(/*childId*/) { return undefined } _destroy() { this.eventHandler.destroy() this.eventHandler = null this.contents = null this._model = null this._parent = null this.os = null this.union = null } get() { return JSON.parse(this.contents) } _merge(newIntervals) { const prevIntervals = this.get() const mergedIntervals = combineErasureIntervals( [prevIntervals], [newIntervals], )[0] return JSON.stringify(mergedIntervals) } merge(newIntervals) { const mergedContents = this._merge(newIntervals) if (this.contents == mergedContents) { return } const insert = { id: this.os.getNextOpId(1), left: null, right: null, origin: null, parent: this._model, content: [mergedContents], struct: "Insert", } const eventHandler = this.eventHandler this.os.requestTransaction(function*() { yield* eventHandler.awaitOps(this, this.applyCreatedOperations, [ [insert], ]) }) // always remember to do that after this.os.requestTransaction // (otherwise values might contain a undefined reference to type) eventHandler.awaitAndPrematurelyCall([insert]) } observe(f) { this.eventHandler.addEventListener(f) } observeDeep(f) { this._deepEventHandler.addEventListener(f) } unobserve(f) { this.eventHandler.removeEventListener(f) } unobserveDeep(f) { this._deepEventHandler.removeEventListener(f) } // eslint-disable-next-line require-yield *_changed(transaction, op) { this.eventHandler.receivedOp(op) } } Y.extend( "Union", new Y.utils.CustomTypeDefinition({ name: "Union", class: YUnion, struct: "Union", initType: function* YUnionInitializer(os, model) { const union = model.union const contents = union != -1 ? yield* this.getOperation(union).content[0] : "[]" return new YUnion(os, model, contents) }, createType: function YUnionCreator(os, model) { const union = new YUnion(os, model, "[]") return union }, }), ) } if (typeof Y !== "undefined") { extendYUnion(Y) }