Compare commits

..

1 Commits

Author SHA1 Message Date
Kevin Jahns
dc1fe7957b v13.0.0-39 -- distribution files 2017-12-05 17:06:12 -08:00
77 changed files with 2140 additions and 2283 deletions

View File

@@ -64,19 +64,6 @@ missing modules.
<script src="./bower_components/yjs/y.js"></script> <script src="./bower_components/yjs/y.js"></script>
``` ```
### CDN
```
<script src="https://cdn.jsdelivr.net/npm/yjs@12/src/y.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-websockets-client@8/dist/y-websockets-client.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-memory@8/dist/y-memory.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-array@10/dist/y-array.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-map@10/dist/y-map.js"></script>
<script src="https://cdn.jsdelivr.net/npm/y-text@9/dist/y-text.js"></script>
// ..
// do the same for all modules you want to use
```
### Npm ### Npm
``` ```
npm install --save yjs % add all y-* modules you want to use npm install --save yjs % add all y-* modules you want to use

View File

@@ -24,8 +24,7 @@
<body> <body>
<div id="aceContainer"></div> <div id="aceContainer"></div>
<script src="../../y.js"></script> <script src="../bower_components/yjs/y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/ace-builds/src/ace.js"></script> <script src="../bower_components/ace-builds/src/ace.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>

View File

@@ -1,17 +1,24 @@
/* global Y, ace */ /* global Y, ace */
let y = new Y('ace-example', { Y({
db: {
name: 'memory'
},
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' room: 'ace-example'
},
sourceDir: '/bower_components',
share: {
ace: 'Text' // y.share.textarea is of type Y.Text
} }
}).then(function (y) {
window.yAce = y
// bind the textarea to a shared text element
var editor = ace.edit('aceContainer')
editor.setTheme('ace/theme/chrome')
editor.getSession().setMode('ace/mode/javascript')
y.share.ace.bindAce(editor)
}) })
window.yAce = y
// bind the textarea to a shared text element
var editor = ace.edit('aceContainer')
editor.setTheme('ace/theme/chrome')
editor.getSession().setMode('ace/mode/javascript')
y.define('ace', Y.Text).bindAce(editor)

View File

@@ -13,7 +13,7 @@
<input type="submit" value="Send"> <input type="submit" value="Send">
</form> </form>
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="../../../y-websockets-client/dist/y-websockets-client.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,9 +1,10 @@
/* global Y */ /* global Y */
let y = new Y('chat-example', { // initialize a shared object. This function call returns a promise!
var y = new Y({
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' room: 'chat-example'
} }
}) })
@@ -22,7 +23,6 @@ function appendMessage (message, position) {
p.appendChild(document.createTextNode(message.message)) p.appendChild(document.createTextNode(message.message))
chatcontainer.insertBefore(p, chatcontainer.children[position] || null) chatcontainer.insertBefore(p, chatcontainer.children[position] || null)
} }
// This function makes sure that only 7 messages exist in the chat history. // This function makes sure that only 7 messages exist in the chat history.
// The rest is deleted // The rest is deleted
function cleanupChat () { function cleanupChat () {
@@ -30,17 +30,23 @@ function cleanupChat () {
chatprotocol.delete(0, chatprotocol.length - 7) chatprotocol.delete(0, chatprotocol.length - 7)
} }
} }
cleanupChat()
// Insert the initial content // Insert the initial content
chatprotocol.toArray().forEach(appendMessage) chatprotocol.toArray().forEach(appendMessage)
cleanupChat()
// whenever content changes, make sure to reflect the changes in the DOM // whenever content changes, make sure to reflect the changes in the DOM
chatprotocol.observe(function (event) { chatprotocol.observe(function (event) {
if (event.type === 'insert') {
for (let i = 0; i < event.length; i++) {
appendMessage(event.values[i], event.index + i)
}
} else if (event.type === 'delete') {
for (let i = 0; i < event.length; i++) {
chatcontainer.children[event.index].remove()
}
}
// concurrent insertions may result in a history > 7, so cleanup here // concurrent insertions may result in a history > 7, so cleanup here
cleanupChat() cleanupChat()
chatcontainer.innerHTML = ''
chatprotocol.toArray().forEach(appendMessage)
}) })
document.querySelector('#chatform').onsubmit = function (event) { document.querySelector('#chatform').onsubmit = function (event) {
// the form is submitted // the form is submitted

View File

@@ -5,8 +5,7 @@
<body> <body>
<div id="codeMirrorContainer"></div> <div id="codeMirrorContainer"></div>
<script src="../../y.js"></script> <script src="../bower_components/yjs/y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/codemirror/lib/codemirror.js"></script> <script src="../bower_components/codemirror/lib/codemirror.js"></script>
<script src="../bower_components/codemirror/mode/javascript/javascript.js"></script> <script src="../bower_components/codemirror/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../bower_components/codemirror/lib/codemirror.css"> <link rel="stylesheet" href="../bower_components/codemirror/lib/codemirror.css">

View File

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

View File

@@ -13,8 +13,11 @@
<button type="button" id="clearDrawingCanvas">Clear Drawing</button> <button type="button" id="clearDrawingCanvas">Clear Drawing</button>
<svg id="drawingCanvas" viewbox="0 0 100 100" width="100%"></svg> <svg id="drawingCanvas" viewbox="0 0 100 100" width="100%"></svg>
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="../../../y-array/y-array.js"></script>
<script src="../bower_components/d3/d3.min.js"></script> <script src="../../../y-map/dist/y-map.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="../bower_components/d3/d3.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,74 +1,84 @@
/* globals Y, d3 */ /* globals Y, d3 */
'strict mode'
let y = new Y('drawing-example', { Y({
db: {
name: 'memory'
},
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' room: 'drawing-example',
url: 'localhost:1234'
},
sourceDir: '/bower_components',
share: {
drawing: 'Array'
} }
}) }).then(function (y) {
window.yDrawing = y
var drawing = y.share.drawing
var renderPath = d3.svg.line()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] })
.interpolate('basis')
window.yDrawing = y var svg = d3.select('#drawingCanvas')
var drawing = y.define('drawing', Y.Array) .call(d3.behavior.drag()
var renderPath = d3.svg.line() .on('dragstart', dragstart)
.x(function (d) { return d[0] }) .on('drag', drag)
.y(function (d) { return d[1] }) .on('dragend', dragend))
.interpolate('basic')
var svg = d3.select('#drawingCanvas') // create line from a shared array object and update the line when the array changes
.call(d3.behavior.drag() function drawLine (yarray) {
.on('dragstart', dragstart) var line = svg.append('path').datum(yarray.toArray())
.on('drag', drag)
.on('dragend', dragend))
// create line from a shared array object and update the line when the array changes
function drawLine (yarray) {
var line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
yarray.observe(function (event) {
line.remove()
line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath) line.attr('d', renderPath)
}) yarray.observe(function (event) {
} // we only implement insert events that are appended to the end of the array
// call drawLine every time an array is appended event.values.forEach(function (value) {
drawing.observe(function (event) { line.datum().push(value)
event.removedElements.forEach(function () { })
// if one is deleted, all will be deleted!! line.attr('d', renderPath)
svg.selectAll('path').remove() })
}) }
event.addedElements.forEach(function (path) { // call drawLine every time an array is appended
drawLine(path) y.share.drawing.observe(function (event) {
}) if (event.type === 'insert') {
}) event.values.forEach(drawLine)
// draw all existing content } else {
for (var i = 0; i < drawing.length; i++) { // just remove all elements (thats what we do anyway)
drawLine(drawing.get(i)) svg.selectAll('path').remove()
} }
})
// clear canvas on request // draw all existing content
document.querySelector('#clearDrawingCanvas').onclick = function () { for (var i = 0; i < drawing.length; i++) {
drawing.delete(0, drawing.length) drawLine(drawing.get(i))
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 10)
sharedLine.push([d3.mouse(this)])
} }
}
function dragend () { // clear canvas on request
sharedLine = null document.querySelector('#clearDrawingCanvas').onclick = function () {
window.clearTimeout(ignoreDrag) drawing.delete(0, drawing.length)
ignoreDrag = null }
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 33)
sharedLine.push([d3.mouse(this)])
}
}
function dragend () {
sharedLine = null
window.clearTimeout(ignoreDrag)
ignoreDrag = null
}
})

View File

@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
</head>
<script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="../bower_components/d3/d3.min.js"></script>
<script src="./index.js"></script>
<style>
magic-drawing .drawingCanvas path {
fill: none;
stroke: blue;
stroke-width: 2px;
stroke-linejoin: round;
stroke-linecap: round;
}
magic-drawing .drawingCanvas {
width: 500px;
height: 500px;
cursor: default;
padding:1px;
border:1px solid #021a40;
}
magic-drawing .clearDrawingButton {
position: absolute;
top: 0;
left: 0;
}
magic-drawing {
position: relative;
display: block;
}
</style>
</head>
<body contenteditable="true">
</body>
</html>

View File

@@ -1,132 +0,0 @@
/* global Y, d3 */
window.onload = function () {
window.yXmlType.bindToDom(document.body)
}
window.addMagicDrawing = function addMagicDrawing () {
let mt = document.createElement('magic-drawing')
mt.dataset.yjsHook = 'magic-drawing'
document.body.append(mt)
}
var renderPath = d3.svg.line()
.x(function (d) { return d[0] })
.y(function (d) { return d[1] })
.interpolate('basic')
function initDrawingBindings (type, dom) {
dom.contentEditable = 'false'
dom.dataset.yjsHook = 'magic-drawing'
var drawing = type.get('drawing')
if (drawing === undefined) {
drawing = type.set('drawing', new Y.Array())
}
var canvas = dom.querySelector('.drawingCanvas')
if (canvas == null) {
canvas = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
canvas.setAttribute('class', 'drawingCanvas')
canvas.setAttribute('viewbox', '0 0 100 100')
dom.insertBefore(canvas, null)
}
var clearDrawingButton = dom.querySelector('.clearDrawingButton')
if (clearDrawingButton == null) {
clearDrawingButton = document.createElement('button')
clearDrawingButton.setAttribute('type', 'button')
clearDrawingButton.setAttribute('class', 'clearDrawingButton')
clearDrawingButton.innerText = 'Clear Drawing'
dom.insertBefore(clearDrawingButton, null)
}
var svg = d3.select(canvas)
.call(d3.behavior.drag()
.on('dragstart', dragstart)
.on('drag', drag)
.on('dragend', dragend))
// create line from a shared array object and update the line when the array changes
function drawLine (yarray, svg) {
var line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
yarray.observe(function (event) {
line.remove()
line = svg.append('path').datum(yarray.toArray())
line.attr('d', renderPath)
})
}
// call drawLine every time an array is appended
drawing.observe(function (event) {
event.removedElements.forEach(function () {
// if one is deleted, all will be deleted!!
svg.selectAll('path').remove()
})
event.addedElements.forEach(function (path) {
drawLine(path, svg)
})
})
// draw all existing content
for (var i = 0; i < drawing.length; i++) {
drawLine(drawing.get(i), svg)
}
// clear canvas on request
clearDrawingButton.onclick = function () {
drawing.delete(0, drawing.length)
}
var sharedLine = null
function dragstart () {
drawing.insert(drawing.length, [Y.Array])
sharedLine = drawing.get(drawing.length - 1)
}
// After one dragged event is recognized, we ignore them for 33ms.
var ignoreDrag = null
function drag () {
if (sharedLine != null && ignoreDrag == null) {
ignoreDrag = window.setTimeout(function () {
ignoreDrag = null
}, 10)
sharedLine.push([d3.mouse(this)])
}
}
function dragend () {
sharedLine = null
window.clearTimeout(ignoreDrag)
ignoreDrag = null
}
}
Y.XmlHook.addHook('magic-drawing', {
fillType: function (dom, type) {
initDrawingBindings(type, dom)
},
createDom: function (type) {
const dom = document.createElement('magic-drawing')
initDrawingBindings(type, dom)
return dom
}
})
let y = new Y('html-editor-drawing-hook-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.yXml = y
window.yXmlType = y.define('xml', Y.XmlFragment)
window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
captureTimeout: 500
})
document.onkeydown = function interceptUndoRedo (e) {
if (e.keyCode === 90 && e.metaKey) {
if (!e.shiftKey) {
window.undoManager.undo()
} else {
window.undoManager.redo()
}
e.preventDefault()
}
}

View File

@@ -1,8 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
</head> </head>
<script src="../../y.js"></script> <!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="../yjs-dist.js"></script>
<script src="./canvasjs.min.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</head> </head>
<body contenteditable="true"> <body contenteditable="true">

