documentation and fix tests
This commit is contained in:
parent
61149b458a
commit
135c6d31be
@ -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.
|
||||||
|
@ -69,7 +69,6 @@ export function insertNodeHelper (yxml, prevExpectedNode, child, _document, bind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove children until `elem` is found.
|
* Remove children until `elem` is found.
|
||||||
*
|
*
|
||||||
|
@ -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 : ''})`
|
||||||
|
}
|
||||||
|
@ -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}`
|
||||||
|
@ -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 = []
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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})`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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')
|
||||||
|
@ -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'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user