From bdb3782f8f6b27db9b90bc601a3763e46781aa7e Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Wed, 2 May 2018 18:42:18 +0200 Subject: [PATCH 01/11] add option to disable gc (compatible with older versions) --- src/MessageHandler/integrateRemoteStructs.js | 2 +- src/Struct/Type.js | 4 ++-- src/Y.js | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MessageHandler/integrateRemoteStructs.js b/src/MessageHandler/integrateRemoteStructs.js index 390f0924..802c3af6 100644 --- a/src/MessageHandler/integrateRemoteStructs.js +++ b/src/MessageHandler/integrateRemoteStructs.js @@ -25,7 +25,7 @@ function _integrateRemoteStructHelper (y, struct) { if (y.ss.getState(id.user) > id.clock) { return } - if (struct.constructor === GC || (struct._parent.constructor !== GC && struct._parent._deleted === false)) { + if (!y.gcEnabled || struct.constructor === GC || (struct._parent.constructor !== GC && struct._parent._deleted === false)) { // Is either a GC or Item with an undeleted parent // save to integrate struct._integrate(y) diff --git a/src/Struct/Type.js b/src/Struct/Type.js index 6202454b..c8b63395 100644 --- a/src/Struct/Type.js +++ b/src/Struct/Type.js @@ -217,8 +217,8 @@ export default class Type extends Item { * collect the children of this type. */ _delete (y, createDelete, gcChildren) { - if (gcChildren === undefined) { - gcChildren = y._hasUndoManager === false + if (gcChildren === undefined || !y.gcEnabled) { + gcChildren = y._hasUndoManager === false && y.gcEnabled } super._delete(y, createDelete, gcChildren) y._transaction.changedTypes.delete(this) diff --git a/src/Y.js b/src/Y.js index 7a1ab318..0279cf17 100644 --- a/src/Y.js +++ b/src/Y.js @@ -28,8 +28,9 @@ export { default as DomBinding } from './Bindings/DomBinding/DomBinding.js' * @param {AbstractPersistence} persistence Persistence adapter instance */ export default class Y extends NamedEventHandler { - constructor (room, opts, persistence) { + constructor (room, opts, persistence, conf = {}) { super() + this.gcEnabled = conf.gc || false /** * The room name that this Yjs instance connects to. * @type {String} From 38558a7fad04739ac4ce22a76887f01af31899f3 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Wed, 2 May 2018 18:42:56 +0200 Subject: [PATCH 02/11] 13.0.0-57 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ac06c647..ed99c0db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-56", + "version": "13.0.0-57", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 266af208..3f998591 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-56", + "version": "13.0.0-57", "description": "A framework for real-time p2p shared editing on any data", "main": "./y.node.js", "browser": "./y.js", From c098e8e7457076be9ff148d42fc4b074a84c1f67 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Sat, 5 May 2018 14:47:59 +0200 Subject: [PATCH 03/11] implement scroll-fixer --- examples/html-editor/index.js | 2 +- src/Bindings/DomBinding/DomBinding.js | 12 +------- src/Bindings/DomBinding/domObserver.js | 3 -- src/Bindings/DomBinding/typeObserver.js | 37 +++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/examples/html-editor/index.js b/examples/html-editor/index.js index 80623031..a13010a4 100644 --- a/examples/html-editor/index.js +++ b/examples/html-editor/index.js @@ -1,7 +1,7 @@ /* global Y */ window.onload = function () { - window.domBinding = new Y.DomBinding(window.yXmlType, document.body) + window.domBinding = new Y.DomBinding(window.yXmlType, document.body, { scrollingElement: document.scrollingElement }) } let y = new Y('htmleditor', { diff --git a/src/Bindings/DomBinding/DomBinding.js b/src/Bindings/DomBinding/DomBinding.js index 1568f08d..b3ecb625 100644 --- a/src/Bindings/DomBinding/DomBinding.js +++ b/src/Bindings/DomBinding/DomBinding.js @@ -33,6 +33,7 @@ export default class DomBinding extends Binding { this.opts = opts opts.document = opts.document || document opts.hooks = opts.hooks || {} + this.scrollingElement = opts.scrollingElement || null /** * Maps each DOM element to the type that it is associated with. * @type {Map} @@ -105,17 +106,6 @@ export default class DomBinding extends Binding { createAssociation(this, target, type) } - /** - * Enables the smart scrolling functionality for a Dom Binding. - * This is useful when YXml is bound to a shared editor. When activated, - * the viewport will be changed to accommodate remote changes. - * - * @param {Element} scrollElement The node that is - */ - enableSmartScrolling (scrollElement) { - // @TODO: implement smart scrolling - } - /** * NOTE: currently does not apply filter to existing elements! * @param {FilterFunction} filter The filter function to use from now on. diff --git a/src/Bindings/DomBinding/domObserver.js b/src/Bindings/DomBinding/domObserver.js index 8304c079..593510a5 100644 --- a/src/Bindings/DomBinding/domObserver.js +++ b/src/Bindings/DomBinding/domObserver.js @@ -125,9 +125,6 @@ export default function domObserver (mutations, _document) { } }) for (let dom of diffChildren) { - if (dom.yOnChildrenChanged !== undefined) { - dom.yOnChildrenChanged() - } const yxml = this.domToType.get(dom) applyChangesFromDom(this, dom, yxml, _document) } diff --git a/src/Bindings/DomBinding/typeObserver.js b/src/Bindings/DomBinding/typeObserver.js index 1417cc4e..6b193159 100644 --- a/src/Bindings/DomBinding/typeObserver.js +++ b/src/Bindings/DomBinding/typeObserver.js @@ -1,13 +1,49 @@ +/* global getSelection */ import YXmlText from '../../Types/YXml/YXmlText.js' import YXmlHook from '../../Types/YXml/YXmlHook.js' import { removeDomChildrenUntilElementFound } from './util.js' +function findScrollReference (scrollingElement) { + if (scrollingElement !== null) { + let anchor = getSelection().anchorNode + if (anchor == null) { + let children = scrollingElement.children + for (let i = 0; i < children.length; i++) { + const elem = children[i] + const rect = elem.getBoundingClientRect() + if (rect.top >= 0) { + return { elem, top: rect.top } + } + } + } else { + if (anchor.nodeType === document.TEXT_NODE) { + anchor = anchor.parentElement + } + const top = anchor.getBoundingClientRect().top + return { elem: anchor, top: top } + } + } + return null +} + +function fixScroll (scrollingElement, ref) { + if (ref !== null) { + const { elem, top } = ref + const currentTop = elem.getBoundingClientRect().top + const newScroll = scrollingElement.scrollTop + currentTop - top + if (newScroll >= 0) { + scrollingElement.scrollTop = newScroll + } + } +} + /** * @private */ export default function typeObserver (events) { this._mutualExclude(() => { + const scrollRef = findScrollReference(this.scrollingElement) events.forEach(event => { const yxml = event.target const dom = this.typeToDom.get(yxml) @@ -59,5 +95,6 @@ export default function typeObserver (events) { } } }) + fixScroll(this.scrollingElement, scrollRef) }) } From 49d2e42b41eb772e7d428d72641584198273c7e4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Sat, 5 May 2018 15:33:16 +0200 Subject: [PATCH 04/11] fix missing dom-binding - still investigating --- src/Bindings/DomBinding/domObserver.js | 14 +++++++++++++- src/Bindings/DomBinding/typeObserver.js | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Bindings/DomBinding/domObserver.js b/src/Bindings/DomBinding/domObserver.js index 593510a5..48929dfa 100644 --- a/src/Bindings/DomBinding/domObserver.js +++ b/src/Bindings/DomBinding/domObserver.js @@ -60,8 +60,8 @@ function applyChangesFromDom (binding, dom, yxml, _document) { removeAssociation(binding, childNode, childType) } else { // child was moved to a different position. - childType._delete(y) removeAssociation(binding, childNode, childType) + childType._delete(y) } prevExpectedType = insertNodeHelper(yxml, prevExpectedType, childNode, _document, binding) } else { @@ -92,6 +92,18 @@ export default function domObserver (mutations, _document) { const yxml = this.domToType.get(dom) if (yxml === false || yxml === undefined || yxml.constructor === YXmlHook) { // dom element is filtered + if (yxml === undefined) { // In case yxml is undefined, we double check if we forgot to bind the dom + console.error('Yjs DomBinding: Reconstructing DomBinding, please report how to reproduce this.') + let parent + let yParent + do { + parent = dom.parentNode + yParent = this.domToType.get(parent) + } while (yParent === undefined) + if (yParent !== false && yParent !== undefined && yParent.constructor !== YXmlHook) { + diffChildren.add(parent) + } + } return } switch (mutation.type) { diff --git a/src/Bindings/DomBinding/typeObserver.js b/src/Bindings/DomBinding/typeObserver.js index 6b193159..d874e7af 100644 --- a/src/Bindings/DomBinding/typeObserver.js +++ b/src/Bindings/DomBinding/typeObserver.js @@ -8,7 +8,7 @@ function findScrollReference (scrollingElement) { if (scrollingElement !== null) { let anchor = getSelection().anchorNode if (anchor == null) { - let children = scrollingElement.children + let children = scrollingElement.children // only iterate through non-text nodes for (let i = 0; i < children.length; i++) { const elem = children[i] const rect = elem.getBoundingClientRect() From 633606451667f0eb516a5ee067482151666947a0 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Sat, 5 May 2018 15:33:53 +0200 Subject: [PATCH 05/11] 13.0.0-58 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ed99c0db..f2342929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-57", + "version": "13.0.0-58", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3f998591..c4f5df64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-57", + "version": "13.0.0-58", "description": "A framework for real-time p2p shared editing on any data", "main": "./y.node.js", "browser": "./y.js", From 1ace7f4b7349e09d6adaa691e9499517575f74e7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Mon, 7 May 2018 11:41:45 +0200 Subject: [PATCH 06/11] fix parentNode binding in case parent doesn't fire MO --- src/Bindings/DomBinding/domObserver.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Bindings/DomBinding/domObserver.js b/src/Bindings/DomBinding/domObserver.js index 48929dfa..3c1b274d 100644 --- a/src/Bindings/DomBinding/domObserver.js +++ b/src/Bindings/DomBinding/domObserver.js @@ -90,21 +90,20 @@ export default function domObserver (mutations, _document) { mutations.forEach(mutation => { const dom = mutation.target const yxml = this.domToType.get(dom) - if (yxml === false || yxml === undefined || yxml.constructor === YXmlHook) { - // dom element is filtered - if (yxml === undefined) { // In case yxml is undefined, we double check if we forgot to bind the dom - console.error('Yjs DomBinding: Reconstructing DomBinding, please report how to reproduce this.') - let parent - let yParent - do { - parent = dom.parentNode - yParent = this.domToType.get(parent) - } while (yParent === undefined) - if (yParent !== false && yParent !== undefined && yParent.constructor !== YXmlHook) { - diffChildren.add(parent) - } + if (yxml === undefined) { // In case yxml is undefined, we double check if we forgot to bind the dom + let parent + let yParent + do { + parent = dom.parentNode + yParent = this.domToType.get(parent) + } while (yParent === undefined && parent !== null) + if (yParent !== false && yParent !== undefined && yParent.constructor !== YXmlHook) { + diffChildren.add(parent) } return + } else if (yxml === false || yxml.constructor === YXmlHook) { + // dom element is filtered / a dom hook + return } switch (mutation.type) { case 'characterData': From 65b8921f05a9d39bfaab931630812deb84f57c94 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Mon, 7 May 2018 11:45:39 +0200 Subject: [PATCH 07/11] 13.0.0-59 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f2342929..0a89f499 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-58", + "version": "13.0.0-59", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c4f5df64..1b738fcb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-58", + "version": "13.0.0-59", "description": "A framework for real-time p2p shared editing on any data", "main": "./y.node.js", "browser": "./y.js", From 92c2fbd6d348ea74a10cd2d0db19504292a52f24 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Mon, 7 May 2018 11:52:37 +0200 Subject: [PATCH 08/11] remove debug dependency --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 1b738fcb..3951bc48 100644 --- a/package.json +++ b/package.json @@ -71,8 +71,5 @@ "rollup-watch": "^3.2.2", "standard": "^10.0.2", "tag-dist-files": "^0.1.6" - }, - "dependencies": { - "debug": "^2.6.8" } } From ca1384982825f147138f2f734a3dbe87c00f8bff Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Tue, 8 May 2018 13:45:51 +0200 Subject: [PATCH 09/11] fix domBinding infinite loop --- src/Bindings/DomBinding/domObserver.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bindings/DomBinding/domObserver.js b/src/Bindings/DomBinding/domObserver.js index 3c1b274d..4284d11f 100644 --- a/src/Bindings/DomBinding/domObserver.js +++ b/src/Bindings/DomBinding/domObserver.js @@ -91,10 +91,10 @@ export default function domObserver (mutations, _document) { const dom = mutation.target const yxml = this.domToType.get(dom) if (yxml === undefined) { // In case yxml is undefined, we double check if we forgot to bind the dom - let parent + let parent = dom let yParent do { - parent = dom.parentNode + parent = parent.parentNode yParent = this.domToType.get(parent) } while (yParent === undefined && parent !== null) if (yParent !== false && yParent !== undefined && yParent.constructor !== YXmlHook) { From e56457a0ef5451bd756abf943cf0fa7ddcba09a7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Tue, 8 May 2018 13:46:27 +0200 Subject: [PATCH 10/11] 13.0.0-60 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a89f499..286c3036 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-59", + "version": "13.0.0-60", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 3951bc48..fd7cd781 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yjs", - "version": "13.0.0-59", + "version": "13.0.0-60", "description": "A framework for real-time p2p shared editing on any data", "main": "./y.node.js", "browser": "./y.js", From c0e630b635768b7923cdea71f12512cc05c18dc7 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@rwth-aachen.de> Date: Wed, 9 May 2018 14:42:24 +0200 Subject: [PATCH 11/11] prefer parentElement instead of parentNode --- src/Bindings/DomBinding/domObserver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bindings/DomBinding/domObserver.js b/src/Bindings/DomBinding/domObserver.js index 4284d11f..0dfb176e 100644 --- a/src/Bindings/DomBinding/domObserver.js +++ b/src/Bindings/DomBinding/domObserver.js @@ -94,7 +94,7 @@ export default function domObserver (mutations, _document) { let parent = dom let yParent do { - parent = parent.parentNode + parent = parent.parentElement yParent = this.domToType.get(parent) } while (yParent === undefined && parent !== null) if (yParent !== false && yParent !== undefined && yParent.constructor !== YXmlHook) {