text: (function() {
  
    // Define "global" variables
    
    var connectButton = null;
    var disconnectButton = null;
    var sendButton = null;
    var messageInputBox = null;
    var receiveBox = null;
    
    var localConnection = null;   // RTCPeerConnection for our "local" connection
    var remoteConnection = null;  // RTCPeerConnection for the "remote"
    
    var sendChannel = null;       // RTCDataChannel for the local (sender)
    var receiveChannel = null;    // RTCDataChannel for the remote (receiver)
    
    // Functions
    
    // Set things up, connect event listeners, etc.
    
    function startup() {
      connectButton = document.getElementById('connectButton');
      disconnectButton = document.getElementById('disconnectButton');
      sendButton = document.getElementById('sendButton');
      messageInputBox = document.getElementById('message');
      receiveBox = document.getElementById('receivebox');
  
      // Set event listeners for user interface widgets
  
      connectButton.addEventListener('click', connectPeers, false);
      disconnectButton.addEventListener('click', disconnectPeers, false);
      sendButton.addEventListener('click', sendMessage, false);
    }
    
    // Connect the two peers. Normally you look for and connect to a remote
    // machine here, but we're just connecting two local objects, so we can
    // bypass that step.
    
    function connectPeers() {
      // Create the local connection and its event listeners
      
      localConnection = new RTCPeerConnection();
      
      // Create the data channel and establish its event listeners
      sendChannel = localConnection.createDataChannel("sendChannel");
      sendChannel.onopen = handleSendChannelStatusChange;
      sendChannel.onclose = handleSendChannelStatusChange;
      
      // Create the remote connection and its event listeners
      
      remoteConnection = new RTCPeerConnection();
      remoteConnection.ondatachannel = receiveChannelCallback;
      
      // Set up the ICE candidates for the two peers
      
      localConnection.onicecandidate = e => !e.candidate
          || remoteConnection.addIceCandidate(e.candidate)
          .catch(handleAddCandidateError);
  
      remoteConnection.onicecandidate = e => !e.candidate
          || localConnection.addIceCandidate(e.candidate)
          .catch(handleAddCandidateError);
      
      // Now create an offer to connect; this starts the process
      
      localConnection.createOffer()
      .then(offer => localConnection.setLocalDescription(offer))
      .then(() => remoteConnection.setRemoteDescription(localConnection.localDescription))
      .then(() => remoteConnection.createAnswer())
      .then(answer => remoteConnection.setLocalDescription(answer))
      .then(() => localConnection.setRemoteDescription(remoteConnection.localDescription))
      .catch(handleCreateDescriptionError);
    }
      
    // Handle errors attempting to create a description;
    // this can happen both when creating an offer and when
    // creating an answer. In this simple example, we handle
    // both the same way.
    
    function handleCreateDescriptionError(error) {
      console.log("Unable to create an offer: " + error.toString());
    }
    
    // Handle successful addition of the ICE candidate
    // on the "local" end of the connection.
    
    function handleLocalAddCandidateSuccess() {
      connectButton.disabled = true;
    }
  
    // Handle successful addition of the ICE candidate
    // on the "remote" end of the connection.
    
    function handleRemoteAddCandidateSuccess() {
      disconnectButton.disabled = false;
    }
  
    // Handle an error that occurs during addition of ICE candidate.
    
    function handleAddCandidateError() {
      console.log("Oh noes! addICECandidate failed!");
    }
  
    // Handles clicks on the "Send" button by transmitting
    // a message to the remote peer.
    
    function sendMessage() {
      var message = messageInputBox.value;
      sendChannel.send(message);
      
      // Clear the input box and re-focus it, so that we're
      // ready for the next message.
      
      messageInputBox.value = "";
      messageInputBox.focus();
    }
    
    // Handle status changes on the local end of the data
    // channel; this is the end doing the sending of data
    // in this example.
    
    function handleSendChannelStatusChange(event) {
      if (sendChannel) {
        var state = sendChannel.readyState;
      
        if (state === "open") {
          messageInputBox.disabled = false;
          messageInputBox.focus();
          sendButton.disabled = false;
          disconnectButton.disabled = false;
          connectButton.disabled = true;
        } else {
          messageInputBox.disabled = true;
          sendButton.disabled = true;
          connectButton.disabled = false;
          disconnectButton.disabled = true;
        }
      }
    }
    
    // Called when the connection opens and the data
    // channel is ready to be connected to the remote.
    
    function receiveChannelCallback(event) {
      receiveChannel = event.channel;
      receiveChannel.onmessage = handleReceiveMessage;
      receiveChannel.onopen = handleReceiveChannelStatusChange;
      receiveChannel.onclose = handleReceiveChannelStatusChange;
    }
    
    // Handle onmessage events for the receiving channel.
    // These are the data messages sent by the sending channel.
    
    function handleReceiveMessage(event) {
      var el = document.createElement("p");
      var txtNode = document.createTextNode(event.data);
      
      el.appendChild(txtNode);
      receiveBox.appendChild(el);
    }
    
    // Handle status changes on the receiver's channel.
    
    function handleReceiveChannelStatusChange(event) {
      if (receiveChannel) {
        console.log("Receive channel's status has changed to " +
                    receiveChannel.readyState);
      }
      
      // Here you would do stuff that needs to be done
      // when the channel's status changes.
    }
    
    // Close the connection, including data channels if they're open.
    // Also update the UI to reflect the disconnected status.
    
    function disconnectPeers() {
    
      // Close the RTCDataChannels if they're open.
      
      sendChannel.close();
      receiveChannel.close();
      
      // Close the RTCPeerConnections
      
      localConnection.close();
      remoteConnection.close();
  
      sendChannel = null;
      receiveChannel = null;
      localConnection = null;
      remoteConnection = null;
      
      // Update user interface elements
      
      connectButton.disabled = false;
      disconnectButton.disabled = true;
      sendButton.disabled = true;
      
      messageInputBox.value = "";
      messageInputBox.disabled = true;
    }
    
    // Set up an event listener which will run the startup
    // function once the page is done loading.
    
    window.addEventListener('load', startup, false);
  })();
  