Files
yjs/src/Y.js

177 lines
4.7 KiB
JavaScript
Raw Normal View History

2017-10-14 23:03:24 +02:00
import DeleteStore from './Store/DeleteStore.js'
import OperationStore from './Store/OperationStore.js'
import StateStore from './Store/StateStore.js'
import { generateUserID } from './Util/generateUserID.js'
import RootID from './Util/RootID.js'
2017-10-16 04:53:12 +02:00
import NamedEventHandler from './Util/NamedEventHandler.js'
2017-10-30 11:33:00 +01:00
import UndoManager from './Util/UndoManager.js'
2017-10-11 03:41:54 +02:00
2017-10-14 23:03:24 +02:00
import { messageToString, messageToRoomname } from './MessageHandler/messageToString.js'
2017-10-11 03:41:54 +02:00
2017-10-14 23:03:24 +02:00
import Connector from './Connector.js'
import Persistence from './Persistence.js'
import YArray from './Type/YArray.js'
import YMap from './Type/YMap.js'
import YText from './Type/YText.js'
import { YXmlFragment, YXmlElement, YXmlText } from './Type/y-xml/y-xml.js'
import BinaryDecoder from './Binary/Decoder.js'
2017-10-11 03:41:54 +02:00
2017-10-15 12:17:25 +02:00
import debug from 'debug'
import Transaction from './Transaction.js'
2017-10-16 04:53:12 +02:00
export default class Y extends NamedEventHandler {
2017-10-11 03:41:54 +02:00
constructor (opts) {
2017-10-16 04:53:12 +02:00
super()
this._opts = opts
this.userID = opts._userID != null ? opts._userID : generateUserID()
2017-11-07 18:31:04 -08:00
this.share = {}
2017-10-11 03:41:54 +02:00
this.ds = new DeleteStore(this)
this.os = new OperationStore(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()
2017-10-16 04:53:12 +02:00
this._readyToIntegrate = []
this._transaction = null
}
_beforeChange () {}
transact (f, remote = false) {
let initialCall = this._transaction === null
if (initialCall) {
this.emit('beforeTransaction', this, remote)
this._transaction = new Transaction(this)
}
try {
2017-10-27 22:28:32 +02:00
f(this)
} catch (e) {
console.error(e)
}
if (initialCall) {
// emit change events on changed types
this._transaction.changedTypes.forEach(function (subs, type) {
2017-11-07 22:44:43 -08:00
if (!type._deleted) {
type._callObserver(subs, remote)
}
})
this._transaction.changedParentTypes.forEach(function (events, type) {
if (!type._deleted) {
events = events
.filter(event =>
!event.target._deleted
)
events
.forEach(event => {
event.currentTarget = type
})
2017-11-07 22:44:43 -08:00
// we don't have to check for events.length
// because there is no way events is empty..
type._deepEventHandler.callEventListeners(events)
}
})
// when all changes & events are processed, emit afterTransaction event
this.emit('afterTransaction', this, remote)
this._transaction = null
}
2017-10-16 04:53:12 +02:00
}
// fake _start for root properties (y.set('name', type))
get _start () {
return null
}
set _start (start) {
return null
2017-10-11 03:41:54 +02:00
}
get room () {
2017-10-16 04:53:12 +02:00
return this._opts.connector.room
2017-10-11 03:41:54 +02:00
}
2017-11-07 18:31:04 -08:00
define (name, TypeConstructor) {
2017-10-11 03:41:54 +02:00
let id = new RootID(name, TypeConstructor)
let type = this.os.get(id)
if (type === null) {
type = new TypeConstructor()
type._id = id
2017-10-16 04:53:12 +02:00
type._parent = this
2017-10-11 03:41:54 +02:00
type._integrate(this)
2017-11-07 18:31:04 -08:00
if (this.share[name] !== undefined) {
throw new Error('Type is already defined with a different constructor!')
}
}
if (this.share[name] === undefined) {
this.share[name] = type
2017-10-11 03:41:54 +02:00
}
return type
}
2017-11-07 18:31:04 -08:00
get (name) {
return this.share[name]
}
2017-10-11 03:41:54 +02:00
disconnect () {
if (this.connected) {
this.connected = false
return this.connector.disconnect()
} else {
return Promise.resolve()
}
}
reconnect () {
if (!this.connected) {
this.connected = true
return this.connector.reconnect()
} else {
return Promise.resolve()
}
}
destroy () {
this.share = null
if (this.connector.destroy != null) {
this.connector.destroy()
} else {
this.connector.disconnect()
}
this.os = null
this.ds = null
this.ss = null
}
2017-11-07 18:31:04 -08:00
whenSynced () {
return new Promise(resolve => {
this.once('synced', () => {
resolve()
})
})
}
2017-10-11 03:41:54 +02:00
}
Y.extend = function extendYjs () {
for (var i = 0; i < arguments.length; i++) {
var f = arguments[i]
if (typeof f === 'function') {
f(Y)
} else {
throw new Error('Expected a function!')
}
}
}
2017-10-16 04:53:12 +02:00
// TODO: The following assignments should be moved to yjs-dist
Y.AbstractConnector = Connector
2017-10-11 03:41:54 +02:00
Y.Persisence = Persistence
Y.Array = YArray
Y.Map = YMap
Y.Text = YText
Y.XmlElement = YXmlElement
Y.XmlFragment = YXmlFragment
Y.XmlText = YXmlText
2017-10-11 03:41:54 +02:00
Y.utils = {
2017-10-30 11:33:00 +01:00
BinaryDecoder,
UndoManager
}
2017-10-15 12:17:25 +02:00
2017-10-11 03:41:54 +02:00
Y.debug = debug
2017-10-14 23:03:24 +02:00
debug.formatters.Y = messageToString
debug.formatters.y = messageToRoomname