documentation and fix tests

This commit is contained in:
Kevin Jahns 2018-03-29 11:58:02 +02:00
parent 61149b458a
commit 135c6d31be
22 changed files with 341 additions and 115 deletions

View File

@ -1,15 +1,11 @@
/* global MutationObserver */ /* global MutationObserver */
import Binding from '../Binding.js' import Binding from '../Binding.js'
import diff from '../../Util/simpleDiff.js' import { createAssociation, removeAssociation } from './util.js'
import YXmlFragment from '../../Types/YXml/YXmlFragment.js'
import YXmlHook from '../../Types/YXml/YXmlHook.js'
import { removeDomChildrenUntilElementFound, createAssociation } from './util.js'
import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js' import { beforeTransactionSelectionFixer, afterTransactionSelectionFixer } from './selection.js'
import { defaultFilter, applyFilterOnType } from './filter.js' import { defaultFilter, applyFilterOnType } from './filter.js'
import typeObserver from './typeObserver.js' import typeObserver from './typeObserver.js'
import domObserver from './domObserver.js' import domObserver from './domObserver.js'
import { removeAssociation } from './util.js'
/** /**
* A binding that binds the children of a YXmlFragment to a DOM element. * A binding that binds the children of a YXmlFragment to a DOM element.

View File

@ -69,7 +69,6 @@ export function insertNodeHelper (yxml, prevExpectedNode, child, _document, bind
} }
} }
/** /**
* Remove children until `elem` is found. * Remove children until `elem` is found.
* *

View File

@ -46,3 +46,20 @@ export function logID (id) {
throw new Error('This is not a valid ID!') throw new Error('This is not a valid ID!')
} }
} }
/**
* Helper utility to convert an item to a readable format.
*
* @param {String} name The name of the item class (YText, ItemString, ..).
* @param {Item} item The item instance.
* @param {String} [append] Additional information to append to the returned
* string.
* @return {String} A readable string that represents the item object.
*
* @private
*/
export function logItemHelper (name, item, append) {
const left = item._left !== null ? item._left._lastId : null
const origin = item._origin !== null ? item._origin._lastId : null
return `${name}(id:${logID(item._id)},start:${logID(item._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(item._right)},parent:${logID(item._parent)},parentSub:${item._parentSub}${append !== undefined ? ' - ' + append : ''})`
}

View File

