Compare commits

..

16 Commits

Author SHA1 Message Date
Kevin Jahns
e6badf98a2 v13.0.0-22 -- distribution files 2017-10-07 00:46:32 +02:00
Kevin Jahns
d9ee67d2f3 13.0.0-22 2017-10-07 00:42:06 +02:00
Kevin Jahns
791f6c12f0 add indexeddb example 2017-10-07 00:40:34 +02:00
Kevin Jahns
23d019c244 add writeObjectToYMap and writeArrayToYArray helper utilities 2017-10-07 00:39:26 +02:00
Kevin Jahns
c8ca80d15f 13.0.0-21 2017-10-02 15:52:11 +02:00
Kevin Jahns
be282c8338 fix lint 2017-10-02 15:50:56 +02:00
Kevin Jahns
829a094c6d check for responsiveness when maxBufferSize is set 2017-10-02 15:45:23 +02:00
Kevin Jahns
725273167e 13.0.0-20 2017-09-29 22:34:18 +02:00
Kevin Jahns
581264c5e3 implement relative position helper 2017-09-29 22:33:28 +02:00
Kevin Jahns
be537c9f8c 13.0.0-19 2017-09-26 21:53:01 +02:00
Kevin Jahns
4028eee39d implemented chunked broadcast of updates 2017-09-26 21:52:07 +02:00
Kevin Jahns
0e3e561ec7 13.0.0-18 2017-09-20 11:34:03 +02:00
Kevin Jahns
7df46cb731 Merge branch 'master' of github.com:y-js/yjs 2017-09-20 11:30:24 +02:00
Kevin Jahns
40fb16ef32 catch y-* related errors 2017-09-20 11:29:13 +02:00
Kevin Jahns
ada5d36cd5 add more y-xml tests 2017-09-19 03:16:48 +02:00
Kevin Jahns
f537a43e29 implement tests for dom filter 2017-09-18 22:14:45 +02:00
20 changed files with 567 additions and 126 deletions

View File

@@ -7,9 +7,9 @@ Y({
}, },
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
// url: 'http://127.0.0.1:1234', url: 'http://127.0.0.1:1234',
url: 'http://192.168.178.81:1234',
room: 'html-editor-example6' room: 'html-editor-example6'
// maxBufferLength: 100
}, },
share: { share: {
xml: 'XmlFragment()' // y.share.xml is of type Y.Xml with tagname "p" xml: 'XmlFragment()' // y.share.xml is of type Y.Xml with tagname "p"

View File

@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="codeMirrorContainer"></div>
<script src="../bower_components/codemirror/lib/codemirror.js"></script>
<script src="../bower_components/codemirror/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../bower_components/codemirror/lib/codemirror.css">
<style>
.CodeMirror {
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
}
</style>
<script type="module" src="./index.js"></script>
</body>
</html>

View File

@@ -0,0 +1,24 @@
/* global Y, CodeMirror */
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'codemirror-example'
},
sourceDir: '/bower_components',
share: {
codemirror: 'Text' // y.share.codemirror is of type Y.Text
}
}).then(function (y) {
window.yCodeMirror = y
var editor = CodeMirror(document.querySelector('#codeMirrorContainer'), {
mode: 'javascript',
lineNumbers: true
})
y.share.codemirror.bindCodeMirror(editor)
})

View File

@@ -4,8 +4,7 @@
<textarea style="width:80%;" rows=40 id="textfield" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea> <textarea style="width:80%;" rows=40 id="textfield" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src="../../../y-array/y-array.js"></script> <script src="../../../y-array/y-array.js"></script>
<script src="../../../y-text/dist/y-text.js"></script> <script src="../../../y-text/y-text.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script> <script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>

View File

@@ -1,8 +1,5 @@
/* global Y */ /* global Y */
// eslint-disable-next-line
let search = new URLSearchParams(location.search)
// initialize a shared object. This function call returns a promise! // initialize a shared object. This function call returns a promise!
Y({ Y({
db: { db: {
@@ -10,10 +7,9 @@ Y({
}, },
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
room: 'Textarea-example', room: 'Textarea-example2',
// url: '//localhost:1234', // url: '//localhost:1234',
url: 'https://yjs-v13.herokuapp.com/' url: 'https://yjs-v13.herokuapp.com/'
// options: { transports: ['websocket'], upgrade: false }
}, },
share: { share: {
textarea: 'Text' textarea: 'Text'
@@ -24,7 +20,4 @@ Y({
// bind the textarea to a shared text element // bind the textarea to a shared text element
y.share.textarea.bind(document.getElementById('textfield')) y.share.textarea.bind(document.getElementById('textfield'))
// thats it..
}).catch(() => {
console.log('Something went wrong while creating the instance..')
}) })

View File

@@ -3,7 +3,7 @@ import Y from '../src/y.js'
import yArray from '../../y-array/src/y-array.js' import yArray from '../../y-array/src/y-array.js'
import yIndexedDB from '../../y-indexeddb/src/y-indexeddb.js' import yIndexedDB from '../../y-indexeddb/src/y-indexeddb.js'
import yMap from '../../y-map/src/y-map.js' import yMap from '../../y-map/src/y-map.js'
import yText from '../../y-text/src/Text.js' import yText from '../../y-text/src/y-text.js'
import yXml from '../../y-xml/src/y-xml.js' import yXml from '../../y-xml/src/y-xml.js'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js' import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-17", "version": "13.0.0-22",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-17", "version": "13.0.0-22",
"description": "A framework for real-time p2p shared editing on any data", "description": "A framework for real-time p2p shared editing on any data",
"main": "./y.node.js", "main": "./y.node.js",
"browser": "./y.js", "browser": "./y.js",

View File

@@ -42,6 +42,11 @@ export default function extendConnector (Y/* :any */) {
if (opts.generateUserId !== false) { if (opts.generateUserId !== false) {
this.setUserId(Y.utils.generateUserId()) this.setUserId(Y.utils.generateUserId())
} }
if (opts.maxBufferLength == null) {
this.maxBufferLength = -1
} else {
this.maxBufferLength = opts.maxBufferLength
}
} }
reconnect () { reconnect () {
@@ -197,14 +202,19 @@ export default function extendConnector (Y/* :any */) {
encoder.writeVarString(self.opts.room) encoder.writeVarString(self.opts.room)
encoder.writeVarString('update') encoder.writeVarString('update')
let ops = self.broadcastOpBuffer let ops = self.broadcastOpBuffer
self.broadcastOpBuffer = []
let length = ops.length let length = ops.length
encoder.writeUint32(length) let encoderPosLen = encoder.pos
for (var i = 0; i < length; i++) { encoder.writeUint32(0)
for (var i = 0; i < length && (self.maxBufferLength < 0 || encoder.length < self.maxBufferLength); i++) {
let op = ops[i] let op = ops[i]
Y.Struct[op.struct].binaryEncode(encoder, op) Y.Struct[op.struct].binaryEncode(encoder, op)
} }
encoder.setUint32(encoderPosLen, i)
self.broadcastOpBuffer = ops.slice(i)
self.broadcast(encoder.createBuffer()) self.broadcast(encoder.createBuffer())
if (i !== length) {
self.whenRemoteResponsive().then(broadcastOperations)
}
} }
} }
if (this.broadcastOpBuffer.length === 0) { if (this.broadcastOpBuffer.length === 0) {
@@ -215,6 +225,20 @@ export default function extendConnector (Y/* :any */) {
} }
} }
/*
* Somehow check the responsiveness of the remote clients/server
* Default behavior:
* Wait 100ms before broadcasting the next batch of operations
*
* Only used when maxBufferLength is set
*
*/
whenRemoteResponsive () {
return new Promise(function (resolve) {
setTimeout(resolve, 100)
})
}
/* /*
You received a raw message, and you know that it is intended for Yjs. Then call this function. You received a raw message, and you know that it is intended for Yjs. Then call this function.
*/ */

View File

@@ -8,6 +8,10 @@ export class BinaryEncoder {
this.data = [] this.data = []
} }
get length () {
return this.data.length
}
get pos () { get pos () {
return this.data.length return this.data.length
} }