View File

@@ -1,17 +1,78 @@
/* global Y */ /* global Y, HTMLElement, customElements, CanvasJS */
window.onload = function () { window.onload = function () {
window.yXmlType.bindToDom(document.body) window.yXmlType.bindToDom(document.body)
let mt = document.createElement('magic-table')
mt.innerHTML = '<table><tr><th>Amount</th></tr><tr><td>1</td></tr><tr><td>1</td></tr></table>'
document.body.append(mt)
} }
let y = new Y('htmleditor', { class MagicTable extends HTMLElement {
connector: { constructor () {
name: 'websockets-client', super()
url: 'http://127.0.0.1:1234' this.createShadowRoot()
}
get _yjsHook () {
return 'magic-table'
}
showTable () {
this.shadowRoot.innerHTML = ''
this.shadowRoot.append(document.createElement('content'))
}
showDiagram () {
let dataPoints = []
this.querySelectorAll('td').forEach(td => {
let number = Number(td.textContent)
dataPoints.push({
x: (dataPoints.length + 1) * 10,
y: number,
label: '<magic-table> content'
})
})
this.shadowRoot.innerHTML = ''
var chart = new CanvasJS.Chart(this.shadowRoot,
{
title: {
text: 'Bar chart'
},
data: [
{
type: 'bar',
dataPoints: dataPoints
}
]
})
chart.render()
// this.shadowRoot.innerHTML = '<p>dtrn</p>'
}
}
customElements.define('magic-table', MagicTable)
Y.XmlHook.addHook('magic-table', {
fillType: function (dom, type) {
type.set('table', new Y.XmlElement(dom.querySelector('table')))
},
createDom: function (type) {
const table = type.get('table').getDom()
const dom = document.createElement('magic-table')
dom.insertBefore(table, null)
return dom
} }
}) })
window.y = y // initialize a shared object. This function call returns a promise!
let y = new Y({
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234',
room: 'html-editor-example6'
// maxBufferLength: 100
}
})
window.yXml = y
window.yXmlType = y.define('xml', Y.XmlFragment) window.yXmlType = y.define('xml', Y.XmlFragment)
window.undoManager = new Y.utils.UndoManager(window.yXmlType, { window.undoManager = new Y.utils.UndoManager(window.yXmlType, {
captureTimeout: 500 captureTimeout: 500

View File

@@ -16,9 +16,6 @@
width: 100%; width: 100%;
} }
</style> </style>
<script src="../../y.js"></script> <script type="module" src="./index.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src='../../../y-indexeddb/y-indexeddb.js'></script>
<script src="./index.js"></script>
</body> </body>
</html> </html>

View File

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

View File

@@ -10,7 +10,7 @@
.one { .one {
grid-column: 1 ; grid-column: 1 ;
} }
.two { .two {
grid-column: 2; grid-column: 2;
} }
.three { .three {
@@ -49,7 +49,10 @@
</div> </div>
</div> </div>
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.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> <script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,38 +1,64 @@
/* global Y */ /* global Y */
function bindYjsInstance (y, suffix) { Y({
y.define('textarea', Y.Text).bind(document.getElementById('textarea' + suffix)) 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 () { y.connector.socket.on('connection', function () {
document.getElementById('container' + suffix).removeAttribute('disconnected') document.getElementById('container2').removeAttribute('disconnected')
}) })
y.connector.socket.on('disconnect', function () { y.connector.socket.on('disconnect', function () {
document.getElementById('container' + suffix).setAttribute('disconnected', true) document.getElementById('container2').setAttribute('disconnected', true)
}) })
} })
let y1 = new Y('infinite-example', { Y({
db: {
name: 'memory'
},
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' 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)
})
}) })
window.y1 = y1
bindYjsInstance(y1, '1')
let y2 = new Y('infinite-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.y2 = y2
bindYjsInstance(y2, '2')
let y3 = new Y('infinite-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
window.y3 = y3
bindYjsInstance(y1, '3')

View File

@@ -17,7 +17,9 @@
</g> </g>
</svg> </svg>
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="../../../y-map/dist/y-map.js"></script>
<script src="../../../y-memory/y-memory.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="../bower_components/d3/d3.js"></script> <script src="../bower_components/d3/d3.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>

View File

@@ -1,67 +1,74 @@
/* @flow */
/* global Y, d3 */ /* global Y, d3 */
let y = new Y('jigsaw-example', { // initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' room: 'Puzzle-example',
url: 'http://localhost:1234'
},
share: {
piece1: 'Map',
piece2: 'Map',
piece3: 'Map',
piece4: 'Map'
} }
}) }).then(function (y) {
window.yJigsaw = y
var origin // mouse start position - translation of piece
var drag = d3.behavior.drag()
.on('dragstart', function (params) {
// get the translation of the element
var translation = d3
.select(this)
.attr('transform')
.slice(10, -1)
.split(',')
.map(Number)
// mouse coordinates
var mouse = d3.mouse(this.parentNode)
origin = {
x: mouse[0] - translation[0],
y: mouse[1] - translation[1]
}
})
.on('drag', function () {
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart
var y = mouse[1] - origin.y
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')')
})
.on('dragend', function (piece, i) {
// save the current translation of the puzzle piece
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x
var y = mouse[1] - origin.y
piece.set('translation', {x: x, y: y})
})
let jigsaw = y.define('jigsaw', Y.Map) var data = [y.share.piece1, y.share.piece2, y.share.piece3, y.share.piece4]
window.yJigsaw = y var pieces = d3.select(document.querySelector('#puzzle-example')).selectAll('path').data(data)
var origin // mouse start position - translation of piece pieces
var drag = d3.behavior.drag() .classed('draggable', true)
.on('dragstart', function (params) { .attr('transform', function (piece) {
// get the translation of the element var translation = piece.get('translation') || {x: 0, y: 0}
var translation = d3 return 'translate(' + translation.x + ',' + translation.y + ')'
.select(this) }).call(drag)
.attr('transform')
.slice(10, -1)
.split(',')
.map(Number)
// mouse coordinates
var mouse = d3.mouse(this.parentNode)
origin = {
x: mouse[0] - translation[0],
y: mouse[1] - translation[1]
}
})
.on('drag', function () {
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x // =^= mouse - mouse at dragstart + translation at dragstart
var y = mouse[1] - origin.y
d3.select(this).attr('transform', 'translate(' + x + ',' + y + ')')
})
.on('dragend', function (piece, i) {
// save the current translation of the puzzle piece
var mouse = d3.mouse(this.parentNode)
var x = mouse[0] - origin.x
var y = mouse[1] - origin.y
jigsaw.set(piece, {x: x, y: y})
})
var data = ['piece1', 'piece2', 'piece3', 'piece4'] data.forEach(function (piece) {
var pieces = d3.select(document.querySelector('#puzzle-example')).selectAll('path').data(data) piece.observe(function () {
// whenever a property of a piece changes, update the translation of the pieces
pieces pieces
.classed('draggable', true) .transition()
.attr('transform', function (piece) { .attr('transform', function (piece) {
var translation = piece.get('translation') || {x: 0, y: 0} var translation = piece.get('translation') || {x: 0, y: 0}
return 'translate(' + translation.x + ',' + translation.y + ')' return 'translate(' + translation.x + ',' + translation.y + ')'
}).call(drag) })
})
data.forEach(function (piece) {
jigsaw.observe(function () {
// whenever a property of a piece changes, update the translation of the pieces
pieces
.transition()
.attr('transform', function (piece) {
var translation = piece.get(piece)
if (translation == null || typeof translation.x !== 'number' || typeof translation.y !== 'number') {
translation = { x: 0, y: 0 }
}
return 'translate(' + translation.x + ',' + translation.y + ')'
})
}) })
}) })

View File

@@ -13,8 +13,8 @@
width: 100%; width: 100%;
} }
</style> </style>
<script src="../../y.js"></script> <script src="../bower_components/yjs/y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="../bower_components/y-websockets-client/y-websockets-client.js"></script>
<script src="../node_modules/monaco-editor/min/vs/loader.js"></script> <script src="../node_modules/monaco-editor/min/vs/loader.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>

View File

@@ -2,21 +2,29 @@
require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' } }) require.config({ paths: { 'vs': '../node_modules/monaco-editor/min/vs' } })
let y = new Y('monaco-example', {
connector: {
name: 'websockets-client',
url: 'http://127.0.0.1:1234'
}
})
require(['vs/editor/editor.main'], function () { require(['vs/editor/editor.main'], function () {
window.yMonaco = y // Initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'monaco-example'
},
sourceDir: '/bower_components',
share: {
monaco: 'Text' // y.share.monaco is of type Y.Text
}
}).then(function (y) {
window.yMonaco = y
// Create Monaco editor // Create Monaco editor
var editor = monaco.editor.create(document.getElementById('monacoContainer'), { var editor = monaco.editor.create(document.getElementById('monacoContainer'), {
language: 'javascript' language: 'javascript'
})
// Bind to y.share.monaco
y.share.monaco.bindMonaco(editor)
}) })
// Bind to y.share.monaco
y.define('monaco', Y.Text).bindMonaco(editor)
}) })

File diff suppressed because it is too large Load Diff

View File

@@ -9,15 +9,12 @@
"author": "Kevin Jahns", "author": "Kevin Jahns",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"monaco-editor": "^0.8.3", "monaco-editor": "^0.8.3"
"rollup": "^0.52.3"
}, },
"devDependencies": { "devDependencies": {
"standard": "^10.0.2" "standard": "^10.0.2"
}, },
"standard": { "standard": {
"ignore": [ "ignore": ["bower_components"]
"bower_components"
]
} }
} }

View File

@@ -26,7 +26,10 @@
<script src="../bower_components/quill/dist/quill.js"></script> <script src="../bower_components/quill/dist/quill.js"></script>
--> -->
<script src="../../y.js"></script> <script src="../../y.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script> <script src="../../../y-array/y-array.js"></script>
<script src="../../../y-richtext/dist/y-richtext.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> <script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -4,7 +4,7 @@ import commonjs from 'rollup-plugin-commonjs'
var pkg = require('./package.json') var pkg = require('./package.json')
export default { export default {
input: 'yjs-dist.mjs', input: 'yjs-dist.esm',
name: 'Y', name: 'Y',
output: { output: {
file: 'yjs-dist.js', file: 'yjs-dist.js',

View File

@@ -1,9 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<body> <body>
<textarea style="width:80%;" rows=40 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-websockets-client/y-websockets-client.js'></script> <script src="../../../y-array/y-array.js"></script>
<script src="../../../y-text/y-text.js"></script>
<script src="../../../y-websockets-client/y-websockets-client.js"></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,15 +1,23 @@
/* global Y */ /* global Y */
let y = new Y('textarea-example', { // initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: { connector: {
name: 'websockets-client', name: 'websockets-client',
url: 'http://127.0.0.1:1234' room: 'Textarea-example2',
} // url: '//localhost:1234',
url: 'https://yjs-v13.herokuapp.com/'
},
share: {
textarea: 'Text'
},
timeout: 5000 // reject if no connection was established within 5 seconds
}).then(function (y) {
window.yTextarea = y
// bind the textarea to a shared text element
y.share.textarea.bind(document.getElementById('textfield'))
}) })
window.yTextarea = y
// bind the textarea to a shared text element
let type = y.define('textarea', Y.Text)
let textarea = document.querySelector('textarea')
window.binding = new Y.TextareaBinding(type, textarea)

View File

@@ -3,8 +3,7 @@
</head> </head>
<!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. --> <!-- jquery is not required for y-xml. It is just here for convenience, and to test batch operations. -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="../../y.js"></script> <script src="../yjs-dist.js"></script>
<script src='../../../y-websockets-client/y-websockets-client.js'></script>
<script src="./index.js"></script> <script src="./index.js"></script>
</head> </head>
<body> <body>

View File

@@ -1,13 +1,23 @@
/* global Y */ /* global Y */
let y = new Y('xml-example', { // initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
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: 'Xml-example'
},
sourceDir: '/bower_components',
share: {
xml: 'Xml("p")' // y.share.xml is of type Y.Xml with tagname "p"
} }
}).then(function (y) {
window.yXml = y
// bind xml type to a dom, and put it in body
window.sharedDom = y.share.xml.getDom()
document.body.appendChild(window.sharedDom)
}) })
window.yXml = y
// bind xml type to a dom, and put it in body
window.sharedDom = y.define('xml', Y.XmlElement).getDom()
document.body.appendChild(window.sharedDom)

