152 lines
4.9 KiB
CoffeeScript
152 lines
4.9 KiB
CoffeeScript
|
|
#
|
|
# @overload createPeerJsConnector peerjs_options, callback
|
|
# @param {Object} peerjs_options Is the options object that is passed to PeerJs.
|
|
# @param {Function} callback The callback is called when the connector is initialized.
|
|
# @overload createPeerJsConnector peerjs_user_id, peerjs_options, callback
|
|
# @param {String} peerjs_user_id The user_id that is passed to PeerJs as the user_id and should be unique between all (also the unconnected) Peers.
|
|
# @param {Object} peerjs_options Is the options object that is passed to PeerJs.
|
|
# @param {Function} callback The callback is called when the connector is initialized.
|
|
#
|
|
createPeerJsConnector = ()->
|
|
peer = null
|
|
if arguments.length is 2
|
|
peer = new Peer arguments[0]
|
|
callback = arguments[1]
|
|
else
|
|
peer = new Peer arguments[0], arguments[1]
|
|
peer.on 'error', (err)->
|
|
throw new Error "Peerjs connector: #{err}"
|
|
peer.on 'disconnected', ()->
|
|
throw new Error "Peerjs connector disconnected from signalling server. Cannot accept new connections. Not fatal, but not so good either.."
|
|
callback = arguments[2]
|
|
|
|
|
|
#
|
|
# PeerJs is a Framework that enables you to connect to other peers. You just need the
|
|
# user-id of the peer (browser/client). And then you can connect to it.
|
|
# @see http://peerjs.com
|
|
#
|
|
class PeerJsConnector
|
|
|
|
#
|
|
# @param {Engine} engine The transformation engine
|
|
# @param {HistoryBuffer} HB
|
|
# @param {Array<Function>} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.
|
|
# @param {Yatta} yatta The Yatta framework.
|
|
#
|
|
constructor: (@engine, @HB, @execution_listener, @yatta)->
|
|
|
|
@peer = peer
|
|
@connections = {}
|
|
@new_connection_listeners = []
|
|
|
|
@peer.on 'connection', (conn)=>
|
|
@addConnection conn
|
|
|
|
sync_every_collaborator = ()=>
|
|
for conn_id, conn of @connections
|
|
conn.send
|
|
sync_state_vector: @HB.getOperationCounter()
|
|
setInterval sync_every_collaborator, 4000
|
|
|
|
send_ = (o)=>
|
|
if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string")
|
|
for conn_id,conn of @connections
|
|
conn.send
|
|
op: o
|
|
@execution_listener.push send_
|
|
|
|
|
|
|
|
|
|
#
|
|
# Connect the Framework to another peer. Therefore you have to receive his
|
|
# user_id. If the other peer is connected to other peers, the PeerJsConnector
|
|
# will automatically connect to them too.
|
|
#
|
|
# Transmitting the user_id is your job.
|
|
# See [TextEditing](../../examples/TextEditing/) for a nice example
|
|
# on how to do that with urls.
|
|
#
|
|
# @param id {String} Connection id
|
|
#
|
|
connectToPeer: (id)->
|
|
if not @connections[id]? and id isnt @yatta.getUserId()
|
|
@addConnection peer.connect id
|
|
|
|
#
|
|
# Receive the id of every connected peer.
|
|
# @return {Array<String>} A list of Peer-Ids
|
|
#
|
|
getAllConnectionIds: ()->
|
|
for conn_id of @connections
|
|
conn_id
|
|
|
|
onNewConnection: (f)->
|
|
@new_connection_listeners.push f
|
|
|
|
#
|
|
# Adds an existing connection to this connector.
|
|
# @param conn {PeerJsConnection}
|
|
#
|
|
addConnection: (conn)->
|
|
#
|
|
# What this method does:
|
|
# * Send state vector
|
|
# * Receive HB -> apply them
|
|
# * Send connections
|
|
# * Receive Connections -> Connect to unknow connections
|
|
@connections[conn.peer] = conn
|
|
initialized_me = false
|
|
initialized_him = false
|
|
conn.on 'data', (data)=>
|
|
if data is "empty_message"
|
|
# nop
|
|
else if data.HB?
|
|
initialized_me = true
|
|
@engine.applyOpsCheckDouble data.HB
|
|
if not data.initialized
|
|
conn.send
|
|
conns: @getAllConnectionIds()
|
|
@new_connection_listeners.map (f)->
|
|
f(conn)
|
|
else if data.op?
|
|
@engine.applyOp data.op
|
|
else if data.conns?
|
|
for conn_id in data.conns
|
|
@connectToPeer conn_id
|
|
else if data.sync_state_vector?
|
|
conn.send
|
|
HB: @yatta.getHistoryBuffer()._encode(data.sync_state_vector)
|
|
initialized: true
|
|
else if data.state_vector?
|
|
if not initialized_him
|
|
# make sure, that it is sent only once
|
|
conn.send
|
|
HB: @yatta.getHistoryBuffer()._encode(data.state_vector)
|
|
initialized: false
|
|
initialized_him = true
|
|
else
|
|
throw new Error "Can't parse this operation: #{data}"
|
|
|
|
sendStateVector = ()=>
|
|
conn.send
|
|
state_vector: @HB.getOperationCounter()
|
|
if not initialized_me
|
|
# Because of a bug in PeerJs,
|
|
# we never know if state vector was actually sent
|
|
setTimeout sendStateVector, 100
|
|
sendStateVector()
|
|
|
|
peer.on 'open', (id)->
|
|
callback PeerJsConnector, id
|
|
|
|
|
|
module.exports = createPeerJsConnector
|
|
if window?
|
|
if not window.Y?
|
|
window.Y = {}
|
|
window.Y.createPeerJsConnector = createPeerJsConnector
|
|
|