Compare commits

..

4 Commits

Author SHA1 Message Date
Kevin Jahns
a0b81c336e v13.0.0-58 -- distribution files 2018-05-05 15:35:07 +02:00
Kevin Jahns
6336064516 13.0.0-58 2018-05-05 15:33:53 +02:00
Kevin Jahns
49d2e42b41 fix missing dom-binding - still investigating 2018-05-05 15:33:16 +02:00
Kevin Jahns
c098e8e745 implement scroll-fixer 2018-05-05 14:47:59 +02:00
12 changed files with 181 additions and 75 deletions

View File

@@ -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', {

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "yjs",
"version": "13.0.0-57",
"version": "13.0.0-58",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -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",

View File

@@ -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.

View File

@@ -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) {
@@ -125,9 +137,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)
}

View File

@@ -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)
@@ -59,5 +95,6 @@ export default function typeObserver (events) {
}
}
})
fixScroll(this.scrollingElement, scrollRef)
})
}

8
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,7 @@
/**
* yjs - A framework for real-time p2p shared editing on any data
* @version v13.0.0-57
* @version v13.0.0-58
* @license MIT
*/
@@ -5267,11 +5267,48 @@ function afterTransactionSelectionFixer (y, domBinding, transaction, remote) {
}
}
/* global getSelection */
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
*/
function typeObserver (events) {
this._mutualExclude(() => {
const scrollRef = findScrollReference(this.scrollingElement);
events.forEach(event => {
const yxml = event.target;
const dom = this.typeToDom.get(yxml);
@@ -5323,6 +5360,7 @@ function typeObserver (events) {
}
}
});
fixScroll(this.scrollingElement, scrollRef);
});
}
@@ -5426,8 +5464,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 {
@@ -5458,6 +5496,18 @@ 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) {
@@ -5491,9 +5541,6 @@ 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);
}
@@ -5529,6 +5576,7 @@ 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}
@@ -5601,17 +5649,6 @@ 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.

File diff suppressed because one or more lines are too long

101
y.test.js
View File

@@ -18334,8 +18334,48 @@ function afterTransactionSelectionFixer (y, domBinding, transaction, remote) {
}
}
/* global getSelection */
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
*/
function typeObserver (events) {
this._mutualExclude(() => {
const scrollRef = findScrollReference(this.scrollingElement);
events.forEach(event => {
const yxml = event.target;
const dom = this.typeToDom.get(yxml);
@@ -18387,6 +18427,7 @@ function typeObserver (events) {
}
}
});
fixScroll(this.scrollingElement, scrollRef);
});
}
@@ -18437,6 +18478,18 @@ function simpleDiff (a, b) {
}
}
/**
* 1. Check if any of the nodes was deleted
* 2. Iterate over the children.
* 2.1 If a node exists that is not yet bound to a type, insert a new node
* 2.2 If _contents.length < dom.childNodes.length, fill the
* rest of _content with childNodes
* 2.3 If a node was moved, delete it and
* recreate a new yxml element that is bound to that node.
* You can detect that a node was moved because expectedId
* !== actualId in the list
* @private
*/
function applyChangesFromDom (binding, dom, yxml, _document) {
if (yxml == null || yxml === false || yxml.constructor === YXmlHook) {
return
@@ -18478,8 +18531,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 {
@@ -18510,6 +18563,18 @@ 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) {
@@ -18543,9 +18608,6 @@ 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);
}
@@ -18570,6 +18632,7 @@ 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}
@@ -18642,17 +18705,6 @@ 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.
@@ -18687,25 +18739,6 @@ class DomBinding extends Binding {
* @typedef {function(nodeName: String, attrs: Map): Map|null} FilterFunction
*/
/**
* Anything that can be encoded with `JSON.stringify` and can be decoded with
* `JSON.parse`.
*
* The following property should hold:
* `JSON.parse(JSON.stringify(key))===key`
*
* At the moment the only safe values are number and string.
*
* @typedef {(number|string)} encodable
*/
/**
* A Yjs instance handles the state of shared data.
*
* @param {string} room Users in the same room share the same content
* @param {Object} opts Connector definition
* @param {AbstractPersistence} persistence Persistence adapter instance
*/
class Y$1 extends NamedEventHandler {
constructor (room, opts, persistence, conf = {}) {
super();

File diff suppressed because one or more lines are too long