From e5f289506f2ad32e66f40e8bd4ed32212263add3 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 7 Nov 2017 22:44:43 -0800 Subject: [PATCH] observeDeep receives array of events --- src/Struct/Type.js | 8 ++- src/Transaction.js | 1 + src/Type/YMap.js | 8 +++ src/Type/y-xml/utils.js | 150 ++++++++++++++++++++-------------------- src/Util/YEvent.js | 36 ++++++---- src/Y.js | 14 +++- 6 files changed, 126 insertions(+), 91 deletions(-) diff --git a/src/Struct/Type.js b/src/Struct/Type.js index bf1952cd..58c17eb6 100644 --- a/src/Struct/Type.js +++ b/src/Struct/Type.js @@ -40,10 +40,16 @@ export default class Type extends Item { this._deepEventHandler = new EventHandler() } _callEventHandler (event) { + const changedParentTypes = this._y._transaction.changedParentTypes this._eventHandler.callEventListeners(event) let type = this while (type !== this._y) { - type._deepEventHandler.callEventListeners(event) + let events = changedParentTypes.get(type) + if (events === undefined) { + events = [] + changedParentTypes.set(type, events) + } + events.push(event) type = type._parent } } diff --git a/src/Transaction.js b/src/Transaction.js index 3376620a..6a99a96b 100644 --- a/src/Transaction.js +++ b/src/Transaction.js @@ -9,6 +9,7 @@ export default class Transaction { this.changedTypes = new Map() this.deletedStructs = new Set() this.beforeState = new Map() + this.changedParentTypes = new Map() } } diff --git a/src/Type/YMap.js b/src/Type/YMap.js index 7a621135..5a00e30a 100644 --- a/src/Type/YMap.js +++ b/src/Type/YMap.js @@ -98,6 +98,14 @@ export default class YMap extends Type { return v._content[v._content.length - 1] } } + has (key) { + let v = this._map.get(key) + if (v === undefined || v._deleted) { + return false + } else { + return true + } + } _logString () { const left = this._left !== null ? this._left._lastId : null const origin = this._origin !== null ? this._origin._lastId : null diff --git a/src/Type/y-xml/utils.js b/src/Type/y-xml/utils.js index 6c351129..a29dc0cf 100644 --- a/src/Type/y-xml/utils.js +++ b/src/Type/y-xml/utils.js @@ -135,90 +135,92 @@ export function applyChangesFromDom (dom) { } } -export function reflectChangesOnDom (event) { - const yxml = event.target - const dom = yxml._dom - if (dom != null) { - this._mutualExclude(() => { - // TODO: do this once before applying stuff - // let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement) - if (yxml.constructor === YXmlText) { - yxml._dom.nodeValue = yxml.toString() - } else { - // update attributes - event.attributesChanged.forEach(attributeName => { - const value = yxml.getAttribute(attributeName) - if (value === undefined) { - dom.removeAttribute(attributeName) - } else { - dom.setAttribute(attributeName, value) - } - }) - if (event.childListChanged) { - // create fragment of undeleted nodes - const fragment = document.createDocumentFragment() - yxml.forEach(function (t) { - fragment.append(t.getDom()) +export function reflectChangesOnDom (events) { + this._mutualExclude(() => { + events.forEach(event => { + const yxml = event.target + const dom = yxml._dom + if (dom != null) { + // TODO: do this once before applying stuff + // let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement) + if (yxml.constructor === YXmlText) { + yxml._dom.nodeValue = yxml.toString() + } else { + // update attributes + event.attributesChanged.forEach(attributeName => { + const value = yxml.getAttribute(attributeName) + if (value === undefined) { + dom.removeAttribute(attributeName) + } else { + dom.setAttribute(attributeName, value) + } }) - // remove remainding nodes - let lastChild = dom.lastChild - while (lastChild !== null) { - dom.removeChild(lastChild) - lastChild = dom.lastChild + if (event.childListChanged) { + // create fragment of undeleted nodes + const fragment = document.createDocumentFragment() + yxml.forEach(function (t) { + fragment.append(t.getDom()) + }) + // remove remainding nodes + let lastChild = dom.lastChild + while (lastChild !== null) { + dom.removeChild(lastChild) + lastChild = dom.lastChild + } + // insert fragment of undeleted nodes + dom.append(fragment) } - // insert fragment of undeleted nodes - dom.append(fragment) } - } - /* TODO: smartscrolling - .. else if (event.type === 'childInserted' || event.type === 'insert') { - let nodes = event.values - for (let i = nodes.length - 1; i >= 0; i--) { - let node = nodes[i] - node.setDomFilter(yxml._domFilter) - node.enableSmartScrolling(yxml._scrollElement) - let dom = node.getDom() - let fixPosition = null - let nextDom = null - if (yxml._content.length > event.index + i + 1) { - nextDom = yxml.get(event.index + i + 1).getDom() - } - yxml._dom.insertBefore(dom, nextDom) - if (anchorViewPosition === null) { - // nop - } else if (anchorViewPosition.anchor !== null) { - // no scrolling when current selection - if (!dom.contains(anchorViewPosition.anchor) && !anchorViewPosition.anchor.contains(dom)) { + /* TODO: smartscrolling + .. else if (event.type === 'childInserted' || event.type === 'insert') { + let nodes = event.values + for (let i = nodes.length - 1; i >= 0; i--) { + let node = nodes[i] + node.setDomFilter(yxml._domFilter) + node.enableSmartScrolling(yxml._scrollElement) + let dom = node.getDom() + let fixPosition = null + let nextDom = null + if (yxml._content.length > event.index + i + 1) { + nextDom = yxml.get(event.index + i + 1).getDom() + } + yxml._dom.insertBefore(dom, nextDom) + if (anchorViewPosition === null) { + // nop + } else if (anchorViewPosition.anchor !== null) { + // no scrolling when current selection + if (!dom.contains(anchorViewPosition.anchor) && !anchorViewPosition.anchor.contains(dom)) { + fixPosition = anchorViewPosition + } + } else if (getBoundingClientRect(dom).top <= 0) { + // adjust scrolling if modified element is out of view, + // there is no anchor element, and the browser did not adjust scrollTop (this is checked later) fixPosition = anchorViewPosition } - } else if (getBoundingClientRect(dom).top <= 0) { - // adjust scrolling if modified element is out of view, - // there is no anchor element, and the browser did not adjust scrollTop (this is checked later) - fixPosition = anchorViewPosition + fixScrollPosition(yxml._scrollElement, fixPosition) } - fixScrollPosition(yxml._scrollElement, fixPosition) - } - } else if (event.type === 'childRemoved' || event.type === 'delete') { - for (let i = event.values.length - 1; i >= 0; i--) { - let dom = event.values[i]._dom - let fixPosition = null - if (anchorViewPosition === null) { - // nop - } else if (anchorViewPosition.anchor !== null) { - // no scrolling when current selection - if (!dom.contains(anchorViewPosition.anchor) && !anchorViewPosition.anchor.contains(dom)) { + } else if (event.type === 'childRemoved' || event.type === 'delete') { + for (let i = event.values.length - 1; i >= 0; i--) { + let dom = event.values[i]._dom + let fixPosition = null + if (anchorViewPosition === null) { + // nop + } else if (anchorViewPosition.anchor !== null) { + // no scrolling when current selection + if (!dom.contains(anchorViewPosition.anchor) && !anchorViewPosition.anchor.contains(dom)) { + fixPosition = anchorViewPosition + } + } else if (getBoundingClientRect(dom).top <= 0) { + // adjust scrolling if modified element is out of view, + // there is no anchor element, and the browser did not adjust scrollTop (this is checked later) fixPosition = anchorViewPosition } - } else if (getBoundingClientRect(dom).top <= 0) { - // adjust scrolling if modified element is out of view, - // there is no anchor element, and the browser did not adjust scrollTop (this is checked later) - fixPosition = anchorViewPosition + dom.remove() + fixScrollPosition(yxml._scrollElement, fixPosition) } - dom.remove() - fixScrollPosition(yxml._scrollElement, fixPosition) } + */ } - */ }) - } + }) } diff --git a/src/Util/YEvent.js b/src/Util/YEvent.js index 3e0b1eee..a6c486b7 100644 --- a/src/Util/YEvent.js +++ b/src/Util/YEvent.js @@ -2,26 +2,32 @@ export default class YEvent { constructor (target) { this.target = target + this._path = null } get path () { - const path = [] - let type = this.target - const y = type._y - while (type._parent !== y) { - let parent = type._parent - if (type._parentSub !== null) { - path.push(type._parentSub) - } else { - // parent is array-ish - for (let [i, child] of parent) { - if (child === type) { - path.push(i) - break + if (this._path !== null) { + return this._path + } else { + const path = [] + let type = this.target + const y = type._y + while (type._parent !== y) { + let parent = type._parent + if (type._parentSub !== null) { + path.push(type._parentSub) + } else { + // parent is array-ish + for (let [i, child] of parent) { + if (child === type) { + path.push(i) + break + } } } + type = parent } - type = parent + this._path = path + return path } - return path } } diff --git a/src/Y.js b/src/Y.js index 045dc101..51c22276 100644 --- a/src/Y.js +++ b/src/Y.js @@ -55,7 +55,19 @@ export default class Y extends NamedEventHandler { if (initialCall) { // emit change events on changed types this._transaction.changedTypes.forEach(function (subs, type) { - type._callObserver(subs, remote) + if (!type._deleted) { + type._callObserver(subs, remote) + } + }) + this._transaction.changedParentTypes.forEach(function (events, type) { + if (!type._deleted) { + events = events.filter(event => + !event.target._deleted + ) + // we don't have to check for events.length + // because there is no way events is empty.. + type._deepEventHandler.callEventListeners(events) + } }) // when all changes & events are processed, emit afterTransaction event this.emit('afterTransaction', this, remote)