Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80ab682b0a | ||
|
|
3f60690880 | ||
|
|
e2f93af86e | ||
|
|
76ebd3043d |
19
Examples/Drawing/index.html
Normal file
19
Examples/Drawing/index.html
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<style>
|
||||||
|
path {
|
||||||
|
fill: none;
|
||||||
|
stroke: blue;
|
||||||
|
stroke-width: 1px;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-linecap: round;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<button type="button" id="clearDrawingCanvas">Clear Drawing</button>
|
||||||
|
<svg id="drawingCanvas" viewbox="0 0 100 100" width="100%"></svg>
|
||||||
|
<script src="../bower_components/yjs/y.js"></script>
|
||||||
|
<script src="../bower_components/d3/d3.js"></script>
|
||||||
|
<script src="./index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
88
Examples/Drawing/index.js
Normal file
88
Examples/Drawing/index.js
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/* globals Y, d3 */
|
||||||
|
'strict mode'
|
||||||
|
|
||||||
|
Y({
|
||||||
|
db: {
|
||||||
|
name: 'memory'
|
||||||
|
},
|
||||||
|
connector: {
|
||||||
|
name: 'websockets-client',
|
||||||
|
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')
|
||||||
|
|
||||||
|
var svg = d3.select('#drawingCanvas')
|
||||||
|
.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) {
|
||||||
|
var line = svg.append('path').datum(yarray.toArray())
|
||||||
|
line.attr('d', renderPath)
|
||||||
|
yarray.observe(function (event) {
|
||||||
|
// we only implement insert events that are appended to the end of the array
|
||||||
|
event.values.forEach(function (value) {
|
||||||
|
line.datum().push(value)
|
||||||
|
})
|
||||||
|
line.attr('d', renderPath)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// call drawLine every time an array is appended
|
||||||
|
y.share.drawing.observe(function (event) {
|
||||||
|
if (event.type === 'insert') {
|
||||||
|
event.values().then(function (values) {
|
||||||
|
values.forEach(drawLine)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// just remove all elements (thats what we do anyway)
|
||||||
|
svg.selectAll('path').remove()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// draw all existing content
|
||||||
|
for (var i = 0; i < drawing.length; i++) {
|
||||||
|
drawing.get(i).then(drawLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear canvas on request
|
||||||
|
document.querySelector('#clearDrawingCanvas').onclick = function () {
|
||||||
|
drawing.delete(0, drawing.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sharedLine = null
|
||||||
|
function dragstart () {
|
||||||
|
drawing.insert(drawing.length, [Y.Array])
|
||||||
|
drawing.get(drawing.length - 1).then(function (array) {
|
||||||
|
sharedLine = array
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -18,10 +18,11 @@
|
|||||||
"y-websockets-client": "latest",
|
"y-websockets-client": "latest",
|
||||||
"y-text": "latest",
|
"y-text": "latest",
|
||||||
"y-indexeddb": "latest",
|
"y-indexeddb": "latest",
|
||||||
"y-xml": "latest",
|
"y-xml": "latest",
|
||||||
"quill": "~0.20.1",
|
"quill": "~0.20.1",
|
||||||
"ace": "~1.2.3",
|
"ace": "~1.2.3",
|
||||||
"ace-builds": "~1.2.3",
|
"ace-builds": "~1.2.3",
|
||||||
"jquery": "~2.2.2"
|
"jquery": "~2.2.2",
|
||||||
|
"d3": "^3.5.16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "11.0.2",
|
"version": "11.1.0",
|
||||||
"homepage": "y-js.org",
|
"homepage": "y-js.org",
|
||||||
"authors": [
|
"authors": [
|
||||||
"Kevin Jahns <kevin.jahns@rwth-aachen.de>"
|
"Kevin Jahns <kevin.jahns@rwth-aachen.de>"
|
||||||
|
|||||||
167
y.es6
167
y.es6
@@ -498,7 +498,7 @@ module.exports = function (Y) {
|
|||||||
} else {
|
} else {
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
}, 10)
|
}, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
globalRoom.whenTransactionsFinished().then(nextFlush)
|
globalRoom.whenTransactionsFinished().then(nextFlush)
|
||||||
@@ -659,7 +659,7 @@ module.exports = function (Y /* :any */) {
|
|||||||
}
|
}
|
||||||
this.gc1 = [] // first stage
|
this.gc1 = [] // first stage
|
||||||
this.gc2 = [] // second stage -> after that, remove the op
|
this.gc2 = [] // second stage -> after that, remove the op
|
||||||
this.gcTimeout = !opts.gcTimeout ? 50000 : opts.gcTimeoutś
|
this.gcTimeout = !opts.gcTimeout ? 50000 : opts.gcTimeouts
|
||||||
function garbageCollect () {
|
function garbageCollect () {
|
||||||
return os.whenTransactionsFinished().then(function () {
|
return os.whenTransactionsFinished().then(function () {
|
||||||
if (os.gc1.length > 0 || os.gc2.length > 0) {
|
if (os.gc1.length > 0 || os.gc2.length > 0) {
|
||||||
@@ -997,7 +997,7 @@ module.exports = function (Y /* :any */) {
|
|||||||
|
|
||||||
// notify parent, if it was instanciated as a custom type
|
// notify parent, if it was instanciated as a custom type
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
let o = Y.utils.copyObject(op)
|
let o = Y.utils.copyOperation(op)
|
||||||
yield* t._changed(transaction, o)
|
yield* t._changed(transaction, o)
|
||||||
}
|
}
|
||||||
if (!op.deleted) {
|
if (!op.deleted) {
|
||||||
@@ -1063,14 +1063,9 @@ module.exports = function (Y /* :any */) {
|
|||||||
this.waitingTransactions.push(makeGen)
|
this.waitingTransactions.push(makeGen)
|
||||||
if (!this.transactionInProgress) {
|
if (!this.transactionInProgress) {
|
||||||
this.transactionInProgress = true
|
this.transactionInProgress = true
|
||||||
if (false || callImmediately) { // TODO: decide whether this is ok or not..
|
setTimeout(() => {
|
||||||
this.transact(this.getNextRequest())
|
this.transact(this.getNextRequest())
|
||||||
} else {
|
}, 0)
|
||||||
var self = this
|
|
||||||
setTimeout(function () {
|
|
||||||
self.transact(self.getNextRequest())
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2560,7 +2555,7 @@ module.exports = function (Y/* :any */) {
|
|||||||
/* this is what we used before.. use this as a reference..
|
/* this is what we used before.. use this as a reference..
|
||||||
* makeOperationReady (startSS, op) {
|
* makeOperationReady (startSS, op) {
|
||||||
op = Y.Struct[op.struct].encode(op)
|
op = Y.Struct[op.struct].encode(op)
|
||||||
op = Y.utils.copyObject(op)
|
op = Y.utils.copyObject(op) -- use copyoperation instead now!
|
||||||
var o = op
|
var o = op
|
||||||
var ids = [op.id]
|
var ids = [op.id]
|
||||||
// search for the new op.right
|
// search for the new op.right
|
||||||
@@ -2696,9 +2691,90 @@ module.exports = function (Y /* : any*/) {
|
|||||||
prematurely called operations are executed
|
prematurely called operations are executed
|
||||||
*/
|
*/
|
||||||
awaitAndPrematurelyCall (ops) {
|
awaitAndPrematurelyCall (ops) {
|
||||||
this.awaiting += ops.length
|
this.awaiting++
|
||||||
ops.forEach(this.onevent)
|
ops.map(Y.utils.copyOperation).forEach(this.onevent)
|
||||||
}
|
}
|
||||||
|
* awaitOps (transaction, f, args) {
|
||||||
|
function notSoSmartSort (array) {
|
||||||
|
// this function sorts insertions in a executable order
|
||||||
|
var result = []
|
||||||
|
while (array.length > 0) {
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
var independent = true
|
||||||
|
for (var j = 0; j < array.length; j++) {
|
||||||
|
if (Y.utils.matchesId(array[j], array[i].left)) {
|
||||||
|
// array[i] depends on array[j]
|
||||||
|
independent = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (independent) {
|
||||||
|
result.push(array.splice(i, 1)[0])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
var before = this.waiting.length
|
||||||
|
// somehow create new operations
|
||||||
|
yield* f.apply(transaction, args)
|
||||||
|
// remove all appended ops / awaited ops
|
||||||
|
this.waiting.splice(before)
|
||||||
|
if (this.awaiting > 0) this.awaiting--
|
||||||
|
// if there are no awaited ops anymore, we can update all waiting ops, and send execute them (if there are still no awaited ops)
|
||||||
|
if (this.awaiting === 0 && this.waiting.length > 0) {
|
||||||
|
// update all waiting ops
|
||||||
|
for (let i = 0; i < this.waiting.length; i++) {
|
||||||
|
var o = this.waiting[i]
|
||||||
|
if (o.struct === 'Insert') {
|
||||||
|
var _o = yield* transaction.getInsertion(o.id)
|
||||||
|
if (!Y.utils.compareIds(_o.id, o.id)) {
|
||||||
|
// o got extended
|
||||||
|
o.left = [o.id[0], o.id[1] - 1]
|
||||||
|
} else if (_o.left == null) {
|
||||||
|
o.left = null
|
||||||
|
} else {
|
||||||
|
// find next undeleted op
|
||||||
|
var left = yield* transaction.getInsertion(_o.left)
|
||||||
|
while (left.deleted != null) {
|
||||||
|
if (left.left != null) {
|
||||||
|
left = yield* transaction.getInsertion(left.left)
|
||||||
|
} else {
|
||||||
|
left = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o.left = left != null ? Y.utils.getLastId(left) : null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// the previous stuff was async, so we have to check again!
|
||||||
|
// We also pull changes from the bindings, if there exists such a method, this could increase awaiting too
|
||||||
|
if (this._pullChanges != null) {
|
||||||
|
this._pullChanges()
|
||||||
|
}
|
||||||
|
if (this.awaiting === 0) {
|
||||||
|
// sort by type, execute inserts first
|
||||||
|
var ins = []
|
||||||
|
var dels = []
|
||||||
|
this.waiting.forEach(function (o) {
|
||||||
|
if (o.struct === 'Delete') {
|
||||||
|
dels.push(o)
|
||||||
|
} else {
|
||||||
|
ins.push(o)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// put in executable order
|
||||||
|
ins = notSoSmartSort(ins)
|
||||||
|
ins.forEach(this.onevent)
|
||||||
|
dels.forEach(this.onevent)
|
||||||
|
this.waiting = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Remove awaitedInserts and awaitedDeletes in favor of awaitedOps, as they are deprecated and do not always work
|
||||||
|
// Do this in one of the coming releases that are breaking anyway
|
||||||
/*
|
/*
|
||||||
Call this when you successfully awaited the execution of n Insert operations
|
Call this when you successfully awaited the execution of n Insert operations
|
||||||
*/
|
*/
|
||||||
@@ -2717,9 +2793,9 @@ module.exports = function (Y /* : any*/) {
|
|||||||
w.right = op.id
|
w.right = op.id
|
||||||
// exclude the effect of w in op
|
// exclude the effect of w in op
|
||||||
op.left = w.left
|
op.left = w.left
|
||||||
} else if (Y.utils.matchesId(w, op.right)) {
|
} else if (Y.utils.compareIds(w.id, op.right)) {
|
||||||
// similar..
|
// similar..
|
||||||
w.left = op.id
|
w.left = Y.utils.getLastId(op)
|
||||||
op.right = w.right
|
op.right = w.right
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2756,12 +2832,42 @@ module.exports = function (Y /* : any*/) {
|
|||||||
/* (private)
|
/* (private)
|
||||||
Try to execute the events for the waiting operations
|
Try to execute the events for the waiting operations
|
||||||
*/
|
*/
|
||||||
_tryCallEvents (n) {
|
_tryCallEvents () {
|
||||||
this.awaiting -= n
|
function notSoSmartSort (array) {
|
||||||
|
var result = []
|
||||||
|
while (array.length > 0) {
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
var independent = true
|
||||||
|
for (var j = 0; j < array.length; j++) {
|
||||||
|
if (Y.utils.matchesId(array[j], array[i].left)) {
|
||||||
|
// array[i] depends on array[j]
|
||||||
|
independent = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (independent) {
|
||||||
|
result.push(array.splice(i, 1)[0])
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
if (this.awaiting > 0) this.awaiting--
|
||||||
if (this.awaiting === 0 && this.waiting.length > 0) {
|
if (this.awaiting === 0 && this.waiting.length > 0) {
|
||||||
var ops = this.waiting
|
var ins = []
|
||||||
|
var dels = []
|
||||||
|
this.waiting.forEach(function (o) {
|
||||||
|
if (o.struct === 'Delete') {
|
||||||
|
dels.push(o)
|
||||||
|
} else {
|
||||||
|
ins.push(o)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
ins = notSoSmartSort(ins)
|
||||||
|
ins.forEach(this.onevent)
|
||||||
|
dels.forEach(this.onevent)
|
||||||
this.waiting = []
|
this.waiting = []
|
||||||
ops.forEach(this.onevent)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2830,6 +2936,20 @@ module.exports = function (Y /* : any*/) {
|
|||||||
}
|
}
|
||||||
Y.utils.copyObject = copyObject
|
Y.utils.copyObject = copyObject
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copy an operation, so that it can be manipulated.
|
||||||
|
Note: You must not change subproperties (except o.content)!
|
||||||
|
*/
|
||||||
|
function copyOperation (o) {
|
||||||
|
o = copyObject(o)
|
||||||
|
if (o.content != null) {
|
||||||
|
o.content = o.content.map(function (c) { return c })
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
Y.utils.copyOperation = copyOperation
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Defines a smaller relation on Id's
|
Defines a smaller relation on Id's
|
||||||
*/
|
*/
|
||||||
@@ -2838,6 +2958,11 @@ module.exports = function (Y /* : any*/) {
|
|||||||
}
|
}
|
||||||
Y.utils.smaller = smaller
|
Y.utils.smaller = smaller
|
||||||
|
|
||||||
|
function inDeletionRange (del, ins) {
|
||||||
|
return del.target[0] === ins[0] && del.target[1] <= ins[1] && ins[1] < del.target[1] + (del.length || 1)
|
||||||
|
}
|
||||||
|
Y.utils.inDeletionRange = inDeletionRange
|
||||||
|
|
||||||
function compareIds (id1, id2) {
|
function compareIds (id1, id2) {
|
||||||
if (id1 == null || id2 == null) {
|
if (id1 == null || id2 == null) {
|
||||||
return id1 === id2
|
return id1 === id2
|
||||||
@@ -2894,9 +3019,9 @@ module.exports = function (Y /* : any*/) {
|
|||||||
I tried to optimize this for performance, therefore no highlevel operations.
|
I tried to optimize this for performance, therefore no highlevel operations.
|
||||||
*/
|
*/
|
||||||
class SmallLookupBuffer extends Store {
|
class SmallLookupBuffer extends Store {
|
||||||
constructor (arg) {
|
constructor (arg1, arg2) {
|
||||||
// super(...arguments) -- do this when this is supported by stable nodejs
|
// super(...arguments) -- do this when this is supported by stable nodejs
|
||||||
super(arg)
|
super(arg1, arg2)
|
||||||
this.writeBuffer = createEmptyOpsArray(5)
|
this.writeBuffer = createEmptyOpsArray(5)
|
||||||
this.readBuffer = createEmptyOpsArray(10)
|
this.readBuffer = createEmptyOpsArray(10)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user