added ace example

This commit is contained in:
Kevin Jahns 2016-02-03 11:37:10 +01:00
parent 5e295b80d7
commit 6904e6018d
10 changed files with 6162 additions and 154 deletions

22
Examples/Ace/index.html Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<style type="text/css" media="screen">
#editor {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
</style>
</head>
<body>
<div id="editor"></div>
<script src="../bower_components/yjs/y.es6"></script>
<script src="../bower_components/ace-builds/src/ace.js"></script>
<script src="./index.js"></script>
</body>
</html>

28
Examples/Ace/index.js Normal file
View File

@ -0,0 +1,28 @@
/* global Y, ace */
// initialize a shared object. This function call returns a promise!
Y({
db: {
name: 'memory'
},
connector: {
name: 'websockets-client',
room: 'ace-example-dev'
// debug: true
// url: 'http://127.0.0.1:2345'
},
sourceDir: '/bower_components',
share: {
ace: 'Text' // y.share.textarea is of type Y.Text
}
}).then(function (y) {
window.y = y
// bind the textarea to a shared text element
var editor = ace.edit('editor')
editor.setTheme('ace/theme/monokai')
editor.getSession().setMode('ace/mode/javascript')
y.share.ace.bindAce(editor)
// thats it..
})

View File

@ -139,7 +139,7 @@
<!-- Include the Quill library -->
<script src="../bower_components/quill/dist/quill.js"></script>
<script src="../bower_components/yjs/y.js"></script>
<script src="../bower_components/yjs/y.es6"></script>
<script src="./index.js"></script>
</body>
</html>

View File

@ -4,13 +4,13 @@
Y({
db: {
name: 'memory',
namespace: 'richtext-example20'
name: 'indexeddb',
idbVersion: 20
},
connector: {
name: 'websockets-client',
room: 'richtext-example20'
// debug: true,
room: 'richtext-example24',
debug: true
// url: 'http://127.0.0.1:1234'
},
sourceDir: '/bower_components',

View File

@ -18,6 +18,8 @@
"y-websockets-client": "~0.7.10",
"y-text": "~0.7.1",
"y-indexeddb": "~0.7.1",
"quill": "~0.20.1"
"quill": "~0.20.1",
"ace": "~1.2.3",
"ace-builds": "~1.2.3"
}
}

View File