View File

@@ -1,9 +1,7 @@
import Y from '../src/Y.js' import Y from '../src/Y.js'
import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js' import yWebsocketsClient from '../../y-websockets-client/src/y-websockets-client.js'
import extendYIndexedDBPersistence from '../../y-indexeddb/src/y-indexeddb.js'
Y.extend(yWebsocketsClient) Y.extend(yWebsocketsClient)
extendYIndexedDBPersistence(Y)
export default Y export default Y

274
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-51", "version": "13.0.0-39",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1039,7 +1039,7 @@
"requires": { "requires": {
"anymatch": "1.3.0", "anymatch": "1.3.0",
"async-each": "1.0.1", "async-each": "1.0.1",
"fsevents": "1.1.3", "fsevents": "1.1.2",
"glob-parent": "2.0.0", "glob-parent": "2.0.0",
"inherits": "2.0.3", "inherits": "2.0.3",
"is-binary-path": "1.0.1", "is-binary-path": "1.0.1",
@@ -1092,6 +1092,12 @@
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
"dev": true "dev": true
}, },
"colors": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
"commander": { "commander": {
"version": "2.10.0", "version": "2.10.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.10.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-2.10.0.tgz",
@@ -1255,6 +1261,16 @@
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
"dev": true "dev": true
}, },
"cors": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.3.tgz",
"integrity": "sha1-TPeOHSMymnSWsvwiJbd8pbteuAI=",
"dev": true,
"requires": {
"object-assign": "4.1.1",
"vary": "1.1.1"
}
},
"currently-unhandled": { "currently-unhandled": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -1767,6 +1783,12 @@
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true "dev": true
}, },
"etag": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
"integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=",
"dev": true
},
"event-emitter": { "event-emitter": {
"version": "0.3.5", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
@@ -1777,6 +1799,21 @@
"es5-ext": "0.10.23" "es5-ext": "0.10.23"
} }
}, },
"event-stream": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
"integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
"dev": true,
"requires": {
"duplexer": "0.1.1",
"from": "0.1.7",
"map-stream": "0.1.0",
"pause-stream": "0.0.11",
"split": "0.3.3",
"stream-combiner": "0.0.4",
"through": "2.3.8"
}
},
"exit-hook": { "exit-hook": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
@@ -1834,6 +1871,11 @@
"integrity": "sha1-ysNCuPqJAm7+c6Jg/p9rgE9J5H8=", "integrity": "sha1-ysNCuPqJAm7+c6Jg/p9rgE9J5H8=",
"dev": true "dev": true
}, },
"fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"fast-levenshtein": { "fast-levenshtein": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -1967,6 +2009,12 @@
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
"dev": true "dev": true
}, },
"fresh": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz",
"integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=",
"dev": true
},
"from": { "from": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
@@ -1992,14 +2040,14 @@
"dev": true "dev": true
}, },
"fsevents": { "fsevents": {
"version": "1.1.3", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
"integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==", "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"nan": "2.8.0", "nan": "2.7.0",
"node-pre-gyp": "0.6.39" "node-pre-gyp": "0.6.36"
}, },
"dependencies": { "dependencies": {
"abbrev": { "abbrev": {
@@ -2157,6 +2205,7 @@
"version": "2.0.5", "version": "2.0.5",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"boom": "2.10.1" "boom": "2.10.1"
} }
@@ -2204,12 +2253,6 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"detect-libc": {
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true
},
"ecc-jsbn": { "ecc-jsbn": {
"version": "0.1.1", "version": "0.1.1",
"bundled": true, "bundled": true,
@@ -2351,6 +2394,7 @@
"version": "3.1.3", "version": "3.1.3",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"boom": "2.10.1", "boom": "2.10.1",
"cryptiles": "2.0.5", "cryptiles": "2.0.5",
@@ -2522,13 +2566,11 @@
"optional": true "optional": true
}, },
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.6.39", "version": "0.6.36",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"detect-libc": "1.0.2",
"hawk": "3.1.3",
"mkdirp": "0.5.1", "mkdirp": "0.5.1",
"nopt": "4.0.1", "nopt": "4.0.1",
"npmlog": "4.1.0", "npmlog": "4.1.0",
@@ -2736,6 +2778,7 @@
"version": "1.0.9", "version": "1.0.9",
"bundled": true, "bundled": true,
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"hoek": "2.16.3" "hoek": "2.16.3"
} }
@@ -3496,7 +3539,7 @@
"chokidar": "1.7.0", "chokidar": "1.7.0",
"colors": "1.1.2", "colors": "1.1.2",
"connect": "3.5.1", "connect": "3.5.1",
"cors": "2.8.4", "cors": "2.8.3",
"event-stream": "3.3.4", "event-stream": "3.3.4",
"faye-websocket": "0.11.1", "faye-websocket": "0.11.1",
"http-auth": "3.1.3", "http-auth": "3.1.3",
@@ -3504,127 +3547,15 @@
"object-assign": "4.1.1", "object-assign": "4.1.1",
"opn": "5.1.0", "opn": "5.1.0",
"proxy-middleware": "0.15.0", "proxy-middleware": "0.15.0",
"send": "0.16.1", "send": "0.15.3",
"serve-index": "1.9.0" "serve-index": "1.9.0"
}, },
"dependencies": { "dependencies": {
"colors": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
"integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
"dev": true
},
"cors": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
"integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
"dev": true,
"requires": {
"object-assign": "4.1.1",
"vary": "1.1.1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=",
"dev": true
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
"dev": true
},
"event-stream": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
"integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=",
"dev": true,
"requires": {
"duplexer": "0.1.1",
"from": "0.1.7",
"map-stream": "0.1.0",
"pause-stream": "0.0.11",
"split": "0.3.3",
"stream-combiner": "0.0.4",
"through": "2.3.8"
}
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
"dev": true
},
"http-errors": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
"dev": true,
"requires": {
"depd": "1.1.1",
"inherits": "2.0.3",
"setprototypeof": "1.0.3",
"statuses": "1.3.1"
}
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
"dev": true
},
"object-assign": { "object-assign": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true "dev": true
},
"opn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz",
"integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==",
"dev": true,
"requires": {
"is-wsl": "1.1.0"
}
},
"proxy-middleware": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
"integrity": "sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY=",
"dev": true
},
"send": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
"integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
"dev": true,
"requires": {
"debug": "2.6.9",
"depd": "1.1.1",
"destroy": "1.0.4",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"etag": "1.8.1",
"fresh": "0.5.2",
"http-errors": "1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "2.3.0",
"range-parser": "1.2.0",
"statuses": "1.3.1"
}
} }
} }
}, },
@@ -3780,6 +3711,12 @@
"regex-cache": "0.4.3" "regex-cache": "0.4.3"
} }
}, },
"mime": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz",
"integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=",
"dev": true
},
"mime-db": { "mime-db": {
"version": "1.27.0", "version": "1.27.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz",
@@ -3844,9 +3781,9 @@
"dev": true "dev": true
}, },
"nan": { "nan": {
"version": "2.8.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
"dev": true, "dev": true,
"optional": true "optional": true
}, },
@@ -3952,6 +3889,15 @@
"integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
"dev": true "dev": true
}, },
"opn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz",
"integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==",
"dev": true,
"requires": {
"is-wsl": "1.1.0"
}
},
"optionator": { "optionator": {
"version": "0.8.2", "version": "0.8.2",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
@@ -4199,6 +4145,12 @@
"integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
"dev": true "dev": true
}, },
"proxy-middleware": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
"integrity": "sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY=",
"dev": true
},
"randomatic": { "randomatic": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz",
@@ -4674,6 +4626,38 @@
"integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
"dev": true "dev": true
}, },
"send": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz",
"integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=",
"dev": true,
"requires": {
"debug": "2.6.7",
"depd": "1.1.0",
"destroy": "1.0.4",
"encodeurl": "1.0.1",
"escape-html": "1.0.3",
"etag": "1.8.0",
"fresh": "0.5.0",
"http-errors": "1.6.1",
"mime": "1.3.4",
"ms": "2.0.0",
"on-finished": "2.3.0",
"range-parser": "1.2.0",
"statuses": "1.3.1"
},
"dependencies": {
"debug": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz",
"integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"serve-index": { "serve-index": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.0.tgz",
@@ -4895,6 +4879,16 @@
"strip-ansi": "3.0.1" "strip-ansi": "3.0.1"
} }
}, },
"string.fromcodepoint": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz",
"integrity": "sha1-jZeDM8C8klOPUPOD5IiPPlYZ1lM="
},
"string.prototype.codepointat": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz",
"integrity": "sha1-aybpvTr8qnvjtCabUm3huCAArHg="
},
"string_decoder": { "string_decoder": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
@@ -5115,6 +5109,20 @@
"integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=", "integrity": "sha1-K1viOjK2Onyd640PKNSFcko98ZA=",
"dev": true "dev": true
}, },
"utf-8": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/utf-8/-/utf-8-1.0.0.tgz",
"integrity": "sha1-QpwJ+xrDLOuvVllh7aSMs/RSIZc=",
"requires": {
"string.fromcodepoint": "0.2.1",
"string.prototype.codepointat": "0.2.0"
}
},
"utf8": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz",
"integrity": "sha1-H6DZJw6b6FDZsFAn9jUZv0ZFfZY="
},
"util-deprecate": { "util-deprecate": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

View File

@@ -1,6 +1,6 @@
{ {
"name": "yjs", "name": "yjs",
"version": "13.0.0-51", "version": "13.0.0-39",
"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",
@@ -65,6 +65,9 @@
"tag-dist-files": "^0.1.6" "tag-dist-files": "^0.1.6"
}, },
"dependencies": { "dependencies": {
"debug": "^2.6.8" "debug": "^2.6.8",
"fast-diff": "^1.1.2",
"utf-8": "^1.0.0",
"utf8": "^2.1.2"
} }
} }

View File

@@ -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 {
input: 'test/index.js', input: 'test/y-xml.tests.js',
name: 'y-tests', name: 'y-tests',
sourcemap: true, sourcemap: true,
output: { output: {
@@ -11,12 +11,12 @@ export default {
format: 'umd' format: 'umd'
}, },
plugins: [ plugins: [
multiEntry(),
nodeResolve({ nodeResolve({
main: true, main: true,
module: true, module: true,
browser: true browser: true
}), }),
commonjs() commonjs(),
multiEntry()
] ]
} }

View File