@ -111,9 +111,10 @@ export default class Delete {
} }
/** /**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private * @private
* Transform this Delete to a readable format.
* Useful for logging as all Items implement this method.
*/ */
_logString () { _logString () {
return `Delete - target: ${logID(this._targetID)}, len: ${this._length}` return `Delete - target: ${logID(this._targetID)}, len: ${this._length}`

View File

@ -48,33 +48,76 @@ export function splitHelper (y, a, b, diff) {
} }
/** /**
* @private
* Abstract class that represents any content. * Abstract class that represents any content.
*/ */
export default class Item { export default class Item {
constructor () { constructor () {
/**
* The uniqe identifier of this type.
* @type {ID}
*/
this._id = null this._id = null
/**
* The item that was originally to the left of this item.
* @type {Item}
*/
this._origin = null this._origin = null
/**
* The item that is currently to the left of this item.
* @type {Item}
*/
this._left = null this._left = null
/**
* The item that is currently to the right of this item.
* @type {Item}
*/
this._right = null this._right = null
/**
* The item that was originally to the right of this item.
* @type {Item}
*/
this._right_origin = null this._right_origin = null
/**
* The parent type.
* @type {Y|YType}
*/
this._parent = null this._parent = null
/**
* If the parent refers to this item with some kind of key (e.g. YMap, the
* key is specified here. The key is then used to refer to the list in which
* to insert this item. If `parentSub = null` type._start is the list in
* which to insert to. Otherwise it is `parent._start`.
* @type {String}
*/
this._parentSub = null this._parentSub = null
/**
* Whether this item was deleted or not.
* @type {Boolean}
*/
this._deleted = false this._deleted = false
/**
* If this type's effect is reundone this type refers to the type that undid
* this operation.
* @type {Item}
*/
this._redone = null this._redone = null
} }
/** /**
* @private
* Creates an Item with the same effect as this Item (without position effect) * Creates an Item with the same effect as this Item (without position effect)
*
* @private
*/ */
_copy () { _copy () {
return new this.constructor() return new this.constructor()
} }
/** /**
* @private
* Redoes the effect of this operation. * Redoes the effect of this operation.
*
* @param {Y} y The Yjs instance.
*
* @private
*/ */
_redo (y) { _redo (y) {
if (this._redone !== null) { if (this._redone !== null) {
@ -117,35 +160,37 @@ export default class Item {
} }
/** /**
* @private
* Computes the last content address of this Item. * Computes the last content address of this Item.
*
* @private
*/ */
get _lastId () { get _lastId () {
return new ID(this._id.user, this._id.clock + this._length - 1) return new ID(this._id.user, this._id.clock + this._length - 1)
} }
/** /**
* @private
* Computes the length of this Item. * Computes the length of this Item.
*
* @private
*/ */
get _length () { get _length () {
return 1 return 1
} }
/** /**
* @private
* Should return false if this Item is some kind of meta information * Should return false if this Item is some kind of meta information
* (e.g. format information). * (e.g. format information).
* *
* * Whether this Item should be addressable via `yarray.get(i)` * * Whether this Item should be addressable via `yarray.get(i)`
* * Whether this Item should be counted when computing yarray.length * * Whether this Item should be counted when computing yarray.length
*
* @private
*/ */
get _countable () { get _countable () {
return true return true
} }
/** /**
* @private
* Splits this Item so that another Items can be inserted in-between. * Splits this Item so that another Items can be inserted in-between.
* This must be overwritten if _length > 1 * This must be overwritten if _length > 1
* Returns right part after split * Returns right part after split
@ -153,6 +198,8 @@ export default class Item {
* * diff === length => this._right * * diff === length => this._right
* * otherwise => split _content and return right part of split * * otherwise => split _content and return right part of split
* (see {@link ItemJSON}/{@link ItemString} for implementation) * (see {@link ItemJSON}/{@link ItemString} for implementation)
*
* @private
*/ */
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {
@ -162,12 +209,13 @@ export default class Item {
} }
/** /**
* @private
* Mark this Item as deleted. * Mark this Item as deleted.
* *
* @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.
*
* @private
*/ */
_delete (y, createDelete = true) { _delete (y, createDelete = true) {
if (!this._deleted) { if (!this._deleted) {
@ -189,16 +237,16 @@ export default class Item {
} }
/** /**
* @private
* This is called right before this Item receives any children. * This is called right before this Item receives any children.
* It can be overwritten to apply pending changes before applying remote changes * It can be overwritten to apply pending changes before applying remote changes
*
* @private
*/ */
_beforeChange () { _beforeChange () {
// nop // nop
} }
/** /**
* @private
* Integrates this Item into the shared structure. * Integrates this Item into the shared structure.
* *
* This method actually applies the change to the Yjs instance. In case of * This method actually applies the change to the Yjs instance. In case of
@ -208,6 +256,8 @@ export default class Item {
* * Integrate the struct so that other types/structs can see it * * Integrate the struct so that other types/structs can see it
* * Add this struct to y.os * * Add this struct to y.os
* * Check if this is struct deleted * * Check if this is struct deleted
*
* @private
*/ */
_integrate (y) { _integrate (y) {
y._transaction.newTypes.add(this) y._transaction.newTypes.add(this)
@ -328,13 +378,14 @@ export default class Item {
} }
/** /**
* @private
* Transform the properties of this type to binary and write it to an * Transform the properties of this type to binary and write it to an
* BinaryEncoder. * BinaryEncoder.
* *
* This is called when this Item is sent to a remote peer. * This is called when this Item is sent to a remote peer.
* *
* @param {BinaryEncoder} encoder The encoder to write data to. * @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/ */
_toBinary (encoder) { _toBinary (encoder) {
encoder.writeUint8(getStructReference(this.constructor)) encoder.writeUint8(getStructReference(this.constructor))
@ -378,13 +429,14 @@ export default class Item {
} }
/** /**
* @private
* Read the next Item in a Decoder and fill this Item with the read data. * Read the next Item in a Decoder and fill this Item with the read data.
* *
* This is called when data is received from a remote peer. * This is called when data is received from a remote peer.
* *
* @param {Y} y The Yjs instance that this Item belongs to. * @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from. * @param {BinaryDecoder} decoder The decoder object to read data from.
*
* @private
*/ */
_fromBinary (y, decoder) { _fromBinary (y, decoder) {
let missing = [] let missing = []

View File

@ -1,5 +1,5 @@
import { default as Item } from './Item.js' import { default as Item } from './Item.js'
import { logID } from '../MessageHandler/messageToString.js' import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemEmbed extends Item { export default class ItemEmbed extends Item {
constructor () { constructor () {
@ -23,9 +23,13 @@ export default class ItemEmbed extends Item {
super._toBinary(encoder) super._toBinary(encoder)
encoder.writeVarString(JSON.stringify(this.embed)) encoder.writeVarString(JSON.stringify(this.embed))
} }
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('ItemEmbed', this, `embed:${JSON.stringify(this.embed)}`)
const origin = this._origin !== null ? this._origin._lastId : null
return `ItemEmbed(id:${logID(this._id)},embed:${JSON.stringify(this.embed)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -1,5 +1,5 @@
import { default as Item } from './Item.js' import { default as Item } from './Item.js'
import { logID } from '../MessageHandler/messageToString.js' import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemFormat extends Item { export default class ItemFormat extends Item {
constructor () { constructor () {
@ -30,9 +30,13 @@ export default class ItemFormat extends Item {
encoder.writeVarString(this.key) encoder.writeVarString(this.key)
encoder.writeVarString(JSON.stringify(this.value)) encoder.writeVarString(JSON.stringify(this.value))
} }
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('ItemFormat', this, `key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)}`)
const origin = this._origin !== null ? this._origin._lastId : null
return `ItemFormat(id:${logID(this._id)},key:${JSON.stringify(this.key)},value:${JSON.stringify(this.value)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -1,5 +1,5 @@
import { splitHelper, default as Item } from './Item.js' import { splitHelper, default as Item } from './Item.js'
import { logID } from '../MessageHandler/messageToString.js' import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemJSON extends Item { export default class ItemJSON extends Item {
constructor () { constructor () {
@ -45,10 +45,14 @@ export default class ItemJSON extends Item {
encoder.writeVarString(encoded) encoder.writeVarString(encoded)
} }
} }
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('ItemJSON', this, `content:${JSON.stringify(this._content)}`)
const origin = this._origin !== null ? this._origin._lastId : null
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {

View File

@ -1,5 +1,5 @@
import { splitHelper, default as Item } from './Item.js' import { splitHelper, default as Item } from './Item.js'
import { logID } from '../MessageHandler/messageToString.js' import { logItemHelper } from '../MessageHandler/messageToString.js'
export default class ItemString extends Item { export default class ItemString extends Item {
constructor () { constructor () {
@ -23,10 +23,14 @@ export default class ItemString extends Item {
super._toBinary(encoder) super._toBinary(encoder)
encoder.writeVarString(this._content) encoder.writeVarString(this._content)
} }
/**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private
*/
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('ItemString', this, `content:"${this._content}"`)
const origin = this._origin !== null ? this._origin._lastId : null
return `ItemJSON(id:${logID(this._id)},content:${JSON.stringify(this._content)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
_splitAt (y, diff) { _splitAt (y, diff) {
if (diff === 0) { if (diff === 0) {

View File

@ -62,22 +62,22 @@ export default class Type extends Item {
} }
const path = [] const path = []
const y = this._y const y = this._y
while (type._parent !== this && this._parent !== y) { while (type !== this && type !== y) {
let parent = type._parent let parent = type._parent
if (type._parentSub !== null) { if (type._parentSub !== null) {
path.push(type._parentSub) path.unshift(type._parentSub)
} else { } else {
// parent is array-ish // parent is array-ish
for (let [i, child] of parent) { for (let [i, child] of parent) {
if (child === type) { if (child === type) {
path.push(i) path.unshift(i)
break break
} }
} }
} }
type = parent type = parent
} }
if (this._parent !== this) { if (type !== this) {
throw new Error('The type is not a child of this node') throw new Error('The type is not a child of this node')
} }
return path return path

View File

@ -1,7 +1,7 @@
import Type from '../../Struct/Type.js' import Type from '../../Struct/Type.js'
import ItemJSON from '../../Struct/ItemJSON.js' import ItemJSON from '../../Struct/ItemJSON.js'
import ItemString from '../../Struct/ItemString.js' import ItemString from '../../Struct/ItemString.js'
import { logID } from '../../MessageHandler/messageToString.js' import { logID, logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js' import YEvent from '../../Util/YEvent.js'
/** /**
@ -367,13 +367,12 @@ export default class YArray extends Type {
} }
/** /**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private * @private
* Transform this YArray to a readable format.
* Useful for logging as all Items implement this method.
*/ */
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('YArray', this, `start:${logID(this._start)}"`)
const origin = this._origin !== null ? this._origin._lastId : null
return `YArray(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -1,7 +1,7 @@
import Type from '../../Struct/Type.js' import Type from '../../Struct/Type.js'
import Item from '../../Struct/Item.js' import Item from '../../Struct/Item.js'
import ItemJSON from '../../Struct/ItemJSON.js' import ItemJSON from '../../Struct/ItemJSON.js'
import { logID } from '../../MessageHandler/messageToString.js' import { logItemHelper } from '../../MessageHandler/messageToString.js'
import YEvent from '../../Util/YEvent.js' import YEvent from '../../Util/YEvent.js'
/** /**
@ -164,13 +164,12 @@ export default class YMap extends Type {
} }
/** /**
* Transform this YXml Type to a readable format.
* Useful for logging as all Items and Delete implement this method.
*
* @private * @private
* Transform this YMap to a readable format.
* Useful for logging as all Items implement this method.
*/ */
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('YMap', this, `mapSize:${this._map.size}`)
const origin = this._origin !== null ? this._origin._lastId : null
return `YMap(id:${logID(this._id)},mapSize:${this._map.size},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -1,9 +1,12 @@
import ItemString from '../../Struct/ItemString.js' import ItemString from '../../Struct/ItemString.js'
import ItemEmbed from '../../Struct/ItemEmbed.js' import ItemEmbed from '../../Struct/ItemEmbed.js'
import ItemFormat from '../../Struct/ItemFormat.js' import ItemFormat from '../../Struct/ItemFormat.js'
import { logID } from '../../MessageHandler/messageToString.js' import { logItemHelper } from '../../MessageHandler/messageToString.js'
import { YArrayEvent, default as YArray } from '../YArray/YArray.js' import { YArrayEvent, default as YArray } from '../YArray/YArray.js'
/**
* @private
*/
function integrateItem (item, parent, y, left, right) { function integrateItem (item, parent, y, left, right) {
item._origin = left item._origin = left
item._left = left item._left = left
@ -19,6 +22,9 @@ function integrateItem (item, parent, y, left, right) {
} }
} }
/**
* @private
*/
function findNextPosition (currentAttributes, parent, left, right, count) { function findNextPosition (currentAttributes, parent, left, right, count) {
while (right !== null && count > 0) { while (right !== null && count > 0) {
switch (right.constructor) { switch (right.constructor) {
@ -46,6 +52,9 @@ function findNextPosition (currentAttributes, parent, left, right, count) {
return [left, right, currentAttributes] return [left, right, currentAttributes]
} }
/**
* @private
*/
function findPosition (parent, index) { function findPosition (parent, index) {
let currentAttributes = new Map() let currentAttributes = new Map()
let left = null let left = null
@ -53,7 +62,11 @@ function findPosition (parent, index) {
return findNextPosition(currentAttributes, parent, left, right, index) return findNextPosition(currentAttributes, parent, left, right, index)
} }
// negate applied formats /**
* Negate applied formats
*
* @private
*/
function insertNegatedAttributes (y, parent, left, right, negatedAttributes) { function insertNegatedAttributes (y, parent, left, right, negatedAttributes) {
// check if we really need to remove attributes // check if we really need to remove attributes
while ( while (
@ -80,6 +93,9 @@ function insertNegatedAttributes (y, parent, left, right, negatedAttributes) {
return [left, right] return [left, right]
} }
/**
* @private
*/
function updateCurrentAttributes (currentAttributes, item) { function updateCurrentAttributes (currentAttributes, item) {
const value = item.value const value = item.value
const key = item.key const key = item.key
@ -90,6 +106,9 @@ function updateCurrentAttributes (currentAttributes, item) {
} }
} }
/**
* @private
*/
function minimizeAttributeChanges (left, right, currentAttributes, attributes) { function minimizeAttributeChanges (left, right, currentAttributes, attributes) {
// go right while attributes[right.key] === right.value (or right is deleted) // go right while attributes[right.key] === right.value (or right is deleted)
while (true) { while (true) {
@ -109,6 +128,9 @@ function minimizeAttributeChanges (left, right, currentAttributes, attributes) {
return [left, right] return [left, right]
} }
/**
* @private
*/
function insertAttributes (y, parent, left, right, attributes, currentAttributes) { function insertAttributes (y, parent, left, right, attributes, currentAttributes) {
const negatedAttributes = new Map() const negatedAttributes = new Map()
// insert format-start items // insert format-start items
@ -125,9 +147,12 @@ function insertAttributes (y, parent, left, right, attributes, currentAttributes
left = format left = format
} }
} }
return negatedAttributes return [left, right, negatedAttributes]
} }
/**
* @private
*/
function insertText (y, text, parent, left, right, currentAttributes, attributes) { function insertText (y, text, parent, left, right, currentAttributes, attributes) {
for (let [key] of currentAttributes) { for (let [key] of currentAttributes) {
if (attributes.hasOwnProperty(key) === false) { if (attributes.hasOwnProperty(key) === false) {
@ -135,7 +160,8 @@ function insertText (y, text, parent, left, right, currentAttributes, attributes
} }
} }
[left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes) [left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes)
const negatedAttributes = insertAttributes(y, parent, left, right, attributes, currentAttributes) let negatedAttributes
[left, right, negatedAttributes] = insertAttributes(y, parent, left, right, attributes, currentAttributes)
// insert content // insert content
let item let item
if (text.constructor === String) { if (text.constructor === String) {
@ -150,9 +176,13 @@ function insertText (y, text, parent, left, right, currentAttributes, attributes
return insertNegatedAttributes(y, parent, left, right, negatedAttributes) return insertNegatedAttributes(y, parent, left, right, negatedAttributes)
} }
/**
* @private
*/
function formatText (y, length, parent, left, right, currentAttributes, attributes) { function formatText (y, length, parent, left, right, currentAttributes, attributes) {
[left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes) [left, right] = minimizeAttributeChanges(left, right, currentAttributes, attributes)
const negatedAttributes = insertAttributes(y, parent, left, right, attributes, currentAttributes) let negatedAttributes
[left, right, negatedAttributes] = insertAttributes(y, parent, left, right, attributes, currentAttributes)
// iterate until first non-format or null is found // iterate until first non-format or null is found
// delete all formats with attributes[format.key] != null // delete all formats with attributes[format.key] != null
while (length > 0 && right !== null) { while (length > 0 && right !== null) {
@ -182,6 +212,9 @@ function formatText (y, length, parent, left, right, currentAttributes, attribut
return insertNegatedAttributes(y, parent, left, right, negatedAttributes) return insertNegatedAttributes(y, parent, left, right, negatedAttributes)
} }
/**
* @private
*/
function deleteText (y, length, parent, left, right, currentAttributes) { function deleteText (y, length, parent, left, right, currentAttributes) {
while (length > 0 && right !== null) { while (length > 0 && right !== null) {
if (right._deleted === false) { if (right._deleted === false) {
@ -234,18 +267,23 @@ function deleteText (y, length, parent, left, right, currentAttributes) {
/** /**
* Event that describes the changes on a YText type. * Event that describes the changes on a YText type.
*
* @private
*/ */
class YTextEvent extends YArrayEvent { class YTextEvent extends YArrayEvent {
constructor (ytext, remote, transaction) { constructor (ytext, remote, transaction) {
super(ytext, remote, transaction) super(ytext, remote, transaction)
this._delta = null this._delta = null
} }
// TODO: Should put this in a separate function. toDelta shouldn't be included
// in every Yjs distribution
/** /**
* Compute the changes in the delta format. * Compute the changes in the delta format.
* *
* @return {Delta} A {@link https://quilljs.com/docs/delta/|Quill Delta}) that * @return {Delta} A {@link https://quilljs.com/docs/delta/|Quill Delta}) that
* represents the changes on the document. * represents the changes on the document.
*
* @public
*/ */
get delta () { get delta () {
if (this._delta === null) { if (this._delta === null) {
@ -438,6 +476,8 @@ export default class YText extends YArray {
/** /**
* Returns the unformatted string representation of this YText type. * Returns the unformatted string representation of this YText type.
*
* @public
*/ */
toString () { toString () {
let str = '' let str = ''
@ -455,6 +495,8 @@ export default class YText extends YArray {
* Apply a {@link Delta} on this shared YText type. * Apply a {@link Delta} on this shared YText type.
* *
* @param {Delta} delta The changes to apply on this element. * @param {Delta} delta The changes to apply on this element.
*
* @public
*/ */
applyDelta (delta) { applyDelta (delta) {
this._transact(y => { this._transact(y => {
@ -478,6 +520,8 @@ export default class YText extends YArray {
* Returns the Delta representation of this YText type. * Returns the Delta representation of this YText type.
* *
* @return {Delta} The Delta representation of this type. * @return {Delta} The Delta representation of this type.
*
* @public
*/ */
toDelta () { toDelta () {
let ops = [] let ops = []
@ -527,6 +571,8 @@ export default class YText extends YArray {
* @param {TextAttributes} attributes Optionally define some formatting * @param {TextAttributes} attributes Optionally define some formatting
* information to apply on the inserted * information to apply on the inserted
* Text. * Text.
*
* @public
*/ */
insert (index, text, attributes = {}) { insert (index, text, attributes = {}) {
if (text.length <= 0) { if (text.length <= 0) {
@ -546,6 +592,7 @@ export default class YText extends YArray {
* @param {TextAttributes} attributes Attribute information to apply on the * @param {TextAttributes} attributes Attribute information to apply on the
* embed * embed
* *
* @public
*/ */
insertEmbed (index, embed, attributes = {}) { insertEmbed (index, embed, attributes = {}) {
if (embed.constructor !== Object) { if (embed.constructor !== Object) {
@ -562,6 +609,8 @@ export default class YText extends YArray {
* *
* @param {Integer} index Index at which to start deleting. * @param {Integer} index Index at which to start deleting.
* @param {Integer} length The number of characters to remove. Defaults to 1. * @param {Integer} length The number of characters to remove. Defaults to 1.
*
* @public
*/ */
delete (index, length) { delete (index, length) {
if (length === 0) { if (length === 0) {
@ -580,6 +629,8 @@ export default class YText extends YArray {
* @param {Integer} length The amount of characters to assign properties to. * @param {Integer} length The amount of characters to assign properties to.
* @param {TextAttributes} attributes Attribute information to apply on the * @param {TextAttributes} attributes Attribute information to apply on the
* text. * text.
*
* @public
*/ */
format (index, length, attributes) { format (index, length, attributes) {
this._transact(y => { this._transact(y => {
@ -590,15 +641,14 @@ export default class YText extends YArray {
formatText(y, length, this, left, right, currentAttributes, attributes) formatText(y, length, this, left, right, currentAttributes, attributes)
}) })
} }
// TODO: De-duplicate code. The following code is in every type.
/** /**
* @private
* Transform this YText to a readable format. * Transform this YText to a readable format.
* Useful for logging as all Items implement this method. * Useful for logging as all Items implement this method.
*
* @private
*/ */
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('YText', this)
const origin = this._origin !== null ? this._origin._lastId : null
return `YText(id:${logID(this._id)},start:${logID(this._start)},left:${logID(left)},origin:${logID(origin)},right:${logID(this._right)},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -43,13 +43,14 @@ export default class YXmlElement extends YXmlFragment {
} }
/** /**
* @private
* Transform the properties of this type to binary and write it to an * Transform the properties of this type to binary and write it to an
* BinaryEncoder. * BinaryEncoder.
* *
* This is called when this Item is sent to a remote peer. * This is called when this Item is sent to a remote peer.
* *
* @param {BinaryEncoder} encoder The encoder to write data to. * @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/ */
_toBinary (encoder) { _toBinary (encoder) {
super._toBinary(encoder) super._toBinary(encoder)
@ -57,7 +58,6 @@ export default class YXmlElement extends YXmlFragment {
} }
/** /**
* @private
* Integrates this Item into the shared structure. * Integrates this Item into the shared structure.
* *
* This method actually applies the change to the Yjs instance. In case of * This method actually applies the change to the Yjs instance. In case of
@ -66,6 +66,10 @@ export default class YXmlElement extends YXmlFragment {
* *
* * Checks for nodeName * * Checks for nodeName
* * Sets domFilter * * Sets domFilter
*
* @param {Y} y The Yjs instance
*
* @private
*/ */
_integrate (y) { _integrate (y) {
if (this.nodeName === null) { if (this.nodeName === null) {
@ -78,6 +82,10 @@ export default class YXmlElement extends YXmlFragment {
* Returns the string representation of this YXmlElement. * Returns the string representation of this YXmlElement.
* The attributes are ordered by attribute-name, so you can easily use this * The attributes are ordered by attribute-name, so you can easily use this
* method to compare YXmlElements * method to compare YXmlElements
*
* @return {String} The string representation of this type.
*
* @public
*/ */
toString () { toString () {
const attrs = this.getAttributes() const attrs = this.getAttributes()
@ -101,6 +109,8 @@ export default class YXmlElement extends YXmlFragment {
* Removes an attribute from this YXmlElement. * Removes an attribute from this YXmlElement.
* *
* @param {String} attributeName The attribute name that is to be removed. * @param {String} attributeName The attribute name that is to be removed.
*
* @public
*/ */
removeAttribute (attributeName) { removeAttribute (attributeName) {
return YMap.prototype.delete.call(this, attributeName) return YMap.prototype.delete.call(this, attributeName)
@ -111,6 +121,8 @@ export default class YXmlElement extends YXmlFragment {
* *
* @param {String} attributeName The attribute name that is to be set. * @param {String} attributeName The attribute name that is to be set.
* @param {String} attributeValue The attribute value that is to be set. * @param {String} attributeValue The attribute value that is to be set.
*
* @public
*/ */
setAttribute (attributeName, attributeValue) { setAttribute (attributeName, attributeValue) {
return YMap.prototype.set.call(this, attributeName, attributeValue) return YMap.prototype.set.call(this, attributeName, attributeValue)
@ -121,7 +133,9 @@ export default class YXmlElement extends YXmlFragment {
* *
* @param {String} attributeName The attribute name that identifies the * @param {String} attributeName The attribute name that identifies the
* queried value. * queried value.
* @return {String} The queried attribute value * @return {String} The queried attribute value.
*
* @public
*/ */
getAttribute (attributeName) { getAttribute (attributeName) {
return YMap.prototype.get.call(this, attributeName) return YMap.prototype.get.call(this, attributeName)
@ -131,6 +145,8 @@ export default class YXmlElement extends YXmlFragment {
* Returns all attribute name/value pairs in a JSON Object. * Returns all attribute name/value pairs in a JSON Object.
* *
* @return {Object} A JSON Object that describes the attributes. * @return {Object} A JSON Object that describes the attributes.
*
* @public
*/ */
getAttributes () { getAttributes () {
const obj = {} const obj = {}
@ -141,11 +157,19 @@ export default class YXmlElement extends YXmlFragment {
} }
return obj return obj
} }
// TODO: outsource the binding property.
/** /**
* Creates a Dom Element that mirrors this YXmlElement. * Creates a Dom Element that mirrors this YXmlElement.
* *
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element} * @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, binding) {
const dom = _document.createElement(this.nodeName) const dom = _document.createElement(this.nodeName)

View File

@ -2,13 +2,39 @@ import YEvent from '../../Util/YEvent.js'
/** /**
* An Event that describes changes on a YXml Element or Yxml Fragment * An Event that describes changes on a YXml Element or Yxml Fragment
*
* @protected
*/ */
export default class YXmlEvent extends YEvent { export default class YXmlEvent extends YEvent {
/**
* @param {YType} target The target on which the event is created.
* @param {Set} subs The set of changed attributes. `null` is included if the
* child list changed.
* @param {Boolean} remote Whether this change was created by a remote peer.
* @param {Transaction} transaction The transaction instance with wich the
* change was created.
*/
constructor (target, subs, remote, transaction) { constructor (target, subs, remote, transaction) {
super(target) super(target)
/**
* The transaction instance for the computed change.
* @type {Transaction}
*/
this._transaction = transaction this._transaction = transaction
/**
* Whether the children changed.
* @type {Boolean}
*/
this.childListChanged = false this.childListChanged = false
/**
* Set of all changed attributes.
* @type {Set}
*/
this.attributesChanged = new Set() this.attributesChanged = new Set()
/**
* Whether this change was created by a remote peer.
* @type {Boolean}
*/
this.remote = remote this.remote = remote
subs.forEach((sub) => { subs.forEach((sub) => {
if (sub === null) { if (sub === null) {

View File

@ -1,13 +1,18 @@
/* global MutationObserver */
import { createAssociation } from '../../Bindings/DomBinding/util.js' import { createAssociation } from '../../Bindings/DomBinding/util.js'
import YXmlTreeWalker from './YXmlTreeWalker.js' import YXmlTreeWalker from './YXmlTreeWalker.js'
import YArray from '../YArray/YArray.js' import YArray from '../YArray/YArray.js'
import YXmlEvent from './YXmlEvent.js' import YXmlEvent from './YXmlEvent.js'
import { YXmlText, YXmlHook } from './YXml.js' import { logItemHelper } from '../../MessageHandler/messageToString.js'
import { logID } from '../../MessageHandler/messageToString.js'
import diff from '../../Util/simpleDiff.js' /**
* Dom filter function.
*
* @callback domFilter
* @param {string} nodeName The nodeName of the element
* @param {Map} attributes The map of attributes.
* @return {boolean} Whether to include the Dom node in the YXmlElement.
*/
/** /**
* Define the elements to which a set of CSS queries apply. * Define the elements to which a set of CSS queries apply.
@ -21,20 +26,31 @@ import diff from '../../Util/simpleDiff.js'
* @typedef {string} CSS_Selector * @typedef {string} CSS_Selector
*/ */
/** /**
* Represents a list of {@link YXmlElement}. * Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
* A YxmlFragment does not have a nodeName and it does not have attributes. * A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
* Therefore it also must not be added as a childElement. * nodeName and it does not have attributes. Though it can be bound to a DOM
* element - in this case the attributes and the nodeName are not shared.
*
* @public
*/ */
export default class YXmlFragment extends YArray { export default class YXmlFragment extends YArray {
/** /**
* Create a subtree of childNodes. * Create a subtree of childNodes.
* *
* @example
* const walker = elem.createTreeWalker(dom => dom.nodeName === 'div')
* for (let node in walker) {
* // `node` is a div node
* nop(node)
* }
*
* @param {Function} filter Function that is called on each child element and * @param {Function} filter Function that is called on each child element and
* returns a Boolean indicating whether the child * returns a Boolean indicating whether the child
* is to be included in the subtree. * is to be included in the subtree.
* @return {TreeWalker} A subtree and a position within it. * @return {TreeWalker} A subtree and a position within it.
*
* @public
*/ */
createTreeWalker (filter) { createTreeWalker (filter) {
return new YXmlTreeWalker(this, filter) return new YXmlTreeWalker(this, filter)
@ -52,6 +68,8 @@ export default class YXmlFragment extends YArray {
* *
* @param {CSS_Selector} query The query on the children. * @param {CSS_Selector} query The query on the children.
* @return {?YXmlElement} The first element that matches the query or null. * @return {?YXmlElement} The first element that matches the query or null.
*
* @public
*/ */
querySelector (query) { querySelector (query) {
query = query.toUpperCase() query = query.toUpperCase()
@ -72,6 +90,8 @@ export default class YXmlFragment extends YArray {
* *
* @param {CSS_Selector} query The query on the children * @param {CSS_Selector} query The query on the children
* @return {Array<YXmlElement>} The elements that match this query. * @return {Array<YXmlElement>} The elements that match this query.
*
* @public
*/ */
querySelectorAll (query) { querySelectorAll (query) {
query = query.toUpperCase() query = query.toUpperCase()
@ -79,17 +99,9 @@ export default class YXmlFragment extends YArray {
} }
/** /**
* Dom filter function.
*
* @callback domFilter
* @param {string} nodeName The nodeName of the element
* @param {Map} attributes The map of attributes.
* @return {boolean} Whether to include the Dom node in the YXmlElement.
*/
/**
* @private
* Creates YArray Event and calls observers. * Creates YArray Event and calls observers.
*
* @private
*/ */
_callObserver (transaction, parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction)) this._callEventHandler(transaction, new YXmlEvent(this, parentSubs, remote, transaction))
@ -111,13 +123,25 @@ 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.
*
* @private
*/ */
_delete (y, createDelete) { _delete (y, createDelete) {
super._delete(y, createDelete) super._delete(y, createDelete)
} }
/** /**
* @return {DocumentFragment} The dom representation of this * Creates a Dom Element that mirrors this YXmlElement.
*
* @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, binding) {
const fragment = _document.createDocumentFragment() const fragment = _document.createDocumentFragment()
@ -128,13 +152,12 @@ export default class YXmlFragment extends YArray {
return fragment return fragment
} }
/** /**
* @private
* Transform this YXml Type to a readable format. * Transform this YXml Type to a readable format.
* Useful for logging as all Items implement this method. * Useful for logging as all Items and Delete implement this method.
*
* @private
*/ */
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null return logItemHelper('YXml', this)
const origin = this._origin !== null ? this._origin._lastId : null
return `YXml(id:${logID(this._id)},left:${logID(left)},origin:${logID(origin)},right:${this._right},parent:${logID(this._parent)},parentSub:${this._parentSub})`
} }
} }

View File

@ -4,21 +4,24 @@ import { getHook, addHook } from './hooks.js'
/** /**
* You can manage binding to a custom type with YXmlHook. * You can manage binding to a custom type with YXmlHook.
* *
* @param {String} hookName nodeName of the Dom Node. * @public
*/ */
export default class YXmlHook extends YMap { export default class YXmlHook extends YMap {
/**
* @param {String} hookName nodeName of the Dom Node.
*/
constructor (hookName) { constructor (hookName) {
super() super()
this.hookName = null this.hookName = null
if (hookName !== undefined) { if (hookName !== undefined) {
this.hookName = hookName this.hookName = hookName
dom._yjsHook = hookName
} }
} }
/** /**
* @private
* Creates an Item with the same effect as this Item (without position effect) * Creates an Item with the same effect as this Item (without position effect)
*
* @private
*/ */
_copy () { _copy () {
const struct = super._copy() const struct = super._copy()
@ -27,9 +30,17 @@ export default class YXmlHook extends YMap {
} }
/** /**
* Creates a DOM element that represents this YXmlHook. * Creates a Dom Element that mirrors this YXmlElement.
* *
* @return Element The DOM representation of this Type. * @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/ */
toDom (_document = document) { toDom (_document = document) {
const dom = getHook(this.hookName).createDom(this) const dom = getHook(this.hookName).createDom(this)
@ -38,13 +49,14 @@ export default class YXmlHook extends YMap {
} }
/** /**
* @private
* Read the next Item in a Decoder and fill this Item with the read data. * Read the next Item in a Decoder and fill this Item with the read data.
* *
* This is called when data is received from a remote peer. * This is called when data is received from a remote peer.
* *
* @param {Y} y The Yjs instance that this Item belongs to. * @param {Y} y The Yjs instance that this Item belongs to.
* @param {BinaryDecoder} decoder The decoder object to read data from. * @param {BinaryDecoder} decoder The decoder object to read data from.
*
* @private
*/ */
_fromBinary (y, decoder) { _fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder) const missing = super._fromBinary(y, decoder)
@ -53,13 +65,14 @@ export default class YXmlHook extends YMap {
} }
/** /**
* @private
* Transform the properties of this type to binary and write it to an * Transform the properties of this type to binary and write it to an
* BinaryEncoder. * BinaryEncoder.
* *
* This is called when this Item is sent to a remote peer. * This is called when this Item is sent to a remote peer.
* *
* @param {BinaryEncoder} encoder The encoder to write data to. * @param {BinaryEncoder} encoder The encoder to write data to.
*
* @private
*/ */
_toBinary (encoder) { _toBinary (encoder) {
super._toBinary(encoder) super._toBinary(encoder)
@ -67,7 +80,6 @@ export default class YXmlHook extends YMap {
} }
/** /**
* @private
* Integrate this type into the Yjs instance. * Integrate this type into the Yjs instance.
* *
* * Save this struct in the os * * Save this struct in the os
@ -75,6 +87,8 @@ export default class YXmlHook extends YMap {
* * Observer functions are fired * * Observer functions are fired
* *
* @param {Y} y The Yjs instance * @param {Y} y The Yjs instance
*
* @private
*/ */
_integrate (y) { _integrate (y) {
if (this.hookName === null) { if (this.hookName === null) {

View File

@ -8,11 +8,18 @@ import { createAssociation } from '../../Bindings/DomBinding/util.js'
* @param {String} arg1 Initial value. * @param {String} arg1 Initial value.
*/ */
export default class YXmlText extends YText { export default class YXmlText extends YText {
/** /**
* Creates a TextNode with the same textual content. * Creates a Dom Element that mirrors this YXmlText.
* *
* @return TextNode * @param {Document} [_document=document] The document object (you must define
* this when calling this method in
* nodejs)
* @param {DomBinding} [binding] You should not set this property. This is
* used if DomBinding wants to create a
* association to the created DOM type.
* @return {Element} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
*
* @public
*/ */
toDom (_document = document, binding) { toDom (_document = document, binding) {
const dom = _document.createTextNode(this.toString()) const dom = _document.createTextNode(this.toString())
@ -21,12 +28,13 @@ export default class YXmlText extends YText {
} }
/** /**
* @private
* Mark this Item as deleted. * Mark this Item as deleted.
* *
* @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.
*
* @private
*/ */
_delete (y, createDelete) { _delete (y, createDelete) {
super._delete(y, createDelete) super._delete(y, createDelete)

View File

@ -17,6 +17,8 @@ import YXmlFragment from './YXmlFragment.js'
* position within them. * position within them.
* *
* Can be created with {@link YXmlFragment#createTreeWalker} * Can be created with {@link YXmlFragment#createTreeWalker}
*
* @public
*/ */
export default class YXmlTreeWalker { export default class YXmlTreeWalker {
constructor (root, f) { constructor (root, f) {
@ -32,6 +34,8 @@ export default class YXmlTreeWalker {
* Get the next node. * Get the next node.
* *
* @return {YXmlElement} The next node. * @return {YXmlElement} The next node.
*
* @public
*/ */
next () { next () {
let n = this._currentNode let n = this._currentNode

View File

@ -3,6 +3,6 @@
<head> <head>
</head> </head>
<body> <body>
<script type="module" src="./index.js"></script> <script type="module" src="./diff.tests.js"></script>
</body> </body>
</html> </html>

View File

@ -14,7 +14,6 @@ test('events', async function xml1 (t) {
var { users, xml0, xml1 } = await initArrays(t, { users: 2 }) var { users, xml0, xml1 } = await initArrays(t, { users: 2 })
var event var event
var remoteEvent var remoteEvent
let expectedEvent
xml0.observe(function (e) { xml0.observe(function (e) {
delete e._content delete e._content
delete e.nodes delete e.nodes
@ -30,21 +29,21 @@ test('events', async function xml1 (t) {
xml0.setAttribute('key', 'value') xml0.setAttribute('key', 'value')
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key') t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key')
await flushAll(t, users) await flushAll(t, users)
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key (remote)') t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on updated key (remote)')
// check attributeRemoved // check attributeRemoved
xml0.removeAttribute('key') xml0.removeAttribute('key')
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute') t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute')
await flushAll(t, users) await flushAll(t, users)
t.assert(event.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute (remote)') t.assert(remoteEvent.attributesChanged.has('key'), 'YXmlEvent.attributesChanged on removed attribute (remote)')
xml0.insert(0, [new Y.XmlText('some text')]) xml0.insert(0, [new Y.XmlText('some text')])
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element') t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element')
await flushAll(t, users) await flushAll(t, users)
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on inserted element (remote)') t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on inserted element (remote)')
// test childRemoved // test childRemoved
xml0.delete(0) xml0.delete(0)
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element') t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element')
await flushAll(t, users) await flushAll(t, users)
t.assert(event.childListChanged, 'YXmlEvent.childListChanged on deleted element (remote)') t.assert(remoteEvent.childListChanged, 'YXmlEvent.childListChanged on deleted element (remote)')
await compareUsers(t, users) await compareUsers(t, users)
}) })
@ -180,7 +179,7 @@ test('Receive a bunch of elements (with disconnect)', async function xml12 (t) {
}) })
test('move element to a different position', async function xml13 (t) { test('move element to a different position', async function xml13 (t) {
var { users, xml0, xml1, dom0, dom1 } = await initArrays(t, { users: 3 }) var { users, dom0, dom1 } = await initArrays(t, { users: 3 })
dom0.append(document.createElement('div')) dom0.append(document.createElement('div'))
dom0.append(document.createElement('h1')) dom0.append(document.createElement('h1'))
await flushAll(t, users) await flushAll(t, users)
@ -193,7 +192,7 @@ test('move element to a different position', async function xml13 (t) {
}) })
test('filter node', async function xml14 (t) { test('filter node', async function xml14 (t) {
var { users, xml0, xml1, dom0, dom1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 }) var { users, dom0, dom1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 })
let domFilter = (nodeName, attrs) => { let domFilter = (nodeName, attrs) => {
if (nodeName === 'H1') { if (nodeName === 'H1') {
return null return null
@ -212,7 +211,7 @@ test('filter node', async function xml14 (t) {
}) })
test('filter attribute', async function xml15 (t) { test('filter attribute', async function xml15 (t) {
var { users, xml0, xml1, dom0, dom1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 }) var { users, dom0, dom1, domBinding0, domBinding1 } = await initArrays(t, { users: 3 })
let domFilter = (nodeName, attrs) => { let domFilter = (nodeName, attrs) => {
attrs.delete('hidden') attrs.delete('hidden')
return attrs return attrs
@ -231,7 +230,7 @@ test('filter attribute', async function xml15 (t) {
}) })
test('deep element insert', async function xml16 (t) { test('deep element insert', async function xml16 (t) {
var { users, xml0, xml1, dom0, dom1 } = await initArrays(t, { users: 3 }) var { users, dom0, dom1 } = await initArrays(t, { users: 3 })
let deepElement = document.createElement('p') let deepElement = document.createElement('p')
let boldElement = document.createElement('b') let boldElement = document.createElement('b')
let attrElement = document.createElement('img') let attrElement = document.createElement('img')

View File

@ -1,4 +1,3 @@
/* global Y */
import { wait } from './helper' import { wait } from './helper'
import { messageToString } from '../src/MessageHandler/messageToString' import { messageToString } from '../src/MessageHandler/messageToString'
import AbstractConnector from '../src/Connector.js' import AbstractConnector from '../src/Connector.js'