@ -1,11 +1,14 @@
# ![Yjs](http://y-js.org/images/yjs.png)
Yjs is a framework for optimistic concurrency control and automatic conflict resolution on shared data. The framework provides similar functionality as [ShareJs] and [OpenCoweb], but it implements a new algorithm that also support peer-to-peer communication protocols. Yjs was designed to handle concurrent actions on arbitrary data types like Text, Json, and XML. We also provide support for storing and manipulating your shared data offline. For more information and demo applications visit our [homepage](http://y-js.org/).
Yjs is a framework for optimistic concurrency control and automatic conflict resolution on shared data. The framework provides similar functionality as [ShareJs] and [OpenCoweb], but supports peer-to-peer communication protocols by default. Yjs was designed to handle concurrent actions on arbitrary data like Text, Json, and XML. We also provide support for storing and manipulating your shared data offline. For more information and demo applications visit our [homepage](http://y-js.org/).
**NOTE** This project is currently migrating. So there may exist some information that is not true anymore..
You can create you own shared types easily. Therefore, you can take matters into your own hand by defining the meaning of the shared types, and ensure that it is valid, while Yjs ensures data consistency (everyone will eventually end up with the same data). We already provide data types for
You can create you own shared types easily.
Therefore, you can design the sturcture of your custom type,
and ensure data validity, while Yjs ensures data consistency (everyone will eventually end up with the same data).
We already provide data types for
| Name | Description |
|----------|-------------------|
@ -15,9 +18,11 @@ You can create you own shared types easily. Therefore, you can take matters into
|[text](https://github.com/y-js/y-text) | Collaborate on text. Supports two way binding to textareas, input elements, or HTML elements (e.g. *h1*, or *p*)|
|[richtext](https://github.com/y-js/y-richtext) | Collaborate on rich text. Supports two way binding to several editors|
Unlike other frameworks, Yjs supports P2P message propagation and is not bound to a specific communication protocol. Therefore, Yjs is extremely scalable and can be used in a wide range of application scenarios.
Yjs supports P2P message propagation, and not bound to a specific communication protocol. Therefore, Yjs is extremely scalable and can be used in a wide range of application scenarios.
We support several communication protocols as so called *Connectors*. You can create your own connector too - read [this wiki page](https://github.com/y-js/yjs/wiki/Custom-Connectors). Currently, we support the following communication protocols:
We support several communication protocols as so called *Connectors*.
You can create your own connector too - read [this wiki page](https://github.com/y-js/yjs/wiki/Custom-Connectors).
Currently, we support the following communication protocols:
|Name | Description |
|----------------|-----------------------------------|

562
y.es6
View File

@ -581,6 +581,13 @@ module.exports = function (Y /* :any */) {
*/
constructor (y, opts) {
this.y = y
var os = this
this.userId = null
var resolve
this.userIdPromise = new Promise(function (r) {
resolve = r
})
this.userIdPromise.resolve = resolve
// whether to broadcast all applied operations (insert & delete hook)
this.forwardAppliedOperations = false
// E.g. this.listenersById[id] : Array<Listener>
@ -602,32 +609,42 @@ module.exports = function (Y /* :any */) {
// TODO: Use ES7 Weak Maps. This way types that are no longer user,
// wont be kept in memory.
this.initializedTypes = {}
this.whenUserIdSetListener = null
this.waitingTransactions = []
this.transactionInProgress = false
this.transactionIsFlushed = false
if (typeof YConcurrency_TestingMode !== 'undefined') {
this.executeOrder = []
}
this.gc1 = [] // first stage
this.gc2 = [] // second stage -> after that, remove the op
this.gcTimeout = opts.gcTimeout || 5000
var os = this
this.gcTimeout = opts.gcTimeout || 50000
function garbageCollect () {
return new Promise((resolve) => {
os.requestTransaction(function * () {
if (os.y.connector != null && os.y.connector.isSynced) {
for (var i = 0; i < os.gc2.length; i++) {
var oid = os.gc2[i]
yield* this.garbageCollectOperation(oid)
}
os.gc2 = os.gc1
os.gc1 = []
}
return os.whenTransactionsFinished().then(function () {
if (os.gc1.length > 0 || os.gc2.length > 0) {
return new Promise((resolve) => {
os.requestTransaction(function * () {
if (os.y.connector != null && os.y.connector.isSynced) {
for (var i = 0; i < os.gc2.length; i++) {
var oid = os.gc2[i]
yield* this.garbageCollectOperation(oid)
}
os.gc2 = os.gc1
os.gc1 = []
}
// TODO: Use setInterval here instead (when garbageCollect is called several times there will be several timeouts..)
if (os.gcTimeout > 0) {
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
}
resolve()
})
})
} else {
// TODO: see above
if (os.gcTimeout > 0) {
os.gcInterval = setTimeout(garbageCollect, os.gcTimeout)
}
resolve()
})
return Promise.resolve()
}
})
}
this.garbageCollect = garbageCollect
@ -706,26 +723,20 @@ module.exports = function (Y /* :any */) {
this.gcInterval = null
}
setUserId (userId) {
var self = this
return new Promise(function (resolve) {
if (!this.userIdPromise.inProgress) {
this.userIdPromise.inProgress = true
var self = this
self.requestTransaction(function * () {
self.userId = userId
var state = yield* this.getState(userId)
self.opClock = state.clock
if (self.whenUserIdSetListener != null) {
self.whenUserIdSetListener()
self.whenUserIdSetListener = null
}
resolve()
self.userIdPromise.resolve(userId)
})
})
}
return this.userIdPromise
}
whenUserIdSet (f) {
if (this.userId != null) {
f()
} else {
this.whenUserIdSetListener = f
}
this.userIdPromise.then(f)
}
getNextOpId () {
if (this._nextUserId != null) {
@ -747,8 +758,10 @@ module.exports = function (Y /* :any */) {
apply (ops) {
for (var key in ops) {
var o = ops[key]
var required = Y.Struct[o.struct].requiredOps(o)
this.whenOperationsExist(required, o)
if (o.id == null || o.id[0] !== this.y.connector.userId) {
var required = Y.Struct[o.struct].requiredOps(o)
this.whenOperationsExist(required, o)
}
}
}
/*
@ -919,29 +932,36 @@ module.exports = function (Y /* :any */) {
return Promise.resolve()
}
}
// Check if there is another transaction request.
// * the last transaction is always a flush :)
getNextRequest () {
if (this.waitingTransactions.length === 0) {
this.transactionInProgress = false
if (this.transactionsFinished != null) {
this.transactionsFinished.resolve()
this.transactionsFinished = null
if (this.transactionIsFlushed) {
this.transactionInProgress = false
this.transactionIsFlushed = false
if (this.transactionsFinished != null) {
this.transactionsFinished.resolve()
this.transactionsFinished = null
}
return null
} else {
this.transactionIsFlushed = true
return function * () {
yield* this.flush()
}
}
return null
} else {
this.transactionIsFlushed = false
return this.waitingTransactions.shift()
}
}
requestTransaction (makeGen/* :any */, callImmediately) {
if (true || callImmediately) { // TODO: decide whether this is ok or not..
this.waitingTransactions.push(makeGen)
if (!this.transactionInProgress) {
this.transactionInProgress = true
this.waitingTransactions.push(makeGen)
if (!this.transactionInProgress) {
this.transactionInProgress = true
if (false || callImmediately) { // TODO: decide whether this is ok or not..
this.transact(this.getNextRequest())
}
} else {
this.waitingTransactions.push(makeGen)
if (!this.transactionInProgress) {
this.transactionInProgress = true
} else {
var self = this
setTimeout(function () {
self.transact(self.getNextRequest())
@ -1082,6 +1102,19 @@ module.exports = function (Y/* :any */) {
execute: function *(op) {
var i // loop counter
var distanceToOrigin = i = yield* Struct.Insert.getDistanceToOrigin.call(this, op) // most cases: 0 (starts from 0)
if (op.origin != null) {
// we save in origin that op originates in it
// we need that later when we eventually garbage collect origin (see transaction)
var origin = yield* this.getOperation(op.origin)
if (origin.originOf == null) {
origin.originOf = []
}
origin.originOf.push(op.id)
yield* this.setOperation(origin)
}
// now we begin to insert op in the list of insertions..
var o
var parent
var start
@ -1532,86 +1565,125 @@ module.exports = function (Y/* :any */) {
/*
Mark an operation as deleted&gc'd
*/
* markGarbageCollected (id) {
* markGarbageCollected (id, len) {
// this.mem.push(["gc", id]);
var n = yield* this.markDeleted(id)
if (!n.gc) {
if (n.id[1] < id[1]) {
// un-extend left
var newlen = n.len - (id[1] - n.id[1])
n.len -= newlen
yield* this.ds.put(n)
n = {id: id, len: newlen, gc: false}
yield* this.ds.put(n)
}
// get prev&next before adding a new operation
var prev = yield* this.ds.findPrev(id)
var next = yield* this.ds.findNext(id)
if (id[1] < n.id[1] + n.len - 1) {
// un-extend right
yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false})
n.len = 1
}
// set gc'd
n.gc = true
// can extend left?
if (
prev != null &&
prev.gc &&
Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id)
) {
prev.len += n.len
yield* this.ds.delete(n.id)
n = prev
// ds.put n here?
}
// can extend right?
if (
next != null &&
next.gc &&
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id)
) {
n.len += next.len
yield* this.ds.delete(next.id)
}
var n = yield* this.markDeleted(id, len)
if (n.id[1] < id[1] && !n.gc) {
// un-extend left
var newlen = n.len - (id[1] - n.id[1])
n.len -= newlen
yield* this.ds.put(n)
n = {id: id, len: newlen, gc: false}
yield* this.ds.put(n)
}
// get prev&next before adding a new operation
var prev = yield* this.ds.findPrev(id)
var next = yield* this.ds.findNext(id)
if (id[1] < n.id[1] + n.len - len && !n.gc) {
// un-extend right
yield* this.ds.put({id: [id[0], id[1] + 1], len: n.len - 1, gc: false})
n.len = 1
}
// set gc'd
n.gc = true
// can extend left?
if (
prev != null &&
prev.gc &&
Y.utils.compareIds([prev.id[0], prev.id[1] + prev.len], n.id)
) {
prev.len += n.len
yield* this.ds.delete(n.id)
n = prev
// ds.put n here?
}
// can extend right?
if (
next != null &&
next.gc &&
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id)
) {
n.len += next.len
yield* this.ds.delete(next.id)
}
yield* this.ds.put(n)
}
/*
Mark an operation as deleted.
returns the delete node
*/
* markDeleted (id) {
* markDeleted (id, length) {
if (length == null) {
length = 1
// debugger // TODO!!
}
// this.mem.push(["del", id]);
var n = yield* this.ds.findWithUpperBound(id)
if (n != null && n.id[0] === id[0]) {
if (n.id[1] <= id[1] && id[1] < n.id[1] + n.len) {
// already deleted
return n
} else if (n.id[1] + n.len === id[1] && !n.gc) {
// can extend existing deletion
n.len++
if (n.id[1] <= id[1] && id[1] <= n.id[1] + n.len) {
// id is in n's range
var diff = id[1] + length - (n.id[1] + n.len) // overlapping right
if (diff > 0) {
// id+length overlaps n
if (!n.gc) {
n.len += diff
} else {
diff = n.id[1] + n.len - id[1] // overlapping left (id till n.end)
if (diff < length) {
// a partial deletion
n = {id: [id[0], id[1] + diff], len: length - diff, gc: false}
yield* this.ds.put(n)
} else {
// already gc'd
throw new Error('Cannot happen! (it dit though.. :()')
// return n
}
}
} else {
// no overlapping, already deleted
return n
}
} else {
// cannot extend left
n = {id: id, len: 1, gc: false}
yield* this.ds.put(n)
// cannot extend left (there is no left!)
n = {id: id, len: length, gc: false}
yield* this.ds.put(n) // TODO: you double-put !!
}
} else {
// cannot extend left
n = {id: id, len: 1, gc: false}
n = {id: id, len: length, gc: false}
yield* this.ds.put(n)
}
// can extend right?
var next = yield* this.ds.findNext(n.id)
if (
next != null &&
Y.utils.compareIds([n.id[0], n.id[1] + n.len], next.id) &&
!next.gc
n.id[0] === next.id[0] &&
n.id[1] + n.len >= next.id[1]
) {
n.len = n.len + next.len
yield* this.ds.delete(next.id)
diff = n.id[1] + n.len - next.id[1] // from next.start to n.end
if (next.gc) {
if (diff >= 0) {
n.len -= diff
if (diff > next.len) {
// need to create another deletion after $next
// TODO: (may not be necessary, because this case shouldn't happen!)
// also this is supposed to return a deletion range. which one to choose? n or the new created deletion?
throw new Error('This case is not handled (on purpose!)')
}
} // else: everything is fine :)
} else {
if (diff >= 0) {
if (diff > next.len) {
// may be neccessary to extend next.next!
// TODO: (may not be necessary, because this case shouldn't happen!)
throw new Error('This case is not handled (on purpose!)')
}
n.len += next.len - diff
yield* this.ds.delete(next.id)
}
}
}
yield* this.ds.put(n)
return n
@ -1640,19 +1712,9 @@ module.exports = function (Y/* :any */) {
*/
* garbageCollectOperation (id) {
this.store.addToDebug('yield* this.garbageCollectOperation(', id, ')')
// check to increase the state of the respective user
var state = yield* this.getState(id[0])
if (state.clock === id[1]) {
state.clock++
// also check if more expected operations were gc'd
yield* this.checkDeleteStoreForState(state)
// then set the state
yield* this.setState(state)
}
yield* this.markGarbageCollected(id)
// if op exists, then clean that mess up..
var o = yield* this.getOperation(id)
yield* this.markGarbageCollected(id, 1) // always mark gc'd
// if op exists, then clean that mess up..
if (o != null) {
/*
if (!o.deleted) {
@ -1672,23 +1734,31 @@ module.exports = function (Y/* :any */) {
if (o.right != null) {
var right = yield* this.getOperation(o.right)
right.left = o.left
if (Y.utils.compareIds(right.origin, o.id)) { // rights origin is o
if (o.originOf != null && o.originOf.length > 0) {
// find new origin of right ops
// origin is the first left deleted operation
var neworigin = o.left
var neworigin_ = null
while (neworigin != null) {
var neworigin_ = yield* this.getOperation(neworigin)
neworigin_ = yield* this.getOperation(neworigin)
if (neworigin_.deleted) {
break
}
neworigin = neworigin_.left
}
// reset origin of all right ops (except first right - duh!),
/* ** The following code does not rely on the the originOf property **
I recently added originOf to all Insert Operations (see Struct.Insert.execute),
which saves which operations originate in a Insert operation.
Garbage collecting without originOf is more memory efficient, but is nearly impossible for large texts, or lists!
But I keep this code for now
```
// reset origin of right
right.origin = neworigin
// reset origin of all right ops (except first right - duh!),
// until you find origin pointer to the left of o
// search until you find origin pointer to the left of o
if (right.right != null) {
var i = yield* this.getOperation(right.right)
var ids = [o.id, o.right]
@ -1709,9 +1779,41 @@ module.exports = function (Y/* :any */) {
}
}
}
} /* otherwise, rights origin is to the left of o,
then there is no right op (from o), that origins in o */
yield* this.setOperation(right)
```
*/
// ** Now the new implementation starts **
// reset neworigin of all originOf[*]
for (var _i in o.originOf) {
var originsIn = yield* this.getOperation(o.originOf[_i])
if (originsIn != null) {
originsIn.origin = neworigin
yield* this.setOperation(originsIn)
}
}
if (neworigin != null) {
if (neworigin_.originOf == null) {
neworigin_.originOf = o.originOf
} else {
neworigin_.originOf = o.originOf.concat(neworigin_.originOf)
}
yield* this.setOperation(neworigin_)
}
// we don't need to set right here, because
// right should be in o.originOf => it is set it the previous for loop
} else {
// we didn't need to reset the origin of right
// so we have to set right here
yield* this.setOperation(right)
}
// o may originate in another operation.
// Since o is deleted, we have to reset o.origin's `originOf` property
if (o.origin != null) {
var origin = yield* this.getOperation(o.origin)
origin.originOf = origin.originOf.filter(function (_id) {
return !Y.utils.compareIds(id, _id)
})
yield* this.setOperation(origin)
}
}
if (o.parent != null) {
@ -1756,9 +1858,7 @@ module.exports = function (Y/* :any */) {
* applyDeleteSet (ds) {
var deletions = []
function createDeletions (user, start, len, gc) {
for (var c = start; c < start + len; c++) {
deletions.push([user, c, gc])
}
deletions.push([user, start, len, gc])
}
for (var user in ds) {
@ -1813,23 +1913,46 @@ module.exports = function (Y/* :any */) {
}
for (var i = 0; i < deletions.length; i++) {
var del = deletions[i]
var id = [del[0], del[1]]
// always try to delete..
var addOperation = yield* this.deleteOperation(id)
if (addOperation) {
// TODO:.. really .. here? You could prevent calling all these functions in operationAdded
yield* this.store.operationAdded(this, {struct: 'Delete', target: id})
var state = yield* this.getState(del[0])
if (del[1] < state.clock) {
for (let c = del[1]; c < del[1] + del[2]; c++) {
var id = [del[0], c]
var addOperation = yield* this.deleteOperation(id)
if (addOperation) {
// TODO:.. really .. here? You could prevent calling all these functions in operationAdded
yield* this.store.operationAdded(this, {struct: 'Delete', target: id})
}
if (del[3]) {
// gc
yield* this.garbageCollectOperation(id)
}
}
} else {
if (del[3]) {
yield* this.markGarbageCollected([del[0], del[1]], del[2])
} else {
yield* this.markDeleted([del[0], del[1]], del[2])
}
}
if (del[2]) {
// gc
yield* this.garbageCollectOperation(id)
if (del[3]) {
// check to increase the state of the respective user
if (state.clock >= del[1] && state.clock < del[1] + del[2]) {
state.clock = del[1] + del[2]
// also check if more expected operations were gc'd
yield* this.checkDeleteStoreForState(state) // TODO: unneccessary?
// then set the state
yield* this.setState(state)
}
}
}
if (this.store.forwardAppliedOperations) {
var ops = deletions.map(function (d) {
return {struct: 'Delete', target: [d[0], d[1]]}
})
this.store.y.connector.broadcastOps(ops)
for (let c = del[1]; c < del[1] + del[2]; c++) {
var ops = deletions.map(function (d) {
return {struct: 'Delete', target: [d[0], c]} // TODO: implement Delete with deletion length!
})
this.store.y.connector.broadcastOps(ops)
}
}
}
* isGarbageCollected (id) {
@ -2073,6 +2196,11 @@ module.exports = function (Y/* :any */) {
return op
}
*/
* flush () {
yield* this.os.flush()
yield* this.ss.flush()
yield* this.ds.flush()
}
}
Y.Transaction = TransactionInterface
}
@ -2100,7 +2228,7 @@ module.exports = function (Y/* :any */) {
```
The structures usually work asynchronously (you have to wait for the
database request to finish). EventHandler will help you to make your type
database request to finish). EventHandler helps you to make your type
synchronous.
*/
module.exports = function (Y /* : any*/) {
@ -2287,7 +2415,7 @@ module.exports = function (Y /* : any*/) {
Defines a smaller relation on Id's
*/
function smaller (a, b) {
return a[0] < b[0] || (a[0] === b[0] && a[1] < b[1])
return a[0] < b[0] || (a[0] === b[0] && (a[1] < b[1] || typeof a[1] < typeof b[1]))
}
Y.utils.smaller = smaller
@ -2305,6 +2433,159 @@ module.exports = function (Y /* : any*/) {
}
}
Y.utils.compareIds = compareIds
function createEmptyOpsArray (n) {
var a = new Array(n)
for (var i = 0; i < a.length; i++) {
a[i] = {
id: [null, null]
}
}
return a
}
function createSmallLookupBuffer (Store) {
/*
This buffer implements a very small buffer that temporarily stores operations
after they are read / before they are written.
The buffer basically implements FIFO. Often requested lookups will be re-queued every time they are looked up / written.
It can speed up lookups on Operation Stores and State Stores. But it does not require notable use of memory or processing power.
Good for os and ss, bot not for ds (because it often uses methods that require a flush)
I tried to optimize this for performance, therefore no highlevel operations.
*/
class SmallLookupBuffer extends Store {
constructor () {
super(...arguments)
this.writeBuffer = createEmptyOpsArray(5)
this.readBuffer = createEmptyOpsArray(10)
}
* find (id) {
var i, r
for (i = this.readBuffer.length - 1; i >= 0; i--) {
r = this.readBuffer[i]
// we don't have to use compareids, because id is always defined!
if (r.id[1] === id[1] && r.id[0] === id[0]) {
// found r
// move r to the end of readBuffer
for (; i < this.readBuffer.length - 1; i++) {
this.readBuffer[i] = this.readBuffer[i + 1]
}
this.readBuffer[this.readBuffer.length - 1] = r
return r
}
}
var o
for (i = this.writeBuffer.length - 1; i >= 0; i--) {
r = this.writeBuffer[i]
if (r.id[1] === id[1] && r.id[0] === id[0]) {
o = r
break
}
}
if (i < 0) {
// did not reach break in last loop
// read id and put it to the end of readBuffer
o = yield* super.find(id)
}
if (o != null) {
for (i = 0; i < this.readBuffer.length - 1; i++) {
this.readBuffer[i] = this.readBuffer[i + 1]
}
this.readBuffer[this.readBuffer.length - 1] = o
}
return o
}
* put (o) {
var id = o.id
var i, r // helper variables
for (i = this.writeBuffer.length - 1; i >= 0; i--) {
r = this.writeBuffer[i]
if (r.id[1] === id[1] && r.id[0] === id[0]) {
// is already in buffer
// forget r, and move o to the end of writeBuffer
for (; i < this.writeBuffer.length - 1; i++) {
this.writeBuffer[i] = this.writeBuffer[i + 1]
}
this.writeBuffer[this.writeBuffer.length - 1] = o
break
}
}
if (i < 0) {
// did not reach break in last loop
// write writeBuffer[0]
var write = this.writeBuffer[0]
if (write.id[0] !== null) {
yield* super.put(write)
}
// put o to the end of writeBuffer
for (i = 0; i < this.writeBuffer.length - 1; i++) {
this.writeBuffer[i] = this.writeBuffer[i + 1]
}
this.writeBuffer[this.writeBuffer.length - 1] = o
}
// check readBuffer for every occurence of o.id, overwrite if found
// whether found or not, we'll append o to the readbuffer
for (i = 0; i < this.readBuffer.length - 1; i++) {
r = this.readBuffer[i + 1]
if (r.id[1] === id[1] && r.id[0] === id[0]) {
this.readBuffer[i] = o
} else {
this.readBuffer[i] = r
}
}
this.readBuffer[this.readBuffer.length - 1] = o
}
* delete (id) {
var i, r
for (i = 0; i < this.readBuffer.length; i++) {
r = this.readBuffer[i]
if (r.id[1] === id[1] && r.id[0] === id[0]) {
this.readBuffer[i] = {
id: [null, null]
}
}
}
yield* this.flush()
yield* super.delete(id)
}
* findWithLowerBound () {
yield* this.flush()
return yield* super.findWithLowerBound.apply(this, arguments)
}
* findWithUpperBound () {
yield* this.flush()
return yield* super.findWithUpperBound.apply(this, arguments)
}
* findNext () {
yield* this.flush()
return yield* super.findNext.apply(this, arguments)
}
* findPrev () {
yield* this.flush()
return yield* super.findPrev.apply(this, arguments)
}
* iterate () {
yield* this.flush()
yield* super.iterate.apply(this, arguments)
}
* flush () {
for (var i = 0; i < this.writeBuffer.length; i++) {
var write = this.writeBuffer[i]
if (write.id[0] !== null) {
yield* super.put(write)
this.writeBuffer[i] = {
id: [null, null]
}
}
}
}
}
return SmallLookupBuffer
}
Y.utils.createSmallLookupBuffer = createSmallLookupBuffer
}
},{}],7:[function(require,module,exports){
@ -2420,9 +2701,9 @@ class YConfig {
share: {[key: string]: any};
*/
constructor (opts, callback) {
this.options = opts
this.db = new Y[opts.db.name](this, opts.db)
this.connector = new Y[opts.connector.name](this, opts.connector)
this.options = opts
}
init (callback) {
var opts = this.options
@ -2441,7 +2722,8 @@ class YConfig {
}
share[propertyname] = yield* this.getType(id)
}
setTimeout(callback, 0)
this.store.whenTransactionsFinished()
.then(callback)
})
}
isConnected () {

File diff suppressed because one or more lines are too long

5673
y.js

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long