implemented module loader for yjs

This commit is contained in:
Kevin Jahns 2015-11-07 22:12:48 +01:00
parent 138afe39dc
commit 6dc347642b
22 changed files with 2299 additions and 2579 deletions

View File

@ -21,31 +21,10 @@ module.exports = function (gulp, helperOptions) {
options.regenerator = false options.regenerator = false
// TODO: include './node_modules/gulp-babel/node_modules/babel-core/node_modules/regenerator/runtime.js' // TODO: include './node_modules/gulp-babel/node_modules/babel-core/node_modules/regenerator/runtime.js'
} }
var concatOrder = [
'y.js',
'Connector.js',
'Database.js',
'Transaction.js',
'Struct.js',
'Utils.js',
'Databases/RedBlackTree.js',
'Databases/Memory.js',
'Databases/IndexedDB.js',
'Connectors/Test.js',
'Types/Array.js',
'Types/Map.js',
'Types/TextBind.js'
]
var yjsfiles = concatOrder.map(function (f) {
return '../yjs/src/' + f
})
var files = { var files = {
dist: helperOptions.polyfills.concat(helperOptions.files.map(function (f) { dist: helperOptions.entry,
return 'src/' + f specs: helperOptions.specs,
})), src: './src/**/*.js'
test: ['../yjs/src/Helper.spec.js'].concat(yjsfiles).concat(helperOptions.files.map(function (f) {
return 'src/' + f
}).concat(['src/' + options.testfiles]))
} }
var babelOptions = { var babelOptions = {
@ -54,38 +33,10 @@ module.exports = function (gulp, helperOptions) {
experimental: true experimental: true
} }
if (options.regenerator) { if (options.regenerator) {
files.test = helperOptions.polyfills.concat(files.test) files.specs = helperOptions.polyfills.concat(files.specs)
} else { } else {
babelOptions.blacklist = 'regenerator' babelOptions.blacklist = 'regenerator'
} }
// babelOptions.blacklist = 'regenerator'
gulp.task('dist', ['build:dist'], function () {
function createDist (pipe) {
return pipe
.pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true})))
.pipe($.concat(options.targetName))
.pipe($.if(!options.debug && options.regenerator, $.uglify()))
.pipe($.if(options.debug, $.sourcemaps.write('.')))
.pipe(gulp.dest('./dist/'))
}
var pipe
if (options.browserify || true) {
var browserify = require('browserify')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
pipe = browserify({
entries: 'build/' + options.targetName,
debug: options.debug
}).bundle()
.pipe(source(options.targetName))
.pipe(buffer())
} else {
pipe = gulp.src('build/' + options.targetName)
}
return createDist(pipe)
})
gulp.task('dist', function () { gulp.task('dist', function () {
var browserify = require('browserify') var browserify = require('browserify')
@ -99,7 +50,7 @@ module.exports = function (gulp, helperOptions) {
.pipe(source(options.targetName)) .pipe(source(options.targetName))
.pipe(buffer()) .pipe(buffer())
.pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true}))) .pipe($.if(options.debug, $.sourcemaps.init({loadMaps: true})))
.pipe($.concat(options.targetName)) .pipe($.if(!options.debug && options.regenerator, $.babel(babelOptions)))
.pipe($.if(!options.debug && options.regenerator, $.uglify())) .pipe($.if(!options.debug && options.regenerator, $.uglify()))
.pipe($.if(options.debug, $.sourcemaps.write('.'))) .pipe($.if(options.debug, $.sourcemaps.write('.')))
.pipe(gulp.dest('./dist/')) .pipe(gulp.dest('./dist/'))
@ -108,11 +59,46 @@ module.exports = function (gulp, helperOptions) {
gulp.task('watch:dist', function (cb) { gulp.task('watch:dist', function (cb) {
options.debug = true options.debug = true
runSequence('dist', function () { runSequence('dist', function () {
gulp.watch(files.dist, ['dist']) gulp.watch(files.src, ['dist'])
cb() cb()
}) })
}) })
gulp.task('dev:node', ['test'], function () {
gulp.watch(files.src, ['test'])
})
gulp.task('spec-build', function () {
var browserify = require('browserify')
var source = require('vinyl-source-stream')
var buffer = require('vinyl-buffer')
return browserify({
entries: files.specs,
debug: options.debug
}).bundle()
.pipe(source('specs.js'))
.pipe(buffer())
.pipe($.sourcemaps.init({loadMaps: true}))
.pipe($.sourcemaps.write())
.pipe(gulp.dest('./build/'))
})
gulp.task('dev:browser', ['spec-build'], function () {
gulp.watch(files.src, ['spec-build'])
return gulp.src('./build/specs.js')
.pipe($.jasmineBrowser.specRunner())
.pipe($.jasmineBrowser.server({port: options.testport}))
})
gulp.task('test', function () {
return gulp.src(files.specs)
.pipe($.jasmine({
verbose: true,
includeStuckTrace: true
}))
})
gulp.task('updateSubmodule', function () { gulp.task('updateSubmodule', function () {
return gulp.src('./package.json', {read: false}) return gulp.src('./package.json', {read: false})
.pipe($.shell([ .pipe($.shell([
@ -168,24 +154,4 @@ module.exports = function (gulp, helperOptions) {
})) }))
}) })
}) })
gulp.task('dev:node', ['test'], function () {
gulp.watch(files.dist, ['test'])
})
gulp.task('dev:browser', ['watch:build'], function () {
return gulp.src(files.test)
.pipe($.watch(['build/**/*']))
.pipe($.jasmineBrowser.specRunner())
.pipe($.jasmineBrowser.server({port: options.testport}))
})
gulp.task('test', function () {
console.log(files.test)
return gulp.src('./dist/y.js')
.pipe($.jasmine({
verbose: true,
includeStuckTrace: true
}))
})
} }

