bugfixes
This commit is contained in:
parent
99f92cb9a0
commit
a54d826d6d
@ -1,7 +1,7 @@
|
|||||||
/* global Y, d3 */
|
/* global Y, d3 */
|
||||||
|
|
||||||
const hooks = {
|
const hooks = {
|
||||||
"magic-drawing": {
|
'magic-drawing': {
|
||||||
fillType: function (dom, type) {
|
fillType: function (dom, type) {
|
||||||
initDrawingBindings(type, dom)
|
initDrawingBindings(type, dom)
|
||||||
},
|
},
|
||||||
@ -19,7 +19,7 @@ window.onload = function () {
|
|||||||
|
|
||||||
window.addMagicDrawing = function addMagicDrawing () {
|
window.addMagicDrawing = function addMagicDrawing () {
|
||||||
let mt = document.createElement('magic-drawing')
|
let mt = document.createElement('magic-drawing')
|
||||||
mt.dataset.yjsHook = 'magic-drawing'
|
mt.setAttribute('data-yjs-hook', 'magic-drawing')
|
||||||
document.body.append(mt)
|
document.body.append(mt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ var renderPath = d3.svg.line()
|
|||||||
|
|
||||||
function initDrawingBindings (type, dom) {
|
function initDrawingBindings (type, dom) {
|
||||||
dom.contentEditable = 'false'
|
dom.contentEditable = 'false'
|
||||||
dom.dataset.yjsHook = 'magic-drawing'
|
dom.setAttribute('data-yjs-hook', 'magic-drawing')
|
||||||
var drawing = type.get('drawing')
|
var drawing = type.get('drawing')
|
||||||
if (drawing === undefined) {
|
if (drawing === undefined) {
|
||||||
drawing = type.set('drawing', new Y.Array())
|
drawing = type.set('drawing', new Y.Array())
|
||||||
|
@ -9,5 +9,5 @@ let y = new Y('xml-example', {
|
|||||||
|
|
||||||
window.yXml = y
|
window.yXml = y
|
||||||
// bind xml type to a dom, and put it in body
|
// bind xml type to a dom, and put it in body
|
||||||
window.sharedDom = y.define('xml', Y.XmlElement).getDom()
|
window.sharedDom = y.define('xml', Y.XmlElement).toDom()
|
||||||
document.body.appendChild(window.sharedDom)
|
document.body.appendChild(window.sharedDom)
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"y.*",
|
"y.*",
|
||||||
"src/*",
|
"src/*",
|
||||||
".esdoc.json"
|
".esdoc.json",
|
||||||
|
"docs/*"
|
||||||
],
|
],
|
||||||
"standard": {
|
"standard": {
|
||||||
"ignore": [
|
"ignore": [
|
||||||
|
@ -51,9 +51,9 @@ export default class DomBinding extends Binding {
|
|||||||
this.filter = opts.filter || defaultFilter
|
this.filter = opts.filter || defaultFilter
|
||||||
// set initial value
|
// set initial value
|
||||||
target.innerHTML = ''
|
target.innerHTML = ''
|
||||||
for (let child of type) {
|
type.forEach(child => {
|
||||||
target.insertBefore(child.toDom(opts.document, opts.hooks, this), null)
|
target.insertBefore(child.toDom(opts.document, opts.hooks, this), null)
|
||||||
}
|
})
|
||||||
this._typeObserver = typeObserver.bind(this)
|
this._typeObserver = typeObserver.bind(this)
|
||||||
this._domObserver = (mutations) => {
|
this._domObserver = (mutations) => {
|
||||||
domObserver.call(this, mutations, opts.document)
|
domObserver.call(this, mutations, opts.document)
|
||||||
|
@ -1,35 +1,37 @@
|
|||||||
|
|
||||||
import { YXmlText, YXmlElement, YXmlHook } from '../../Types/YXml/YXml.js'
|
import { YXmlText, YXmlElement, YXmlHook } from '../../Types/YXml/YXml.js'
|
||||||
import { createAssociation } from './util.js'
|
import { createAssociation, domsToTypes } from './util.js'
|
||||||
import { filterDomAttributes } from './filter.js'
|
import { filterDomAttributes, defaultFilter } from './filter.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
* Creates a Yjs type (YXml) based on the contents of a DOM Element.
|
||||||
*
|
*
|
||||||
* @param {Element|TextNode} element The DOM Element
|
* @param {Element|TextNode} element The DOM Element
|
||||||
* @param {?Document} _document Optional. Provide the global document object.
|
* @param {?Document} _document Optional. Provide the global document object
|
||||||
* @param {?DomBinding} binding This property should only be set if the type
|
* @param {Hooks} [hooks = {}] Optional. Set of Yjs Hooks
|
||||||
* is going to be bound with the dom-binding.
|
* @param {Filter} [filter=defaultFilter] Optional. Dom element filter
|
||||||
|
* @param {?DomBinding} binding Warning: This property is for internal use only!
|
||||||
* @return {YXmlElement | YXmlText}
|
* @return {YXmlElement | YXmlText}
|
||||||
*/
|
*/
|
||||||
export default function domToType (element, _document = document, binding) {
|
export default function domToType (element, _document = document, hooks = {}, filter = defaultFilter, binding) {
|
||||||
let type
|
let type
|
||||||
switch (element.nodeType) {
|
switch (element.nodeType) {
|
||||||
case _document.ELEMENT_NODE:
|
case _document.ELEMENT_NODE:
|
||||||
let hookName = element.dataset.yjsHook
|
let hookName = null
|
||||||
let hook
|
let hook
|
||||||
// configure `hookName !== undefined` if element is a hook.
|
// configure `hookName !== undefined` if element is a hook.
|
||||||
if (hookName !== undefined) {
|
if (element.hasAttribute('data-yjs-hook')) {
|
||||||
hook = binding.opts.hooks[hookName]
|
hookName = element.getAttribute('data-yjs-hook')
|
||||||
|
hook = hooks[hookName]
|
||||||
if (hook === undefined) {
|
if (hook === undefined) {
|
||||||
console.error(`Unknown hook "${hookName}". Deleting yjsHook dataset property.`)
|
console.error(`Unknown hook "${hookName}". Deleting yjsHook dataset property.`)
|
||||||
delete element.dataset.yjsHook
|
delete element.removeAttribute('data-yjs-hook')
|
||||||
hookName = undefined
|
hookName = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hookName === undefined) {
|
if (hookName === null) {
|
||||||
// Not a hook
|
// Not a hook
|
||||||
const attrs = filterDomAttributes(element, binding.filter)
|
const attrs = filterDomAttributes(element, filter)
|
||||||
if (attrs === null) {
|
if (attrs === null) {
|
||||||
type = false
|
type = false
|
||||||
} else {
|
} else {
|
||||||
@ -37,14 +39,7 @@ export default function domToType (element, _document = document, binding) {
|
|||||||
attrs.forEach((val, key) => {
|
attrs.forEach((val, key) => {
|
||||||
type.setAttribute(key, val)
|
type.setAttribute(key, val)
|
||||||
})
|
})
|
||||||
const children = []
|
type.insert(0, domsToTypes(element.childNodes, document, hooks, filter, binding))
|
||||||
for (let elem of element.childNodes) {
|
|
||||||
const type = domToType(elem, _document, binding)
|
|
||||||
if (type !== false) {
|
|
||||||
children.push(type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
type.insert(0, children)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Is a hook
|
// Is a hook
|
||||||
|
@ -14,7 +14,7 @@ export function defaultFilter (nodeName, attrs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export function filterDomAttributes (dom, filter) {
|
export function filterDomAttributes (dom, filter) {
|
||||||
const attrs = new Map()
|
const attrs = new Map()
|
||||||
|
@ -17,7 +17,10 @@ export function iterateUntilUndeleted (item) {
|
|||||||
* Removes an association (the information that a DOM element belongs to a
|
* Removes an association (the information that a DOM element belongs to a
|
||||||
* type).
|
* type).
|
||||||
*
|
*
|
||||||
* @private
|
* @param {DomBinding} domBinding The binding object
|
||||||
|
* @param {Element} dom The dom that is to be associated with type
|
||||||
|
* @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
export function removeAssociation (domBinding, dom, type) {
|
export function removeAssociation (domBinding, dom, type) {
|
||||||
domBinding.domToType.delete(dom)
|
domBinding.domToType.delete(dom)
|
||||||
@ -28,7 +31,10 @@ export function removeAssociation (domBinding, dom, type) {
|
|||||||
* Creates an association (the information that a DOM element belongs to a
|
* Creates an association (the information that a DOM element belongs to a
|
||||||
* type).
|
* type).
|
||||||
*
|
*
|
||||||
* @private
|
* @param {DomBinding} domBinding The binding object
|
||||||
|
* @param {Element} dom The dom that is to be associated with type
|
||||||
|
* @param {YXmlElement|YXmlHook} type The type that is to be associated with dom
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
export function createAssociation (domBinding, dom, type) {
|
export function createAssociation (domBinding, dom, type) {
|
||||||
if (domBinding !== undefined) {
|
if (domBinding !== undefined) {
|
||||||
@ -37,6 +43,24 @@ export function createAssociation (domBinding, dom, type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If oldDom is associated with a type, associate newDom with the type and
|
||||||
|
* forget about oldDom. If oldDom is not associated with any type, nothing happens.
|
||||||
|
*
|
||||||
|
* @param {DomBinding} domBinding The binding object
|
||||||
|
* @param {Element} oldDom The existing dom
|
||||||
|
* @param {Element} newDom The new dom object
|
||||||
|
*/
|
||||||
|
export function switchAssociation (domBinding, oldDom, newDom) {
|
||||||
|
if (domBinding !== undefined) {
|
||||||
|
const type = domBinding.domToType.get(oldDom)
|
||||||
|
if (type !== undefined) {
|
||||||
|
removeAssociation(domBinding, oldDom, type)
|
||||||
|
createAssociation(domBinding, newDom, type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert Dom Elements after one of the children of this YXmlFragment.
|
* Insert Dom Elements after one of the children of this YXmlFragment.
|
||||||
* The Dom elements will be bound to a new YXmlElement and inserted at the
|
* The Dom elements will be bound to a new YXmlElement and inserted at the
|
||||||
@ -54,14 +78,19 @@ export function createAssociation (domBinding, dom, type) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
|
export function insertDomElementsAfter (type, prev, doms, _document, binding) {
|
||||||
|
const types = domsToTypes(doms, _document, binding.opts.hooks, binding.filter, binding)
|
||||||
|
return type.insertAfter(prev, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function domsToTypes (doms, _document, hooks, filter, binding) {
|
||||||
const types = []
|
const types = []
|
||||||
for (let dom of doms) {
|
for (let dom of doms) {
|
||||||
const t = domToType(dom, _document, binding)
|
const t = domToType(dom, _document, hooks, filter, binding)
|
||||||
if (t !== false) {
|
if (t !== false) {
|
||||||
types.push(t)
|
types.push(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return type.insertAfter(prev, types)
|
return types
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { getStruct } from '../Util/structReferences.js'
|
import { getStruct } from '../Util/structReferences.js'
|
||||||
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
import BinaryDecoder from '../Util/Binary/Decoder.js'
|
||||||
import { logID } from './messageToString.js'
|
import { logID } from './messageToString.js'
|
||||||
import GC from '../Struct/GC.js';
|
import GC from '../Struct/GC.js'
|
||||||
|
|
||||||
class MissingEntry {
|
class MissingEntry {
|
||||||
constructor (decoder, missing, struct) {
|
constructor (decoder, missing, struct) {
|
||||||
|
@ -91,4 +91,4 @@ export default class GC {
|
|||||||
gc._length = this._length - diff
|
gc._length = this._length - diff
|
||||||
return gc
|
return gc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,7 +238,7 @@ export default class Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gcChildren (y) {}
|
_gcChildren (y) {}
|
||||||
|
|
||||||
_gc (y) {
|
_gc (y) {
|
||||||
const gc = new GC()
|
const gc = new GC()
|
||||||
gc._id = this._id
|
gc._id = this._id
|
||||||
|
@ -213,6 +213,8 @@ export default class Type extends Item {
|
|||||||
* @param {Y} y The Yjs instance
|
* @param {Y} y The Yjs instance
|
||||||
* @param {boolean} createDelete Whether to propagate a message that this
|
* @param {boolean} createDelete Whether to propagate a message that this
|
||||||
* Type was deleted.
|
* Type was deleted.
|
||||||
|
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
|
||||||
|
* collect the children of this type.
|
||||||
*/
|
*/
|
||||||
_delete (y, createDelete, gcChildren) {
|
_delete (y, createDelete, gcChildren) {
|
||||||
if (gcChildren === undefined) {
|
if (gcChildren === undefined) {
|
||||||
|
@ -198,7 +198,7 @@ export default class YArray extends Type {
|
|||||||
content = this._item._content[this._itemElement++]
|
content = this._item._content[this._itemElement++]
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
value: [this._count, content],
|
value: content,
|
||||||
done: false
|
done: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -123,6 +123,8 @@ export default class YXmlFragment extends YArray {
|
|||||||
* @param {Y} y The Yjs instance
|
* @param {Y} y The Yjs instance
|
||||||
* @param {boolean} createDelete Whether to propagate a message that this
|
* @param {boolean} createDelete Whether to propagate a message that this
|
||||||
* Type was deleted.
|
* Type was deleted.
|
||||||
|
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
|
||||||
|
* collect the children of this type.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -52,7 +52,7 @@ export default class YXmlHook extends YMap {
|
|||||||
} else {
|
} else {
|
||||||
dom = document.createElement(this.hookName)
|
dom = document.createElement(this.hookName)
|
||||||
}
|
}
|
||||||
dom.dataset.yjsHook = this.hookName
|
dom.setAttribute('data-yjs-hook', this.hookName)
|
||||||
createAssociation(binding, dom, this)
|
createAssociation(binding, dom, this)
|
||||||
return dom
|
return dom
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ export default class YXmlText extends YText {
|
|||||||
* @param {Y} y The Yjs instance
|
* @param {Y} y The Yjs instance
|
||||||
* @param {boolean} createDelete Whether to propagate a message that this
|
* @param {boolean} createDelete Whether to propagate a message that this
|
||||||
* Type was deleted.
|
* Type was deleted.
|
||||||
|
* @param {boolean} [gcChildren=y._hasUndoManager===false] Whether to garbage
|
||||||
|
* collect the children of this type.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import ID from './ID/ID.js'
|
import ID from './ID/ID.js'
|
||||||
import RootID from './ID/RootID.js'
|
import RootID from './ID/RootID.js'
|
||||||
|
import GC from '../Struct/GC.js'
|
||||||
|
|
||||||
// TODO: Implement function to describe ranges
|
// TODO: Implement function to describe ranges
|
||||||
|
|
||||||
@ -76,6 +77,9 @@ export function fromRelativePosition (y, rpos) {
|
|||||||
id = new RootID(rpos[3], rpos[4])
|
id = new RootID(rpos[3], rpos[4])
|
||||||
}
|
}
|
||||||
const type = y.os.get(id)
|
const type = y.os.get(id)
|
||||||
|
if (type === null || type.constructor === GC) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
type,
|
type,
|
||||||
offset: type.length
|
offset: type.length
|
||||||
@ -84,7 +88,7 @@ export function fromRelativePosition (y, rpos) {
|
|||||||
let offset = 0
|
let offset = 0
|
||||||
let struct = y.os.findNodeWithUpperBound(new ID(rpos[0], rpos[1])).val
|
let struct = y.os.findNodeWithUpperBound(new ID(rpos[0], rpos[1])).val
|
||||||
const parent = struct._parent
|
const parent = struct._parent
|
||||||
if (parent._deleted) {
|
if (struct.constructor === GC || parent._deleted) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
if (!struct._deleted) {
|
if (!struct._deleted) {
|
||||||
|
@ -56,4 +56,4 @@ registerStruct(7, YXmlElement)
|
|||||||
registerStruct(8, YXmlText)
|
registerStruct(8, YXmlText)
|
||||||
registerStruct(9, YXmlHook)
|
registerStruct(9, YXmlHook)
|
||||||
|
|
||||||
registerStruct(12, GC)
|
registerStruct(12, GC)
|
||||||
|
@ -20,6 +20,8 @@ import DomBinding from './Bindings/DomBinding/DomBinding.js'
|
|||||||
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
|
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
|
||||||
|
|
||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
|
import domToType from './Bindings/DomBinding/domToType.js'
|
||||||
|
import { domsToTypes, switchAssociation } from './Bindings/DomBinding/util.js'
|
||||||
|
|
||||||
// TODO: The following assignments should be moved to yjs-dist
|
// TODO: The following assignments should be moved to yjs-dist
|
||||||
Y.AbstractConnector = Connector
|
Y.AbstractConnector = Connector
|
||||||
@ -36,6 +38,10 @@ Y.TextareaBinding = TextareaBinding
|
|||||||
Y.QuillBinding = QuillBinding
|
Y.QuillBinding = QuillBinding
|
||||||
Y.DomBinding = DomBinding
|
Y.DomBinding = DomBinding
|
||||||
|
|
||||||
|
DomBinding.domToType = domToType
|
||||||
|
DomBinding.domsToTypes = domsToTypes
|
||||||
|
DomBinding.switchAssociation = switchAssociation
|
||||||
|
|
||||||
Y.utils = {
|
Y.utils = {
|
||||||
BinaryDecoder,
|
BinaryDecoder,
|
||||||
UndoManager,
|
UndoManager,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { test } from '../node_modules/cutest/cutest.mjs'
|
import { test } from '../node_modules/cutest/cutest.mjs'
|
||||||
import simpleDiff from '../src/Util/simpleDiff.js'
|
|
||||||
import Chance from 'chance'
|
import Chance from 'chance'
|
||||||
import DeleteStore from '../src/Store/DeleteStore.js'
|
import DeleteStore from '../src/Store/DeleteStore.js'
|
||||||
import ID from '../src/Util/ID/ID.js'
|
import ID from '../src/Util/ID/ID.js'
|
||||||
|
@ -8,7 +8,7 @@ import ItemJSON from '../src/Struct/ItemJSON.js'
|
|||||||
import ItemString from '../src/Struct/ItemString.js'
|
import ItemString from '../src/Struct/ItemString.js'
|
||||||
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
import { defragmentItemContent } from '../src/Util/defragmentItemContent.js'
|
||||||
import Quill from 'quill'
|
import Quill from 'quill'
|
||||||
import GC from '../src/Struct/GC.js';
|
import GC from '../src/Struct/GC.js'
|
||||||
|
|
||||||
export const Y = _Y
|
export const Y = _Y
|
||||||
|
|
||||||
@ -42,41 +42,6 @@ function getDeleteSet (y) {
|
|||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove?
|
|
||||||
export function attrsObject (dom) {
|
|
||||||
let keys = []
|
|
||||||
let yxml = dom._yxml
|
|
||||||
for (let i = 0; i < dom.attributes.length; i++) {
|
|
||||||
keys.push(dom.attributes[i].name)
|
|
||||||
}
|
|
||||||
keys = yxml._domFilter(dom, keys)
|
|
||||||
let obj = {}
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
let key = keys[i]
|
|
||||||
obj[key] = dom.getAttribute(key)
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: remove?
|
|
||||||
export function domToJson (dom) {
|
|
||||||
if (dom.nodeType === document.TEXT_NODE) {
|
|
||||||
return dom.textContent
|
|
||||||
} else if (dom.nodeType === document.ELEMENT_NODE) {
|
|
||||||
let attributes = attrsObject(dom)
|
|
||||||
let children = Array.from(dom.childNodes.values())
|
|
||||||
.filter(d => d._yxml !== false)
|
|
||||||
.map(domToJson)
|
|
||||||
return {
|
|
||||||
name: dom.nodeName,
|
|
||||||
children: children,
|
|
||||||
attributes: attributes
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Unsupported node type')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 1. reconnect and flush all
|
* 1. reconnect and flush all
|
||||||
* 2. user 0 gc
|
* 2. user 0 gc
|
||||||
|
Loading…
x
Reference in New Issue
Block a user