Implemented Xml Struct
This commit is contained in:
		
							parent
							
								
									86c46cf0ec
								
							
						
					
					
						commit
						ab6cde07e6
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,4 @@
 | 
				
			|||||||
node_modules
 | 
					node_modules
 | 
				
			||||||
bower_components
 | 
					bower_components
 | 
				
			||||||
/y.*
 | 
					/y.*
 | 
				
			||||||
 | 
					/examples/yjs-dist.js*
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										58
									
								
								examples/infiniteyjs/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								examples/infiniteyjs/index.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					  <style>
 | 
				
			||||||
 | 
					    .wrapper {
 | 
				
			||||||
 | 
					      display: grid;
 | 
				
			||||||
 | 
					      grid-template-columns: repeat(3, 1fr);
 | 
				
			||||||
 | 
					      grid-gap: 7px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .one {
 | 
				
			||||||
 | 
					      grid-column: 1 ;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .two { 
 | 
				
			||||||
 | 
					      grid-column: 2;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .three {
 | 
				
			||||||
 | 
					      grid-column: 3;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    textarea {
 | 
				
			||||||
 | 
					      width: calc(100% - 10px)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .editor-container {
 | 
				
			||||||
 | 
					      background-color: #4caf50;
 | 
				
			||||||
 | 
					      padding: 4px 5px 10px 5px;
 | 
				
			||||||
 | 
					      border-radius: 11px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .editor-container[disconnected] {
 | 
				
			||||||
 | 
					      background-color: red;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .disconnected-info {
 | 
				
			||||||
 | 
					      display: none;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    .editor-container[disconnected] .disconnected-info {
 | 
				
			||||||
 | 
					      display: inline;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  </style>
 | 
				
			||||||
 | 
					  <div class="wrapper">
 | 
				
			||||||
 | 
					    <div id="container1" class="one editor-container">
 | 
				
			||||||
 | 
					      <h1>Server 1 <span class="disconnected-info">(disconnected)</span></h1>
 | 
				
			||||||
 | 
					      <textarea id="textarea1" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div id="container2" class="two editor-container">
 | 
				
			||||||
 | 
					      <h1>Server 2 <span class="disconnected-info">(disconnected)</span></h1>
 | 
				
			||||||
 | 
					      <textarea id="textarea2" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div id="container3" class="three editor-container">
 | 
				
			||||||
 | 
					      <h1>Server 3 <span class="disconnected-info">(disconnected)</span></h1>
 | 
				
			||||||
 | 
					      <textarea id="textarea3" rows=40 autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					  <script src="../../y.js"></script>
 | 
				
			||||||
 | 
					  <script src="../../../y-array/y-array.js"></script>
 | 
				
			||||||
 | 
					  <script src="../../../y-text/dist/y-text.js"></script>
 | 
				
			||||||
 | 
					  <script src="../../../y-memory/y-memory.js"></script>
 | 
				
			||||||
 | 
					  <script src="../../../y-websockets-client/y-websockets-client.js"></script>
 | 
				
			||||||
 | 
					  <script src="./index.js"></script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										64
									
								
								examples/infiniteyjs/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								examples/infiniteyjs/index.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					/* global Y */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Y({
 | 
				
			||||||
 | 
					  db: {
 | 
				
			||||||
 | 
					    name: 'memory'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  connector: {
 | 
				
			||||||
 | 
					    name: 'websockets-client',
 | 
				
			||||||
 | 
					    room: 'Textarea-example',
 | 
				
			||||||
 | 
					    url: 'https://yjs-v13.herokuapp.com/'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  share: {
 | 
				
			||||||
 | 
					    textarea: 'Text'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}).then(function (y) {
 | 
				
			||||||
 | 
					  window.y1 = y
 | 
				
			||||||
 | 
					  y.share.textarea.bind(document.getElementById('textarea1'))
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Y({
 | 
				
			||||||
 | 
					  db: {
 | 
				
			||||||
 | 
					    name: 'memory'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  connector: {
 | 
				
			||||||
 | 
					    name: 'websockets-client',
 | 
				
			||||||
 | 
					    room: 'Textarea-example',
 | 
				
			||||||
 | 
					    url: 'https://yjs-v13-second.herokuapp.com/'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  share: {
 | 
				
			||||||
 | 
					    textarea: 'Text'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}).then(function (y) {
 | 
				
			||||||
 | 
					  window.y2 = y
 | 
				
			||||||
 | 
					  y.share.textarea.bind(document.getElementById('textarea2'))
 | 
				
			||||||
 | 
					  y.connector.socket.on('connection', function () {
 | 
				
			||||||
 | 
					    document.getElementById('container2').removeAttribute('disconnected')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  y.connector.socket.on('disconnect', function () {
 | 
				
			||||||
 | 
					    document.getElementById('container2').setAttribute('disconnected', true)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Y({
 | 
				
			||||||
 | 
					  db: {
 | 
				
			||||||
 | 
					    name: 'memory'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  connector: {
 | 
				
			||||||
 | 
					    name: 'websockets-client',
 | 
				
			||||||
 | 
					    room: 'Textarea-example',
 | 
				
			||||||
 | 
					    url: 'https://yjs-v13-third.herokuapp.com/'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  share: {
 | 
				
			||||||
 | 
					    textarea: 'Text'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}).then(function (y) {
 | 
				
			||||||
 | 
					  window.y3 = y
 | 
				
			||||||
 | 
					  y.share.textarea.bind(document.getElementById('textarea3'))
 | 
				
			||||||
 | 
					  y.connector.socket.on('connection', function () {
 | 
				
			||||||
 | 
					    document.getElementById('container3').removeAttribute('disconnected')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  y.connector.socket.on('disconnect', function () {
 | 
				
			||||||
 | 
					    document.getElementById('container3').setAttribute('disconnected', true)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@ -2,6 +2,10 @@
 | 
				
			|||||||
  "name": "examples",
 | 
					  "name": "examples",
 | 
				
			||||||
  "version": "0.0.0",
 | 
					  "version": "0.0.0",
 | 
				
			||||||
  "description": "",
 | 
					  "description": "",
 | 
				
			||||||
 | 
					  "scripts": {
 | 
				
			||||||
 | 
					    "dist": "rollup -c",
 | 
				
			||||||
 | 
					    "watch": "rollup -cw"
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  "author": "Kevin Jahns",
 | 
					  "author": "Kevin Jahns",
 | 
				
			||||||
  "license": "MIT",
 | 
					  "license": "MIT",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										27
									
								
								examples/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								examples/rollup.config.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					import nodeResolve from 'rollup-plugin-node-resolve'
 | 
				
			||||||
 | 
					import commonjs from 'rollup-plugin-commonjs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var pkg = require('./package.json')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  entry: 'yjs-dist.esm',
 | 
				
			||||||
 | 
					  dest: 'yjs-dist.js',
 | 
				
			||||||
 | 
					  moduleName: 'Y',
 | 
				
			||||||
 | 
					  format: 'umd',
 | 
				
			||||||
 | 
					  plugins: [
 | 
				
			||||||
 | 
					    nodeResolve({
 | 
				
			||||||
 | 
					      main: true,
 | 
				
			||||||
 | 
					      module: true,
 | 
				
			||||||
 | 
					      browser: true
 | 
				
			||||||
 | 
					    }),
 | 
				
			||||||
 | 
					    commonjs()
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  sourceMap: true,
 | 
				
			||||||
 | 
					  banner: `
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ${pkg.name} - ${pkg.description}
 | 
				
			||||||
 | 
					 * @version v${pkg.version}
 | 
				
			||||||
 | 
					 * @license ${pkg.license}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -12,12 +12,12 @@ Y({
 | 
				
			|||||||
  connector: {
 | 
					  connector: {
 | 
				
			||||||
    name: 'websockets-client',
 | 
					    name: 'websockets-client',
 | 
				
			||||||
    room: 'Textarea-example',
 | 
					    room: 'Textarea-example',
 | 
				
			||||||
    url: url || 'http://127.0.0.1:1234'
 | 
					    // url: '//localhost:1234',
 | 
				
			||||||
 | 
					    url: 'https://yjs-v13.herokuapp.com/',
 | 
				
			||||||
 | 
					    // options: { transports: ['websocket'], upgrade: false }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  sourceDir: '/bower_components',
 | 
					 | 
				
			||||||
  share: {
 | 
					  share: {
 | 
				
			||||||
    textarea: 'Text', // y.share.textarea is of type Y.Text
 | 
					    textarea: 'Text'
 | 
				
			||||||
    test: 'Array'
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}).then(function (y) {
 | 
					}).then(function (y) {
 | 
				
			||||||
  window.yTextarea = y
 | 
					  window.yTextarea = y
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html>
 | 
					<html>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
  <script src="../bower_components/yjs/y.js"></script>
 | 
					  <!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
 | 
				
			||||||
  <script src="../bower_components/jquery/dist/jquery.min.js"></script>
 | 
					  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
 | 
				
			||||||
 | 
					  <script src="../yjs-dist.js"></script>
 | 
				
			||||||
  <script src="./index.js"></script>
 | 
					  <script src="./index.js"></script>
 | 
				
			||||||
</head>
 | 
					</head>
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,8 @@ Y({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  connector: {
 | 
					  connector: {
 | 
				
			||||||
    name: 'websockets-client',
 | 
					    name: 'websockets-client',
 | 
				
			||||||
 | 
					    // url: 'http://127.0.0.1:1234',
 | 
				
			||||||
 | 
					    url: 'http://192.168.178.81:1234',
 | 
				
			||||||
    room: 'Xml-example'
 | 
					    room: 'Xml-example'
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  sourceDir: '/bower_components',
 | 
					  sourceDir: '/bower_components',
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								examples/yjs-dist.esm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								examples/yjs-dist.esm
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					import Y from '../src/y.js'
 | 
				
			||||||
 | 
					import yArray from '../../y-array/src/y-array.js'
 | 
				
			||||||
 | 
					import yMap from '../../y-map/src/Map.js'
 | 
				
			||||||
 | 
					import yText from '../../y-text/src/Text.js'
 | 
				
			||||||
 | 
					import yXml from '../../y-xml/src/y-xml.js'
 | 
				
			||||||
 | 
					import yMemory from '../../y-memory/src/y-memory.js'
 | 
				
			||||||
 | 
					import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Y.extend(yArray, yMap, yText, yXml, yMemory, yWebsocketsClient)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Y
 | 
				
			||||||
@ -10,6 +10,7 @@
 | 
				
			|||||||
    "debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
 | 
					    "debug": "concurrently 'rollup -wc rollup.test.js' 'cutest-serve y.test.js -o'",
 | 
				
			||||||
    "lint": "standard",
 | 
					    "lint": "standard",
 | 
				
			||||||
    "dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
 | 
					    "dist": "rollup -c rollup.browser.js; rollup -c rollup.node.js",
 | 
				
			||||||
 | 
					    "watch": "concurrently 'rollup -wc rollup.browser.js' 'rollup -wc rollup.node.js'",
 | 
				
			||||||
    "postversion": "npm run dist",
 | 
					    "postversion": "npm run dist",
 | 
				
			||||||
    "postpublish": "tag-dist-files --overwrite-existing-tag"
 | 
					    "postpublish": "tag-dist-files --overwrite-existing-tag"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs'
 | 
				
			|||||||
import multiEntry from 'rollup-plugin-multi-entry'
 | 
					import multiEntry from 'rollup-plugin-multi-entry'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  entry: 'test/*',
 | 
					  entry: 'test/y-xml.tests.js',
 | 
				
			||||||
  moduleName: 'y-tests',
 | 
					  moduleName: 'y-tests',
 | 
				
			||||||
  format: 'umd',
 | 
					  format: 'umd',
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [
 | 
				
			||||||
 | 
				
			|||||||
@ -588,7 +588,7 @@ export default function extendDatabase (Y /* :any */) {
 | 
				
			|||||||
    createType (typedefinition, id) {
 | 
					    createType (typedefinition, id) {
 | 
				
			||||||
      var structname = typedefinition[0].struct
 | 
					      var structname = typedefinition[0].struct
 | 
				
			||||||
      id = id || this.getNextOpId(1)
 | 
					      id = id || this.getNextOpId(1)
 | 
				
			||||||
      var op = Y.Struct[structname].create(id)
 | 
					      var op = Y.Struct[structname].create(id, typedefinition[1])
 | 
				
			||||||
      op.type = typedefinition[0].name
 | 
					      op.type = typedefinition[0].name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.requestTransaction(function * () {
 | 
					      this.requestTransaction(function * () {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										131
									
								
								src/Struct.js
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								src/Struct.js
									
									
									
									
									
								
							@ -2,6 +2,7 @@ const CDELETE = 0
 | 
				
			|||||||
const CINSERT = 1
 | 
					const CINSERT = 1
 | 
				
			||||||
const CLIST = 2
 | 
					const CLIST = 2
 | 
				
			||||||
const CMAP = 3
 | 
					const CMAP = 3
 | 
				
			||||||
 | 
					const CXML = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 An operation also defines the structure of a type. This is why operation and
 | 
					 An operation also defines the structure of a type. This is why operation and
 | 
				
			||||||
@ -22,21 +23,25 @@ const CMAP = 3
 | 
				
			|||||||
     - Operations that are required to execute this operation.
 | 
					     - Operations that are required to execute this operation.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
export default function extendStruct (Y) {
 | 
					export default function extendStruct (Y) {
 | 
				
			||||||
  var Struct = {
 | 
					  let Struct = {}
 | 
				
			||||||
    binaryDecodeOperation: function (decoder) {
 | 
					  Y.Struct = Struct
 | 
				
			||||||
 | 
					  Struct.binaryDecodeOperation = function (decoder) {
 | 
				
			||||||
    let code = decoder.peekUint8()
 | 
					    let code = decoder.peekUint8()
 | 
				
			||||||
    if (code === CDELETE) {
 | 
					    if (code === CDELETE) {
 | 
				
			||||||
        return Y.Struct.Delete.binaryDecode(decoder)
 | 
					      return Struct.Delete.binaryDecode(decoder)
 | 
				
			||||||
    } else if (code === CINSERT) {
 | 
					    } else if (code === CINSERT) {
 | 
				
			||||||
        return Y.Struct.Insert.binaryDecode(decoder)
 | 
					      return Struct.Insert.binaryDecode(decoder)
 | 
				
			||||||
    } else if (code === CLIST) {
 | 
					    } else if (code === CLIST) {
 | 
				
			||||||
        return Y.Struct.List.binaryDecode(decoder)
 | 
					      return Struct.List.binaryDecode(decoder)
 | 
				
			||||||
    } else if (code === CMAP) {
 | 
					    } else if (code === CMAP) {
 | 
				
			||||||
        return Y.Struct.Map.binaryDecode(decoder)
 | 
					      return Struct.Map.binaryDecode(decoder)
 | 
				
			||||||
 | 
					    } else if (code === CXML) {
 | 
				
			||||||
 | 
					      return Struct.Xml.binaryDecode(decoder)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      throw new Error('Unable to decode operation!')
 | 
					      throw new Error('Unable to decode operation!')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* This is the only operation that is actually not a structure, because
 | 
					  /* This is the only operation that is actually not a structure, because
 | 
				
			||||||
  it is not stored in the OS. This is why it _does not_ have an id
 | 
					  it is not stored in the OS. This is why it _does not_ have an id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -44,7 +49,7 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
    target: Id
 | 
					    target: Id
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
    Delete: {
 | 
					  Struct.Delete = {
 | 
				
			||||||
    encode: function (op) {
 | 
					    encode: function (op) {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        target: op.target,
 | 
					        target: op.target,
 | 
				
			||||||
@ -71,8 +76,8 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
    execute: function * (op) {
 | 
					    execute: function * (op) {
 | 
				
			||||||
      return yield * this.deleteOperation(op.target, op.length || 1)
 | 
					      return yield * this.deleteOperation(op.target, op.length || 1)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
    Insert: {
 | 
					
 | 
				
			||||||
  /* {
 | 
					  /* {
 | 
				
			||||||
      content: [any],
 | 
					      content: [any],
 | 
				
			||||||
      opContent: Id,
 | 
					      opContent: Id,
 | 
				
			||||||
@ -84,6 +89,7 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
      parentSub: string (optional), // child of Map type
 | 
					      parentSub: string (optional), // child of Map type
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
 | 
					  Struct.Insert = {
 | 
				
			||||||
    encode: function (op/* :Insertion */) /* :Insertion */ {
 | 
					    encode: function (op/* :Insertion */) /* :Insertion */ {
 | 
				
			||||||
      // TODO: you could not send the "left" property, then you also have to
 | 
					      // TODO: you could not send the "left" property, then you also have to
 | 
				
			||||||
      // "op.left = null" in $execute or $decode
 | 
					      // "op.left = null" in $execute or $decode
 | 
				
			||||||
@ -388,8 +394,8 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
        yield * this.tryCombineWithLeft(m)
 | 
					        yield * this.tryCombineWithLeft(m)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
    List: {
 | 
					
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    start: null,
 | 
					    start: null,
 | 
				
			||||||
@ -399,6 +405,7 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
    id: this.os.getNextOpId(1)
 | 
					    id: this.os.getNextOpId(1)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
 | 
					  Struct.List = {
 | 
				
			||||||
    create: function (id) {
 | 
					    create: function (id) {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        start: null,
 | 
					        start: null,
 | 
				
			||||||
@ -413,28 +420,21 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
        id: op.id,
 | 
					        id: op.id,
 | 
				
			||||||
        type: op.type
 | 
					        type: op.type
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        if (op.info != null) {
 | 
					 | 
				
			||||||
          e.info = op.info
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      return e
 | 
					      return e
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    binaryEncode: function (encoder, op) {
 | 
					    binaryEncode: function (encoder, op) {
 | 
				
			||||||
      encoder.writeUint8(CLIST)
 | 
					      encoder.writeUint8(CLIST)
 | 
				
			||||||
      encoder.writeOpID(op.id)
 | 
					      encoder.writeOpID(op.id)
 | 
				
			||||||
      encoder.writeVarString(op.type)
 | 
					      encoder.writeVarString(op.type)
 | 
				
			||||||
        let info = op.info != null ? JSON.stringify(op.info) : ''
 | 
					 | 
				
			||||||
        encoder.writeVarString(info)
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    binaryDecode: function (decoder) {
 | 
					    binaryDecode: function (decoder) {
 | 
				
			||||||
      decoder.skip8()
 | 
					      decoder.skip8()
 | 
				
			||||||
      let op = {
 | 
					      let op = {
 | 
				
			||||||
        id: decoder.readOpID(),
 | 
					        id: decoder.readOpID(),
 | 
				
			||||||
        type: decoder.readVarString(),
 | 
					        type: decoder.readVarString(),
 | 
				
			||||||
          struct: 'List'
 | 
					        struct: 'List',
 | 
				
			||||||
        }
 | 
					        start: null,
 | 
				
			||||||
        let info = decoder.readVarString()
 | 
					        end: null
 | 
				
			||||||
        if (info.length > 0) {
 | 
					 | 
				
			||||||
          op.info = JSON.parse(info)
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return op
 | 
					      return op
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -487,8 +487,8 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return res
 | 
					      return res
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    },
 | 
					  }
 | 
				
			||||||
    Map: {
 | 
					
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      map: {},
 | 
					      map: {},
 | 
				
			||||||
@ -497,6 +497,7 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
      id: this.os.getNextOpId(1)
 | 
					      id: this.os.getNextOpId(1)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
 | 
					  Struct.Map = {
 | 
				
			||||||
    create: function (id) {
 | 
					    create: function (id) {
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        id: id,
 | 
					        id: id,
 | 
				
			||||||
@ -511,22 +512,12 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
        id: op.id,
 | 
					        id: op.id,
 | 
				
			||||||
        map: {} // overwrite map!!
 | 
					        map: {} // overwrite map!!
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        if (op.requires != null) {
 | 
					 | 
				
			||||||
          e.requires = op.require
 | 
					 | 
				
			||||||
          // TODO: !!
 | 
					 | 
				
			||||||
          console.warn('requires is used! see same note above for List')
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (op.info != null) {
 | 
					 | 
				
			||||||
          e.info = op.info
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      return e
 | 
					      return e
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    binaryEncode: function (encoder, op) {
 | 
					    binaryEncode: function (encoder, op) {
 | 
				
			||||||
      encoder.writeUint8(CMAP)
 | 
					      encoder.writeUint8(CMAP)
 | 
				
			||||||
      encoder.writeOpID(op.id)
 | 
					      encoder.writeOpID(op.id)
 | 
				
			||||||
      encoder.writeVarString(op.type)
 | 
					      encoder.writeVarString(op.type)
 | 
				
			||||||
        let info = op.info != null ? JSON.stringify(op.info) : ''
 | 
					 | 
				
			||||||
        encoder.writeVarString(info)
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    binaryDecode: function (decoder) {
 | 
					    binaryDecode: function (decoder) {
 | 
				
			||||||
      decoder.skip8()
 | 
					      decoder.skip8()
 | 
				
			||||||
@ -536,16 +527,15 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
        struct: 'Map',
 | 
					        struct: 'Map',
 | 
				
			||||||
        map: {}
 | 
					        map: {}
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
        let info = decoder.readVarString()
 | 
					 | 
				
			||||||
        if (info.length > 0) {
 | 
					 | 
				
			||||||
          op.info = JSON.parse(info)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      return op
 | 
					      return op
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    requiredOps: function () {
 | 
					    requiredOps: function () {
 | 
				
			||||||
      return []
 | 
					      return []
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
      execute: function * () {},
 | 
					    execute: function * (op) {
 | 
				
			||||||
 | 
					      op.start = null
 | 
				
			||||||
 | 
					      op.end = null
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
      Get a property by name
 | 
					      Get a property by name
 | 
				
			||||||
    */
 | 
					    */
 | 
				
			||||||
@ -563,6 +553,67 @@ export default function extendStruct (Y) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /*
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      map: {},
 | 
				
			||||||
 | 
					      start: null,
 | 
				
			||||||
 | 
					      end: null,
 | 
				
			||||||
 | 
					      struct: "Xml",
 | 
				
			||||||
 | 
					      type: "",
 | 
				
			||||||
 | 
					      id: this.os.getNextOpId(1)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  Struct.Xml = {
 | 
				
			||||||
 | 
					    create: function (id, args) {
 | 
				
			||||||
 | 
					      let nodeName = args != null ? args.nodeName : null
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        id: id,
 | 
				
			||||||
 | 
					        map: {},
 | 
				
			||||||
 | 
					        start: null,
 | 
				
			||||||
 | 
					        end: null,
 | 
				
			||||||
 | 
					        struct: 'Xml',
 | 
				
			||||||
 | 
					        nodeName
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    encode: function (op) {
 | 
				
			||||||
 | 
					      var e = {
 | 
				
			||||||
 | 
					        struct: 'Xml',
 | 
				
			||||||
 | 
					        type: op.type,
 | 
				
			||||||
 | 
					        id: op.id,
 | 
				
			||||||
 | 
					        map: {},
 | 
				
			||||||
 | 
					        nodeName: op.nodeName
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return e
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    binaryEncode: function (encoder, op) {
 | 
				
			||||||
 | 
					      encoder.writeUint8(CXML)
 | 
				
			||||||
 | 
					      encoder.writeOpID(op.id)
 | 
				
			||||||
 | 
					      encoder.writeVarString(op.type)
 | 
				
			||||||
 | 
					      encoder.writeVarString(op.nodeName)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    binaryDecode: function (decoder) {
 | 
				
			||||||
 | 
					      decoder.skip8()
 | 
				
			||||||
 | 
					      let op = {
 | 
				
			||||||
 | 
					        id: decoder.readOpID(),
 | 
				
			||||||
 | 
					        type: decoder.readVarString(),
 | 
				
			||||||
 | 
					        struct: 'Xml',
 | 
				
			||||||
 | 
					        map: {},
 | 
				
			||||||
 | 
					        start: null,
 | 
				
			||||||
 | 
					        end: null,
 | 
				
			||||||
 | 
					        nodeName: decoder.readVarString()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return op
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    requiredOps: function () {
 | 
				
			||||||
 | 
					      return []
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    execute: function * () {},
 | 
				
			||||||
 | 
					    ref: Struct.List.ref,
 | 
				
			||||||
 | 
					    map: Struct.List.map,
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					      Get a property by name
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    get: Struct.Map.get
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  Y.Struct = Struct
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -863,7 +863,12 @@ export default function extendTransaction (Y) {
 | 
				
			|||||||
        var comp = id[1].split('_')
 | 
					        var comp = id[1].split('_')
 | 
				
			||||||
        if (comp.length > 1) {
 | 
					        if (comp.length > 1) {
 | 
				
			||||||
          var struct = comp[0]
 | 
					          var struct = comp[0]
 | 
				
			||||||
          var op = Y.Struct[struct].create(id)
 | 
					          let type = Y[comp[1]]
 | 
				
			||||||
 | 
					          let args = null
 | 
				
			||||||
 | 
					          if (type != null) {
 | 
				
			||||||
 | 
					            args = Y.utils.parseTypeDefinition(type, comp[3])
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          var op = Y.Struct[struct].create(id, args)
 | 
				
			||||||
          op.type = comp[1]
 | 
					          op.type = comp[1]
 | 
				
			||||||
          yield * this.setOperation(op)
 | 
					          yield * this.setOperation(op)
 | 
				
			||||||
          return op
 | 
					          return op
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/Utils.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/Utils.js
									
									
									
									
									
								
							@ -840,4 +840,17 @@ export default function Utils (Y) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  Y.utils.generateUserId = generateUserId
 | 
					  Y.utils.generateUserId = generateUserId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Y.utils.parseTypeDefinition = function parseTypeDefinition (type, typeArgs) {
 | 
				
			||||||
 | 
					    var args = []
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      args = JSON.parse('[' + typeArgs + ']')
 | 
				
			||||||
 | 
					    } catch (e) {
 | 
				
			||||||
 | 
					      throw new Error('Was not able to parse type definition!')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (type.typeDefinition.parseArguments != null) {
 | 
				
			||||||
 | 
					      args = type.typeDefinition.parseArguments(args[0])[1]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return args
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								src/y.js
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/y.js
									
									
									
									
									
								
							@ -176,23 +176,15 @@ class YConfig extends Y.utils.NamedEventHandler {
 | 
				
			|||||||
      // create shared object
 | 
					      // create shared object
 | 
				
			||||||
      for (var propertyname in opts.share) {
 | 
					      for (var propertyname in opts.share) {
 | 
				
			||||||
        var typeConstructor = opts.share[propertyname].split('(')
 | 
					        var typeConstructor = opts.share[propertyname].split('(')
 | 
				
			||||||
 | 
					        let typeArgs = ''
 | 
				
			||||||
 | 
					        if (typeConstructor.length === 2) {
 | 
				
			||||||
 | 
					          typeArgs = typeConstructor[1].split(')')[0] || ''
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        var typeName = typeConstructor.splice(0, 1)
 | 
					        var typeName = typeConstructor.splice(0, 1)
 | 
				
			||||||
        var type = Y[typeName]
 | 
					        var type = Y[typeName]
 | 
				
			||||||
        var typedef = type.typeDefinition
 | 
					        var typedef = type.typeDefinition
 | 
				
			||||||
        var id = [0xFFFFFF, typedef.struct + '_' + typeName + '_' + propertyname + '_' + typeConstructor]
 | 
					        var id = [0xFFFFFF, typedef.struct + '_' + typeName + '_' + propertyname + '_' + typeArgs]
 | 
				
			||||||
        var args = []
 | 
					        let args = Y.utils.parseTypeDefinition(type, typeArgs)
 | 
				
			||||||
        if (typeConstructor.length === 1) {
 | 
					 | 
				
			||||||
          try {
 | 
					 | 
				
			||||||
            args = JSON.parse('[' + typeConstructor[0].split(')')[0] + ']')
 | 
					 | 
				
			||||||
          } catch (e) {
 | 
					 | 
				
			||||||
            throw new Error('Was not able to parse type definition! (share.' + propertyname + ')')
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          if (type.typeDefinition.parseArguments == null) {
 | 
					 | 
				
			||||||
            throw new Error(typeName + ' does not expect arguments!')
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            args = typedef.parseArguments(args[0])[1]
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        share[propertyname] = yield * this.store.initType.call(this, id, args)
 | 
					        share[propertyname] = yield * this.store.initType.call(this, id, args)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
				
			|||||||
@ -163,30 +163,6 @@ test('encode/decode List operations', async function binList (t) {
 | 
				
			|||||||
    id: [100, 33],
 | 
					    id: [100, 33],
 | 
				
			||||||
    type: 'Array'
 | 
					    type: 'Array'
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is an object')
 | 
					 | 
				
			||||||
  testEncoding(t, writeList, readList, {
 | 
					 | 
				
			||||||
    struct: 'List',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Array',
 | 
					 | 
				
			||||||
    info: { prop: 'yay' }
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is a string')
 | 
					 | 
				
			||||||
  testEncoding(t, writeList, readList, {
 | 
					 | 
				
			||||||
    struct: 'List',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Array',
 | 
					 | 
				
			||||||
    info: 'hi'
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is a number')
 | 
					 | 
				
			||||||
  testEncoding(t, writeList, readList, {
 | 
					 | 
				
			||||||
    struct: 'List',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Array',
 | 
					 | 
				
			||||||
    info: 400
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const writeMap = Y.Struct.Map.binaryEncode
 | 
					const writeMap = Y.Struct.Map.binaryEncode
 | 
				
			||||||
@ -199,31 +175,4 @@ test('encode/decode Map operations', async function binMap (t) {
 | 
				
			|||||||
    type: 'Map',
 | 
					    type: 'Map',
 | 
				
			||||||
    map: {}
 | 
					    map: {}
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is an object')
 | 
					 | 
				
			||||||
  testEncoding(t, writeMap, readMap, {
 | 
					 | 
				
			||||||
    struct: 'Map',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Map',
 | 
					 | 
				
			||||||
    info: { prop: 'yay' },
 | 
					 | 
				
			||||||
    map: {}
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is a string')
 | 
					 | 
				
			||||||
  testEncoding(t, writeMap, readMap, {
 | 
					 | 
				
			||||||
    struct: 'Map',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Map',
 | 
					 | 
				
			||||||
    map: {},
 | 
					 | 
				
			||||||
    info: 'hi'
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  t.log('info is a number')
 | 
					 | 
				
			||||||
  testEncoding(t, writeMap, readMap, {
 | 
					 | 
				
			||||||
    struct: 'Map',
 | 
					 | 
				
			||||||
    id: [100, 33],
 | 
					 | 
				
			||||||
    type: 'Map',
 | 
					 | 
				
			||||||
    map: {},
 | 
					 | 
				
			||||||
    info: 400
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										288
									
								
								test/y-xml.tests.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								test/y-xml.tests.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,288 @@
 | 
				
			|||||||
 | 
					import { wait, initArrays, compareUsers, Y, flushAll, garbageCollectUsers, applyRandomTests } from '../../yjs/tests-lib/helper.js'
 | 
				
			||||||
 | 
					import { test, proxyConsole } from 'cutest'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('set property', async function xml0 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0, xml1 } = await initArrays(t, { users: 2 })
 | 
				
			||||||
 | 
					  xml0.setAttribute('height', 10)
 | 
				
			||||||
 | 
					  t.assert(xml0.getAttribute('height') === 10, 'Simple set+get works')
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.assert(xml1.getAttribute('height') === 10, 'Simple set+get works (remote)')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('events', async function xml1 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0, xml1 } = await initArrays(t, { users: 2 })
 | 
				
			||||||
 | 
					  var event
 | 
				
			||||||
 | 
					  var remoteEvent
 | 
				
			||||||
 | 
					  let expectedEvent
 | 
				
			||||||
 | 
					  xml0.observe(function (e) {
 | 
				
			||||||
 | 
					    delete e._content
 | 
				
			||||||
 | 
					    delete e.nodes
 | 
				
			||||||
 | 
					    delete e.values
 | 
				
			||||||
 | 
					    event = e
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  xml1.observe(function (e) {
 | 
				
			||||||
 | 
					    delete e._content
 | 
				
			||||||
 | 
					    delete e.nodes
 | 
				
			||||||
 | 
					    delete e.values
 | 
				
			||||||
 | 
					    remoteEvent = e
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  xml0.setAttribute('key', 'value')
 | 
				
			||||||
 | 
					  expectedEvent = {
 | 
				
			||||||
 | 
					    type: 'attributeChanged',
 | 
				
			||||||
 | 
					    value: 'value',
 | 
				
			||||||
 | 
					    name: 'key'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  t.compare(event, expectedEvent, 'attribute changed event')
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.compare(remoteEvent, expectedEvent, 'attribute changed event (remote)')
 | 
				
			||||||
 | 
					  // check attributeRemoved
 | 
				
			||||||
 | 
					  xml0.removeAttribute('key')
 | 
				
			||||||
 | 
					  expectedEvent = {
 | 
				
			||||||
 | 
					    type: 'attributeRemoved',
 | 
				
			||||||
 | 
					    name: 'key'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  t.compare(event, expectedEvent, 'attribute deleted event')
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.compare(remoteEvent, expectedEvent, 'attribute deleted event (remote)')
 | 
				
			||||||
 | 
					  // test childInserted event
 | 
				
			||||||
 | 
					  expectedEvent = {
 | 
				
			||||||
 | 
					    type: 'childInserted',
 | 
				
			||||||
 | 
					    index: 0
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.XmlText('some text')])
 | 
				
			||||||
 | 
					  t.compare(event, expectedEvent, 'child inserted event')
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.compare(remoteEvent, expectedEvent, 'child inserted event (remote)')
 | 
				
			||||||
 | 
					  // test childRemoved
 | 
				
			||||||
 | 
					  xml0.delete(0)
 | 
				
			||||||
 | 
					  expectedEvent = {
 | 
				
			||||||
 | 
					    type: 'childRemoved',
 | 
				
			||||||
 | 
					    index: 0
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  t.compare(event, expectedEvent, 'child deleted event')
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.compare(remoteEvent, expectedEvent, 'child deleted event (remote)')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('attribute modifications (y -> dom)', async function xml2 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.setAttribute('height', '100px')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(dom0.getAttribute('height') === '100px', 'setAttribute')
 | 
				
			||||||
 | 
					  xml0.removeAttribute('height')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(dom0.getAttribute('height') == null, 'removeAttribute')
 | 
				
			||||||
 | 
					  xml0.setAttribute('class', 'stuffy stuff')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(dom0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('attribute modifications (dom -> y)', async function xml3 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  dom0.setAttribute('height', '100px')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.getAttribute('height') === '100px', 'setAttribute')
 | 
				
			||||||
 | 
					  dom0.removeAttribute('height')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.getAttribute('height') == null, 'removeAttribute')
 | 
				
			||||||
 | 
					  dom0.setAttribute('class', 'stuffy stuff')
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.getAttribute('class') === 'stuffy stuff', 'set class attribute')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('element insert (dom -> y)', async function xml4 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  dom0.insertBefore(document.createTextNode('some text'), null)
 | 
				
			||||||
 | 
					  dom0.insertBefore(document.createElement('p'), null)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.get(0).toString() === 'some text', 'Retrieve Text Node')
 | 
				
			||||||
 | 
					  t.assert(xml0.get(1).nodeName === 'P', 'Retrieve Element node')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('element insert (y -> dom)', async function xml5 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.XmlText('some text')])
 | 
				
			||||||
 | 
					  xml0.insert(1, [Y.Xml('p')])
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].textContent === 'some text', 'Retrieve Text node')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[1].nodeName === 'P', 'Retrieve Element node')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y on insert, then delete (dom -> y)', async function xml6 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  dom0.insertBefore(document.createElement('p'), null)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 1, 'one node present')
 | 
				
			||||||
 | 
					  dom0.childNodes[0].remove()
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 0, 'no node present after delete')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y on insert, then delete (y -> dom)', async function xml7 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.Xml('p')])
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].nodeName === 'P', 'Get inserted element from dom')
 | 
				
			||||||
 | 
					  xml0.delete(0, 1)
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 0, '#childNodes is empty after delete')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('delete consecutive (1) (Text)', async function xml8 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, ['1', '2', '3'].map(Y.XmlText))
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  xml0.delete(1, 2)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 1, 'check length (y)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 1, 'check length (dom)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].textContent === '1', 'check content')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('delete consecutive (2) (Text)', async function xml9 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, ['1', '2', '3'].map(Y.XmlText))
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  xml0.delete(0, 1)
 | 
				
			||||||
 | 
					  xml0.delete(1, 1)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 1, 'check length (y)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 1, 'check length (dom)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].textContent === '2', 'check content')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('delete consecutive (1) (Element)', async function xml10 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  xml0.delete(1, 2)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 1, 'check length (y)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 1, 'check length (dom)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].nodeName === 'A', 'check content')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('delete consecutive (2) (Element)', async function xml11 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  xml0.delete(0, 1)
 | 
				
			||||||
 | 
					  xml0.delete(1, 1)
 | 
				
			||||||
 | 
					  await wait()
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 1, 'check length (y)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 1, 'check length (dom)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes[0].nodeName === 'B', 'check content')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('Receive a bunch of elements (with disconnect)', async function xml12 (t) {
 | 
				
			||||||
 | 
					  var { users, xml0, xml1 } = await initArrays(t, { users: 3 })
 | 
				
			||||||
 | 
					  let dom0 = xml0.getDom()
 | 
				
			||||||
 | 
					  let dom1 = xml1.getDom()
 | 
				
			||||||
 | 
					  users[1].disconnect()
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.Xml('A'), Y.Xml('B'), Y.Xml('C')])
 | 
				
			||||||
 | 
					  xml0.insert(0, [Y.Xml('X'), Y.Xml('Y'), Y.Xml('Z')])
 | 
				
			||||||
 | 
					  await users[1].reconnect()
 | 
				
			||||||
 | 
					  await flushAll(t, users)
 | 
				
			||||||
 | 
					  t.assert(xml0.length === 6, 'check length (y)')
 | 
				
			||||||
 | 
					  t.assert(xml1.length === 6, 'check length (y) (reconnected user)')
 | 
				
			||||||
 | 
					  t.assert(dom0.childNodes.length === 6, 'check length (dom)')
 | 
				
			||||||
 | 
					  t.assert(dom1.childNodes.length === 6, 'check length (dom) (reconnected user)')
 | 
				
			||||||
 | 
					  await compareUsers(t, users)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: move elements
 | 
				
			||||||
 | 
					var xmlTransactions = [
 | 
				
			||||||
 | 
					  function attributeChange (t, user, chance) {
 | 
				
			||||||
 | 
					    user.share.xml.getDom().setAttribute(chance.word(), chance.word())
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function insertText (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
 | 
				
			||||||
 | 
					    dom.insertBefore(document.createTextNode(chance.word()), succ)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function insertDom (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    var succ = dom.children.length > 0 ? chance.pickone(dom.children) : null
 | 
				
			||||||
 | 
					    dom.insertBefore(document.createElement(chance.word()), succ)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function deleteChild (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    if (dom.childNodes.length > 0) {
 | 
				
			||||||
 | 
					      var d = chance.pickone(dom.childNodes)
 | 
				
			||||||
 | 
					      d.remove()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function insertTextSecondLayer (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    if (dom.children.length > 0) {
 | 
				
			||||||
 | 
					      let dom2 = chance.pickone(dom.children)
 | 
				
			||||||
 | 
					      let succ = dom2.childNodes.length > 0 ? chance.pickone(dom2.childNodes) : null
 | 
				
			||||||
 | 
					      dom2.insertBefore(document.createTextNode(chance.word()), succ)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function insertDomSecondLayer (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    if (dom.children.length > 0) {
 | 
				
			||||||
 | 
					      let dom2 = chance.pickone(dom.children)
 | 
				
			||||||
 | 
					      let succ = dom2.childNodes.length > 0 ? chance.pickone(dom2.childNodes) : null
 | 
				
			||||||
 | 
					      dom2.insertBefore(document.createElement(chance.word()), succ)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  function deleteChildSecondLayer (t, user, chance) {
 | 
				
			||||||
 | 
					    let dom = user.share.xml.getDom()
 | 
				
			||||||
 | 
					    if (dom.children.length > 0) {
 | 
				
			||||||
 | 
					      let dom2 = chance.pickone(dom.children)
 | 
				
			||||||
 | 
					      if (dom2.childNodes.length > 0) {
 | 
				
			||||||
 | 
					        let d = chance.pickone(dom2.childNodes)
 | 
				
			||||||
 | 
					        d.remove()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (10)', async function randomXml10 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 10)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (42)', async function randomXml42 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 42)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (43)', async function randomXml43 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 43)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (44)', async function randomXml44 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 44)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (45)', async function randomXml45 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 45)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (46)', async function randomXml46 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 46)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test('y-xml: Random tests (47)', async function randomXml47 (t) {
 | 
				
			||||||
 | 
					  await applyRandomTests(t, xmlTransactions, 47)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@ -3,14 +3,16 @@ import _Y from '../../yjs/src/y.js'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import yMemory from '../../y-memory/src/y-memory.js'
 | 
					import yMemory from '../../y-memory/src/y-memory.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 yMap from '../../y-map/src/Map.js'
 | 
					import yMap from '../../y-map/src/Map.js'
 | 
				
			||||||
 | 
					import yXml from '../../y-xml/src/y-xml.js'
 | 
				
			||||||
import yTest from './test-connector.js'
 | 
					import yTest from './test-connector.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Chance from 'chance'
 | 
					import Chance from 'chance'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export let Y = _Y
 | 
					export let Y = _Y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Y.extend(yMemory, yArray, yMap, yTest)
 | 
					Y.extend(yMemory, yArray, yText, yMap, yTest, yXml)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export var database = { name: 'memory' }
 | 
					export var database = { name: 'memory' }
 | 
				
			||||||
export var connector = { name: 'test', url: 'http://localhost:1234' }
 | 
					export var connector = { name: 'test', url: 'http://localhost:1234' }
 | 
				
			||||||
@ -47,6 +49,31 @@ 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) {
 | 
				
			||||||
 | 
					  let obj = {}
 | 
				
			||||||
 | 
					  for (var i = 0; i < attrs.length; i++) {
 | 
				
			||||||
 | 
					    let attr = attrs[i]
 | 
				
			||||||
 | 
					    obj[attr.name] = attr.value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return obj
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function domToJson (dom) {
 | 
				
			||||||
 | 
					  if (dom.nodeType === document.TEXT_NODE) {
 | 
				
			||||||
 | 
					    return dom.textContent
 | 
				
			||||||
 | 
					  } else if (dom.nodeType === document.ELEMENT_NODE) {
 | 
				
			||||||
 | 
					    let attributes = attrsToObject(dom.attributes)
 | 
				
			||||||
 | 
					    let children = Array.from(dom.childNodes.values()).map(domToJson)
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      name: dom.nodeName,
 | 
				
			||||||
 | 
					      children: children,
 | 
				
			||||||
 | 
					      attributes: attributes
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    throw new Error('Unsupported node type')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * 1. reconnect and flush all
 | 
					 * 1. reconnect and flush all
 | 
				
			||||||
 * 2. user 0 gc
 | 
					 * 2. user 0 gc
 | 
				
			||||||
@ -73,6 +100,7 @@ export async function compareUsers (t, users) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable)
 | 
					  var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable)
 | 
				
			||||||
  var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable)
 | 
					  var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable)
 | 
				
			||||||
 | 
					  var userXmlValues = users.map(u => u.share.xml.getDom()).map(domToJson)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await users[0].db.garbageCollect()
 | 
					  await users[0].db.garbageCollect()
 | 
				
			||||||
  await users[0].db.garbageCollect()
 | 
					  await users[0].db.garbageCollect()
 | 
				
			||||||
@ -133,6 +161,7 @@ export async function compareUsers (t, users) {
 | 
				
			|||||||
      t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types')
 | 
					      t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types')
 | 
				
			||||||
      t.compare(userMapOneValues[i], userMapOneValues[i + 1], 'map types (propery "one")')
 | 
					      t.compare(userMapOneValues[i], userMapOneValues[i + 1], 'map types (propery "one")')
 | 
				
			||||||
      t.compare(userMapTwoValues[i], userMapTwoValues[i + 1], 'map types (propery "two")')
 | 
					      t.compare(userMapTwoValues[i], userMapTwoValues[i + 1], 'map types (propery "two")')
 | 
				
			||||||
 | 
					      t.compare(userXmlValues[i], userXmlValues[i + 1], 'xml types')
 | 
				
			||||||
      t.compare(data[i].os, data[i + 1].os, 'os')
 | 
					      t.compare(data[i].os, data[i + 1].os, 'os')
 | 
				
			||||||
      t.compare(data[i].ds, data[i + 1].ds, 'ds')
 | 
					      t.compare(data[i].ds, data[i + 1].ds, 'ds')
 | 
				
			||||||
      t.compare(data[i].ss, data[i + 1].ss, 'ss')
 | 
					      t.compare(data[i].ss, data[i + 1].ss, 'ss')
 | 
				
			||||||
@ -147,7 +176,7 @@ export async function initArrays (t, opts) {
 | 
				
			|||||||
  var result = {
 | 
					  var result = {
 | 
				
			||||||
    users: []
 | 
					    users: []
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map' }, opts.share)
 | 
					  var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map', xml: 'Xml("div")' }, opts.share)
 | 
				
			||||||
  var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
 | 
					  var chance = opts.chance || new Chance(t.getSeed() * 1000000000)
 | 
				
			||||||
  var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector)
 | 
					  var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector)
 | 
				
			||||||
  for (let i = 0; i < opts.users; i++) {
 | 
					  for (let i = 0; i < opts.users; i++) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user