View File

@ -49,23 +49,15 @@ var runSequence = require('run-sequence').use(gulp)
require('./gulpfile.helper.js')(gulp, { require('./gulpfile.helper.js')(gulp, {
polyfills: [], polyfills: [],
files: [ entry: './src/y.js',
'y.js',
'Connector.js',
'Database.js',
'Transaction.js',
'Struct.js',
'Utils.js',
'Databases/RedBlackTree.js',
'Databases/Memory.js',
'Databases/IndexedDB.js',
'Connectors/Test.js',
'Types/Array.js',
'Types/Map.js',
'Types/TextBind.js'
],
targetName: 'y.js', targetName: 'y.js',
moduleName: 'yjs' moduleName: 'yjs',
specs: [
'./src/Databases/RedBlackTree.spec.js',
'./src/Types/Array.spec.js',
'./src/Types/Map.spec.js',
'./src/Database.spec.js'
]
}) })
gulp.task('dev:examples', ['updateSubmodule', 'watch:dist'], function () { gulp.task('dev:examples', ['updateSubmodule', 'watch:dist'], function () {

View File

@ -68,6 +68,7 @@
"run-sequence": "^1.1.4", "run-sequence": "^1.1.4",
"standard": "^5.2.2", "standard": "^5.2.2",
"vinyl-buffer": "^1.0.0", "vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0" "vinyl-source-stream": "^1.1.0",
"watchify": "^3.6.0"
} }
} }

View File

