diff --git a/README.md b/README.md
index b2dc629a..f4f8c5db 100644
--- a/README.md
+++ b/README.md
@@ -255,6 +255,8 @@ position 0.
get(index:number)
+ slice(start:number, end:number):Array<Object|boolean|Array|string|number|Uint8Array|Y.Type>
+ Retrieve a range of content
length:number
@@ -320,6 +322,8 @@ or any of its children.
get(index:number)
+ clone():Y.Map
+ Clone this type into a fresh Yjs type.
toJSON():Object<string, Object|boolean|Array|string|number|Uint8Array>
Copies the [key,value]
pairs of this YMap to a new Object.It
@@ -451,8 +455,12 @@ or any of its children.
get(index:number)
+ slice(start:number, end:number):Array<Y.XmlElement|Y.XmlText>
+ Retrieve a range of content
length:number
+ clone():Y.XmlFragment
+ Clone this type into a fresh Yjs type.
toArray():Array<Y.XmlElement|Y.XmlText>
Copies the children to a new Array.
toDOM():DocumentFragment
@@ -512,6 +520,12 @@ content and be actually XML compliant.
getAttributes(attributeName:string):Object<string,string>
+ get(i:number):Y.XmlElement|Y.XmlText
+ Retrieve the i-th element.
+ slice(start:number, end:number):Array<Y.XmlElement|Y.XmlText>
+ Retrieve a range of content
+ clone():Y.XmlElement
+ Clone this type into a fresh Yjs type.
toArray():Array<Y.XmlElement|Y.XmlText>
Copies the children to a new Array.
toDOM():Element
diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js
index b628b902..c3513d68 100644
--- a/src/types/AbstractType.js
+++ b/src/types/AbstractType.js
@@ -309,6 +309,13 @@ export class AbstractType {
throw error.methodUnimplemented()
}
+ /**
+ * @return {AbstractType}
+ */
+ clone () {
+ throw error.methodUnimplemented()
+ }
+
/**
* @param {AbstractUpdateEncoder} encoder
*/
@@ -381,6 +388,43 @@ export class AbstractType {
toJSON () {}
}
+/**
+ * @param {AbstractType} type
+ * @param {number} start
+ * @param {number} end
+ * @return {Array}
+ *
+ * @private
+ * @function
+ */
+export const typeListSlice = (type, start, end) => {
+ if (start < 0) {
+ start = type._length + start
+ }
+ if (end < 0) {
+ end = type._length + end
+ }
+ let len = end - start
+ const cs = []
+ let n = type._start
+ while (n !== null && len > 0) {
+ if (n.countable && !n.deleted) {
+ const c = n.content.getContent()
+ if (c.length <= start) {
+ start -= c.length
+ } else {
+ for (let i = start; i < c.length && len > 0; i++) {
+ cs.push(c[i])
+ len--
+ }
+ start = 0
+ }
+ }
+ n = n.right
+ }
+ return cs
+}
+
/**
* @param {AbstractType} type
* @return {Array}
diff --git a/src/types/YArray.js b/src/types/YArray.js
index b0f78fdb..e5991750 100644
--- a/src/types/YArray.js
+++ b/src/types/YArray.js
@@ -17,6 +17,7 @@ import {
transact,
ArraySearchMarker, AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, Transaction, Item // eslint-disable-line
} from '../internals.js'
+import { typeListSlice } from './AbstractType.js'
/**
* Event that describes the changes on a YArray
@@ -73,6 +74,17 @@ export class YArray extends AbstractType {
return new YArray()
}
+ /**
+ * @return {YArray}
+ */
+ clone () {
+ const arr = new YArray()
+ arr.insert(0, this.toArray().map(el =>
+ el instanceof AbstractType ? el.clone() : el
+ ))
+ return arr
+ }
+
get length () {
return this._prelimContent === null ? this._length : this._prelimContent.length
}
@@ -167,6 +179,17 @@ export class YArray extends AbstractType {
return typeListToArray(this)
}
+ /**
+ * Transforms this YArray to a JavaScript Array.
+ *
+ * @param {number} [start]
+ * @param {number} [end]
+ * @return {Array}
+ */
+ slice (start = 0, end = this.length) {
+ return typeListSlice(this, start, end)
+ }
+
/**
* Transforms this Shared Type to a JSON object.
*
diff --git a/src/types/YMap.js b/src/types/YMap.js
index a0cb9733..efaa16f5 100644
--- a/src/types/YMap.js
+++ b/src/types/YMap.js
@@ -84,6 +84,17 @@ export class YMap extends AbstractType {
return new YMap()
}
+ /**
+ * @return {YMap}
+ */
+ clone () {
+ const map = new YMap()
+ this.forEach((value, key) => {
+ map.set(key, value instanceof AbstractType ? value.clone() : value)
+ })
+ return map
+ }
+
/**
* Creates YMapEvent and calls observers.
*
diff --git a/src/types/YText.js b/src/types/YText.js
index 6430a1f4..2c129385 100644
--- a/src/types/YText.js
+++ b/src/types/YText.js
@@ -762,6 +762,15 @@ export class YText extends AbstractType {
return new YText()
}
+ /**
+ * @return {YText}
+ */
+ clone () {
+ const text = new YText()
+ text.applyDelta(this.toDelta())
+ return text
+ }
+
/**
* Creates YTextEvent and calls observers.
*
diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js
index 6d91da15..0757e1b2 100644
--- a/src/types/YXmlElement.js
+++ b/src/types/YXmlElement.js
@@ -8,7 +8,7 @@ import {
typeMapGetAll,
typeListForEach,
YXmlElementRefID,
- AbstractUpdateDecoder, AbstractUpdateEncoder, Snapshot, Doc, Item // eslint-disable-line
+ AbstractType, AbstractUpdateDecoder, AbstractUpdateEncoder, Snapshot, Doc, Item // eslint-disable-line
} from '../internals.js'
/**
@@ -55,6 +55,20 @@ export class YXmlElement extends YXmlFragment {
return new YXmlElement(this.nodeName)
}
+ /**
+ * @return {YXmlElement}
+ */
+ clone () {
+ const el = new YXmlElement(this.nodeName)
+ const attrs = this.getAttributes()
+ for (const key in attrs) {
+ el.setAttribute(key, attrs[key])
+ }
+ // @ts-ignore
+ el.insert(0, el.toArray().map(item => item instanceof AbstractType ? item.clone() : item))
+ return el
+ }
+
/**
* Returns the XML serialization of this YXmlElement.
* The attributes are ordered by attribute-name, so you can easily use this
diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js
index 4fcf09e7..711c9687 100644
--- a/src/types/YXmlFragment.js
+++ b/src/types/YXmlFragment.js
@@ -14,6 +14,8 @@ import {
YXmlFragmentRefID,
callTypeObservers,
transact,
+ typeListGet,
+ typeListSlice,
AbstractUpdateDecoder, AbstractUpdateEncoder, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, Snapshot // eslint-disable-line
} from '../internals.js'
@@ -148,6 +150,16 @@ export class YXmlFragment extends AbstractType {
return new YXmlFragment()
}
+ /**
+ * @return {YXmlFragment}
+ */
+ clone () {
+ const el = new YXmlFragment()
+ // @ts-ignore
+ el.insert(0, el.toArray().map(item => item instanceof AbstractType ? item.clone() : item))
+ return el
+ }
+
get length () {
return this._prelimContent === null ? this._length : this._prelimContent.length
}
@@ -316,6 +328,45 @@ export class YXmlFragment extends AbstractType {
return typeListToArray(this)
}
+ /**
+ * Appends content to this YArray.
+ *
+ * @param {Array} content Array of content to append.
+ */
+ push (content) {
+ this.insert(this.length, content)
+ }
+
+ /**
+ * Preppends content to this YArray.
+ *
+ * @param {Array} content Array of content to preppend.
+ */
+ unshift (content) {
+ this.insert(0, content)
+ }
+
+ /**
+ * Returns the i-th element from a YArray.
+ *
+ * @param {number} index The index of the element to return from the YArray
+ * @return {YXmlElement|YXmlText}
+ */
+ get (index) {
+ return typeListGet(this, index)
+ }
+
+ /**
+ * Transforms this YArray to a JavaScript Array.
+ *
+ * @param {number} [start]
+ * @param {number} [end]
+ * @return {Array}
+ */
+ slice (start = 0, end = this.length) {
+ return typeListSlice(this, start, end)
+ }
+
/**
* Transform the properties of this type to binary and write it to an
* BinaryEncoder.
diff --git a/src/types/YXmlHook.js b/src/types/YXmlHook.js
index 51a98366..e28f70a3 100644
--- a/src/types/YXmlHook.js
+++ b/src/types/YXmlHook.js
@@ -29,6 +29,17 @@ export class YXmlHook extends YMap {
return new YXmlHook(this.hookName)
}
+ /**
+ * @return {YXmlHook}
+ */
+ clone () {
+ const el = new YXmlHook(this.hookName)
+ this.forEach((value, key) => {
+ el.set(key, value)
+ })
+ return el
+ }
+
/**
* Creates a Dom Element that mirrors this YXmlElement.
*
diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js
index cada1585..697e31c9 100644
--- a/src/types/YXmlText.js
+++ b/src/types/YXmlText.js
@@ -14,6 +14,15 @@ export class YXmlText extends YText {
return new YXmlText()
}
+ /**
+ * @return {YXmlText}
+ */
+ clone () {
+ const text = new YXmlText()
+ text.applyDelta(this.toDelta())
+ return text
+ }
+
/**
* Creates a Dom Element that mirrors this YXmlText.
*
diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js
index 5b0827c2..45022ed6 100644
--- a/tests/y-array.tests.js
+++ b/tests/y-array.tests.js
@@ -17,6 +17,21 @@ export const testBasicUpdate = tc => {
t.compare(doc2.getArray('array').toArray(), ['hi'])
}
+/**
+ * @param {t.TestCase} tc
+ */
+export const testSlice = tc => {
+ const doc1 = new Y.Doc()
+ const arr = doc1.getArray('array')
+ arr.insert(0, [1, 2, 3])
+ t.compareArrays(arr.slice(0), [1, 2, 3])
+ t.compareArrays(arr.slice(1), [2, 3])
+ t.compareArrays(arr.slice(0, -1), [1, 2])
+ arr.insert(0, [0])
+ t.compareArrays(arr.slice(0), [0, 1, 2, 3])
+ t.compareArrays(arr.slice(0, 2), [0, 1])
+}
+
/**
* @param {t.TestCase} tc
*/