File: src/signaling/muc_signaling.coffee
{Deferred} = require('../internal/promise')
{Signaling,SignalingPeer} = require('./signaling')
EventEmitter = require('events').EventEmitter
###*
# @module rtc.signaling
###
###*
# Signaling peer for multi user chats.
#
# For a detailed description of the signaling protocol see `rtc.signaling.MucSignaling`
#
# @extends rtc.signaling.SignalingPeer
# @class rtc.signaling.MucSignalingPeer
#
# @constructor
# @param {rtc.signaling.Channel} channel The channel to the siganling server
# @param {String} peer_id The id of the remote peer
# @param {Object} status The status of the remote peer
# @param {Boolean} first Whether the local peer was in the room before the remote peer
###
class exports.MucSignalingPeer extends SignalingPeer
###*
# The id of the remote peer
# @property id
# @type String
###
constructor: (@channel, @id, @status, @first) ->
recv_msg = (data) =>
if data.peer != @id
# message is not for us
return
if not data.type?
# invalid message
return
switch data.type
when 'from'
if not data.event? or not data.data?
# invalid message
return
@emit(data.event, data.data)
when 'peer_left'
@emit('left')
@channel.removeListener('message', recv_msg)
when 'peer_status'
@status = data.status
@emit('status_changed', @status)
@channel.on('message', recv_msg)
send: (event, data={}) ->
return @channel.send({
type: 'to'
peer: @id
event: event
data: data
})
###*
# Signaling for multi user chats
#
# The following messages are sent to the server:
#
# // join the room. has to be sent before any other message.
# // response will be 'joined' on success
# // other peers in the room will get 'peer_joined'
# {
# "type": "join",
# "status": { .. status .. }
# }
#
# // leave the room. server will close the connectino.
# {
# "type": "leave"
# }
#
# // update status object
# // other peers will get 'peer_status'
# {
# "type": "status",
# "status": { .. status .. }
# }
#
# // send message to a peer. will be received as 'from'
# {
# "type": "to",
# "peer": "peer_id",
# "event": "event_id",
# "data": { .. custom data .. }
# }
#
# The following messages are received form the server:
#
# // joined the room. is the response to 'join'
# {
# "type": "joined",
# "id": "own_id",
# "peers": {
# "peer_id": { .. status .. }
# }
# }
#
# // another peer joined the room.
# {
# "type": "peer_joined",
# "peer": "peer_id",
# "status": { .. status .. }
# }
#
# // anosther peer updated its status object using 'status'
# {
# "type": "peer_status",
# "peer": "peer_id",
# "status": { .. status .. }
# }
#
# // another peer left the room
# {
# "type": "peer_left",
# "peer": "peer_id"
# }
#
# // message from another peer sent by 'to'
# {
# "type": "from",
# "peer": "peer_id",
# "event": "event_id",
# "data": { .. custom data .. }
# }
#
# The messages transmitted in the `to`/`from` messages are emitted as events in `MucSignalingPeer`
#
# @extends rtc.signaling.Signaling
# @class rtc.signaling.MucSignaling
#
# @constructor
# @param {rtc.signaling.Channel} channel The channel to the signaling server
###
class exports.MucSignaling extends Signaling
###*
# The id of the local peer. Only available after joining.
# @property id
# @type String
###
constructor: (@channel) ->
@status = {}
join_d = new Deferred()
@join_p = join_d.promise
@channel.on 'closed', () =>
@emit('closed')
@channel.on 'message', (data) =>
if not data.type?
# invalid message
return
switch data.type
when 'joined'
if not data.peers?
# invalid ...
return
for peer_id, status of data.peers
peer = new exports.MucSignalingPeer(@channel, peer_id, status, false)
@emit('peer_joined', peer)
@id = data.id
join_d.resolve()
when 'peer_joined'
if not data.peer?
# invalid ...
return
peer = new exports.MucSignalingPeer(@channel, data.peer, data.status, true)
@emit('peer_joined', peer)
connect: () ->
if not @connect_p?
@connect_p = @channel.connect().then () =>
return @channel.send({
type: 'join'
status: @status
})
.then () =>
return @join_d
return @connect_p
setStatus: (status) ->
@status = status
if @connect_p
@connect_p.then () =>
return @channel.send({
type: 'status'
status: status
})
leave: () ->
@channel.send({
type: 'leave'
}).then () ->
@channel.close()