@ -1,7 +1,7 @@
/* globals Y */
'use strict' 'use strict'
class AbstractConnector { module.exports = function (Y) {
class AbstractConnector {
/* /*
opts contains the following information: opts contains the following information:
role : String Role of this client ("master" or "slave") role : String Role of this client ("master" or "slave")
@ -324,5 +324,6 @@ class AbstractConnector {
throw new Error("I can't encode this json!") throw new Error("I can't encode this json!")
} }
} }
}
Y.AbstractConnector = AbstractConnector
} }
Y.AbstractConnector = AbstractConnector

View File

@ -1,7 +1,8 @@
/* global getRandom, Y, wait, async */ /* global getRandom, wait, async */
'use strict' 'use strict'
var globalRoom = { module.exports = function (Y) {
var globalRoom = {
users: {}, users: {},
buffers: {}, buffers: {},
removeUser: function (user) { removeUser: function (user) {
@ -22,10 +23,10 @@ var globalRoom = {
} }
} }
} }
} }
Y.utils.globalRoom = globalRoom Y.utils.globalRoom = globalRoom
function flushOne () { function flushOne () {
var bufs = [] var bufs = []
for (var i in globalRoom.buffers) { for (var i in globalRoom.buffers) {
if (globalRoom.buffers[i].length > 0) { if (globalRoom.buffers[i].length > 0) {
@ -41,13 +42,13 @@ function flushOne () {
} else { } else {
return false return false
} }
} }
// setInterval(flushOne, 10) // setInterval(flushOne, 10)
var userIdCounter = 0 var userIdCounter = 0
class Test extends Y.AbstractConnector { class Test extends Y.AbstractConnector {
constructor (y, options) { constructor (y, options) {
if (options === undefined) { if (options === undefined) {
throw new Error('Options must not be undefined!') throw new Error('Options must not be undefined!')
@ -131,6 +132,7 @@ class Test extends Y.AbstractConnector {
flushOne () { flushOne () {
flushOne() flushOne()
} }
} }
Y.Test = Test Y.Test = Test
}

View File

@ -1,7 +1,7 @@
/* global Y */
'use strict' 'use strict'
/* module.exports = function (Y) {
/*
Partial definition of an OperationStore. Partial definition of an OperationStore.
TODO: name it Database, operation store only holds operations. TODO: name it Database, operation store only holds operations.
@ -12,8 +12,8 @@
- request a transaction - request a transaction
* destroy() * destroy()
- destroy the database - destroy the database
*/ */
class AbstractDatabase { class AbstractDatabase {
constructor (y, opts) { constructor (y, opts) {
this.y = y this.y = y
// E.g. this.listenersById[id] : Array<Listener> // E.g. this.listenersById[id] : Array<Listener>
@ -337,5 +337,6 @@ class AbstractDatabase {
this.waitingTransactions.push(makeGen) this.waitingTransactions.push(makeGen)
} }
} }
}
Y.AbstractDatabase = AbstractDatabase
} }
Y.AbstractDatabase = AbstractDatabase

View File

@ -1,5 +1,8 @@
/* global Y, async, databases */ /* global async, databases */
/* eslint-env browser,jasmine,console */ /* eslint-env browser,jasmine,console */
'use strict'
var Y = require('./SpecHelper.js')
for (let database of databases) { for (let database of databases) {
describe(`Database (${database})`, function () { describe(`Database (${database})`, function () {

View File

@ -1,8 +1,6 @@
/* global Y */
'use strict' 'use strict'
Y.IndexedDB = (function () { module.exports = function (Y) {
class Store { class Store {
constructor (transaction, name) { constructor (transaction, name) {
this.store = transaction.objectStore(name) this.store = transaction.objectStore(name)
@ -177,5 +175,5 @@ Y.IndexedDB = (function () {
yield window.indexedDB.deleteDatabase(this.namespace) yield window.indexedDB.deleteDatabase(this.namespace)
} }
} }
return OperationStore Y.IndexedDB = OperationStore
})() }

View File

@ -1,19 +0,0 @@
/* global Y */
/* eslint-env browser,jasmine */
if (typeof window !== 'undefined' && false) {
describe('IndexedDB', function () {
var ob
beforeAll(function () {
ob = new Y.IndexedDB(null, {namespace: 'Test', gcTimeout: -1})
})
afterAll(function (done) {
ob.requestTransaction(function *() {
yield* ob.removeDatabase()
ob = null
done()
})
})
})
}

View File

@ -1,7 +1,6 @@
/* global Y */
'use strict' 'use strict'
Y.Memory = (function () { module.exports = function (Y) {
class Transaction extends Y.Transaction { class Transaction extends Y.Transaction {
constructor (store) { constructor (store) {
super(store) super(store)
@ -59,5 +58,5 @@ Y.Memory = (function () {
delete this.ds delete this.ds
} }
} }
return Database Y.Memory = Database
})() }

View File

@ -1,11 +1,11 @@
/* global Y */
'use strict' 'use strict'
/* /*
This file contains a not so fancy implemantion of a Red Black Tree. This file contains a not so fancy implemantion of a Red Black Tree.
*/ */
class N { module.exports = function (Y) {
class N {
// A created node is always red! // A created node is always red!
constructor (val) { constructor (val) {
this.val = val this.val = val
@ -123,9 +123,9 @@ class N {
return this.parent.parent.left return this.parent.parent.left
} }
} }
} }
class RBTree { class RBTree {
constructor () { constructor () {
this.root = null this.root = null
this.length = 0 this.length = 0
@ -484,6 +484,7 @@ class RBTree {
} }
} }
} }
} }
Y.utils.RBTree = RBTree Y.utils.RBTree = RBTree
}

View File

@ -1,6 +1,7 @@
/* global Y */
/* eslint-env browser,jasmine,console */ /* eslint-env browser,jasmine,console */
'use strict'
var Y = require('../SpecHelper.js')
var numberOfRBTreeTests = 1000 var numberOfRBTreeTests = 1000
function itRedNodesDoNotHaveBlackChildren () { function itRedNodesDoNotHaveBlackChildren () {

View File

@ -1,288 +0,0 @@
/* global Y */
/* eslint-env browser, jasmine */
/*
This is just a compilation of functions that help to test this library!
*/
// When testing, you store everything on the global object. We call it g
var g
if (typeof global !== 'undefined') {
g = global
} else if (typeof window !== 'undefined') {
g = window
} else {
throw new Error('No global object?')
}
g.g = g
g.YConcurrency_TestingMode = true
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000
g.describeManyTimes = function describeManyTimes (times, name, f) {
for (var i = 0; i < times; i++) {
describe(name, f)
}
}
/*
Wait for a specified amount of time (in ms). defaults to 5ms
*/
function wait (t) {
if (t == null) {
t = 5
}
return new Promise(function (resolve) {
setTimeout(function () {
resolve()
}, t * 2)
})
}
g.wait = wait
g.databases = ['Memory']
if (typeof window !== 'undefined') {
g.databases.push('IndexedDB')
}
/*
returns a random element of o.
works on Object, and Array
*/
function getRandom (o) {
if (o instanceof Array) {
return o[Math.floor(Math.random() * o.length)]
} else if (o.constructor === Object) {
var ks = []
for (var key in o) {
ks.push(key)
}
return o[getRandom(ks)]
}
}
g.getRandom = getRandom
function getRandomNumber (n) {
if (n == null) {
n = 9999
}
return Math.floor(Math.random() * n)
}
g.getRandomNumber = getRandomNumber
function * applyTransactions (relAmount, numberOfTransactions, objects, users, transactions) {
function randomTransaction (root) {
var f = getRandom(transactions)
f(root)
}
for (var i = 0; i < numberOfTransactions * relAmount + 1; i++) {
var r = Math.random()
if (r >= 0.5) {
// 50% chance to flush
users[0].connector.flushOne() // flushes for some user.. (not necessarily 0)
} else if (r >= 0.05) {
// 45% chance to create operation
randomTransaction(getRandom(objects))
} else {
// 5% chance to disconnect/reconnect
var u = getRandom(users)
if (u.connector.isDisconnected()) {
yield u.reconnect()
} else {
yield u.disconnect()
}
}
yield wait()
}
}
g.applyRandomTransactionsAllRejoinNoGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
yield* applyTransactions(1, numberOfTransactions, objects, users, transactions)
yield users[0].connector.flushAll()
yield wait()
for (var u in users) {
yield users[u].reconnect()
}
yield wait(100)
yield users[0].connector.flushAll()
yield g.garbageCollectAllUsers(users)
})
g.applyRandomTransactionsWithGC = async(function * applyRandomTransactions (users, objects, transactions, numberOfTransactions) {
yield* applyTransactions(1, numberOfTransactions, objects, users.slice(1), transactions)
yield users[0].connector.flushAll()
yield g.garbageCollectAllUsers(users)
yield wait(100)
for (var u in users) {
// TODO: here, we enforce that two users never sync at the same time with u[0]
// enforce that in the connector itself!
yield users[u].reconnect()
}
yield wait(100)
yield users[0].connector.flushAll()
yield wait(100)
yield g.garbageCollectAllUsers(users)
})
g.garbageCollectAllUsers = async(function * garbageCollectAllUsers (users) {
// gc two times because of the two gc phases (really collect everything)
yield wait(100)
for (var i in users) {
yield users[i].db.garbageCollect()
yield users[i].db.garbageCollect()
}
yield wait(100)
})
g.compareAllUsers = async(function * compareAllUsers (users) {
var s1, s2 // state sets
var ds1, ds2 // delete sets
var allDels1, allDels2 // all deletions
var db1 = [] // operation store of user1
// t1 and t2 basically do the same. They define t[1,2], ds[1,2], and allDels[1,2]
function * t1 () {
s1 = yield* this.getStateSet()
ds1 = yield* this.getDeleteSet()
allDels1 = []
yield* this.ds.iterate(this, null, null, function * (d) {
allDels1.push(d)
})
}
function * t2 () {
s2 = yield* this.getStateSet()
ds2 = yield* this.getDeleteSet()
allDels2 = []
yield* this.ds.iterate(this, null, null, function * (d) {
allDels2.push(d)
})
}
yield users[0].connector.flushAll()
yield wait()
yield g.garbageCollectAllUsers(users)
for (var uid = 0; uid < users.length; uid++) {
var u = users[uid]
u.db.requestTransaction(function * () {
// compare deleted ops against deleteStore
yield* this.os.iterate(this, null, null, function * (o) {
if (o.deleted === true) {
expect(yield* this.isDeleted(o.id)).toBeTruthy()
}
})
// compare deleteStore against deleted ops
var ds = []
yield* this.ds.iterate(this, null, null, function * (d) {
ds.push(d)
})
for (var j in ds) {
var d = ds[j]
for (var i = 0; i < d.len; i++) {
var o = yield* this.getOperation([d.id[0], d.id[1] + i])
// gc'd or deleted
if (d.gc) {
expect(o).toBeFalsy()
} else {
expect(o.deleted).toBeTruthy()
}
}
}
})
// compare allDels tree
yield wait()
if (s1 == null) {
u.db.requestTransaction(function * () {
yield* t1.call(this)
yield* this.os.iterate(this, null, null, function * (o) {
o = Y.utils.copyObject(o)
delete o.origin
db1.push(o)
})
})
yield wait()
} else {
// TODO: make requestTransaction return a promise..
u.db.requestTransaction(function * () {
yield* t2.call(this)
expect(s1).toEqual(s2)
expect(allDels1).toEqual(allDels2) // inner structure
expect(ds1).toEqual(ds2) // exported structure
var count = 0
yield* this.os.iterate(this, null, null, function * (o) {
o = Y.utils.copyObject(o)
delete o.origin
expect(db1[count++]).toEqual(o)
})
})
yield wait()
}
}
})
g.createUsers = async(function * createUsers (self, numberOfUsers, database) {
if (Y.utils.globalRoom.users[0] != null) {
yield Y.utils.globalRoom.users[0].flushAll()
}
// destroy old users
for (var u in Y.utils.globalRoom.users) {
Y.utils.globalRoom.users[u].y.destroy()
}
self.users = null
var promises = []
for (var i = 0; i < numberOfUsers; i++) {
promises.push(Y({
db: {
name: database,
namespace: 'User ' + i,
cleanStart: true,
gcTimeout: -1
},
connector: {
name: 'Test',
debug: false
}
}))
}
self.users = yield Promise.all(promises)
return self.users
})
/*
Until async/await arrives in js, we use this function to wait for promises
by yielding them.
*/
function async (makeGenerator) {
return function (arg) {
var generator = makeGenerator.apply(this, arguments)
function handle (result) {
if (result.done) return Promise.resolve(result.value)
return Promise.resolve(result.value).then(function (res) {
return handle(generator.next(res))
}, function (err) {
return handle(generator.throw(err))
})
}
try {
return handle(generator.next())
} catch (ex) {
generator.throw(ex)
// return Promise.reject(ex)
}
}
}
g.async = async
function logUsers (self) {
if (self.constructor === Array) {
self = {users: self}
}
self.users[0].db.logTable()
self.users[1].db.logTable()
self.users[2].db.logTable()
}
g.logUsers = logUsers

View File

@ -1,4 +1,3 @@
/* global Y */
'use strict' 'use strict'
/* /*
@ -19,8 +18,8 @@
* requiredOps * requiredOps
- Operations that are required to execute this operation. - Operations that are required to execute this operation.
*/ */
module.exports = function (Y) {
var Struct = { var Struct = {
/* This is the only operation that is actually not a structure, because /* This is the only operation that is actually not a structure, because
it is not stored in the OS. This is why it _does not_ have an id it is not stored in the OS. This is why it _does not_ have an id
@ -332,5 +331,6 @@ var Struct = {
} }
} }
} }
}
Y.Struct = Struct
} }
Y.Struct = Struct

View File

@ -1,4 +1,3 @@
/* global Y */
'use strict' 'use strict'
/* /*
@ -74,7 +73,8 @@
- this is called only by `getOperations(startSS)`. It makes an operation - this is called only by `getOperations(startSS)`. It makes an operation
applyable on a given SS. applyable on a given SS.
*/ */
class Transaction { module.exports = function (Y) {
class Transaction {
/* /*
Get a type based on the id of its model. Get a type based on the id of its model.
If it does not exist yes, create it. If it does not exist yes, create it.
@ -649,5 +649,6 @@ class Transaction {
op.left = op.origin op.left = op.origin
return op return op
} }
}
Y.Transaction = Transaction
} }
Y.Transaction = Transaction

View File

@ -1,7 +1,6 @@
/* global Y */
'use strict' 'use strict'
;(function () { function extend (Y) {
class YArray { class YArray {
constructor (os, _model, idArray, valArray) { constructor (os, _model, idArray, valArray) {
this.os = os this.os = os
@ -166,7 +165,7 @@
} }
} }
Y.Array = new Y.utils.CustomType({ Y.extend('Array', new Y.utils.CustomType({
class: YArray, class: YArray,
createType: function * YArrayCreator () { createType: function * YArrayCreator () {
var modelid = this.store.getNextOpId() var modelid = this.store.getNextOpId()
@ -188,5 +187,11 @@
}) })
return new YArray(os, model.id, idArray, valArray) return new YArray(os, model.id, idArray, valArray)
} }
}) }))
})() }
if (typeof Y !== 'undefined') {
extend(Y)
} else {
module.exports = extend
}

View File

@ -1,6 +1,8 @@
/* global createUsers, databases, wait, Y, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */ /* global createUsers, databases, wait, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, garbageCollectAllUsers, describeManyTimes */
/* eslint-env browser,jasmine */ /* eslint-env browser,jasmine */
'use strict'
var Y = require('../SpecHelper.js')
var numberOfYArrayTests = 10 var numberOfYArrayTests = 10
var repeatArrayTests = 2 var repeatArrayTests = 2

View File

@ -1,7 +1,6 @@
/* global Y */
'use strict' 'use strict'
;(function () { module.exports = function (Y) {
class YMap { class YMap {
constructor (os, model, contents, opContents) { constructor (os, model, contents, opContents) {
this._model = model.id this._model = model.id
@ -292,4 +291,4 @@
return new YMap(os, model, contents, opContents) return new YMap(os, model, contents, opContents)
} }
}) })
})() }

View File

@ -1,6 +1,8 @@
/* global createUsers, Y, databases, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */ /* global createUsers, databases, compareAllUsers, getRandomNumber, applyRandomTransactionsAllRejoinNoGC, applyRandomTransactionsWithGC, async, describeManyTimes */
/* eslint-env browser,jasmine */ /* eslint-env browser,jasmine */
'use strict'
var Y = require('../SpecHelper.js')
var numberOfYMapTests = 10 var numberOfYMapTests = 10
var repeatMapTeasts = 1 var repeatMapTeasts = 1

View File

@ -1,7 +1,6 @@
/* global Y */
'use strict' 'use strict'
;(function () { module.exports = function (Y) {
class YTextBind extends Y.Array['class'] { class YTextBind extends Y.Array['class'] {
constructor (os, _model, idArray, valArray) { constructor (os, _model, idArray, valArray) {
super(os, _model, idArray, valArray) super(os, _model, idArray, valArray)
@ -287,4 +286,4 @@
return new YTextBind(os, model.id, idArray, valArray) return new YTextBind(os, model.id, idArray, valArray)
} }
}) })
})() }

View File

@ -1,4 +1,3 @@
/* global Y */
'use strict' 'use strict'
/* /*
@ -21,7 +20,10 @@
database request to finish). EventHandler will help you to make your type database request to finish). EventHandler will help you to make your type
synchronously. synchronously.
*/ */
class EventHandler { module.exports = function (Y) {
Y.utils = {}
class EventHandler {
/* /*
onevent: is called when the structure changes. onevent: is called when the structure changes.
@ -132,10 +134,10 @@ class EventHandler {
this.onevent(events) this.onevent(events)
} }
} }
} }
Y.utils.EventHandler = EventHandler Y.utils.EventHandler = EventHandler
/* /*
A wrapper for the definition of a custom type. A wrapper for the definition of a custom type.
Every custom type must have three properties: Every custom type must have three properties:
@ -145,8 +147,8 @@ Y.utils.EventHandler = EventHandler
- Given a model, creates a custom type - Given a model, creates a custom type
* class * class
- the constructor of the custom type (e.g. in order to inherit from a type) - the constructor of the custom type (e.g. in order to inherit from a type)
*/ */
class CustomType { // eslint-disable-line class CustomType { // eslint-disable-line
constructor (def) { constructor (def) {
if (def.createType == null || if (def.createType == null ||
def.initType == null || def.initType == null ||
@ -158,31 +160,31 @@ class CustomType { // eslint-disable-line
this.initType = def.initType this.initType = def.initType
this.class = def.class this.class = def.class
} }
} }
Y.utils.CustomType = CustomType Y.utils.CustomType = CustomType
/* /*
Make a flat copy of an object Make a flat copy of an object
(just copy properties) (just copy properties)
*/ */
function copyObject (o) { function copyObject (o) {
var c = {} var c = {}
for (var key in o) { for (var key in o) {
c[key] = o[key] c[key] = o[key]
} }
return c return c
} }
Y.utils.copyObject = copyObject Y.utils.copyObject = copyObject
/* /*
Defines a smaller relation on Id's Defines a smaller relation on Id's
*/ */
function smaller (a, b) { 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])
} }
Y.utils.smaller = smaller Y.utils.smaller = smaller
function compareIds (id1, id2) { function compareIds (id1, id2) {
if (id1 == null || id2 == null) { if (id1 == null || id2 == null) {
if (id1 == null && id2 == null) { if (id1 == null && id2 == null) {
return true return true
@ -194,5 +196,6 @@ function compareIds (id1, id2) {
} else { } else {
return false return false
} }
}
Y.utils.compareIds = compareIds
} }
Y.utils.compareIds = compareIds

View File

@ -1,7 +1,63 @@
/* @flow */ /* @flow */
'use strict' 'use strict'
require('./Connector.js')(Y)
require('./Database.js')(Y)
require('./Transaction.js')(Y)
require('./Struct.js')(Y)
require('./Utils.js')(Y)
require('./Databases/RedBlackTree.js')(Y)
require('./Databases/Memory.js')(Y)
require('./Databases/IndexedDB.js')(Y)
require('./Connectors/Test.js')(Y)
var requiringModules = {}
module.exports = Y
Y.extend = function (name, value) {
Y[name] = value
var resolves = requiringModules[name]
if (requiringModules[name] != null) {
for (var i = 0; i < resolves.length; i++) {
resolves[i]()
}
delete requiringModules[name]
}
}
require('./Types/Array.js')(Y)
require('./Types/Map.js')(Y)
require('./Types/TextBind.js')(Y)
function Y (opts) { function Y (opts) {
opts.types = opts.types != null ? opts.types : []
var modules = [opts.db.name, opts.connector.name].concat(opts.types)
var promises = []
for (var i = 0; i < modules.length; i++) {
if (Y[modules[i]] == null) {
try {
require(modules[i])(Y)
} catch (e) {
// module does not exist
if (window != null) {
if (requiringModules[modules[i]] == null) {
var imported = document.createElement('script')
var name = modules[i].toLowerCase()
imported.src = opts.sourceDir + '/y-' + name + '/y-' + name + '.js'
document.head.appendChild(imported)
requiringModules[modules[i]] = []
}
promises.push(new Promise(function (resolve) {
requiringModules[modules[i]].push(resolve)
}))
} else {
throw e
}
}
}
}
return Promise.all(promises).then(function () {
return new Promise(function (resolve) { return new Promise(function (resolve) {
var yconfig = new YConfig(opts, function () { var yconfig = new YConfig(opts, function () {
yconfig.db.whenUserIdSet(function () { yconfig.db.whenUserIdSet(function () {
@ -9,6 +65,7 @@ function Y (opts) {
}) })
}) })
}) })
})
} }
class YConfig { class YConfig {
@ -49,9 +106,3 @@ class YConfig {
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
window.Y = Y window.Y = Y
} }
if (typeof YConcurrency_TestingMode !== 'undefined') {
g.Y = Y //eslint-disable-line
// debugger //eslint-disable-line
}
Y.utils = {}