2016-02-23 15:41:22 +01:00

318 lines
12 KiB
JavaScript

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/* global Y */
'use strict'
function extend (Y) {
Y.requestModules(['memory']).then(function () {
class Store {
constructor (transaction, name) {
this.store = transaction.objectStore(name)
}
* find (id) {
return yield this.store.get(id)
}
* put (v) {
yield this.store.put(v)
}
* delete (id) {
yield this.store.delete(id)
}
* findWithLowerBound (start) {
return yield this.store.openCursor(window.IDBKeyRange.lowerBound(start))
}
* findWithUpperBound (end) {
return yield this.store.openCursor(window.IDBKeyRange.upperBound(end), 'prev')
}
* findNext (id) {
return yield* this.findWithLowerBound([id[0], id[1] + 1])
}
* findPrev (id) {
return yield* this.findWithUpperBound([id[0], id[1] - 1])
}
* iterate (t, start, end, gen) {
var range = null
if (start != null && end != null) {
range = window.IDBKeyRange.bound(start, end)
} else if (start != null) {
range = window.IDBKeyRange.lowerBound(start)
} else if (end != null) {
range = window.IDBKeyRange.upperBound(end)
}
var cursorResult
if (range != null) {
cursorResult = this.store.openCursor(range)
} else {
cursorResult = this.store.openCursor()
}
while ((yield cursorResult) != null) {
yield* gen.call(t, cursorResult.result.value)
cursorResult.result.continue()
}
}
* flush () {}
}
function createStoreClone (Store) {
class Clone extends Store {
constructor () {
super(...arguments)
this.buffer = []
this._copyTo = null
}
// copy to this store
// it may be neccessary to reset this every time you create a transaction
copyTo (store) {
this._copyTo = store
return this
}
* put (v, dontCopy) {
if (!dontCopy) {
this.buffer.push(this._copyTo.put(v))
}
yield* super.put(v)
}
* delete (id) {
this.buffer.push(this._copyTo.delete(id))
yield* super.delete(id)
}
* flush () {
yield* super.flush()
for (var i = 0; i < this.buffer.length; i++) {
yield* this.buffer[i]
}
yield* this._copyTo.flush()
}
}
return Clone
}
Y.utils.createStoreClone = createStoreClone
var BufferedStore = Y.utils.createSmallLookupBuffer(Store)
var ClonedStore = Y.utils.createStoreClone(Y.utils.RBTree)
class Transaction extends Y.Transaction {
constructor (store) {
super(store)
var transaction = store.db.transaction(['OperationStore', 'StateStore', 'DeleteStore'], 'readwrite')
this.store = store
this.ss = new BufferedStore(transaction, 'StateStore')
this.os = new BufferedStore(transaction, 'OperationStore')
this._ds = new BufferedStore(transaction, 'DeleteStore')
this.ds = store.dsClone.copyTo(this._ds)
}
}
class OperationStore extends Y.AbstractDatabase {
constructor (y, options) {
super(y, options)
// dsClone is persistent over transactions!
// _ds is not
this.dsClone = new ClonedStore()
if (options == null) {
options = {}
}
this.options = options
if (options.namespace == null) {
if (y.options.connector.room == null) {
throw new Error('IndexedDB: expect a string (options.namespace)! (you can also skip this step if your connector has a room property)')
} else {
options.namespace = y.options.connector.room
}
}
if (options.idbVersion != null) {
this.idbVersion = options.idbVersion
} else {
this.idbVersion = 5
}
var store = this
// initialize database!
this.requestTransaction(function * () {
store.db = yield window.indexedDB.open(options.namespace, store.idbVersion)
})
if (options.cleanStart) {
delete window.localStorage[JSON.stringify(['Yjs_indexeddb', options.namespace])]
this.requestTransaction(function * () {
yield this.os.store.clear()
yield this._ds.store.clear()
yield this.ss.store.clear()
})
}
this.whenUserIdSet(function (userid) {
if (window.localStorage[JSON.stringify(['Yjs_indexeddb', options.namespace])] == null) {
window.localStorage[JSON.stringify(['Yjs_indexeddb', options.namespace])] = JSON.stringify([userid, 0])
}
})
this.requestTransaction(function * () {
// this should be executed after the previous two defined transactions
// after we computed the upgrade event (see `yield indexedDB.open(..)`), we can check if userid is still stored on localstorage
var uid = window.localStorage[JSON.stringify(['Yjs_indexeddb', options.namespace])]
if (uid != null) {
store.setUserId(uid)
var nextuid = JSON.parse(uid)
nextuid[1] = nextuid[1] + 1
window.localStorage[JSON.stringify(['Yjs_indexeddb', options.namespace])] = JSON.stringify(nextuid)
}
// copy from persistent Store to not persistent StoreClone. (there could already be content in Store)
yield* this._ds.iterate(this, null, null, function * (o) {
yield* this.ds.put(o, true)
})
})
this.operationAddedNamespace = JSON.stringify(['__YJS__', this.options.namespace])
var operationsToAdd = []
window.addEventListener('storage', function (event) {
if (event.key === store.operationAddedNamespace) {
operationsToAdd.push(JSON.parse(event.newValue))
var op, i // helper variables
if (operationsToAdd.length === 1) {
store.requestTransaction(function * () {
/* about nextRound:
if op is not a delete, we retrieve it again from the db
then it could be true that op.left is not yet added to store
- but the types _change function expects that it is..
In this case left has to be executed first
What is left to say: we only put ready to execute ops in nextRound!
TODO: implement a smart buffer in eventHelper!!!!!
*/
var nextRound = []
for (i = 0; i < operationsToAdd.length; i++) {
op = operationsToAdd[i]
if (op.struct !== 'Delete') {
op = yield* this.getOperation(op.id)
while (op.left != null) {
var left = yield* this.getOperation(op.left)
if (!left.deleted) {
break
}
op.left = left.left
}
}
nextRound.push(op)
}
operationsToAdd = []
while (nextRound.length > 0) {
var add = nextRound
nextRound = []
for (i = 0; i < add.length; i++) {
op = add[i]
if (op.struct === 'Insert') {
var ready = true
for (let j = i + 1; j < add.length; j++) {
let _op = add[j]
if (Y.utils.compareIds(_op.id, op.left)) {
ready = false
break
}
}
if (ready) {
for (let j = 0; j < nextRound.length; j++) {
let _op = add[j]
if (Y.utils.compareIds(_op.id, op.left)) {
ready = false
break
}
}
}
if (!ready) {
// it is necessary to execute left first
nextRound.push(op)
continue
}
}
yield* this.store.operationAdded(this, op, true)
}
}
})
}
}
}, false)
}
* operationAdded (transaction, op, noAdd) {
yield* super.operationAdded(transaction, op)
if (!noAdd) {
window.localStorage[this.operationAddedNamespace] = JSON.stringify(op)
}
}
transact (makeGen) {
var transaction = this.db != null ? new Transaction(this) : null
var store = this
var gen = makeGen.call(transaction)
handleTransactions(gen.next())
function handleTransactions (result) {
var request = result.value
if (result.done) {
makeGen = store.getNextRequest()
if (makeGen != null) {
if (transaction == null && store.db != null) {
transaction = new Transaction(store)
}
gen = makeGen.call(transaction)
handleTransactions(gen.next())
} // else no transaction in progress!
return
}
console.log('new request', request.source != null ? request.source.name : null)
if (request.constructor === window.IDBRequest) {
request.onsuccess = function () {
var res = request.result
if (res != null && res.constructor === window.IDBCursorWithValue) {
res = res.value
}
handleTransactions(gen.next(res))
}
request.onerror = function (err) {
gen.throw(err)
}
} else if (request.constructor === window.IDBCursor) {
request.onsuccess = function () {
handleTransactions(gen.next(request.result != null ? request.result.value : null))
}
request.onerror = function (err) {
gen.throw(err)
}
} else if (request.constructor === window.IDBOpenDBRequest) {
request.onsuccess = function (event) {
var db = event.target.result
handleTransactions(gen.next(db))
}
request.onerror = function () {
gen.throw("Couldn't open IndexedDB database!")
}
request.onupgradeneeded = function (event) {
var db = event.target.result
try {
delete window.localStorage[JSON.stringify(['Yjs_indexeddb', store.options.namespace])]
db.deleteObjectStore('OperationStore')
db.deleteObjectStore('DeleteStore')
db.deleteObjectStore('StateStore')
} catch (e) {}
db.createObjectStore('OperationStore', {keyPath: 'id'})
db.createObjectStore('DeleteStore', {keyPath: 'id'})
db.createObjectStore('StateStore', {keyPath: 'id'})
}
} else {
gen.throw('You must not yield this type!')
}
}
}
// TODO: implement "free"..
* destroy () {
this.db.close()
yield window.indexedDB.deleteDatabase(this.options.namespace)
}
}
Y.extend('indexeddb', OperationStore)
})
}
module.exports = extend
if (typeof Y !== 'undefined') {
extend(Y)
}
},{}]},{},[1])