implemented xml type for new event system

This commit is contained in:
Kevin Jahns
2017-10-19 17:36:28 +02:00
parent 1311c7a0d8
commit 755c9eb16e
38 changed files with 1272 additions and 322 deletions

View File

@@ -1,9 +1,35 @@
import { getReference } from '../Util/structReferences.js'
import ID from '../Util/ID.js'
/**
* Delete all items in an ID-range
* TODO: implement getItemCleanStartNode for better performance (only one lookup)
*/
export function deleteItemRange (y, user, clock, range) {
let items = y.os.getItems(this._target, this._length)
for (let i = items.length - 1; i >= 0; i--) {
items[i]._delete(y, false)
const createDelete = y.connector._forwardAppliedStructs
let item = y.os.getItemCleanStart(new ID(user, clock))
if (item !== null) {
if (!item._deleted) {
item._splitAt(y, range)
item._delete(y, createDelete)
}
let itemLen = item._length
range -= itemLen
clock += itemLen
if (range > 0) {
let node = y.os.findNode(new ID(user, clock))
while (node !== null && range > 0 && node.val._id.equals(new ID(user, clock))) {
const nodeVal = node.val
if (!nodeVal._deleted) {
nodeVal._splitAt(y, range)
nodeVal._delete(y, createDelete)
}
const nodeLen = nodeVal._length
range -= nodeLen
clock += nodeLen
node = node.next()
}
}
}
}
@@ -18,6 +44,7 @@ export default class Delete {
_fromBinary (y, decoder) {
this._targetID = decoder.readID()
this._length = decoder.readVarUint()
return []
}
_toBinary (encoder) {
encoder.writeUint8(getReference(this.constructor))

View File

@@ -34,6 +34,9 @@ export default class Item {
this._parentSub = null
this._deleted = false
}
get _lastId () {
return new ID(this._id.user, this._id.clock + this._length - 1)
}
get _length () {
return 1
}
@@ -61,6 +64,17 @@ export default class Item {
del._length = this._length
del._integrate(y, true)
}
const parent = this._parent
if (parent !== y && !parent._deleted) {
y._transactionChangedTypes.set(parent, this._parentSub)
}
}
/**
* This is called right before this struct receives any children.
* It can be overwritten to apply pending changes before applying remote changes
*/
_beforeChange () {
// nop
}
/*
* - Integrate the struct so that other types/structs can see it
@@ -68,12 +82,26 @@ export default class Item {
* - Check if this is struct deleted
*/
_integrate (y) {
const parent = this._parent
const selfID = this._id
const userState = selfID === null ? 0 : y.ss.getState(selfID.user)
if (selfID === null) {
this._id = y.ss.getNextID(this._length)
} else if (selfID.clock < y.ss.getState(selfID.user)) {
} else if (selfID.user === RootFakeUserID) {
// nop
} else if (selfID.clock < userState) {
// already applied..
return []
} else if (selfID.clock === userState) {
y.ss.setState(selfID.user, userState + this._length)
} else {
// missing content from user
throw new Error('Can not apply yet!')
}
if (!parent._deleted && !y._transactionChangedTypes.has(parent) && !y._transactionNewTypes.has(parent)) {
// this is the first time parent is updated
// or this types is new
this._parent._beforeChange()
}
/*
# $this has to find a unique position between origin and the next known character
@@ -96,7 +124,7 @@ export default class Item {
if (this._left !== null) {
o = this._left._right
} else if (this._parentSub !== null) {
o = this._parent._map.get(this._parentSub)
o = this._parent._map.get(this._parentSub) || null
} else {
o = this._parent._start
}
@@ -124,14 +152,36 @@ export default class Item {
}
o = o._right
}
// reconnect left/right + update parent map/start if necessary
const parentSub = this._parentSub
if (this._left === null) {
if (this._parentSub !== null) {
this._parent._map.set(this._parentSub, this)
let right
if (parentSub !== null) {
const pmap = parent._map
right = pmap.get(parentSub) || null
pmap.set(parentSub, this)
} else {
this._parent._start = this
right = parent._start
parent._start = this
}
this._right = right
if (right !== null) {
right._left = this
}
} else {
const left = this._left
const right = left._right
this._right = right
left._right = this
if (right !== null) {
right._left = this
}
}
y.os.put(this)
if (parent !== y && !parent._deleted) {
y._transactionChangedTypes.set(parent, parentSub)
}
if (this._id.user !== RootFakeUserID) {
if (y.connector._forwardAppliedStructs || this._id.user === y.userID) {
y.connector.broadcastStruct(this)
@@ -160,10 +210,10 @@ export default class Item {
encoder.writeUint8(info)
encoder.writeID(this._id)
if (info & 0b1) {
encoder.writeID(this._origin._id)
encoder.writeID(this._origin._lastId)
}
if (info & 0b10) {
encoder.writeID(this._left._id)
encoder.writeID(this._left._lastId)
}
if (info & 0b100) {
encoder.writeID(this._right_origin._id)
@@ -179,7 +229,8 @@ export default class Item {
_fromBinary (y, decoder) {
let missing = []
const info = decoder.readUint8()
this._id = decoder.readID()
const id = decoder.readID()
this._id = id
// read origin
if (info & 0b1) {
// origin != null
@@ -214,9 +265,9 @@ export default class Item {
// right != null
const rightID = decoder.readID()
if (this._right_origin === null) {
const right = y.os.getCleanStart(rightID)
const right = y.os.getItemCleanStart(rightID)
if (right === null) {
missing.push(right)
missing.push(rightID)
} else {
this._right = right
this._right_origin = right
@@ -230,7 +281,7 @@ export default class Item {
if (this._parent === null) {
const parent = y.os.get(parentID)
if (parent === null) {
missing.push(parent)
missing.push(parentID)
} else {
this._parent = parent
}
@@ -239,11 +290,15 @@ export default class Item {
if (this._origin !== null) {
this._parent = this._origin._parent
} else if (this._right_origin !== null) {
this._parent = this._origin._parent
this._parent = this._right_origin._parent
}
}
if (info & 0b1000) {
this._parentSub = decoder.readVarString()
// TODO: maybe put this in read parent condition (you can also read parentsub from left/right)
this._parentSub = JSON.parse(decoder.readVarString())
}
if (y.ss.getState(id.user) < id.clock) {
missing.push(new ID(id.user, id.clock - 1))
}
return missing
}

View File

@@ -13,7 +13,8 @@ export default class ItemJSON extends Item {
let len = decoder.readVarUint()
this._content = new Array(len)
for (let i = 0; i < len; i++) {
this._content[i] = JSON.parse(decoder.readVarString())
const ctnt = decoder.readVarString()
this._content[i] = JSON.parse(ctnt)
}
return missing
}

View File

@@ -1,4 +1,18 @@
import Item from './Item.js'
import EventHandler from '../Util/EventHandler.js'
// restructure children as if they were inserted one after another
function integrateChildren (y, start) {
let right
do {
right = start._right
start._right = null
start._right_origin = null
start._origin = start._left
start._integrate(y)
start = right
} while (right !== null)
}
export default class Type extends Item {
constructor () {
@@ -6,24 +20,46 @@ export default class Type extends Item {
this._map = new Map()
this._start = null
this._y = null
this._eventHandler = new EventHandler()
}
observe (f) {
this._eventHandler.addEventListener(f)
}
unobserve (f) {
this._eventHandler.removeEventListener(f)
}
_integrate (y) {
y._transactionNewTypes.add(this)
super._integrate(y)
this._y = y
// when integrating children we must make sure to
// integrate start
const start = this._start
if (start !== null) {
this._start = null
integrateChildren(y, start)
}
// integrate map children
const map = this._map
for (let [key, t] of map) {
map.delete(key)
integrateChildren(y, t)
}
}
_delete (y) {
super._delete(y)
_delete (y, createDelete) {
super._delete(y, createDelete)
y._transactionChangedTypes.delete(this)
// delete map types
for (let value of this._map.values()) {
if (value instanceof Item && !value._deleted) {
value._delete()
value._delete(y, false)
}
}
// delete array types
let t = this._start
while (t !== null) {
if (!t._deleted) {
t._delete()
t._delete(y, false)
}
t = t._right
}