### WebsocketRails JavaScript Client

Setting up the dispatcher:

var dispatcher = new WebSocketRails('localhost:3000/websocket');
dispatcher.on_open = function() {
  // trigger a server event immediately after opening connection
  dispatcher.trigger('new_user',{user_name: 'guest'});
})

Triggering a new event on the server

dispatcherer.trigger('event_name',object_to_be_serialized_to_json);

Listening for new events from the server

dispatcher.bind('event_name', function(data) {
  console.log(data.user_name);
});

Stop listening for new events from the server

dispatcher.unbind('event')

### class @WebSocketRails

constructor: (@url, @use_websockets = true) ->
  @callbacks = {}
  @channels  = {}
  @queue     = {}

  @connect()

connect: ->
  @state = 'connecting'

  unless @supports_websockets() and @use_websockets
    @_conn = new WebSocketRails.HttpConnection @url, @
  else
    @_conn = new WebSocketRails.WebSocketConnection @url, @

  @_conn.new_message = @new_message

disconnect: ->
  if @_conn
    @_conn.close()
    delete @_conn._conn
    delete @_conn

  @state     = 'disconnected'

# Reconnects the whole connection, 
# keeping the messages queue and its' connected channels.
# 
# After successfull connection, this will:
# - reconnect to all channels, that were active while disconnecting
# - resend all events from which we haven't received any response yet
reconnect: =>
  old_connection_id = @_conn?.connection_id

  @disconnect()
  @connect()

  # Resend all unfinished events from the previous connection.
  for id, event of @queue
    if event.connection_id == old_connection_id && !event.is_result()
      @trigger_event event

  @reconnect_channels()

new_message: (data) =>
  for socket_message in data
    event = new WebSocketRails.Event( socket_message )
    if event.is_result()
      @queue[event.id]?.run_callbacks(event.success, event.data)
      delete @queue[event.id]
    else if event.is_channel()
      @dispatch_channel event
    else if event.is_ping()
      @pong()
    else
      @dispatch event

    if @state == 'connecting' and event.name == 'client_connected'
      @connection_established event.data

connection_established: (data) =>
  @state         = 'connected'
  @_conn.setConnectionId(data.connection_id)
  @_conn.flush_queue()
  if @on_open?
    @on_open(data)

bind: (event_name, callback) =>
  @callbacks[event_name] ?= []
  @callbacks[event_name].push callback

unbind: (event_name) =>
  delete @callbacks[event_name]

trigger: (event_name, data, success_callback, failure_callback) =>
  event = new WebSocketRails.Event( [event_name, data, @_conn?.connection_id], success_callback, failure_callback )
  @trigger_event event

trigger_event: (event) =>
  @queue[event.id] ?= event # Prevent replacing an event that has callbacks stored
  @_conn.trigger event if @_conn
  event

dispatch: (event) =>
  return unless @callbacks[event.name]?
  for callback in @callbacks[event.name]
    callback event.data

subscribe: (channel_name, success_callback, failure_callback) =>
  unless @channels[channel_name]?
    channel = new WebSocketRails.Channel channel_name, @, false, success_callback, failure_callback
    @channels[channel_name] = channel
    channel
  else
    @channels[channel_name]

subscribe_private: (channel_name, success_callback, failure_callback) =>
  unless @channels[channel_name]?
    channel = new WebSocketRails.Channel channel_name, @, true, success_callback, failure_callback
    @channels[channel_name] = channel
    channel
  else
    @channels[channel_name]

unsubscribe: (channel_name) =>
  return unless @channels[channel_name]?
  @channels[channel_name].destroy()
  delete @channels[channel_name]

dispatch_channel: (event) =>
  return unless @channels[event.channel]?
  @channels[event.channel].dispatch event.name, event.data

supports_websockets: =>
  (typeof(WebSocket) == "function" or typeof(WebSocket) == "object")

pong: =>
  pong = new WebSocketRails.Event( ['websocket_rails.pong', {}, @_conn?.connection_id] )
  @_conn.trigger pong

connection_stale: =>
  @state != 'connected'

# Destroy and resubscribe to all existing @channels.
reconnect_channels: ->
  for name, channel of @channels
    callbacks = channel._callbacks
    channel.destroy()
    delete @channels[name]

    channel = if channel.is_private
      @subscribe_private name
    else
      @subscribe name
    channel._callbacks = callbacks
    channel