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/package-lock.json b/package-lock.json
index ac06c647..286c3036 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "yjs",
- "version": "13.0.0-56",
+ "version": "13.0.0-60",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index 266af208..fd7cd781 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "yjs",
- "version": "13.0.0-56",
+ "version": "13.0.0-60",
"description": "A framework for real-time p2p shared editing on any data",
"main": "./y.node.js",
"browser": "./y.js",
@@ -71,8 +71,5 @@
"rollup-watch": "^3.2.2",
"standard": "^10.0.2",
"tag-dist-files": "^0.1.6"
- },
- "dependencies": {
- "debug": "^2.6.8"
}
}
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..0dfb176e 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 {
@@ -90,8 +90,19 @@ 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
+ let parent = dom
+ let yParent
+ do {
+ parent = parent.parentElement
+ 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) {
@@ -125,9 +136,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 26c168b5..762af70d 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 // only iterate through non-text nodes
+ 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)
@@ -58,5 +94,6 @@ export default function typeObserver (events) {
}
}
})
+ fixScroll(this.scrollingElement, scrollRef)
})
}
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}