diff --git a/src/Transaction.js b/src/Transaction.js new file mode 100644 index 00000000..a99b2b47 --- /dev/null +++ b/src/Transaction.js @@ -0,0 +1,24 @@ + +export default class Transaction { + constructor (y) { + this.y = y + // types added during transaction + this.newTypes = new Set() + // changed types (does not include new types) + // maps from type to parentSubs (item.parentSub = null for array elements) + this.changedTypes = new Map() + } +} + +export function transactionTypeChanged (y, type, sub) { + if (type !== y && !type._deleted) { + const changedTypes = y._transaction.changedTypes + let subs = changedTypes.get(type) + if (subs === undefined) { + // create if it doesn't exist yet + subs = new Set() + changedTypes.set(type, subs) + } + subs.add(sub) + } +} diff --git a/src/Type/y-xml/YXmlEvent.js b/src/Type/y-xml/YXmlEvent.js new file mode 100644 index 00000000..9435f718 --- /dev/null +++ b/src/Type/y-xml/YXmlEvent.js @@ -0,0 +1,16 @@ + +export default class YXmlEvent { + constructor (target, subs, remote) { + this.target = target + this.childListChanged = false + this.attributesChanged = new Set() + this.remote = remote + subs.forEach((sub) => { + if (sub === null) { + this.childListChanged = true + } else { + this.attributesChanged.add(sub) + } + }) + } +} diff --git a/src/Util/defragmentItemContent.js b/src/Util/defragmentItemContent.js new file mode 100644 index 00000000..724d5dbe --- /dev/null +++ b/src/Util/defragmentItemContent.js @@ -0,0 +1,57 @@ + +import ID from '../Util/ID.js' +import ItemJSON from '../Struct/ItemJSON.js' +import ItemString from '../Struct/ItemString.js' + +/** + * Try to merge all items in os with their successors. + * + * Some transformations (like delete) fragment items. + * Item(c: 'ab') + Delete(1,1) + Delete(0, 1) -> Item(c: 'a',deleted);Item(c: 'b',deleted) + * + * This functions merges the fragmented nodes together: + * Item(c: 'a',deleted);Item(c: 'b',deleted) -> Item(c: 'ab', deleted) + * + * TODO: The Tree implementation does not support deletions in-spot. + * This is why all deletions must be performed after the traversal. + * + */ +export function defragmentItemContent (y) { + const os = y.os + if (os.length < 2) { + return + } + let deletes = [] + let node = os.findSmallestNode() + let next = node.next() + while (next !== null) { + let a = node.val + let b = next.val + if ( + (a instanceof ItemJSON || a instanceof ItemString) && + a.constructor === b.constructor && + a._deleted === b._deleted && + a._right === b && + (new ID(a._id.user, a._id.clock + a._length)).equals(b._id) + ) { + a._right = b._right + if (a instanceof ItemJSON) { + a._content = a._content.concat(b._content) + } else if (a instanceof ItemString) { + a._content += b._content + } + // delete b later + deletes.push(b._id) + // do not iterate node! + // !(node = next) + } else { + // not able to merge node, get next node + node = next + } + // update next + next = next.next() + } + for (let i = deletes.length - 1; i >= 0; i--) { + os.delete(deletes[i]) + } +}