@@ -1,3 +1,4 @@
import utf8 from 'utf-8'
import ID from '../Util/ID.js' import ID from '../Util/ID.js'
import { default as RootID, RootFakeUserID } from '../Util/RootID.js' import { default as RootID, RootFakeUserID } from '../Util/RootID.js'
@@ -90,8 +91,7 @@ export default class BinaryDecoder {
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
bytes[i] = this.uint8arr[this.pos++] bytes[i] = this.uint8arr[this.pos++]
} }
let encodedString = String.fromCodePoint(...bytes) return utf8.getStringFromBytes(bytes)
return decodeURIComponent(escape(encodedString))
} }
/** /**
* Look ahead and read varString without incrementing position * Look ahead and read varString without incrementing position

View File

@@ -1,3 +1,4 @@
import utf8 from 'utf-8'
import { RootFakeUserID } from '../Util/RootID.js' import { RootFakeUserID } from '../Util/RootID.js'
const bits7 = 0b1111111 const bits7 = 0b1111111
@@ -61,8 +62,7 @@ export default class BinaryEncoder {
} }
writeVarString (str) { writeVarString (str) {
let encodedString = unescape(encodeURIComponent(str)) let bytes = utf8.setBytesFromString(str)
let bytes = encodedString.split('').map(c => c.codePointAt())
let len = bytes.length let len = bytes.length
this.writeVarUint(len) this.writeVarUint(len)
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {

View File

@@ -1,14 +0,0 @@
import { createMutualExclude } from '../Util/mutualExclude.js'
export default class Binding {
constructor (type, target) {
this.type = type
this.target = target
this._mutualExclude = createMutualExclude()
}
destroy () {
this.type = null
this.target = null
}
}

View File

@@ -1,45 +0,0 @@
import Binding from './Binding.js'
import simpleDiff from '../Util/simpleDiff.js'
import { getRelativePosition, fromRelativePosition } from '../Util/relativePosition.js'
function typeObserver () {
this._mutualExclude(() => {
const textarea = this.target
const textType = this.type
const relativeStart = getRelativePosition(textType, textarea.selectionStart)
const relativeEnd = getRelativePosition(textType, textarea.selectionEnd)
textarea.value = textType.toString()
const start = fromRelativePosition(textType._y, relativeStart)
const end = fromRelativePosition(textType._y, relativeEnd)
textarea.setSelectionRange(start, end)
})
}
function domObserver () {
this._mutualExclude(() => {
let diff = simpleDiff(this.type.toString(), this.target.value)
this.type.delete(diff.pos, diff.remove)
this.type.insert(diff.pos, diff.insert)
})
}
export default class TextareaBinding extends Binding {
constructor (textType, domTextarea) {
// Binding handles textType as this.type and domTextarea as this.target
super(textType, domTextarea)
// set initial value
domTextarea.value = textType.toString()
// Observers are handled by this class
this._typeObserver = typeObserver.bind(this)
this._domObserver = domObserver.bind(this)
textType.observe(this._typeObserver)
domTextarea.addEventListener('input', this._domObserver)
}
destroy () {
// Remove everything that is handled by this class
this.type.unobserve(this._typeObserver)
this.target.unobserve(this._domObserver)
super.destroy()
}
}

View File

@@ -132,7 +132,6 @@ export default class AbstractConnector {
f() f()
} }
this.whenSyncedListeners = [] this.whenSyncedListeners = []
this.y._setContentReady()
this.y.emit('synced') this.y.emit('synced')
} }
} }
@@ -269,7 +268,7 @@ export default class AbstractConnector {
if (messageType === 'sync step 2' && senderConn.auth === 'write') { if (messageType === 'sync step 2' && senderConn.auth === 'write') {
readSyncStep2(decoder, encoder, y, senderConn, sender) readSyncStep2(decoder, encoder, y, senderConn, sender)
} else if (messageType === 'update' && (skipAuth || senderConn.auth === 'write')) { } else if (messageType === 'update' && (skipAuth || senderConn.auth === 'write')) {
integrateRemoteStructs(y, decoder) integrateRemoteStructs(decoder, encoder, y, senderConn, sender)
} else { } else {
throw new Error('Unable to receive message') throw new Error('Unable to receive message')
} }

View File

@@ -1,16 +0,0 @@
import { writeStructs } from './syncStep1.js'
import { integrateRemoteStructs } from './integrateRemoteStructs.js'
import { readDeleteSet, writeDeleteSet } from './deleteSet.js'
import BinaryEncoder from '../Binary/Encoder.js'
export function fromBinary (y, decoder) {
integrateRemoteStructs(y, decoder)
readDeleteSet(y, decoder)
}
export function toBinary (y) {
let encoder = new BinaryEncoder()
writeStructs(y, encoder, new Map())
writeDeleteSet(y, encoder)
return encoder
}

View File

@@ -65,7 +65,7 @@ export function stringifyStructs (y, decoder, strBuilder) {
} }
} }
export function integrateRemoteStructs (y, decoder) { export function integrateRemoteStructs (decoder, encoder, y) {
const len = decoder.readUint32() const len = decoder.readUint32()
for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
let reference = decoder.readVarUint() let reference = decoder.readVarUint()

View File

@@ -30,7 +30,7 @@ export function sendSyncStep1 (connector, syncUser) {
connector.send(syncUser, encoder.createBuffer()) connector.send(syncUser, encoder.createBuffer())
} }
export function writeStructs (y, encoder, ss) { export default function writeStructs (encoder, decoder, y, ss) {
const lenPos = encoder.pos const lenPos = encoder.pos
encoder.writeUint32(0) encoder.writeUint32(0)
let len = 0 let len = 0
@@ -60,7 +60,7 @@ export function readSyncStep1 (decoder, encoder, y, senderConn, sender) {
encoder.writeVarString('sync step 2') encoder.writeVarString('sync step 2')
encoder.writeVarString(y.connector.authInfo || '') encoder.writeVarString(y.connector.authInfo || '')
const ss = readStateSet(decoder) const ss = readStateSet(decoder)
writeStructs(y, encoder, ss) writeStructs(encoder, decoder, y, ss)
writeDeleteSet(y, encoder) writeDeleteSet(y, encoder)
y.connector.send(senderConn.uid, encoder.createBuffer()) y.connector.send(senderConn.uid, encoder.createBuffer())
senderConn.receivedSyncStep2 = true senderConn.receivedSyncStep2 = true

View File

@@ -22,7 +22,7 @@ export function stringifySyncStep2 (y, decoder, strBuilder) {
} }
export function readSyncStep2 (decoder, encoder, y, senderConn, sender) { export function readSyncStep2 (decoder, encoder, y, senderConn, sender) {
integrateRemoteStructs(y, decoder) integrateRemoteStructs(decoder, encoder, y)
readDeleteSet(y, decoder) readDeleteSet(y, decoder)
y.connector._setSyncedWith(sender) y.connector._setSyncedWith(sender)
} }

View File

@@ -1,119 +1,47 @@
import BinaryEncoder from './Binary/Encoder.js' // import BinaryEncoder from './Binary/Encoder.js'
import BinaryDecoder from './Binary/Decoder.js'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import { createMutualExclude } from './Util/mutualExclude.js'
function getFreshCnf () { export default function extendPersistence (Y) {
let buffer = new BinaryEncoder() class AbstractPersistence {
buffer.writeUint32(0) constructor (y, opts) {
return { this.y = y
len: 0, this.opts = opts
buffer this.saveOperationsBuffer = []
} this.log = Y.debug('y:persistence')
}
export default class AbstractPersistence {
constructor (opts) {
this.opts = opts
this.ys = new Map()
}
_init (y) {
let cnf = this.ys.get(y)
if (cnf === undefined) {
cnf = getFreshCnf()
cnf.mutualExclude = createMutualExclude()
this.ys.set(y, cnf)
return this.init(y).then(() => {
y.on('afterTransaction', (y, transaction) => {
let cnf = this.ys.get(y)
if (cnf.len > 0) {
cnf.buffer.setUint32(0, cnf.len)
this.saveUpdate(y, cnf.buffer.createBuffer(), transaction)
let _cnf = getFreshCnf()
for (let key in _cnf) {
cnf[key] = _cnf[key]
}
}
})
return this.retrieve(y)
}).then(function () {
return Promise.resolve(cnf)
})
} else {
return Promise.resolve(cnf)
} }
}
deinit (y) {
this.ys.delete(y)
y.persistence = null
}
destroy () { saveToMessageQueue (binary) {
this.ys = null this.log('Room %s: Save message to message queue', this.y.options.connector.room)
} }
/** saveOperations (ops) {
* Remove all persisted data that belongs to a room. ops = ops.map(function (op) {
* Automatically destroys all Yjs all Yjs instances that persist to return Y.Struct[op.struct].encode(op)
* the room. If `destroyYjsInstances = false` the persistence functionality })
* will be removed from the Yjs instances. /*
* const saveOperations = () => {
* ** Must be overwritten! ** if (this.saveOperationsBuffer.length > 0) {
*/ let encoder = new BinaryEncoder()
removePersistedData (room, destroyYjsInstances = true) { encoder.writeVarString(this.opts.room)
this.ys.forEach((cnf, y) => { encoder.writeVarString('update')
if (y.room === room) { let ops = this.saveOperationsBuffer
if (destroyYjsInstances) { this.saveOperationsBuffer = []
y.destroy() let length = ops.length
} else { encoder.writeUint32(length)
this.deinit(y) for (var i = 0; i < length; i++) {
let op = ops[i]
Y.Struct[op.struct].binaryEncode(encoder, op)
}
this.saveToMessageQueue(encoder.createBuffer())
} }
} }
}) */
} if (this.saveOperationsBuffer.length === 0) {
this.saveOperationsBuffer = ops
/* overwrite */ } else {
saveUpdate (buffer) { this.saveOperationsBuffer = this.saveOperationsBuffer.concat(ops)
} }
/**
* Save struct to update buffer.
* saveUpdate is called when transaction ends
*/
saveStruct (y, struct) {
let cnf = this.ys.get(y)
if (cnf !== undefined) {
cnf.mutualExclude(function () {
struct._toBinary(cnf.buffer)
cnf.len++
})
} }
} }
/* overwrite */ Y.AbstractPersistence = AbstractPersistence
retrieve (y, model, updates) {
let cnf = this.ys.get(y)
if (cnf !== undefined) {
cnf.mutualExclude(function () {
y.transact(function () {
if (model != null) {
fromBinary(y, new BinaryDecoder(new Uint8Array(model)))
}
if (updates != null) {
for (let i = 0; i < updates.length; i++) {
integrateRemoteStructs(y, new BinaryDecoder(new Uint8Array(updates[i])))
}
}
})
y.emit('persistenceReady')
})
}
}
/* overwrite */
persist (y) {
return toBinary(y).createBuffer()
}
} }

View File

