Skip to content
Snippets Groups Projects
y-union.js 3.82 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* global Y */
    
    import { combineErasureIntervals } from "./erasure.js"
    
    export const Union = {
      create: function(id) {
        return {
          id: id,
    
          struct: "Union",
        }
      },
      encode: function(op) {
        const e = {
          struct: "Union",
          type: op.type,
          id: op.id,
    
        }
        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 = model.union ? Y.utils.copyObject(model.union) : null
    
          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,
    
            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 !== null ? 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)
    }