View File

@@ -49,6 +49,51 @@ export default function Utils (Y) {
} }
} }
Y.utils.getRelativePosition = function (type, offset) {
if (type == null) {
return null
} else {
if (type._content.length <= offset) {
return ['endof', type._model[0], type._model[1]]
} else {
return type._content[offset].id
}
}
}
Y.utils.fromRelativePosition = function (y, id) {
var offset = 0
var op
if (id[0] === 'endof') {
id = y.db.os.find(id.slice(1)).end
op = y.db.os.findNodeWithUpperBound(id).val
if (!op.deleted) {
offset = op.content != null ? op.content.length : 1
}
} else {
op = y.db.os.findNodeWithUpperBound(id).val
if (!op.deleted) {
offset = id[1] - op.id[1]
}
}
var type = y.db.getType(op.parent)
if (type == null || y.db.os.find(op.parent).deleted) {
return null
}
while (op.left != null) {
op = y.db.os.findNodeWithUpperBound(op.left).val
if (!op.deleted) {
offset += op.content != null ? op.content.length : 1
}
}
return {
type: type,
offset: offset
}
}
class NamedEventHandler { class NamedEventHandler {
constructor () { constructor () {
this._eventListener = {} this._eventListener = {}
@@ -67,7 +112,11 @@ export default function Utils (Y) {
this._eventListener[name] = listener.filter(e => e !== f) this._eventListener[name] = listener.filter(e => e !== f)
} }
emit (name, value) { emit (name, value) {
(this._eventListener[name] || []).forEach(l => l(value)) let listener = this._eventListener[name] || []
if (name === 'error' && listener.length === 0) {
console.error(value)
}
listener.forEach(l => l(value))
} }
destroy () { destroy () {
this._eventListener = null this._eventListener = null
@@ -853,4 +902,34 @@ export default function Utils (Y) {
} }
return args return args
} }
Y.utils.writeObjectToYMap = function writeObjectToYMap (object, type) {
for (var key in object) {
var val = object[key]
if (Array.isArray(val)) {
type.set(key, Y.Array)
Y.utils.writeArrayToYArray(val, type.get(key))
} else if (typeof val === 'object') {
type.set(key, Y.Map)
Y.utils.writeObjectToYMap(val, type.get(key))
} else {
type.set(key, val)
}
}
}
Y.utils.writeArrayToYArray = function writeArrayToYArray (array, type) {
for (var i = array.length - 1; i >= 0; i--) {
var val = array[i]
if (Array.isArray(val)) {
type.insert(0, [Y.Array])
Y.utils.writeArrayToYArray(val, type.get(0))
} else if (typeof val === 'object') {
type.insert(0, [Y.Map])
Y.utils.writeObjectToYMap(val, type.get(0))
} else {
type.insert(0, [val])
}
}
}
} }

View File

