gc more efficiently
This commit is contained in:
@@ -197,6 +197,29 @@ export const typeArrayToArray = type => {
|
||||
return cs
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {Array<any>}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeArrayToArraySnapshot = (type, snapshot) => {
|
||||
const cs = []
|
||||
let n = type._start
|
||||
while (n !== null) {
|
||||
if (n.countable && isVisible(n, snapshot)) {
|
||||
const c = n.getContent()
|
||||
for (let i = 0; i < c.length; i++) {
|
||||
cs.push(c[i])
|
||||
}
|
||||
}
|
||||
n = n.right
|
||||
}
|
||||
return cs
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a provided function on once on overy element of this YArray.
|
||||
*
|
||||
|
||||
@@ -147,7 +147,7 @@ const insertNegatedAttributes = (transaction, parent, left, right, negatedAttrib
|
||||
left = new ItemFormat(nextID(transaction), left, left === null ? null : left.lastId, right, right === null ? null : right.id, parent, null, key, val)
|
||||
left.integrate(transaction)
|
||||
}
|
||||
return {left, right}
|
||||
return { left, right }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -617,10 +617,11 @@ export class YText extends AbstractType {
|
||||
constructor (string) {
|
||||
super()
|
||||
/**
|
||||
* @type {Array<string>?}
|
||||
* Array of pending operations on this type
|
||||
* @type {Array<function():void>?}
|
||||
* @private
|
||||
*/
|
||||
this._prelimContent = string !== undefined ? [string] : []
|
||||
this._pending = string !== undefined ? [() => this.insert(0, string)] : []
|
||||
}
|
||||
|
||||
get length () {
|
||||
@@ -635,9 +636,13 @@ export class YText extends AbstractType {
|
||||
*/
|
||||
_integrate (y, item) {
|
||||
super._integrate(y, item)
|
||||
// @ts-ignore this._prelimContent is still defined
|
||||
this.insert(0, this._prelimContent.join(''))
|
||||
this._prelimContent = null
|
||||
try {
|
||||
// @ts-ignore this._prelimContent is still defined
|
||||
this._pending.forEach(f => f())
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
this._pending = null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -737,6 +742,9 @@ export class YText extends AbstractType {
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.applyDelta(delta))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,9 +844,12 @@ export class YText extends AbstractType {
|
||||
const y = this._y
|
||||
if (y !== null) {
|
||||
transact(y, transaction => {
|
||||
const {left, right, currentAttributes} = findPosition(transaction, y.store, this, index)
|
||||
const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index)
|
||||
insertText(transaction, this, left, right, currentAttributes, text, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.insert(index, text, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,6 +873,9 @@ export class YText extends AbstractType {
|
||||
const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index)
|
||||
insertText(transaction, this, left, right, currentAttributes, embed, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.insertEmbed(index, embed, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,6 +897,9 @@ export class YText extends AbstractType {
|
||||
const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index)
|
||||
deleteText(transaction, left, right, currentAttributes, length)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.delete(index, length))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,6 +923,9 @@ export class YText extends AbstractType {
|
||||
}
|
||||
formatText(transaction, this, left, right, currentAttributes, length, attributes)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
this._pending.push(() => this.format(index, length, attributes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,244 +1,19 @@
|
||||
/**
|
||||
* @module YXml
|
||||
*/
|
||||
|
||||
import {
|
||||
YXmlEvent,
|
||||
AbstractType,
|
||||
typeArrayMap,
|
||||
typeArrayForEach,
|
||||
YXmlFragment,
|
||||
transact,
|
||||
typeMapDelete,
|
||||
typeMapSet,
|
||||
typeMapGet,
|
||||
typeMapGetAll,
|
||||
typeArrayInsertGenerics,
|
||||
typeArrayDelete,
|
||||
typeMapSet,
|
||||
typeMapDelete,
|
||||
typeArrayForEach,
|
||||
YXmlElementRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Y, Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
Snapshot, Y, ItemType // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
import * as decoding from 'lib0/decoding.js'
|
||||
|
||||
/**
|
||||
* Define the elements to which a set of CSS queries apply.
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
|
||||
*
|
||||
* @example
|
||||
* query = '.classSelector'
|
||||
* query = 'nodeSelector'
|
||||
* query = '#idSelector'
|
||||
*
|
||||
* @typedef {string} CSS_Selector
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a subset of the nodes of a YXmlElement / YXmlFragment and a
|
||||
* position within them.
|
||||
*
|
||||
* Can be created with {@link YXmlFragment#createTreeWalker}
|
||||
*
|
||||
* @public
|
||||
* @implements {IterableIterator}
|
||||
*/
|
||||
export class YXmlTreeWalker {
|
||||
/**
|
||||
* @param {YXmlFragment | YXmlElement} root
|
||||
* @param {function(AbstractType<any>):boolean} [f]
|
||||
*/
|
||||
constructor (root, f = () => true) {
|
||||
this._filter = f
|
||||
this._root = root
|
||||
/**
|
||||
* @type {ItemType | null}
|
||||
*/
|
||||
// @ts-ignore
|
||||
this._currentNode = root._start
|
||||
this._firstCall = true
|
||||
}
|
||||
|
||||
[Symbol.iterator] () {
|
||||
return this
|
||||
}
|
||||
/**
|
||||
* Get the next node.
|
||||
*
|
||||
* @return {IteratorResult<YXmlElement|YXmlText|YXmlHook>} The next node.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
next () {
|
||||
let n = this._currentNode
|
||||
if (n !== null && (!this._firstCall || n.deleted || !this._filter(n.type))) { // if first call, we check if we can use the first item
|
||||
do {
|
||||
if (!n.deleted && (n.type.constructor === YXmlElement || n.type.constructor === YXmlFragment) && n.type._start !== null) {
|
||||
// walk down in the tree
|
||||
// @ts-ignore
|
||||
n = n.type._start
|
||||
} else {
|
||||
// walk right or up in the tree
|
||||
while (n !== null) {
|
||||
if (n.right !== null) {
|
||||
// @ts-ignore
|
||||
n = n.right
|
||||
break
|
||||
} else if (n.parent === this._root) {
|
||||
n = null
|
||||
} else {
|
||||
n = n.parent._item
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (n !== null && (n.deleted || !this._filter(n.type)))
|
||||
}
|
||||
this._firstCall = false
|
||||
this._currentNode = n
|
||||
if (n === null) {
|
||||
// @ts-ignore return undefined if done=true (the expected result)
|
||||
return { value: undefined, done: true }
|
||||
}
|
||||
// @ts-ignore
|
||||
return { value: n.type, done: false }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
|
||||
* A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
|
||||
* 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
|
||||
* @extends AbstractType<YXmlEvent>
|
||||
*/
|
||||
export class YXmlFragment extends AbstractType {
|
||||
/**
|
||||
* 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(AbstractType<any>):boolean} filter Function that is called on each child element and
|
||||
* returns a Boolean indicating whether the child
|
||||
* is to be included in the subtree.
|
||||
* @return {YXmlTreeWalker} A subtree and a position within it.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
createTreeWalker (filter) {
|
||||
return new YXmlTreeWalker(this, filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first YXmlElement that matches the query.
|
||||
* Similar to DOM's {@link querySelector}.
|
||||
*
|
||||
* Query support:
|
||||
* - tagname
|
||||
* TODO:
|
||||
* - id
|
||||
* - attribute
|
||||
*
|
||||
* @param {CSS_Selector} query The query on the children.
|
||||
* @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
querySelector (query) {
|
||||
query = query.toUpperCase()
|
||||
// @ts-ignore
|
||||
const iterator = new YXmlTreeWalker(this, element => element.nodeName === query)
|
||||
const next = iterator.next()
|
||||
if (next.done) {
|
||||
return null
|
||||
} else {
|
||||
return next.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all YXmlElements that match the query.
|
||||
* Similar to Dom's {@link querySelectorAll}.
|
||||
*
|
||||
* @todo Does not yet support all queries. Currently only query by tagName.
|
||||
*
|
||||
* @param {CSS_Selector} query The query on the children
|
||||
* @return {Array<YXmlElement|YXmlText|YXmlHook|null>} The elements that match this query.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
querySelectorAll (query) {
|
||||
query = query.toUpperCase()
|
||||
// @ts-ignore
|
||||
return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates YXmlEvent and calls observers.
|
||||
* @private
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
||||
*/
|
||||
_callObserver (transaction, parentSubs) {
|
||||
callTypeObservers(this, transaction, new YXmlEvent(this, parentSubs, transaction))
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.toDomString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of all the children of this YXmlFragment.
|
||||
*
|
||||
* @return {string} The string representation of all children.
|
||||
*/
|
||||
toDomString () {
|
||||
return typeArrayMap(this, xml => xml.toDomString()).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {Object<string, any>} [hooks={}] Optional property to customize how hooks
|
||||
* are presented in the DOM
|
||||
* @param {any} [binding] You should not set this property. This is
|
||||
* used if DomBinding wants to create a
|
||||
* association to the created DOM type.
|
||||
* @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
toDom (_document = document, hooks = {}, binding) {
|
||||
const fragment = _document.createDocumentFragment()
|
||||
if (binding !== undefined) {
|
||||
binding._createAssociation(fragment, this)
|
||||
}
|
||||
typeArrayForEach(this, xmlType => {
|
||||
fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
|
||||
})
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An YXmlElement imitates the behavior of a
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}.
|
||||
@@ -250,11 +25,6 @@ export class YXmlElement extends YXmlFragment {
|
||||
constructor (nodeName = 'UNDEFINED') {
|
||||
super()
|
||||
this.nodeName = nodeName.toUpperCase()
|
||||
/**
|
||||
* @type {Array<any>|null}
|
||||
* @private
|
||||
*/
|
||||
this._prelimContent = []
|
||||
/**
|
||||
* @type {Map<string, any>|null}
|
||||
* @private
|
||||
@@ -389,44 +159,6 @@ export class YXmlElement extends YXmlFragment {
|
||||
return typeMapGetAll(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new content at an index.
|
||||
*
|
||||
* @example
|
||||
* // Insert character 'a' at position 0
|
||||
* xml.insert(0, [new Y.XmlText('text')])
|
||||
*
|
||||
* @param {number} index The index to insert content at
|
||||
* @param {Array<YXmlElement|YXmlText>} content The array of content
|
||||
*/
|
||||
insert (index, content) {
|
||||
if (this._y !== null) {
|
||||
transact(this._y, transaction => {
|
||||
typeArrayInsertGenerics(transaction, this, index, content)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, 0, ...content)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes elements starting from an index.
|
||||
*
|
||||
* @param {number} index Index at which to start deleting elements
|
||||
* @param {number} [length=1] The number of elements to remove. Defaults to 1.
|
||||
*/
|
||||
delete (index, length = 1) {
|
||||
if (this._y !== null) {
|
||||
transact(this._y, transaction => {
|
||||
typeArrayDelete(transaction, this, index, length)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, length)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Dom Element that mirrors this YXmlElement.
|
||||
*
|
||||
@@ -480,11 +212,3 @@ export class YXmlElement extends YXmlFragment {
|
||||
* @function
|
||||
*/
|
||||
export const readYXmlElement = decoder => new YXmlElement(decoding.readVarString(decoder))
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @return {YXmlFragment}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const readYXmlFragment = decoder => new YXmlFragment()
|
||||
|
||||
313
src/types/YXmlFragment.js
Normal file
313
src/types/YXmlFragment.js
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* @module YXml
|
||||
*/
|
||||
|
||||
import {
|
||||
YXmlEvent,
|
||||
YXmlElement,
|
||||
AbstractType,
|
||||
typeArrayMap,
|
||||
typeArrayForEach,
|
||||
typeArrayInsertGenerics,
|
||||
typeArrayDelete,
|
||||
typeArrayToArray,
|
||||
YXmlFragmentRefID,
|
||||
callTypeObservers,
|
||||
transact,
|
||||
Transaction, ItemType, YXmlText, YXmlHook, Snapshot // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as encoding from 'lib0/encoding.js'
|
||||
import * as decoding from 'lib0/decoding.js' // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Define the elements to which a set of CSS queries apply.
|
||||
* {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors|CSS_Selectors}
|
||||
*
|
||||
* @example
|
||||
* query = '.classSelector'
|
||||
* query = 'nodeSelector'
|
||||
* query = '#idSelector'
|
||||
*
|
||||
* @typedef {string} CSS_Selector
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a subset of the nodes of a YXmlElement / YXmlFragment and a
|
||||
* position within them.
|
||||
*
|
||||
* Can be created with {@link YXmlFragment#createTreeWalker}
|
||||
*
|
||||
* @public
|
||||
* @implements {IterableIterator}
|
||||
*/
|
||||
export class YXmlTreeWalker {
|
||||
/**
|
||||
* @param {YXmlFragment | YXmlElement} root
|
||||
* @param {function(AbstractType<any>):boolean} [f]
|
||||
*/
|
||||
constructor (root, f = () => true) {
|
||||
this._filter = f
|
||||
this._root = root
|
||||
/**
|
||||
* @type {ItemType | null}
|
||||
*/
|
||||
// @ts-ignore
|
||||
this._currentNode = root._start
|
||||
this._firstCall = true
|
||||
}
|
||||
|
||||
[Symbol.iterator] () {
|
||||
return this
|
||||
}
|
||||
/**
|
||||
* Get the next node.
|
||||
*
|
||||
* @return {IteratorResult<YXmlElement|YXmlText|YXmlHook>} The next node.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
next () {
|
||||
let n = this._currentNode
|
||||
if (n !== null && (!this._firstCall || n.deleted || !this._filter(n.type))) { // if first call, we check if we can use the first item
|
||||
do {
|
||||
if (!n.deleted && (n.type.constructor === YXmlElement || n.type.constructor === YXmlFragment) && n.type._start !== null) {
|
||||
// walk down in the tree
|
||||
// @ts-ignore
|
||||
n = n.type._start
|
||||
} else {
|
||||
// walk right or up in the tree
|
||||
while (n !== null) {
|
||||
if (n.right !== null) {
|
||||
// @ts-ignore
|
||||
n = n.right
|
||||
break
|
||||
} else if (n.parent === this._root) {
|
||||
n = null
|
||||
} else {
|
||||
n = n.parent._item
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (n !== null && (n.deleted || !this._filter(n.type)))
|
||||
}
|
||||
this._firstCall = false
|
||||
this._currentNode = n
|
||||
if (n === null) {
|
||||
// @ts-ignore return undefined if done=true (the expected result)
|
||||
return { value: undefined, done: true }
|
||||
}
|
||||
// @ts-ignore
|
||||
return { value: n.type, done: false }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a list of {@link YXmlElement}.and {@link YXmlText} types.
|
||||
* A YxmlFragment is similar to a {@link YXmlElement}, but it does not have a
|
||||
* 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
|
||||
* @extends AbstractType<YXmlEvent>
|
||||
*/
|
||||
export class YXmlFragment extends AbstractType {
|
||||
constructor () {
|
||||
super()
|
||||
/**
|
||||
* @type {Array<any>|null}
|
||||
* @private
|
||||
*/
|
||||
this._prelimContent = []
|
||||
}
|
||||
/**
|
||||
* 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(AbstractType<any>):boolean} filter Function that is called on each child element and
|
||||
* returns a Boolean indicating whether the child
|
||||
* is to be included in the subtree.
|
||||
* @return {YXmlTreeWalker} A subtree and a position within it.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
createTreeWalker (filter) {
|
||||
return new YXmlTreeWalker(this, filter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first YXmlElement that matches the query.
|
||||
* Similar to DOM's {@link querySelector}.
|
||||
*
|
||||
* Query support:
|
||||
* - tagname
|
||||
* TODO:
|
||||
* - id
|
||||
* - attribute
|
||||
*
|
||||
* @param {CSS_Selector} query The query on the children.
|
||||
* @return {YXmlElement|YXmlText|YXmlHook|null} The first element that matches the query or null.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
querySelector (query) {
|
||||
query = query.toUpperCase()
|
||||
// @ts-ignore
|
||||
const iterator = new YXmlTreeWalker(this, element => element.nodeName === query)
|
||||
const next = iterator.next()
|
||||
if (next.done) {
|
||||
return null
|
||||
} else {
|
||||
return next.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all YXmlElements that match the query.
|
||||
* Similar to Dom's {@link querySelectorAll}.
|
||||
*
|
||||
* @todo Does not yet support all queries. Currently only query by tagName.
|
||||
*
|
||||
* @param {CSS_Selector} query The query on the children
|
||||
* @return {Array<YXmlElement|YXmlText|YXmlHook|null>} The elements that match this query.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
querySelectorAll (query) {
|
||||
query = query.toUpperCase()
|
||||
// @ts-ignore
|
||||
return Array.from(new YXmlTreeWalker(this, element => element.nodeName === query))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates YXmlEvent and calls observers.
|
||||
* @private
|
||||
*
|
||||
* @param {Transaction} transaction
|
||||
* @param {Set<null|string>} parentSubs Keys changed on this type. `null` if list was modified.
|
||||
*/
|
||||
_callObserver (transaction, parentSubs) {
|
||||
callTypeObservers(this, transaction, new YXmlEvent(this, parentSubs, transaction))
|
||||
}
|
||||
|
||||
toString () {
|
||||
return this.toDomString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string representation of all the children of this YXmlFragment.
|
||||
*
|
||||
* @return {string} The string representation of all children.
|
||||
*/
|
||||
toDomString () {
|
||||
return typeArrayMap(this, xml => xml.toDomString()).join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {Object<string, any>} [hooks={}] Optional property to customize how hooks
|
||||
* are presented in the DOM
|
||||
* @param {any} [binding] You should not set this property. This is
|
||||
* used if DomBinding wants to create a
|
||||
* association to the created DOM type.
|
||||
* @return {Node} The {@link https://developer.mozilla.org/en-US/docs/Web/API/Element|Dom Element}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
toDom (_document = document, hooks = {}, binding) {
|
||||
const fragment = _document.createDocumentFragment()
|
||||
if (binding !== undefined) {
|
||||
binding._createAssociation(fragment, this)
|
||||
}
|
||||
typeArrayForEach(this, xmlType => {
|
||||
fragment.insertBefore(xmlType.toDom(_document, hooks, binding), null)
|
||||
})
|
||||
return fragment
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts new content at an index.
|
||||
*
|
||||
* @example
|
||||
* // Insert character 'a' at position 0
|
||||
* xml.insert(0, [new Y.XmlText('text')])
|
||||
*
|
||||
* @param {number} index The index to insert content at
|
||||
* @param {Array<YXmlElement|YXmlText>} content The array of content
|
||||
*/
|
||||
insert (index, content) {
|
||||
if (this._y !== null) {
|
||||
transact(this._y, transaction => {
|
||||
typeArrayInsertGenerics(transaction, this, index, content)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, 0, ...content)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes elements starting from an index.
|
||||
*
|
||||
* @param {number} index Index at which to start deleting elements
|
||||
* @param {number} [length=1] The number of elements to remove. Defaults to 1.
|
||||
*/
|
||||
delete (index, length = 1) {
|
||||
if (this._y !== null) {
|
||||
transact(this._y, transaction => {
|
||||
typeArrayDelete(transaction, this, index, length)
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore _prelimContent is defined because this is not yet integrated
|
||||
this._prelimContent.splice(index, length)
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Transforms this YArray to a JavaScript Array.
|
||||
*
|
||||
* @return {Array<YXmlElement|YXmlText|YXmlHook>}
|
||||
*/
|
||||
toArray () {
|
||||
return typeArrayToArray(this)
|
||||
}
|
||||
/**
|
||||
* Transform the properties of this type to binary and write it to an
|
||||
* BinaryEncoder.
|
||||
*
|
||||
* This is called when this Item is sent to a remote peer.
|
||||
*
|
||||
* @private
|
||||
* @param {encoding.Encoder} encoder The encoder to write data to.
|
||||
*/
|
||||
_write (encoder) {
|
||||
encoding.writeVarUint(encoder, YXmlFragmentRefID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {decoding.Decoder} decoder
|
||||
* @return {YXmlFragment}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const readYXmlFragment = decoder => new YXmlFragment()
|
||||
Reference in New Issue
Block a user