WebRTC DataChannel manual signaling chat

A WebRTC DataChannel chat without a signaling server. Signaling is carried manually by the users.

HTML

<button id="server">Initiate call</button>

<textarea id="offer" placeholder="Initiate call or paste your partner's call here."></textarea>
<textarea id="answer" style="display:none;" placeholder="Paste your partner's answer here."></textarea>
<pre id="data"></pre>
<form id="send" style="display:none;">
    <input id="text" placeholder="Enter text to send here"></input>
    <input type="submit" value="Send"></input>
</form>

CSS

html, body {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
}

textarea {
    width: 90%;
    height: 45%;
    margin: 0 auto;
    padding: 0;
}

CoffeeScript

# From: https://gist.github.com/zziuni/3741933
#       http://www.stunprotocol.org/
    
ICE_SERVERS = [
    url: 'stun:stun.stunprotocol.org:3478'
  , url: 'stun:stun.l.google.com:19302'
# , url: 'stun:stun1.l.google.com:19302'
# , url: 'stun:stun2.l.google.com:19302'
# , url: 'stun:stun3.l.google.com:19302'
# , url: 'stun:stun4.l.google.com:19302'
# , url: 'stun:stun01.sipphone.com'
# , url: 'stun:stun.ekiga.net'
# , url: 'stun:stun.fwdnet.net'
# , url: 'stun:stun.ideasip.com'
# , url: 'stun:stun.iptel.org'
# , url: 'stun:stun.rixtelecom.se'
# , url: 'stun:stun.schlund.de'
# , url: 'stun:stunserver.org'
# , url: 'stun:stun.softjoys.com'
# , url: 'stun:stun.voiparound.com'
# , url: 'stun:stun.voipbuster.com'
# , url: 'stun:stun.voipstunt.com'
# , url: 'stun:stun.voxgratia.org'
# , url: 'stun:stun.xten.com'
]

PEERCONNECTION_OPTIONS =
  iceServers: ICE_SERVERS
  requestIdentity: 'no'
    
NO_AUDIO_VIDEO =
  mandatory:
    OfferToReceiveVideo: false
    OfferToReceiveAudio: false
    
DATACHANNEL_OPTIONS =
  reliable: true
  ordered: true
  protocol: 'tcp'
    
##  UTILITIES  ##
error = (err) ->
  alert "Error: #{err}"
  location.reload()

pasteHandler = ($element, cb) ->
  # Mouse selects the whole text
  $element.on 'mouseup', ->
    $element.select()
    
  # Delay paste one event loop (otherwise the value is before pasting)
  $element.on 'paste', (e) ->
    setTimeout (-> cb e), 0

##  SHIMMING  ##
RTCPeerConnection = window.webkitRTCPeerConnection or window.mozRTCPeerConnection or window.RTCPeerConnection

RTCSessionDescription = window.webkitRTCSessionDescription or window.mozRTCSessionDescription or window.RTCSessionDescription
    
if not RTCPeerConnection? or not RTCSessionDescription?
  error 'WebRTC is required!'
else $ ->
  ##  DOM  ##
  $document = $ document
  $server = $ '#server'
  $offer = $ '#offer'
  $answer = $ '#answer'
  $data = $ '#data'
  $text = $ '#text'
  $send = $ '#send'
    
  $offer.select()
    
  ##  COMMON  ##    
  pc = new...