@@ -1,4 +1,3 @@
import extendRBTree from './RedBlackTree' import extendRBTree from './RedBlackTree'
export default function extend (Y) { export default function extend (Y) {
@@ -48,9 +47,13 @@ export default function extend (Y) {
} }
transact (makeGen) { transact (makeGen) {
const t = new Transaction(this) const t = new Transaction(this)
while (makeGen != null) { try {
makeGen.call(t) while (makeGen != null) {
makeGen = this.getNextRequest() makeGen.call(t)
makeGen = this.getNextRequest()
}
} catch (e) {
this.y.emit('error', e)
} }
} }
destroy () { destroy () {

View File

@@ -224,16 +224,65 @@ test('move element to a different position', async function xml13 (t) {
await compareUsers(t, users) await compareUsers(t, users)
}) })
test('filter node', async function xml14 (t) {
var { users, xml0, xml1 } = await initArrays(t, { users: 3 })
let dom0 = xml0.getDom()
let dom1 = xml1.getDom()
let domFilter = (node, attrs) => {
if (node.nodeName === 'H1') {
return null
} else {
return attrs
}
}
xml0.setDomFilter(domFilter)
xml1.setDomFilter(domFilter)
dom0.append(document.createElement('div'))
dom0.append(document.createElement('h1'))
await flushAll(t, users)
t.assert(dom1.childNodes.length === 1, 'Only one node was not transmitted')
t.assert(dom1.childNodes[0].nodeName === 'DIV', 'div node was transmitted')
await compareUsers(t, users)
})
test('filter attribute', async function xml15 (t) {
var { users, xml0, xml1 } = await initArrays(t, { users: 3 })
let dom0 = xml0.getDom()
let dom1 = xml1.getDom()
let domFilter = (node, attrs) => {
return attrs.filter(name => name !== 'hidden')
}
xml0.setDomFilter(domFilter)
xml1.setDomFilter(domFilter)
dom0.setAttribute('hidden', 'true')
dom0.setAttribute('style', 'height: 30px')
dom0.setAttribute('data-me', '77')
await flushAll(t, users)
t.assert(dom0.getAttribute('hidden') === 'true', 'User 0 still has the attribute')
t.assert(dom1.getAttribute('hidden') == null, 'User 1 did not receive update')
t.assert(dom1.getAttribute('style') === 'height: 30px', 'User 1 received style update')
t.assert(dom1.getAttribute('data-me') === '77', 'User 1 received data update')
await compareUsers(t, users)
})
// TODO: move elements // TODO: move elements
var xmlTransactions = [ var xmlTransactions = [
function attributeChange (t, user, chance) { function attributeChange (t, user, chance) {
user.share.xml.getDom().setAttribute(chance.word(), chance.word()) user.share.xml.getDom().setAttribute(chance.word(), chance.word())
}, },
function attributeChangeHidden (t, user, chance) {
user.share.xml.getDom().setAttribute('hidden', chance.word())
},
function insertText (t, user, chance) { function insertText (t, user, chance) {
let dom = user.share.xml.getDom() let dom = user.share.xml.getDom()
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
dom.insertBefore(document.createTextNode(chance.word()), succ) dom.insertBefore(document.createTextNode(chance.word()), succ)
}, },
function insertHiddenDom (t, user, chance) {
let dom = user.share.xml.getDom()
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
dom.insertBefore(document.createElement('hidden'), succ)
},
function insertDom (t, user, chance) { function insertDom (t, user, chance) {
let dom = user.share.xml.getDom() let dom = user.share.xml.getDom()
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
@@ -274,30 +323,30 @@ var xmlTransactions = [
} }
] ]
test('y-xml: Random tests (10)', async function randomXml10 (t) { test('y-xml: Random tests (10)', async function xmlRandom10 (t) {
await applyRandomTests(t, xmlTransactions, 10) await applyRandomTests(t, xmlTransactions, 10)
}) })
test('y-xml: Random tests (42)', async function randomXml42 (t) { test('y-xml: Random tests (42)', async function xmlRandom42 (t) {
await applyRandomTests(t, xmlTransactions, 42) await applyRandomTests(t, xmlTransactions, 42)
}) })
test('y-xml: Random tests (43)', async function randomXml43 (t) { test('y-xml: Random tests (43)', async function xmlRandom43 (t) {
await applyRandomTests(t, xmlTransactions, 43) await applyRandomTests(t, xmlTransactions, 43)
}) })
test('y-xml: Random tests (44)', async function randomXml44 (t) { test('y-xml: Random tests (44)', async function xmlRandom44 (t) {
await applyRandomTests(t, xmlTransactions, 44) await applyRandomTests(t, xmlTransactions, 44)
}) })
test('y-xml: Random tests (45)', async function randomXml45 (t) { test('y-xml: Random tests (45)', async function xmlRandom45 (t) {
await applyRandomTests(t, xmlTransactions, 45) await applyRandomTests(t, xmlTransactions, 45)
}) })
test('y-xml: Random tests (46)', async function randomXml46 (t) { test('y-xml: Random tests (46)', async function xmlRandom46 (t) {
await applyRandomTests(t, xmlTransactions, 46) await applyRandomTests(t, xmlTransactions, 46)
}) })
test('y-xml: Random tests (47)', async function randomXml47 (t) { test('y-xml: Random tests (47)', async function xmlRandom47 (t) {
await applyRandomTests(t, xmlTransactions, 47) await applyRandomTests(t, xmlTransactions, 47)
}) })

View File

@@ -2,7 +2,7 @@
import _Y from '../../yjs/src/y.js' import _Y from '../../yjs/src/y.js'
import yArray from '../../y-array/src/y-array.js' import yArray from '../../y-array/src/y-array.js'
import yText from '../../y-text/src/Text.js' import yText from '../../y-text/src/y-text.js'
import yMap from '../../y-map/src/y-map.js' import yMap from '../../y-map/src/y-map.js'
import yXml from '../../y-xml/src/y-xml.js' import yXml from '../../y-xml/src/y-xml.js'
import yTest from './test-connector.js' import yTest from './test-connector.js'
@@ -48,11 +48,17 @@ export async function garbageCollectUsers (t, users) {
await Promise.all(users.map(u => u.db.emptyGarbageCollector())) await Promise.all(users.map(u => u.db.emptyGarbageCollector()))
} }
export function attrsToObject (attrs) { export function attrsObject (dom) {
let keys = []
let yxml = dom.__yxml
for (let i = 0; i < dom.attributes.length; i++) {
keys.push(dom.attributes[i].name)
}
keys = yxml._domFilter(dom, keys)
let obj = {} let obj = {}
for (var i = 0; i < attrs.length; i++) { for (let i = 0; i < keys.length; i++) {
let attr = attrs[i] let key = keys[i]
obj[attr.name] = attr.value obj[key] = dom.getAttribute(key)
} }
return obj return obj
} }
@@ -61,8 +67,10 @@ export function domToJson (dom) {
if (dom.nodeType === document.TEXT_NODE) { if (dom.nodeType === document.TEXT_NODE) {
return dom.textContent return dom.textContent
} else if (dom.nodeType === document.ELEMENT_NODE) { } else if (dom.nodeType === document.ELEMENT_NODE) {
let attributes = attrsToObject(dom.attributes) let attributes = attrsObject(dom, dom.__yxml)
let children = Array.from(dom.childNodes.values()).map(domToJson) let children = Array.from(dom.childNodes.values())
.filter(d => d.__yxml !== false)
.map(domToJson)
return { return {
name: dom.nodeName, name: dom.nodeName,
children: children, children: children,
@@ -198,6 +206,13 @@ export async function initArrays (t, opts) {
for (let name in share) { for (let name in share) {
result[name + i] = y.share[name] result[name + i] = y.share[name]
} }
y.share.xml.setDomFilter(function (d, attrs) {
if (d.nodeName === 'HIDDEN') {
return null
} else {
return attrs.filter(a => a !== 'hidden')
}
})
} }
result.array0.delete(0, result.array0.length) result.array0.delete(0, result.array0.length)
if (result.users[0].connector.testRoom != null) { if (result.users[0].connector.testRoom != null) {

8
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

127
y.node.js
View File

@@ -1,7 +1,7 @@
/** /**
* yjs - A framework for real-time p2p shared editing on any data * yjs - A framework for real-time p2p shared editing on any data
* @version v13.0.0-17 * @version v13.0.0-22
* @license MIT * @license MIT
*/ */
@@ -292,6 +292,10 @@ class BinaryEncoder {
this.data = []; this.data = [];
} }
get length () {
return this.data.length
}
get pos () { get pos () {
return this.data.length return this.data.length
} }
@@ -666,6 +670,11 @@ function extendConnector (Y/* :any */) {
if (opts.generateUserId !== false) { if (opts.generateUserId !== false) {
this.setUserId(Y.utils.generateUserId()); this.setUserId(Y.utils.generateUserId());
} }
if (opts.maxBufferLength == null) {
this.maxBufferLength = -1;
} else {
this.maxBufferLength = opts.maxBufferLength;
}
} }
reconnect () { reconnect () {
@@ -821,14 +830,19 @@ function extendConnector (Y/* :any */) {
encoder.writeVarString(self.opts.room); encoder.writeVarString(self.opts.room);
encoder.writeVarString('update'); encoder.writeVarString('update');
let ops = self.broadcastOpBuffer; let ops = self.broadcastOpBuffer;
self.broadcastOpBuffer = [];
let length = ops.length; let length = ops.length;
encoder.writeUint32(length); let encoderPosLen = encoder.pos;
for (var i = 0; i < length; i++) { encoder.writeUint32(0);
for (var i = 0; i < length && (self.maxBufferLength < 0 || encoder.length < self.maxBufferLength); i++) {
let op = ops[i]; let op = ops[i];
Y.Struct[op.struct].binaryEncode(encoder, op); Y.Struct[op.struct].binaryEncode(encoder, op);
} }
encoder.setUint32(encoderPosLen, i);
self.broadcastOpBuffer = ops.slice(i);
self.broadcast(encoder.createBuffer()); self.broadcast(encoder.createBuffer());
if (i !== length) {
self.whenRemoteResponsive().then(broadcastOperations);
}
} }
} }
if (this.broadcastOpBuffer.length === 0) { if (this.broadcastOpBuffer.length === 0) {
@@ -839,6 +853,20 @@ function extendConnector (Y/* :any */) {
} }
} }
/*
* Somehow check the responsiveness of the remote clients/server
* Default behavior:
* Wait 100ms before broadcasting the next batch of operations
*
* Only used when maxBufferLength is set
*
*/
whenRemoteResponsive () {
return new Promise(function (resolve) {
setTimeout(resolve, 100);
})
}
/* /*
You received a raw message, and you know that it is intended for Yjs. Then call this function. You received a raw message, and you know that it is intended for Yjs. Then call this function.
*/ */
@@ -3434,6 +3462,51 @@ function Utils (Y) {
} }
}; };
Y.utils.getRelativePosition = function (type, offset) {
if (type == null) {
return null
} else {
if (type._content.length <= offset) {
return ['endof', type._model[0], type._model[1]]
} else {
return type._content[offset].id
}
}
};
Y.utils.fromRelativePosition = function (y, id) {
var offset = 0;
var op;
if (id[0] === 'endof') {
id = y.db.os.find(id.slice(1)).end;
op = y.db.os.findNodeWithUpperBound(id).val;
if (!op.deleted) {
offset = op.content != null ? op.content.length : 1;
}
} else {
op = y.db.os.findNodeWithUpperBound(id).val;
if (!op.deleted) {
offset = id[1] - op.id[1];
}
}
var type = y.db.getType(op.parent);
if (type == null || y.db.os.find(op.parent).deleted) {
return null
}
while (op.left != null) {
op = y.db.os.findNodeWithUpperBound(op.left).val;
if (!op.deleted) {
offset += op.content != null ? op.content.length : 1;
}
}
return {
type: type,
offset: offset
}
};
class NamedEventHandler { class NamedEventHandler {
constructor () { constructor () {
this._eventListener = {}; this._eventListener = {};
@@ -3452,7 +3525,11 @@ function Utils (Y) {
this._eventListener[name] = listener.filter(e => e !== f); this._eventListener[name] = listener.filter(e => e !== f);
} }
emit (name, value) { emit (name, value) {
(this._eventListener[name] || []).forEach(l => l(value)); let listener = this._eventListener[name] || [];
if (name === 'error' && listener.length === 0) {
console.error(value);
}
listener.forEach(l => l(value));
} }
destroy () { destroy () {
this._eventListener = null; this._eventListener = null;
@@ -4238,6 +4315,36 @@ function Utils (Y) {
} }
return args return args
}; };
Y.utils.writeObjectToYMap = function writeObjectToYMap (object, type) {
for (var key in object) {
var val = object[key];
if (Array.isArray(val)) {
type.set(key, Y.Array);
Y.utils.writeArrayToYArray(val, type.get(key));
} else if (typeof val === 'object') {
type.set(key, Y.Map);
Y.utils.writeObjectToYMap(val, type.get(key));
} else {
type.set(key, val);
}
}
};
Y.utils.writeArrayToYArray = function writeArrayToYArray (array, type) {
for (var i = array.length - 1; i >= 0; i--) {
var val = array[i];
if (Array.isArray(val)) {
type.insert(0, [Y.Array]);
Y.utils.writeArrayToYArray(val, type.get(0));
} else if (typeof val === 'object') {
type.insert(0, [Y.Map]);
Y.utils.writeObjectToYMap(val, type.get(0));
} else {
type.insert(0, [val]);
}
}
};
} }
function extendRBTree (Y) { function extendRBTree (Y) {
@@ -4793,9 +4900,13 @@ function extend (Y) {
} }
transact (makeGen) { transact (makeGen) {
const t = new Transaction(this); const t = new Transaction(this);
while (makeGen != null) { try {
makeGen.call(t); while (makeGen != null) {
makeGen = this.getNextRequest(); makeGen.call(t);
makeGen = this.getNextRequest();
}
} catch (e) {
this.y.emit('error', e);
} }
} }
destroy () { destroy () {

File diff suppressed because one or more lines are too long

267
y.test.js
View File

@@ -5615,7 +5615,7 @@ function createCommonjsModule$1(fn, module) {
} }
var chance_1 = createCommonjsModule$1(function (module, exports) { var chance_1 = createCommonjsModule$1(function (module, exports) {
// Chance.js 1.0.10 // Chance.js 1.0.11
// http://chancejs.com // http://chancejs.com
// (c) 2013 Victor Quinn // (c) 2013 Victor Quinn
// Chance may be freely distributed or modified under the MIT license. // Chance may be freely distributed or modified under the MIT license.
@@ -5680,7 +5680,7 @@ var chance_1 = createCommonjsModule$1(function (module, exports) {
return this; return this;
} }
Chance.prototype.VERSION = "1.0.10"; Chance.prototype.VERSION = "1.0.11";
// Random helper functions // Random helper functions
function initOptions(options, defaults) { function initOptions(options, defaults) {
@@ -5899,6 +5899,16 @@ var chance_1 = createCommonjsModule$1(function (module, exports) {
return integer.toString(16); return integer.toString(16);
}; };
Chance.prototype.letter = function(options) {
options = initOptions(options, {casing: 'lower'});
var pool = "abcdefghijklmnopqrstuvwxyz";
var letter = this.character({pool: pool});
if (options.casing === 'upper') {
letter = letter.toUpperCase();
}
return letter;
};
/** /**
* Return a random string * Return a random string
* *
@@ -14271,7 +14281,7 @@ function extendDatabase (Y /* :any */) {
* Always: * Always:
* * Call type * * Call type
*/ */
operationAdded (transaction, op) { operationAdded (transaction, op) {
if (op.struct === 'Delete') { if (op.struct === 'Delete') {
var type = this.initializedTypes[JSON.stringify(op.targetParent)]; var type = this.initializedTypes[JSON.stringify(op.targetParent)];
if (type != null) { if (type != null) {
@@ -14330,6 +14340,7 @@ function extendDatabase (Y /* :any */) {
} }
} }
} }
whenTransactionsFinished () { whenTransactionsFinished () {
if (this.transactionInProgress) { if (this.transactionInProgress) {
if (this.transactionsFinished == null) { if (this.transactionsFinished == null) {
@@ -17592,7 +17603,7 @@ var y = d * 365.25;
* @api public * @api public
*/ */
var index$1$1 = function(val, options) { var ms = function(val, options) {
options = options || {}; options = options || {};
var type = typeof val; var type = typeof val;
if (type === 'string' && val.length > 0) { if (type === 'string' && val.length > 0) {
@@ -17734,7 +17745,7 @@ exports.coerce = coerce;
exports.disable = disable; exports.disable = disable;
exports.enable = enable; exports.enable = enable;
exports.enabled = enabled; exports.enabled = enabled;
exports.humanize = index$1$1; exports.humanize = ms;
/** /**
* The currently active debug mode names, and names to skip. * The currently active debug mode names, and names to skip.
@@ -17793,8 +17804,8 @@ function createDebug(namespace) {
// set `diff` timestamp // set `diff` timestamp
var curr = +new Date(); var curr = +new Date();
var ms = curr - (prevTime || curr); var ms$$1 = curr - (prevTime || curr);
self.diff = ms; self.diff = ms$$1;
self.prev = prevTime; self.prev = prevTime;
self.curr = curr; self.curr = curr;
prevTime = curr; prevTime = curr;
@@ -19805,9 +19816,11 @@ function merge_tuples (diffs, start, length) {
return diffs; return diffs;
} }
/* global Y, Element */
var monacoIdentifierTemplate = { major: 0, minor: 0 }; var monacoIdentifierTemplate = { major: 0, minor: 0 };
function extend$2 (Y) { function extendYText (Y) {
Y.requestModules(['Array']).then(function () { Y.requestModules(['Array']).then(function () {
class YText extends Y.Array.typeDefinition['class'] { class YText extends Y.Array.typeDefinition['class'] {
constructor (os, _model, _content, args) { constructor (os, _model, _content, args) {
@@ -20385,9 +20398,8 @@ function extend$2 (Y) {
}); });
} }
var Text = extend$2;
if (typeof Y !== 'undefined') { if (typeof Y !== 'undefined') {
extend$2(Y); extendYText(Y);
} }
/* global Y */ /* global Y */
@@ -21288,7 +21300,7 @@ diff$3.INSERT = DIFF_INSERT$1;
diff$3.DELETE = DIFF_DELETE$1; diff$3.DELETE = DIFF_DELETE$1;
diff$3.EQUAL = DIFF_EQUAL$1; diff$3.EQUAL = DIFF_EQUAL$1;
var diff_1$2 = diff$3; var diff_1$1 = diff$3;
/* /*
* Modify a diff such that the cursor position points to the start of a change: * Modify a diff such that the cursor position points to the start of a change:
@@ -21495,6 +21507,8 @@ function extendYXmlText (Y, _document, _MutationObserver) {
}); });
} }
setDomFilter () {}
_setDom (dom) { _setDom (dom) {
if (this.dom != null) { if (this.dom != null) {
this._unbindFromDom(); this._unbindFromDom();
@@ -21509,7 +21523,7 @@ function extendYXmlText (Y, _document, _MutationObserver) {
dom.__yxml = this; dom.__yxml = this;
this._domObserverListener = () => { this._domObserverListener = () => {
this._mutualExcluse(() => { this._mutualExcluse(() => {
var diffs = diff_1$2(this.toString(), this.dom.nodeValue); var diffs = diff_1$1(this.toString(), this.dom.nodeValue);
var pos = 0; var pos = 0;
for (var i = 0; i < diffs.length; i++) { for (var i = 0; i < diffs.length; i++) {
var d = diffs[i]; var d = diffs[i];
@@ -21612,6 +21626,10 @@ function extendYXmlText (Y, _document, _MutationObserver) {
}); });
} }
function defaultDomFilter (node, attributes) {
return attributes
}
/* /*
* 1. Check if any of the nodes was deleted * 1. Check if any of the nodes was deleted
* 2. Iterate over the children. * 2. Iterate over the children.
@@ -21636,11 +21654,14 @@ function applyChangesFromDom (yxml) {
// 2. iterate // 2. iterate
let childNodes = yxml.dom.childNodes; let childNodes = yxml.dom.childNodes;
let len = childNodes.length; let len = childNodes.length;
for (let i = 0; i < len; i++) { for (let domCnt = 0, yCnt = 0; domCnt < len; domCnt++) {
let child = childNodes[i]; let child = childNodes[domCnt];
if (child.__yxml != null) { if (child.__yxml != null) {
if (i < yxml.length) { if (child.__yxml === false) {
let expectedNode = yxml.get(i); continue
}
if (yCnt < yxml.length) {
let expectedNode = yxml.get(yCnt);
if (expectedNode !== child.__yxml) { if (expectedNode !== child.__yxml) {
// 2.3 Not expected node // 2.3 Not expected node
let index = yxml._content.findIndex(c => c.type[0] === child.__yxml._model[0] && c.type[1] === child.__yxml._model[1]); let index = yxml._content.findIndex(c => c.type[0] === child.__yxml._model[0] && c.type[1] === child.__yxml._model[1]);
@@ -21650,16 +21671,18 @@ function applyChangesFromDom (yxml) {
} else { } else {
yxml.delete(index, 1); yxml.delete(index, 1);
} }
yxml.insertDomElements(i, [child]); yCnt += yxml.insertDomElements(yCnt, [child]);
} else {
yCnt++;
} }
// if this is the expected node id, just continue // if this is the expected node id, just continue
} else { } else {
// 2.2 fill _conten with child nodes // 2.2 fill _conten with child nodes
yxml.insertDomElements(i, [child]); yCnt += yxml.insertDomElements(yCnt, [child]);
} }
} else { } else {
// 2.1 A new node was found // 2.1 A new node was found
yxml.insertDomElements(i, [child]); yCnt += yxml.insertDomElements(yCnt, [child]);
} }
} }
} }
@@ -21672,6 +21695,7 @@ function extendYXmlFragment (Y, _document, _MutationObserver) {
this.dom = null; this.dom = null;
this._domObserver = null; this._domObserver = null;
this._domObserverListener = null; this._domObserverListener = null;
this._domFilter = defaultDomFilter;
var token = true; var token = true;
this._mutualExcluse = f => { this._mutualExcluse = f => {
// take and process current records // take and process current records
@@ -21697,9 +21721,10 @@ function extendYXmlFragment (Y, _document, _MutationObserver) {
if (this.dom != null) { if (this.dom != null) {
this._mutualExcluse(() => { this._mutualExcluse(() => {
if (event.type === 'insert') { if (event.type === 'insert') {
let nodes = event.values.map(v => v.getDom()); for (let i = event.values.length - 1; i >= 0; i--) {
for (let i = nodes.length - 1; i >= 0; i--) { let val = event.values[i];
let dom = nodes[i]; val.setDomFilter(this._domFilter);
let dom = val.getDom();
let nextDom = null; let nextDom = null;
if (this._content.length > event.index + i + 1) { if (this._content.length > event.index + i + 1) {
nextDom = this.get(event.index + i + 1).getDom(); nextDom = this.get(event.index + i + 1).getDom();
@@ -21716,6 +21741,13 @@ function extendYXmlFragment (Y, _document, _MutationObserver) {
}); });
} }
setDomFilter (f) {
this._domFilter = f;
this.toArray().forEach(function (c) {
c.setDomFilter(f);
});
}
insertDomElements () { insertDomElements () {
return Y.XmlElement.typeDefinition.class.prototype.insertDomElements.apply(this, arguments) return Y.XmlElement.typeDefinition.class.prototype.insertDomElements.apply(this, arguments)
} }
@@ -21814,16 +21846,6 @@ function extendYXmlFragment (Y, _document, _MutationObserver) {
// import diff from 'fast-diff' // import diff from 'fast-diff'
function extendXmlElement (Y, _document, _MutationObserver) { function extendXmlElement (Y, _document, _MutationObserver) {
function domToType (dom) {
if (dom.nodeType === _document.TEXT_NODE) {
return Y.XmlText(dom)
} else if (dom.nodeType === _document.ELEMENT_NODE) {
return Y.XmlElement(dom)
} else {
throw new Error('Unsupported node!')
}
}
function yarrayEventHandler (op) { function yarrayEventHandler (op) {
if (op.struct === 'Insert') { if (op.struct === 'Insert') {
// when using indexeddb db adapter, the op could already exist (see y-js/y-indexeddb#2) // when using indexeddb db adapter, the op could already exist (see y-js/y-indexeddb#2)
@@ -22001,7 +22023,7 @@ function extendXmlElement (Y, _document, _MutationObserver) {
} }
class YXmlElement extends Y.utils.CustomType { class YXmlElement extends Y.utils.CustomType {
constructor (os, model, arrayContent, contents, opContents, dom) { constructor (os, model, arrayContent, contents, opContents, dom, domFilter) {
super(); super();
this._os = os; this._os = os;
this.os = os; this.os = os;
@@ -22028,6 +22050,7 @@ function extendXmlElement (Y, _document, _MutationObserver) {
this._eventListenerHandler = eventHandler; this._eventListenerHandler = eventHandler;
this._domObserver = null; this._domObserver = null;
this.dom = null; this.dom = null;
this._domFilter = domFilter;
if (dom != null) { if (dom != null) {
this._setDom(dom); this._setDom(dom);
} }
@@ -22066,6 +22089,7 @@ function extendXmlElement (Y, _document, _MutationObserver) {
let nodes = event.nodes; let nodes = event.nodes;
for (let i = nodes.length - 1; i >= 0; i--) { for (let i = nodes.length - 1; i >= 0; i--) {
let node = nodes[i]; let node = nodes[i];
node.setDomFilter(this._domFilter);
let dom = node.getDom(); let dom = node.getDom();
let nextDom = null; let nextDom = null;
if (this._content.length > event.index + i + 1) { if (this._content.length > event.index + i + 1) {
@@ -22083,6 +22107,14 @@ function extendXmlElement (Y, _document, _MutationObserver) {
}); });
} }
setDomFilter (f) {
this._domFilter = f;
let len = this._content.length;
for (let i = 0; i < len; i++) {
this.get(i).setDomFilter(f);
}
}
get length () { get length () {
return this._content.length return this._content.length
} }
@@ -22132,13 +22164,27 @@ function extendXmlElement (Y, _document, _MutationObserver) {
} }
insertDomElements (pos, doms) { insertDomElements (pos, doms) {
let types = [];
doms.forEach(d => { doms.forEach(d => {
if (d.__yxml != null) { if (d.__yxml != null && d.__yxml !== false) {
d.__yxml._unbindFromDom(); d.__yxml._unbindFromDom();
} }
if (this._domFilter(d, []) !== null) {
let type;
if (d.nodeType === _document.TEXT_NODE) {
type = Y.XmlText(d, this._domFilter);
} else if (d.nodeType === _document.ELEMENT_NODE) {
type = Y.XmlElement(d, this._domFilter);
} else {
throw new Error('Unsupported node!')
}
types.push(type);
} else {
d.__yxml = false;
}
}); });
let types = doms.map(domToType);
this.insert(pos, types); this.insert(pos, types);
return types.length
} }
insert (pos, types) { insert (pos, types) {
@@ -22195,13 +22241,16 @@ function extendXmlElement (Y, _document, _MutationObserver) {
let diffChildren = false; let diffChildren = false;
mutations.forEach(mutation => { mutations.forEach(mutation => {
if (mutation.type === 'attributes') { if (mutation.type === 'attributes') {
var name = mutation.attributeName; let name = mutation.attributeName;
var val = mutation.target.getAttribute(mutation.attributeName); // check if filter accepts attribute
if (this.getAttribute(name) !== val) { if (this._domFilter(this.dom, [name]).length > 0) {
if (val == null) { var val = mutation.target.getAttribute(name);
this.removeAttribute(name); if (this.getAttribute(name) !== val) {
} else { if (val == null) {
this.setAttribute(name, val); this.removeAttribute(name);
} else {
this.setAttribute(name, val);
}
} }
} }
} else if (mutation.type === 'childList') { } else if (mutation.type === 'childList') {
@@ -22227,18 +22276,17 @@ function extendXmlElement (Y, _document, _MutationObserver) {
dom.__yxml = this; dom.__yxml = this;
// tag is already set in constructor // tag is already set in constructor
// set attributes // set attributes
for (var i = 0; i < dom.attributes.length; i++) { let attrNames = [];
var attr = dom.attributes[i]; for (let i = 0; i < dom.attributes.length; i++) {
this.setAttribute(attr.name, attr.value); attrNames.push(dom.attributes[i].name);
} }
this.insert(0, Array.prototype.map.call(dom.childNodes, dom => { attrNames = this._domFilter(dom, attrNames);
if (dom.__yxml != null) { for (let i = 0; i < attrNames.length; i++) {
// it is ok to reset here. It was probably moved from another node, and will be removed by that node let attrName = attrNames[i];
dom.__yxml._domObserver.disconnect(); let attrValue = dom.getAttribute(attrName);
dom.__yxml = null; this.setAttribute(attrName, attrValue);
} }
return domToType(dom) this.insertDomElements(0, Array.prototype.slice.call(dom.childNodes));
}));
if (_MutationObserver != null) { if (_MutationObserver != null) {
this.dom = this._bindToDom(dom); this.dom = this._bindToDom(dom);
} }
@@ -22329,19 +22377,27 @@ function extendXmlElement (Y, _document, _MutationObserver) {
name: 'XmlElement', name: 'XmlElement',
class: YXmlElement, class: YXmlElement,
struct: 'Xml', struct: 'Xml',
parseArguments: function (arg) { parseArguments: function (arg, arg2) {
let domFilter;
if (typeof arg2 === 'function') {
domFilter = arg2;
} else {
domFilter = defaultDomFilter;
}
if (typeof arg === 'string') { if (typeof arg === 'string') {
return [this, { return [this, {
nodeName: arg.toUpperCase(), nodeName: arg.toUpperCase(),
dom: null dom: null,
domFilter
}] }]
} else if (arg.nodeType === _document.ELEMENT_NODE) { } else if (arg.nodeType === _document.ELEMENT_NODE) {
return [this, { return [this, {
nodeName: arg.nodeName, nodeName: arg.nodeName,
dom: arg dom: arg,
domFilter
}] }]
} else { } else {
throw new Error('Y.Xml requires an argument which is a string!') throw new Error('Y.XmlElement requires an argument which is a string!')
} }
}, },
initType: function YXmlElementInitializer (os, model, init) { initType: function YXmlElementInitializer (os, model, init) {
@@ -22382,10 +22438,10 @@ function extendXmlElement (Y, _document, _MutationObserver) {
contents[name] = op.content[0]; contents[name] = op.content[0];
} }
} }
return new YXmlElement(os, model, _content, contents, opContents, init != null ? init.dom : null) return new YXmlElement(os, model, _content, contents, opContents, init != null ? init.dom : null, init != null ? init.domFilter : defaultDomFilter)
}, },
createType: function YXmlElementCreator (os, model, args) { createType: function YXmlElementCreator (os, model, args) {
return new YXmlElement(os, model, [], {}, {}, args.dom) return new YXmlElement(os, model, [], {}, {}, args.dom, args.domFilter)
} }
})); }));
} }
@@ -22399,7 +22455,6 @@ function extendXml (Y, _document, _MutationObserver) {
if (typeof MutationObserver !== 'undefined') { if (typeof MutationObserver !== 'undefined') {
_MutationObserver = MutationObserver; _MutationObserver = MutationObserver;
} else { } else {
console.warn('MutationObserver is not available. y-xml won\'t listen to changes on the DOM');
_MutationObserver = null; _MutationObserver = null;
} }
extendXmlElement(Y, _document, _MutationObserver); extendXmlElement(Y, _document, _MutationObserver);
@@ -22577,7 +22632,7 @@ if (typeof Y !== 'undefined') {
let Y$2 = Y$1; let Y$2 = Y$1;
Y$2.extend(extend$1, Text, extendYMap, extendTestConnector, extendXml); Y$2.extend(extend$1, extendYText, extendYMap, extendTestConnector, extendXml);
var database = { name: 'memory' }; var database = { name: 'memory' };
var connector = { name: 'test', url: 'http://localhost:1234' }; var connector = { name: 'test', url: 'http://localhost:1234' };
@@ -22614,11 +22669,17 @@ async function garbageCollectUsers (t, users) {
await Promise.all(users.map(u => u.db.emptyGarbageCollector())); await Promise.all(users.map(u => u.db.emptyGarbageCollector()));
} }
function attrsToObject (attrs) { function attrsObject (dom) {
let keys = [];
let yxml = dom.__yxml;
for (let i = 0; i < dom.attributes.length; i++) {
keys.push(dom.attributes[i].name);
}
keys = yxml._domFilter(dom, keys);
let obj = {}; let obj = {};
for (var i = 0; i < attrs.length; i++) { for (let i = 0; i < keys.length; i++) {
let attr = attrs[i]; let key = keys[i];
obj[attr.name] = attr.value; obj[key] = dom.getAttribute(key);
} }
return obj return obj
} }
@@ -22627,8 +22688,10 @@ function domToJson (dom) {
if (dom.nodeType === document.TEXT_NODE) { if (dom.nodeType === document.TEXT_NODE) {
return dom.textContent return dom.textContent
} else if (dom.nodeType === document.ELEMENT_NODE) { } else if (dom.nodeType === document.ELEMENT_NODE) {
let attributes = attrsToObject(dom.attributes); let attributes = attrsObject(dom, dom.__yxml);
let children = Array.from(dom.childNodes.values()).map(domToJson); let children = Array.from(dom.childNodes.values())
.filter(d => d.__yxml !== false)
.map(domToJson);
return { return {
name: dom.nodeName, name: dom.nodeName,
children: children, children: children,
@@ -22764,6 +22827,13 @@ async function initArrays (t, opts) {
for (let name in share) { for (let name in share) {
result[name + i] = y.share[name]; result[name + i] = y.share[name];
} }
y.share.xml.setDomFilter(function (d, attrs) {
if (d.nodeName === 'HIDDEN') {
return null
} else {
return attrs.filter(a => a !== 'hidden')
}
});
} }
result.array0.delete(0, result.array0.length); result.array0.delete(0, result.array0.length);
if (result.users[0].connector.testRoom != null) { if (result.users[0].connector.testRoom != null) {
@@ -23838,16 +23908,65 @@ test('move element to a different position', async function xml13 (t) {
await compareUsers(t, users); await compareUsers(t, users);
}); });
test('filter node', async function xml14 (t) {
var { users, xml0, xml1 } = await initArrays(t, { users: 3 });
let dom0 = xml0.getDom();
let dom1 = xml1.getDom();
let domFilter = (node, attrs) => {
if (node.nodeName === 'H1') {
return null
} else {
return attrs
}
};
xml0.setDomFilter(domFilter);
xml1.setDomFilter(domFilter);
dom0.append(document.createElement('div'));
dom0.append(document.createElement('h1'));
await flushAll(t, users);
t.assert(dom1.childNodes.length === 1, 'Only one node was not transmitted');
t.assert(dom1.childNodes[0].nodeName === 'DIV', 'div node was transmitted');
await compareUsers(t, users);
});
test('filter attribute', async function xml15 (t) {
var { users, xml0, xml1 } = await initArrays(t, { users: 3 });
let dom0 = xml0.getDom();
let dom1 = xml1.getDom();
let domFilter = (node, attrs) => {
return attrs.filter(name => name !== 'hidden')
};
xml0.setDomFilter(domFilter);
xml1.setDomFilter(domFilter);
dom0.setAttribute('hidden', 'true');
dom0.setAttribute('style', 'height: 30px');
dom0.setAttribute('data-me', '77');
await flushAll(t, users);
t.assert(dom0.getAttribute('hidden') === 'true', 'User 0 still has the attribute');
t.assert(dom1.getAttribute('hidden') == null, 'User 1 did not receive update');
t.assert(dom1.getAttribute('style') === 'height: 30px', 'User 1 received style update');
t.assert(dom1.getAttribute('data-me') === '77', 'User 1 received data update');
await compareUsers(t, users);
});
// TODO: move elements // TODO: move elements
var xmlTransactions = [ var xmlTransactions = [
function attributeChange (t, user, chance) { function attributeChange (t, user, chance) {
user.share.xml.getDom().setAttribute(chance.word(), chance.word()); user.share.xml.getDom().setAttribute(chance.word(), chance.word());
}, },
function attributeChangeHidden (t, user, chance) {
user.share.xml.getDom().setAttribute('hidden', chance.word());
},
function insertText (t, user, chance) { function insertText (t, user, chance) {
let dom = user.share.xml.getDom(); let dom = user.share.xml.getDom();
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null; var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null;
dom.insertBefore(document.createTextNode(chance.word()), succ); dom.insertBefore(document.createTextNode(chance.word()), succ);
}, },
function insertHiddenDom (t, user, chance) {
let dom = user.share.xml.getDom();
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null;
dom.insertBefore(document.createElement('hidden'), succ);
},
function insertDom (t, user, chance) { function insertDom (t, user, chance) {
let dom = user.share.xml.getDom(); let dom = user.share.xml.getDom();
var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null; var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null;
@@ -23888,31 +24007,31 @@ var xmlTransactions = [
} }
]; ];
test('y-xml: Random tests (10)', async function randomXml10 (t) { test('y-xml: Random tests (10)', async function xmlRandom10 (t) {
await applyRandomTests(t, xmlTransactions, 10); await applyRandomTests(t, xmlTransactions, 10);
}); });
test('y-xml: Random tests (42)', async function randomXml42 (t) { test('y-xml: Random tests (42)', async function xmlRandom42 (t) {
await applyRandomTests(t, xmlTransactions, 42); await applyRandomTests(t, xmlTransactions, 42);
}); });
test('y-xml: Random tests (43)', async function randomXml43 (t) { test('y-xml: Random tests (43)', async function xmlRandom43 (t) {
await applyRandomTests(t, xmlTransactions, 43); await applyRandomTests(t, xmlTransactions, 43);
}); });
test('y-xml: Random tests (44)', async function randomXml44 (t) { test('y-xml: Random tests (44)', async function xmlRandom44 (t) {
await applyRandomTests(t, xmlTransactions, 44); await applyRandomTests(t, xmlTransactions, 44);
}); });
test('y-xml: Random tests (45)', async function randomXml45 (t) { test('y-xml: Random tests (45)', async function xmlRandom45 (t) {
await applyRandomTests(t, xmlTransactions, 45); await applyRandomTests(t, xmlTransactions, 45);
}); });
test('y-xml: Random tests (46)', async function randomXml46 (t) { test('y-xml: Random tests (46)', async function xmlRandom46 (t) {
await applyRandomTests(t, xmlTransactions, 46); await applyRandomTests(t, xmlTransactions, 46);
}); });
test('y-xml: Random tests (47)', async function randomXml47 (t) { test('y-xml: Random tests (47)', async function xmlRandom47 (t) {
await applyRandomTests(t, xmlTransactions, 47); await applyRandomTests(t, xmlTransactions, 47);
}); });

File diff suppressed because one or more lines are too long