@@ -7,7 +7,7 @@ import { logID } from '../MessageHandler/messageToString.js'
* TODO: implement getItemCleanStartNode for better performance (only one lookup) * TODO: implement getItemCleanStartNode for better performance (only one lookup)
*/ */
export function deleteItemRange (y, user, clock, range) { export function deleteItemRange (y, user, clock, range) {
const createDelete = y.connector !== null && y.connector._forwardAppliedStructs const createDelete = y.connector._forwardAppliedStructs
let item = y.os.getItemCleanStart(new ID(user, clock)) let item = y.os.getItemCleanStart(new ID(user, clock))
if (item !== null) { if (item !== null) {
if (!item._deleted) { if (!item._deleted) {
@@ -70,12 +70,12 @@ export default class Delete {
// from remote // from remote
const id = this._targetID const id = this._targetID
deleteItemRange(y, id.user, id.clock, this._length) deleteItemRange(y, id.user, id.clock, this._length)
} else if (y.connector !== null) { } else {
// from local // from local
y.connector.broadcastStruct(this) y.connector.broadcastStruct(this)
} }
if (y.persistence !== null) { if (y.persistence !== null) {
y.persistence.saveStruct(y, this) y.persistence.saveOperations(this)
} }
} }
_logString () { _logString () {

View File

@@ -55,16 +55,14 @@ export default class Item {
/** /**
* Copy the effect of struct * Copy the effect of struct
*/ */
_copy (undeleteChildren, copyPosition) { _copy () {
let struct = new this.constructor() let struct = new this.constructor()
if (copyPosition) { struct._origin = this._left
struct._origin = this._left struct._left = this._left
struct._left = this._left struct._right = this
struct._right = this struct._right_origin = this
struct._right_origin = this struct._parent = this._parent
struct._parent = this._parent struct._parentSub = this._parentSub
struct._parentSub = this._parentSub
}
return struct return struct
} }
get _lastId () { get _lastId () {
@@ -89,22 +87,16 @@ export default class Item {
return this._right return this._right
} }
_delete (y, createDelete = true) { _delete (y, createDelete = true) {
if (!this._deleted) { this._deleted = true
this._deleted = true y.ds.markDeleted(this._id, this._length)
y.ds.markDeleted(this._id, this._length) if (createDelete) {
let del = new Delete() let del = new Delete()
del._targetID = this._id del._targetID = this._id
del._length = this._length del._length = this._length
if (createDelete) { del._integrate(y, true)
// broadcast and persists Delete
del._integrate(y, true)
} else if (y.persistence !== null) {
// only persist Delete
y.persistence.saveStruct(y, del)
}
transactionTypeChanged(y, this._parent, this._parentSub)
y._transaction.deletedStructs.add(this)
} }
transactionTypeChanged(y, this._parent, this._parentSub)
y._transaction.deletedStructs.add(this)
} }
/** /**
* This is called right before this struct receives any children. * This is called right before this struct receives any children.
@@ -119,11 +111,9 @@ export default class Item {
* - Check if this is struct deleted * - Check if this is struct deleted
*/ */
_integrate (y) { _integrate (y) {
y._transaction.newTypes.add(this)
const parent = this._parent const parent = this._parent
const selfID = this._id const selfID = this._id
const user = selfID === null ? y.userID : selfID.user const userState = selfID === null ? 0 : y.ss.getState(selfID.user)
const userState = y.ss.getState(user)
if (selfID === null) { if (selfID === null) {
this._id = y.ss.getNextID(this._length) this._id = y.ss.getNextID(this._length)
} else if (selfID.user === RootFakeUserID) { } else if (selfID.user === RootFakeUserID) {
@@ -226,11 +216,11 @@ export default class Item {
y.os.put(this) y.os.put(this)
transactionTypeChanged(y, parent, parentSub) transactionTypeChanged(y, parent, parentSub)
if (this._id.user !== RootFakeUserID) { if (this._id.user !== RootFakeUserID) {
if (y.connector !== null && (y.connector._forwardAppliedStructs || this._id.user === y.userID)) { if (y.connector._forwardAppliedStructs || this._id.user === y.userID) {
y.connector.broadcastStruct(this) y.connector.broadcastStruct(this)
} }
if (y.persistence !== null) { if (y.persistence !== null) {
y.persistence.saveStruct(y, this) y.persistence.saveOperations(this)
} }
} }
} }

View File

@@ -6,8 +6,8 @@ export default class ItemJSON extends Item {
super() super()
this._content = null this._content = null
} }
_copy (undeleteChildren, copyPosition) { _copy () {
let struct = super._copy(undeleteChildren, copyPosition) let struct = super._copy()
struct._content = this._content struct._content = this._content
return struct return struct
} }

View File

@@ -6,8 +6,8 @@ export default class ItemString extends Item {
super() super()
this._content = null this._content = null
} }
_copy (undeleteChildren, copyPosition) { _copy () {
let struct = super._copy(undeleteChildren, copyPosition) let struct = super._copy()
struct._content = this._content struct._content = this._content
return struct return struct
} }

View File

@@ -79,16 +79,15 @@ export default class Type extends Item {
type = type._parent type = type._parent
} }
} }
_copy (undeleteChildren, copyPosition) { _copy (undeleteChildren) {
let copy = super._copy(undeleteChildren, copyPosition) let copy = super._copy()
let map = new Map() let map = new Map()
copy._map = map copy._map = map
for (let [key, value] of this._map) { for (let [key, value] of this._map) {
if (undeleteChildren.has(value) || !value.deleted) { if (undeleteChildren.has(value) || !value.deleted) {
let _item = value._copy(undeleteChildren, false) let _item = value._copy(undeleteChildren)
_item._parent = copy _item._parent = copy
_item._parentSub = key map.set(key, value._copy(undeleteChildren))
map.set(key, _item)
} }
} }
let prevUndeleted = null let prevUndeleted = null
@@ -96,7 +95,7 @@ export default class Type extends Item {
let item = this._start let item = this._start
while (item !== null) { while (item !== null) {
if (undeleteChildren.has(item) || !item.deleted) { if (undeleteChildren.has(item) || !item.deleted) {
let _item = item._copy(undeleteChildren, false) let _item = item._copy(undeleteChildren)
_item._left = prevUndeleted _item._left = prevUndeleted
_item._origin = prevUndeleted _item._origin = prevUndeleted
_item._right = null _item._right = null
@@ -134,6 +133,7 @@ export default class Type extends Item {
this._deepEventHandler.removeEventListener(f) this._deepEventHandler.removeEventListener(f)
} }
_integrate (y) { _integrate (y) {
y._transaction.newTypes.add(this)
super._integrate(y) super._integrate(y)
this._y = y this._y = y
// when integrating children we must make sure to // when integrating children we must make sure to

View File

@@ -5,42 +5,15 @@ import { logID } from '../MessageHandler/messageToString.js'
import YEvent from '../Util/YEvent.js' import YEvent from '../Util/YEvent.js'
class YArrayEvent extends YEvent { class YArrayEvent extends YEvent {
constructor (yarray, remote, transaction) { constructor (yarray, remote) {
super(yarray) super(yarray)
this.remote = remote this.remote = remote
this._transaction = transaction
this._addedElements = null
}
get addedElements () {
if (this._addedElements === null) {
const target = this.target
const transaction = this._transaction
const addedElements = new Set()
transaction.newTypes.forEach(function (type) {
if (type._parent === target && !transaction.deletedStructs.has(type)) {
addedElements.add(type)
}
})
this._addedElements = addedElements
}
return this._addedElements
}
get removedElements () {
const target = this.target
const transaction = this._transaction
const removedElements = new Set()
transaction.deletedStructs.forEach(function (struct) {
if (struct._parent === target && !transaction.newTypes.has(struct)) {
removedElements.add(struct)
}
})
return removedElements
} }
} }
export default class YArray extends Type { export default class YArray extends Type {
_callObserver (transaction, parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YArrayEvent(this, remote, transaction)) this._callEventHandler(transaction, new YArrayEvent(this, remote))
} }
get (pos) { get (pos) {
let n = this._start let n = this._start
@@ -211,12 +184,8 @@ export default class YArray extends Type {
prevJsonIns._content.push(c) prevJsonIns._content.push(c)
} }
} }
if (prevJsonIns !== null) { if (prevJsonIns !== null && y !== null) {
if (y !== null) { prevJsonIns._integrate(y)
prevJsonIns._integrate(y)
} else if (prevJsonIns._left === null) {
this._start = prevJsonIns
}
} }
}) })
} }
@@ -245,17 +214,6 @@ export default class YArray extends Type {
} }
this.insertAfter(left, content) this.insertAfter(left, content)
} }
push (content) {
let n = this._start
let lastUndeleted = null
while (n !== null) {
if (!n._deleted) {
lastUndeleted = n
}
n = n._right
}
this.insertAfter(lastUndeleted, content)
}
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null const left = this._left !== null ? this._left._lastId : null
const origin = this._origin !== null ? this._origin._lastId : null const origin = this._origin !== null ? this._origin._lastId : null

View File

@@ -56,7 +56,7 @@ export default class YMap extends Type {
this._transact(y => { this._transact(y => {
const old = this._map.get(key) || null const old = this._map.get(key) || null
if (old !== null) { if (old !== null) {
if (old.constructor === ItemJSON && !old._deleted && old._content[0] === value) { if (old instanceof ItemJSON && old._content[0] === value) {
// Trying to overwrite with same value // Trying to overwrite with same value
// break here // break here
return value return value

View File

@@ -24,9 +24,6 @@ export default class YText extends YArray {
return strBuilder.join('') return strBuilder.join('')
} }
insert (pos, text) { insert (pos, text) {
if (text.length <= 0) {
return
}
this._transact(y => { this._transact(y => {
let left = null let left = null
let right = this._start let right = this._start

View File

@@ -1,3 +1,4 @@
// import diff from 'fast-diff'
import { defaultDomFilter } from './utils.js' import { defaultDomFilter } from './utils.js'
import YMap from '../YMap.js' import YMap from '../YMap.js'
@@ -20,8 +21,8 @@ export default class YXmlElement extends YXmlFragment {
this._domFilter = arg2 this._domFilter = arg2
} }
} }
_copy (undeleteChildren, copyPosition) { _copy (undeleteChildren) {
let struct = super._copy(undeleteChildren, copyPosition) let struct = super._copy(undeleteChildren)
struct.nodeName = this.nodeName struct.nodeName = this.nodeName
return struct return struct
} }
@@ -47,11 +48,6 @@ export default class YXmlElement extends YXmlFragment {
return dom return dom
} }
} }
_bindToDom (dom, _document) {
_document = _document || document
this._dom = dom
dom._yxml = this
}
_fromBinary (y, decoder) { _fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder) const missing = super._fromBinary(y, decoder)
this.nodeName = decoder.readVarString() this.nodeName = decoder.readVarString()

View File

@@ -7,7 +7,7 @@ import YArray from '../YArray.js'
import YXmlEvent from './YXmlEvent.js' import YXmlEvent from './YXmlEvent.js'
import { YXmlText, YXmlHook } from './y-xml' import { YXmlText, YXmlHook } from './y-xml'
import { logID } from '../../MessageHandler/messageToString.js' import { logID } from '../../MessageHandler/messageToString.js'
import diff from '../../Util/simpleDiff.js' import diff from 'fast-diff'
function domToYXml (parent, doms, _document) { function domToYXml (parent, doms, _document) {
const types = [] const types = []
@@ -55,7 +55,7 @@ class YXmlTreeWalker {
} }
} }
do { do {
if (!n._deleted && (n.constructor === YXmlFragment._YXmlElement || n.constructor === YXmlFragment) && n._start !== null) { if (!n._deleted && n.constructor === YXmlFragment._YXmlElement && n._start !== null) {
// walk down in the tree // walk down in the tree
n = n._start n = n._start
} else { } else {
@@ -101,11 +101,9 @@ export default class YXmlFragment extends YArray {
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }
/*
if (this._domObserver !== null) { if (this._domObserver !== null) {
this._domObserver.takeRecords() this._domObserver.takeRecords()
} }
*/
token = true token = true
} }
} }
@@ -145,27 +143,8 @@ export default class YXmlFragment extends YArray {
} }
setDomFilter (f) { setDomFilter (f) {
this._domFilter = f this._domFilter = f
let attributes = new Map() this.forEach(xml => {
if (this.getAttributes !== undefined) { xml.setDomFilter(f)
let attrs = this.getAttributes()
for (let key in attrs) {
attributes.set(key, attrs[key])
}
}
this._y.transact(() => {
let result = this._domFilter(this.nodeName, new Map(attributes))
if (result === null) {
this._delete(this._y)
} else {
attributes.forEach((value, key) => {
if (!result.has(key)) {
this.removeAttribute(key)
}
})
}
this.forEach(xml => {
xml.setDomFilter(f)
})
}) })
} }
_callObserver (transaction, parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
@@ -187,9 +166,6 @@ export default class YXmlFragment extends YArray {
this._dom._yxml = null this._dom._yxml = null
this._dom = null this._dom = null
} }
if (this._beforeTransactionHandler !== undefined) {
this._y.off('beforeTransaction', this._beforeTransactionHandler)
}
} }
insertDomElementsAfter (prev, doms, _document) { insertDomElementsAfter (prev, doms, _document) {
const types = domToYXml(this, doms, _document) const types = domToYXml(this, doms, _document)
@@ -223,7 +199,9 @@ export default class YXmlFragment extends YArray {
_document = _document || document _document = _document || document
this._dom = dom this._dom = dom
dom._yxml = this dom._yxml = this
if (this._parent === null) { // TODO: refine this..
if ((this.constructor !== YXmlFragment && this._parent !== this._y) || this._parent === null) {
// TODO: only top level YXmlFragment can bind. Also allow YXmlElements..
return return
} }
this._y.on('beforeTransaction', beforeTransactionSelectionFixer) this._y.on('beforeTransaction', beforeTransactionSelectionFixer)
@@ -276,14 +254,15 @@ export default class YXmlFragment extends YArray {
}) })
// Apply Y.Xml events to dom // Apply Y.Xml events to dom
this.observeDeep(events => { this.observeDeep(events => {
reflectChangesOnDom.call(this, events, _document) this._mutualExclude(() => {
reflectChangesOnDom.call(this, events, _document)
})
}) })
// Apply Dom changes on Y.Xml // Apply Dom changes on Y.Xml
if (typeof MutationObserver !== 'undefined') { if (typeof MutationObserver !== 'undefined') {
this._beforeTransactionHandler = () => { this._y.on('beforeTransaction', () => {
this._domObserverListener(this._domObserver.takeRecords()) this._domObserverListener(this._domObserver.takeRecords())
} })
this._y.on('beforeTransaction', this._beforeTransactionHandler)
this._domObserverListener = mutations => { this._domObserverListener = mutations => {
this._mutualExclude(() => { this._mutualExclude(() => {
this._y.transact(() => { this._y.transact(() => {
@@ -297,9 +276,19 @@ export default class YXmlFragment extends YArray {
} }
switch (mutation.type) { switch (mutation.type) {
case 'characterData': case 'characterData':
var change = diff(yxml.toString(), dom.nodeValue) var diffs = diff(yxml.toString(), dom.nodeValue)
yxml.delete(change.pos, change.remove) var pos = 0
yxml.insert(change.pos, change.insert) for (var i = 0; i < diffs.length; i++) {
var d = diffs[i]
if (d[0] === 0) { // EQUAL
pos += d[1].length
} else if (d[0] === -1) { // DELETE
yxml.delete(pos, d[1].length)
} else { // INSERT
yxml.insert(pos, d[1])
pos += d[1].length
}
}
break break
case 'attributes': case 'attributes':
if (yxml.constructor === YXmlFragment) { if (yxml.constructor === YXmlFragment) {
@@ -326,9 +315,6 @@ export default class YXmlFragment extends YArray {
} }
}) })
for (let dom of diffChildren) { for (let dom of diffChildren) {
if (dom.yOnChildrenChanged !== undefined) {
dom.yOnChildrenChanged()
}
if (dom._yxml != null && dom._yxml !== false) { if (dom._yxml != null && dom._yxml !== false) {
applyChangesFromDom(dom) applyChangesFromDom(dom)
} }

View File

@@ -14,11 +14,6 @@ export default class YXmlHook extends YMap {
getHook(hookName).fillType(dom, this) getHook(hookName).fillType(dom, this)
} }
} }
_copy (undeleteChildren, copyPosition) {
const struct = super._copy(undeleteChildren, copyPosition)
struct.hookName = this.hookName
return struct
}
getDom (_document) { getDom (_document) {
_document = _document || document _document = _document || document
if (this._dom === null) { if (this._dom === null) {
@@ -29,11 +24,6 @@ export default class YXmlHook extends YMap {
} }
return this._dom return this._dom
} }
_unbindFromDom () {
this._dom._yxml = null
this._yxml = null
// TODO: cleanup hook?
}
_fromBinary (y, decoder) { _fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder) const missing = super._fromBinary(y, decoder)
this.hookName = decoder.readVarString() this.hookName = decoder.readVarString()

View File

@@ -68,6 +68,7 @@ export function afterTransactionSelectionFixer (y, transaction, remote) {
} }
} }
if (shouldUpdate) { if (shouldUpdate) {
console.info('updating selection!!')
browserSelection.setBaseAndExtent( browserSelection.setBaseAndExtent(
anchorNode, anchorNode,
anchorOffset, anchorOffset,

View File

@@ -174,7 +174,7 @@ export function reflectChangesOnDom (events, _document) {
// let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement) // let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement)
if (yxml.constructor === YXmlText) { if (yxml.constructor === YXmlText) {
yxml._dom.nodeValue = yxml.toString() yxml._dom.nodeValue = yxml.toString()
} else if (event.attributesChanged !== undefined) { } else {
// update attributes // update attributes
event.attributesChanged.forEach(attributeName => { event.attributesChanged.forEach(attributeName => {
const value = yxml.getAttribute(attributeName) const value = yxml.getAttribute(attributeName)
@@ -193,27 +193,19 @@ export function reflectChangesOnDom (events, _document) {
* only in the attributes (above) * only in the attributes (above)
*/ */
if (event.childListChanged && yxml.constructor !== YXmlHook) { if (event.childListChanged && yxml.constructor !== YXmlHook) {
let currentChild = dom.firstChild // create fragment of undeleted nodes
const fragment = _document.createDocumentFragment()
yxml.forEach(function (t) { yxml.forEach(function (t) {
let expectedChild = t.getDom(_document) fragment.appendChild(t.getDom(_document))
if (expectedChild.parentNode === dom) {
// is already attached to the dom. Look for it
while (currentChild !== expectedChild) {
let del = currentChild
currentChild = currentChild.nextSibling
dom.removeChild(del)
}
currentChild = currentChild.nextSibling
} else {
// this dom is not yet attached to dom
dom.insertBefore(expectedChild, currentChild)
}
}) })
while (currentChild !== null) { // remove remainding nodes
let tmp = currentChild.nextSibling let lastChild = dom.lastChild
dom.removeChild(currentChild) while (lastChild !== null) {
currentChild = tmp dom.removeChild(lastChild)
lastChild = dom.lastChild
} }
// insert fragment of undeleted nodes
dom.appendChild(fragment)
} }
} }
/* TODO: smartscrolling /* TODO: smartscrolling

View File

@@ -11,10 +11,6 @@ export default class ID {
return id !== null && id.user === this.user && id.clock === this.clock return id !== null && id.user === this.user && id.clock === this.clock
} }
lessThan (id) { lessThan (id) {
if (id.constructor === ID) { return this.user < id.user || (this.user === id.user && this.clock < id.clock)
return this.user < id.user || (this.user === id.user && this.clock < id.clock)
} else {
return false
}
} }
} }

View File

@@ -1,7 +1,6 @@
export default class NamedEventHandler { export default class NamedEventHandler {
constructor () { constructor () {
this._eventListener = new Map() this._eventListener = new Map()
this._stateListener = new Map()
} }
_getListener (name) { _getListener (name) {
let listeners = this._eventListener.get(name) let listeners = this._eventListener.get(name)
@@ -22,32 +21,16 @@ export default class NamedEventHandler {
let listeners = this._getListener(name) let listeners = this._getListener(name)
listeners.on.add(f) listeners.on.add(f)
} }
_initStateListener (name) {
let state = this._stateListener.get(name)
if (state === undefined) {
state = {}
state.promise = new Promise(function (resolve) {
state.resolve = resolve
})
this._stateListener.set(name, state)
}
return state
}
when (name) {
return this._initStateListener(name).promise
}
off (name, f) { off (name, f) {
if (name == null || f == null) { if (name == null || f == null) {
throw new Error('You must specify event name and function!') throw new Error('You must specify event name and function!')
} }
const listener = this._eventListener.get(name) const listener = this._eventListener.get(name)
if (listener !== undefined) { if (listener !== undefined) {
listener.on.delete(f) listener.remove(f)
listener.once.delete(f)
} }
} }
emit (name, ...args) { emit (name, ...args) {
this._initStateListener(name).resolve()
const listener = this._eventListener.get(name) const listener = this._eventListener.get(name)
if (listener !== undefined) { if (listener !== undefined) {
listener.on.forEach(f => f.apply(null, args)) listener.on.forEach(f => f.apply(null, args))

View File

@@ -12,10 +12,6 @@ export default class RootID {
return id !== null && id.user === this.user && id.name === this.name && id.type === this.type return id !== null && id.user === this.user && id.name === this.name && id.type === this.type
} }
lessThan (id) { lessThan (id) {
if (id.constructor === RootID) { return this.user < id.user || (this.user === id.user && (this.name < id.name || (this.name === id.name && this.type < id.type)))
return this.user < id.user || (this.user === id.user && (this.name < id.name || (this.name === id.name && this.type < id.type)))
} else {
return true
}
} }
} }

View File

@@ -50,7 +50,7 @@ function applyReverseOperation (y, scope, reverseBuffer) {
) )
) { ) {
performedUndo = true performedUndo = true
op = op._copy(undoOp.deletedStructs, true) op = op._copy(undoOp.deletedStructs)
op._integrate(y) op._integrate(y)
} }
} }

View File

@@ -1,15 +0,0 @@
export function createMutualExclude () {
var token = true
return function mutualExclude (f) {
if (token) {
token = false
try {
f()
} catch (e) {
console.error(e)
}
token = true
}
}
}

View File

@@ -2,31 +2,37 @@ import ID from './ID.js'
import RootID from './RootID.js' import RootID from './RootID.js'
export function getRelativePosition (type, offset) { export function getRelativePosition (type, offset) {
let t = type._start if (offset === 0) {
while (t !== null) { return ['startof', type._id.user, type._id.clock || null, type._id.name || null, type._id.type || null]
if (t._deleted === false) { } else {
if (t._length > offset) { let t = type._start
return [t._id.user, t._id.clock + offset] while (t !== null) {
if (t._deleted === false) {
if (t._length >= offset) {
return [t._id.user, t._id.clock + offset - 1]
}
if (t._right === null) {
return [t._id.user, t._id.clock + t._length - 1]
}
offset -= t._length
} }
offset -= t._length t = t._right
} }
t = t._right return null
} }
return ['endof', type._id.user, type._id.clock || null, type._id.name || null, type._id.type || null]
} }
export function fromRelativePosition (y, rpos) { export function fromRelativePosition (y, rpos) {
if (rpos[0] === 'endof') { if (rpos[0] === 'startof') {
let id let id
if (rpos[3] === null) { if (rpos[3] === null) {
id = new ID(rpos[1], rpos[2]) id = new ID(rpos[1], rpos[2])
} else { } else {
id = new RootID(rpos[3], rpos[4]) id = new RootID(rpos[3], rpos[4])
} }
const type = y.os.get(id)
return { return {
type, type: y.os.get(id),
offset: type.length offset: 0
} }
} else { } else {
let offset = 0 let offset = 0
@@ -36,7 +42,7 @@ export function fromRelativePosition (y, rpos) {
return null return null
} }
if (!struct._deleted) { if (!struct._deleted) {
offset = rpos[1] - struct._id.clock offset = rpos[1] - struct._id.clock + 1
} }
struct = struct._left struct = struct._left
while (struct !== null) { while (struct !== null) {

View File

@@ -1,19 +0,0 @@
export default function simpleDiff (a, b) {
let left = 0 // number of same characters counting from left
let right = 0 // number of same characters counting from right
while (left < a.length && left < b.length && a[left] === b[left]) {
left++
}
if (left !== a.length || left !== b.length) {
// Only check right if a !== b
while (right + left < a.length && right + left < b.length && a[a.length - right - 1] === b[b.length - right - 1]) {
right++
}
}
return {
pos: left,
remove: a.length - left - right,
insert: b.slice(left, b.length - right)
}
}

View File

@@ -5,7 +5,6 @@ import { generateUserID } from './Util/generateUserID.js'
import RootID from './Util/RootID.js' import RootID from './Util/RootID.js'
import NamedEventHandler from './Util/NamedEventHandler.js' import NamedEventHandler from './Util/NamedEventHandler.js'
import UndoManager from './Util/UndoManager.js' import UndoManager from './Util/UndoManager.js'
import { integrateRemoteStructs } from './MessageHandler/integrateRemoteStructs.js'
import { messageToString, messageToRoomname } from './MessageHandler/messageToString.js' import { messageToString, messageToRoomname } from './MessageHandler/messageToString.js'
@@ -22,58 +21,26 @@ import { addStruct as addType } from './Util/structReferences.js'
import debug from 'debug' import debug from 'debug'
import Transaction from './Transaction.js' import Transaction from './Transaction.js'
import TextareaBinding from './Binding/TextareaBinding.js'
import { toBinary, fromBinary } from './MessageHandler/binaryEncode.js'
export default class Y extends NamedEventHandler { export default class Y extends NamedEventHandler {
constructor (room, opts, persistence) { constructor (opts) {
super() super()
this.room = room
if (opts != null) {
opts.connector.room = room
}
this._contentReady = false
this._opts = opts this._opts = opts
this.userID = generateUserID() this.userID = opts._userID != null ? opts._userID : generateUserID()
this.share = {} this.share = {}
this.ds = new DeleteStore(this) this.ds = new DeleteStore(this)
this.os = new OperationStore(this) this.os = new OperationStore(this)
this.ss = new StateStore(this) this.ss = new StateStore(this)
this.connector = new Y[opts.connector.name](this, opts.connector)
if (opts.persistence != null) {
this.persistence = new Y[opts.persistence.name](this, opts.persistence)
this.persistence.retrieveContent()
} else {
this.persistence = null
}
this.connected = true
this._missingStructs = new Map() this._missingStructs = new Map()
this._readyToIntegrate = [] this._readyToIntegrate = []
this._transaction = null this._transaction = null
this.connector = null
this.connected = false
let initConnection = () => {
if (opts != null) {
this.connector = new Y[opts.connector.name](this, opts.connector)
this.connected = true
this.emit('connectorReady')
}
}
if (persistence != null) {
this.persistence = persistence
persistence._init(this).then(initConnection)
} else {
this.persistence = null
initConnection()
}
}
_setContentReady () {
if (!this._contentReady) {
this._contentReady = true
this.emit('content')
}
}
whenContentReady () {
if (this._contentReady) {
return Promise.resolve()
} else {
return new Promise(resolve => {
this.once('content', resolve)
})
}
} }
_beforeChange () {} _beforeChange () {}
transact (f, remote = false) { transact (f, remote = false) {
@@ -123,6 +90,9 @@ export default class Y extends NamedEventHandler {
set _start (start) { set _start (start) {
return null return null
} }
get room () {
return this._opts.connector.room
}
define (name, TypeConstructor) { define (name, TypeConstructor) {
let id = new RootID(name, TypeConstructor) let id = new RootID(name, TypeConstructor)
let type = this.os.get(id) let type = this.os.get(id)
@@ -153,18 +123,11 @@ export default class Y extends NamedEventHandler {
} }
} }
destroy () { destroy () {
super.destroy()
this.share = null this.share = null
if (this.connector != null) { if (this.connector.destroy != null) {
if (this.connector.destroy != null) { this.connector.destroy()
this.connector.destroy() } else {
} else { this.connector.disconnect()
this.connector.disconnect()
}
}
if (this.persistence !== null) {
this.persistence.deinit(this)
this.persistence = null
} }
this.os = null this.os = null
this.ds = null this.ds = null
@@ -192,7 +155,7 @@ Y.extend = function extendYjs () {
// TODO: The following assignments should be moved to yjs-dist // TODO: The following assignments should be moved to yjs-dist
Y.AbstractConnector = Connector Y.AbstractConnector = Connector
Y.AbstractPersistence = Persistence Y.Persisence = Persistence
Y.Array = YArray Y.Array = YArray
Y.Map = YMap Y.Map = YMap
Y.Text = YText Y.Text = YText
@@ -201,17 +164,12 @@ Y.XmlFragment = YXmlFragment
Y.XmlText = YXmlText Y.XmlText = YXmlText
Y.XmlHook = YXmlHook Y.XmlHook = YXmlHook
Y.TextareaBinding = TextareaBinding
Y.utils = { Y.utils = {
BinaryDecoder, BinaryDecoder,
UndoManager, UndoManager,
getRelativePosition, getRelativePosition,
fromRelativePosition, fromRelativePosition,
addType, addType
integrateRemoteStructs,
toBinary,
fromBinary
} }
Y.debug = debug Y.debug = debug

View File

@@ -1,29 +0,0 @@
import { test } from '../node_modules/cutest/cutest.mjs'
import simpleDiff from '../src/Util/simpleDiff.js'
import Chance from 'chance'
function runDiffTest (t, a, b, expected) {
let result = simpleDiff(a, b)
t.compare(result, expected, `Compare "${a}" with "${b}"`)
}
test('diff tests', async function diff1 (t) {
runDiffTest(t, 'abc', 'axc', { pos: 1, remove: 1, insert: 'x' })
runDiffTest(t, 'bc', 'xc', { pos: 0, remove: 1, insert: 'x' })
runDiffTest(t, 'ab', 'ax', { pos: 1, remove: 1, insert: 'x' })
runDiffTest(t, 'b', 'x', { pos: 0, remove: 1, insert: 'x' })
runDiffTest(t, '', 'abc', { pos: 0, remove: 0, insert: 'abc' })
runDiffTest(t, 'abc', 'xyz', { pos: 0, remove: 3, insert: 'xyz' })
runDiffTest(t, 'axz', 'au', { pos: 1, remove: 2, insert: 'u' })
runDiffTest(t, 'ax', 'axy', { pos: 2, remove: 0, insert: 'y' })
})
test('random diff tests', async function randomDiff (t) {
const chance = new Chance(t.getSeed() * 1000000000)
let a = chance.word()
let b = chance.word()
let change = simpleDiff(a, b)
let arr = Array.from(a)
arr.splice(change.pos, change.remove, ...Array.from(change.insert))
t.assert(arr.join('') === b, 'Applying change information is correct')
})

View File

@@ -54,9 +54,6 @@ test('varString', async function varString (t) {
testEncoding(t, writeVarString, readVarString, 'test!') testEncoding(t, writeVarString, readVarString, 'test!')
testEncoding(t, writeVarString, readVarString, '☺☺☺') testEncoding(t, writeVarString, readVarString, '☺☺☺')
testEncoding(t, writeVarString, readVarString, '1234') testEncoding(t, writeVarString, readVarString, '1234')
testEncoding(t, writeVarString, readVarString, '쾟')
testEncoding(t, writeVarString, readVarString, '龟') // surrogate length 3
testEncoding(t, writeVarString, readVarString, '😝') // surrogate length 4
}) })
test('varString random', async function varStringRandom (t) { test('varString random', async function varStringRandom (t) {

View File

@@ -3,6 +3,6 @@
<head> <head>
</head> </head>
<body> <body>
<script type="module" src="./index.js"></script> <script type="module" src="./encode-decode.js"></script>
</body> </body>
</html> </html>

View File

@@ -1,6 +0,0 @@
import './red-black-tree.js'
import './y-array.tests.js'
import './y-map.tests.js'
import './y-xml.tests.js'
import './encode-decode.tests.js'
import './diff.tests.js'

View File

@@ -280,6 +280,7 @@ test('deep element insert', async function xml16 (t) {
deepElement.append(boldElement) deepElement.append(boldElement)
deepElement.append(attrElement) deepElement.append(attrElement)
dom0.append(deepElement) dom0.append(deepElement)
console.log(dom0.outerHTML)
let str0 = dom0.outerHTML let str0 = dom0.outerHTML
await flushAll(t, users) await flushAll(t, users)
let str1 = dom1.outerHTML let str1 = dom1.outerHTML
@@ -339,6 +340,7 @@ test('Filtering remote changes', async function xmlFilteringRemote (t) {
// check dom // check dom
paragraph.getDom().setAttribute('malicious', 'true') paragraph.getDom().setAttribute('malicious', 'true')
span.getDom().setAttribute('malicious', 'true') span.getDom().setAttribute('malicious', 'true')
console.log(xml0.toString())
// check incoming attributes // check incoming attributes
xml1.get(0).get(0).setAttribute('malicious', 'true') xml1.get(0).get(0).setAttribute('malicious', 'true')
xml1.insert(0, [new Y.XmlElement('hideMe')]) xml1.insert(0, [new Y.XmlElement('hideMe')])

View File

@@ -145,7 +145,7 @@ export async function initArrays (t, opts) {
} else { } else {
connOpts = Object.assign({ role: 'slave' }, conn) connOpts = Object.assign({ role: 'slave' }, conn)
} }
let y = new Y(connOpts.room, { let y = new Y({
_userID: i, // evil hackery, don't try this at home _userID: i, // evil hackery, don't try this at home
connector: connOpts connector: connOpts
}) })

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

1637
y.node.js

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

272
y.test.js
View File

@@ -1,8 +1,8 @@
(function (global, factory) { (function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(factory) : typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory()); (factory((global['y-tests'] = {})));
}(this, (function () { 'use strict'; }(this, (function (exports) { 'use strict';
class N { class N {
// A created node is always red! // A created node is always red!
@@ -2201,38 +2201,15 @@ class YEvent {
} }
class YArrayEvent extends YEvent { class YArrayEvent extends YEvent {
constructor (yarray, remote, transaction) { constructor (yarray, remote) {
super(yarray); super(yarray);
this.remote = remote; this.remote = remote;
this._transaction = transaction;
}
get addedElements () {
const target = this.target;
const transaction = this._transaction;
const addedElements = new Set();
transaction.newTypes.forEach(function (type) {
if (type._parent === target && !transaction.deletedStructs.has(type)) {
addedElements.add(type);
}
});
return addedElements
}
get removedElements () {
const target = this.target;
const transaction = this._transaction;
const removedElements = new Set();
transaction.deletedStructs.forEach(function (struct) {
if (struct._parent === target && !transaction.newTypes.has(struct)) {
removedElements.add(struct);
}
});
return removedElements
} }
} }
class YArray extends Type { class YArray extends Type {
_callObserver (transaction, parentSubs, remote) { _callObserver (transaction, parentSubs, remote) {
this._callEventHandler(transaction, new YArrayEvent(this, remote, transaction)); this._callEventHandler(transaction, new YArrayEvent(this, remote));
} }
get (pos) { get (pos) {
let n = this._start; let n = this._start;
@@ -2403,12 +2380,8 @@ class YArray extends Type {
prevJsonIns._content.push(c); prevJsonIns._content.push(c);
} }
} }
if (prevJsonIns !== null) { if (prevJsonIns !== null && y !== null) {
if (y !== null) { prevJsonIns._integrate(y);
prevJsonIns._integrate(y);
} else if (prevJsonIns._left === null) {
this._start = prevJsonIns;
}
} }
}); });
} }
@@ -2437,17 +2410,6 @@ class YArray extends Type {
} }
this.insertAfter(left, content); this.insertAfter(left, content);
} }
push (content) {
let n = this._start;
let lastUndeleted = null;
while (n !== null) {
if (!n._deleted) {
lastUndeleted = n;
}
n = n._right;
}
this.insertAfter(lastUndeleted, content);
}
_logString () { _logString () {
const left = this._left !== null ? this._left._lastId : null; const left = this._left !== null ? this._left._lastId : null;
const origin = this._origin !== null ? this._origin._lastId : null; const origin = this._origin !== null ? this._origin._lastId : null;
@@ -2591,17 +2553,14 @@ class YText extends YArray {
let right = this._start; let right = this._start;
let count = 0; let count = 0;
while (right !== null) { while (right !== null) {
const rightLen = right._deleted ? 0 : (right._length - 1); if (count <= pos && pos < count + right._length) {
if (count <= pos && pos <= count + rightLen) {
const splitDiff = pos - count; const splitDiff = pos - count;
right = right._splitAt(this._y, splitDiff); right = right._splitAt(this._y, pos - count);
left = right._left; left = right._left;
count += splitDiff; count += splitDiff;
break break
} }
if (!right._deleted) { count += right._length;
count += right._length;
}
left = right; left = right;
right = right._right; right = right._right;
} }
@@ -2761,7 +2720,7 @@ function reflectChangesOnDom (events, _document) {
// let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement) // let anchorViewPosition = getAnchorViewPosition(yxml._scrollElement)
if (yxml.constructor === YXmlText) { if (yxml.constructor === YXmlText) {
yxml._dom.nodeValue = yxml.toString(); yxml._dom.nodeValue = yxml.toString();
} else if (event.attributesChanged !== undefined) { } else {
// update attributes // update attributes
event.attributesChanged.forEach(attributeName => { event.attributesChanged.forEach(attributeName => {
const value = yxml.getAttribute(attributeName); const value = yxml.getAttribute(attributeName);
@@ -2780,27 +2739,19 @@ function reflectChangesOnDom (events, _document) {
* only in the attributes (above) * only in the attributes (above)
*/ */
if (event.childListChanged && yxml.constructor !== YXmlHook) { if (event.childListChanged && yxml.constructor !== YXmlHook) {
let currentChild = dom.firstChild; // create fragment of undeleted nodes
const fragment = _document.createDocumentFragment();
yxml.forEach(function (t) { yxml.forEach(function (t) {
let expectedChild = t.getDom(_document); fragment.appendChild(t.getDom(_document));
if (expectedChild.parentNode === dom) {
// is already attached to the dom. Look for it
while (currentChild !== expectedChild) {
let del = currentChild;
currentChild = currentChild.nextSibling;
dom.removeChild(del);
}
currentChild = currentChild.nextSibling;
} else {
// this dom is not yet attached to dom
dom.insertBefore(expectedChild, currentChild);
}
}); });
while (currentChild !== null) { // remove remainding nodes
let tmp = currentChild.nextSibling; let lastChild = dom.lastChild;
dom.removeChild(currentChild); while (lastChild !== null) {
currentChild = tmp; dom.removeChild(lastChild);
lastChild = dom.lastChild;
} }
// insert fragment of undeleted nodes
dom.appendChild(fragment);
} }
} }
/* TODO: smartscrolling /* TODO: smartscrolling
@@ -2982,6 +2933,7 @@ function afterTransactionSelectionFixer (y, transaction, remote) {
} }
} }
if (shouldUpdate) { if (shouldUpdate) {
console.info('updating selection!!');
browserSelection.setBaseAndExtent( browserSelection.setBaseAndExtent(
anchorNode, anchorNode,
anchorOffset, anchorOffset,
@@ -2989,6 +2941,9 @@ function afterTransactionSelectionFixer (y, transaction, remote) {
focusOffset focusOffset
); );
} }
// delete, so the objects can be gc'd
relativeSelection = null;
browserSelection = null;
} }
class YXmlEvent extends YEvent { class YXmlEvent extends YEvent {
@@ -3756,7 +3711,7 @@ function domToYXml (parent, doms, _document) {
} }
if (parent._domFilter(d.nodeName, new Map()) !== null) { if (parent._domFilter(d.nodeName, new Map()) !== null) {
let type; let type;
const hookName = d._yjsHook || (d.dataset != null ? d.dataset.yjsHook : undefined); const hookName = d._yjsHook;
if (hookName !== undefined) { if (hookName !== undefined) {
type = new YXmlHook(hookName, d); type = new YXmlHook(hookName, d);
} else if (d.nodeType === d.TEXT_NODE) { } else if (d.nodeType === d.TEXT_NODE) {
@@ -3794,7 +3749,7 @@ class YXmlTreeWalker {
} }
} }
do { do {
if (!n._deleted && (n.constructor === YXmlFragment._YXmlElement || n.constructor === YXmlFragment) && n._start !== null) { if (!n._deleted && n.constructor === YXmlFragment._YXmlElement && n._start !== null) {
// walk down in the tree // walk down in the tree
n = n._start; n = n._start;
} else { } else {
@@ -3882,23 +3837,6 @@ class YXmlFragment extends YArray {
} }
setDomFilter (f) { setDomFilter (f) {
this._domFilter = f; this._domFilter = f;
let attributes = new Map();
if (this.getAttributes !== undefined) {
let attrs = this.getAttributes();
for (let key in attrs) {
attributes.set(key, attrs[key]);
}
}
let result = this._domFilter(this.nodeName, new Map(attributes));
if (result === null) {
this._delete(this._y);
} else {
attributes.forEach((value, key) => {
if (!result.has(key)) {
this.removeAttribute(key);
}
});
}
this.forEach(xml => { this.forEach(xml => {
xml.setDomFilter(f); xml.setDomFilter(f);
}); });
@@ -4024,7 +3962,7 @@ class YXmlFragment extends YArray {
mutations.forEach(mutation => { mutations.forEach(mutation => {
const dom = mutation.target; const dom = mutation.target;
const yxml = dom._yxml; const yxml = dom._yxml;
if (yxml == null || yxml.constructor === YXmlHook) { if (yxml == null) {
// dom element is filtered // dom element is filtered
return return
} }
@@ -4240,7 +4178,6 @@ class YXmlHook extends YMap {
if (hookName !== undefined) { if (hookName !== undefined) {
this.hookName = hookName; this.hookName = hookName;
this._dom = dom; this._dom = dom;
dom._yjsHook = hookName;
dom._yxml = this; dom._yxml = this;
getHook(hookName).fillType(dom, this); getHook(hookName).fillType(dom, this);
} }
@@ -4251,15 +4188,9 @@ class YXmlHook extends YMap {
const dom = getHook(this.hookName).createDom(this); const dom = getHook(this.hookName).createDom(this);
this._dom = dom; this._dom = dom;
dom._yxml = this; dom._yxml = this;
dom._yjsHook = this.hookName;
} }
return this._dom return this._dom
} }
_unbindFromDom () {
this._dom._yxml = null;
this._yxml = null;
// TODO: cleanup hook?
}
_fromBinary (y, decoder) { _fromBinary (y, decoder) {
const missing = super._fromBinary(y, decoder); const missing = super._fromBinary(y, decoder);
this.hookName = decoder.readVarString(); this.hookName = decoder.readVarString();
@@ -4275,12 +4206,6 @@ class YXmlHook extends YMap {
} }
super._integrate(y); super._integrate(y);
} }
setDomFilter () {
// TODO: implement new modfilter method!
}
enableSmartScrolling () {
// TODO: implement new smartscrolling method!
}
} }
YXmlHook.addHook = addHook; YXmlHook.addHook = addHook;
@@ -4743,7 +4668,7 @@ var y = d * 365.25;
* @api public * @api public
*/ */
var index = 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) {
@@ -4885,7 +4810,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; exports.humanize = ms;
/** /**
* The currently active debug mode names, and names to skip. * The currently active debug mode names, and names to skip.
@@ -4944,8 +4869,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;
@@ -4964,19 +4889,19 @@ function createDebug(namespace) {
} }
// apply any `formatters` transformations // apply any `formatters` transformations
var index$$1 = 0; var index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index // if we encounter an escaped % then don't increase the array index
if (match === '%%') return match; if (match === '%%') return match;
index$$1++; index++;
var formatter = exports.formatters[format]; var formatter = exports.formatters[format];
if ('function' === typeof formatter) { if ('function' === typeof formatter) {
var val = args[index$$1]; var val = args[index];
match = formatter.call(self, val); match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format` // now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index$$1, 1); args.splice(index, 1);
index$$1--; index--;
} }
return match; return match;
}); });
@@ -5939,7 +5864,7 @@ if (typeof Y !== 'undefined') {
} }
var chance_1 = createCommonjsModule(function (module, exports) { var chance_1 = createCommonjsModule(function (module, exports) {
// Chance.js 1.0.10 // Chance.js 1.0.12
// 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.
@@ -6004,7 +5929,7 @@ var chance_1 = createCommonjsModule(function (module, exports) {
return this; return this;
} }
Chance.prototype.VERSION = "1.0.10"; Chance.prototype.VERSION = "1.0.12";
// Random helper functions // Random helper functions
function initOptions(options, defaults) { function initOptions(options, defaults) {
@@ -6223,6 +6148,16 @@ var chance_1 = createCommonjsModule(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
* *
@@ -7165,8 +7100,25 @@ var chance_1 = createCommonjsModule(function (module, exports) {
return this.word({length: options.length}) + '@' + (options.domain || this.domain()); return this.word({length: options.length}) + '@' + (options.domain || this.domain());
}; };
/**
* #Description:
* ===============================================
* Generate a random Facebook id, aka fbid.
*
* NOTE: At the moment (Sep 2017), Facebook ids are
* "numeric strings" of length 16.
* However, Facebook Graph API documentation states that
* "it is extremely likely to change over time".
* @see https://developers.facebook.com/docs/graph-api/overview/
*
* #Examples:
* ===============================================
* chance.fbid() => '1000035231661304'
*
* @return [string] facebook id
*/
Chance.prototype.fbid = function () { Chance.prototype.fbid = function () {
return parseInt('10000' + this.natural({max: 100000000000}), 10); return '10000' + this.string({pool: "1234567890", length: 11});
}; };
Chance.prototype.google_analytics = function () { Chance.prototype.google_analytics = function () {
@@ -8007,8 +7959,72 @@ var chance_1 = createCommonjsModule(function (module, exports) {
// -- End Regional // -- End Regional
// -- Music --
Chance.prototype.note = function(options) {
// choices for 'notes' option:
// flatKey - chromatic scale with flat notes (default)
// sharpKey - chromatic scale with sharp notes
// flats - just flat notes
// sharps - just sharp notes
// naturals - just natural notes
// all - naturals, sharps and flats
options = initOptions(options, { notes : 'flatKey'});
var scales = {
naturals: ['C', 'D', 'E', 'F', 'G', 'A', 'B'],
flats: ['D♭', 'E♭', 'G♭', 'A♭', 'B♭'],
sharps: ['C♯', 'D♯', 'F♯', 'G♯', 'A♯']
};
scales.all = scales.naturals.concat(scales.flats.concat(scales.sharps));
scales.flatKey = scales.naturals.concat(scales.flats);
scales.sharpKey = scales.naturals.concat(scales.sharps);
return this.pickone(scales[options.notes]);
};
Chance.prototype.midi_note = function(options) {
var min = 0;
var max = 127;
options = initOptions(options, { min : min, max : max });
return this.integer({min: options.min, max: options.max});
};
Chance.prototype.chord_quality = function(options) {
options = initOptions(options, { jazz: true });
var chord_qualities = ['maj', 'min', 'aug', 'dim'];
if (options.jazz){
chord_qualities = [
'maj7',
'min7',
'7',
'sus',
'dim',
'ø'
];
}
return this.pickone(chord_qualities);
};
Chance.prototype.chord = function (options) {
options = initOptions(options);
return this.note(options) + this.chord_quality(options);
};
Chance.prototype.tempo = function (options) {
var min = 40;
var max = 320;
options = initOptions(options, {min: min, max: max});
return this.integer({min: options.min, max: options.max});
};
// -- End Music
// -- Miscellaneous -- // -- Miscellaneous --
// Coin - Flip, flip, flipadelphia
Chance.prototype.coin = function(options) {
return this.bool() ? "heads" : "tails";
};
// Dice - For all the board game geeks out there, myself included ;) // Dice - For all the board game geeks out there, myself included ;)
function diceFn (range) { function diceFn (range) {
return function () { return function () {
@@ -13489,7 +13505,7 @@ async function applyRandomTests (t, mods, iterations) {
return initInformation return initInformation
} }
var index$1$1 = function (str) { var index$1 = function (str) {
return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
return '%' + c.charCodeAt(0).toString(16).toUpperCase(); return '%' + c.charCodeAt(0).toString(16).toUpperCase();
}); });
@@ -13675,7 +13691,7 @@ function parserForArrayFormat(opts) {
function encode(value, opts) { function encode(value, opts) {
if (opts.encode) { if (opts.encode) {
return opts.strict ? index$1$1(value) : encodeURIComponent(value); return opts.strict ? index$1(value) : encodeURIComponent(value);
} }
return value; return value;
@@ -13787,7 +13803,7 @@ var stringify = function (obj, opts) {
}).join('&') : ''; }).join('&') : '';
}; };
var index$2 = { var index = {
extract: extract, extract: extract,
parse: parse$1, parse: parse$1,
stringify: stringify stringify: stringify
@@ -13802,7 +13818,7 @@ const browserSupport =
function createTestLink (params) { function createTestLink (params) {
if (typeof location !== 'undefined') { if (typeof location !== 'undefined') {
var query = index$2.parse(location.search); var query = index.parse(location.search);
delete query.test; delete query.test;
delete query.seed; delete query.seed;
delete query.args; delete query.args;
@@ -13812,7 +13828,7 @@ function createTestLink (params) {
query[name] = params[name]; query[name] = params[name];
} }
} }
return location.protocol + '//' + location.host + location.pathname + '?' + index$2.stringify(query) + location.hash return location.protocol + '//' + location.host + location.pathname + '?' + index.stringify(query) + location.hash
} }
} }
@@ -13823,7 +13839,7 @@ class TestHandler {
this.repeatingRun = 0; this.repeatingRun = 0;
this.tests = {}; this.tests = {};
if (typeof location !== 'undefined') { if (typeof location !== 'undefined') {
this.opts = index$2.parse(location.search); this.opts = index.parse(location.search);
if (this.opts.case != null) { if (this.opts.case != null) {
this.opts.case = Number(this.opts.case); this.opts.case = Number(this.opts.case);
} }
@@ -13964,7 +13980,7 @@ function createCommonjsModule$1(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports; return module = { exports: {} }, fn(module, module.exports), module.exports;
} }
var isBrowser = typeof index$2 !== 'undefined'; var isBrowser = typeof index !== 'undefined';
var environment = { var environment = {
isBrowser: isBrowser isBrowser: isBrowser
@@ -18513,8 +18529,8 @@ var stacktraceGps = createCommonjsModule$1(function (module, exports) {
* @returns {String} original representation of the base64-encoded string. * @returns {String} original representation of the base64-encoded string.
*/ */
function _atob(b64str) { function _atob(b64str) {
if (typeof index$2 !== 'undefined' && index$2.atob) { if (typeof index !== 'undefined' && index.atob) {
return index$2.atob(b64str); return index.atob(b64str);
} else { } else {
throw new Error('You must supply a polyfill for window.atob in this environment'); throw new Error('You must supply a polyfill for window.atob in this environment');
} }
@@ -19024,7 +19040,7 @@ var stacktrace = createCommonjsModule$1(function (module, exports) {
})); }));
}); });
index$2.stacktrace = stacktrace; index.stacktrace = stacktrace;
function test (testDescription, ...args) { function test (testDescription, ...args) {
let location = stacktrace.getSync()[1]; let location = stacktrace.getSync()[1];
@@ -19312,6 +19328,7 @@ test('deep element insert', async function xml16 (t) {
deepElement.append(boldElement); deepElement.append(boldElement);
deepElement.append(attrElement); deepElement.append(attrElement);
dom0.append(deepElement); dom0.append(deepElement);
console.log(dom0.outerHTML);
let str0 = dom0.outerHTML; let str0 = dom0.outerHTML;
await flushAll(t, users); await flushAll(t, users);
let str1 = dom1.outerHTML; let str1 = dom1.outerHTML;
@@ -19371,6 +19388,7 @@ test('Filtering remote changes', async function xmlFilteringRemote (t) {
// check dom // check dom
paragraph.getDom().setAttribute('malicious', 'true'); paragraph.getDom().setAttribute('malicious', 'true');
span.getDom().setAttribute('malicious', 'true'); span.getDom().setAttribute('malicious', 'true');
console.log(xml0.toString());
// check incoming attributes // check incoming attributes
xml1.get(0).get(0).setAttribute('malicious', 'true'); xml1.get(0).get(0).setAttribute('malicious', 'true');
xml1.insert(0, [new Y$1.XmlElement('hideMe')]); xml1.insert(0, [new Y$1.XmlElement('hideMe')]);
@@ -19481,5 +19499,7 @@ test('y-xml: Random tests (1000)', async function xmlRandom1000 (t) {
await applyRandomTests(t, xmlTransactions, 1000); await applyRandomTests(t, xmlTransactions, 1000);
}); });
Object.defineProperty(exports, '__esModule', { value: true });
}))); })));
//# sourceMappingURL=y.test.js.map //# sourceMappingURL=y.test.js.map

File diff suppressed because one or more lines are too long