WebRTC-텍스트 데모

이 장에서는 별도의 장치에있는 두 사용자가 WebRTC를 사용하여 서로 메시지를 보낼 수있는 클라이언트 응용 프로그램을 구축 할 것입니다. 신청서에는 두 페이지가 있습니다. 하나는 로그인 용이고 다른 하나는 다른 사용자에게 메시지를 보내기위한 것입니다.

두 페이지는 div 태그가됩니다. 대부분의 입력은 간단한 이벤트 핸들러를 통해 이루어집니다.

시그널링 서버

WebRTC 연결을 생성하려면 클라이언트는 WebRTC 피어 연결을 사용하지 않고 메시지를 전송할 수 있어야합니다. 여기에서 HTML5 WebSockets-두 끝점 사이의 양방향 소켓 연결-웹 서버와 웹 브라우저를 사용할 것입니다. 이제 WebSocket 라이브러리 사용을 시작하겠습니다. server.js 파일을 생성하고 다음 코드를 삽입하십시오-

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090});
  
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server"); 
});

첫 번째 줄에는 이미 설치 한 WebSocket 라이브러리가 필요합니다. 그런 다음 포트 9090에 소켓 서버를 만듭니다. 다음으로 연결 이벤트를 수신합니다 . 이 코드는 사용자가 서버에 WebSocket 연결을 할 때 실행됩니다. 그런 다음 사용자가 보낸 모든 메시지를 듣습니다. 마지막으로 연결된 사용자에게 "Hello from server"라는 응답을 보냅니다.

시그널링 서버에서는 각 연결에 문자열 기반 사용자 이름을 사용하여 메시지를 보낼 위치를 알 수 있습니다. 연결 핸들러를 약간 변경해 보겠습니다.

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON");
      data = {}; 
   } 
});

이렇게하면 JSON 메시지 만 허용됩니다. 다음으로 연결된 모든 사용자를 어딘가에 저장해야합니다. 이를 위해 간단한 Javascript 객체를 사용합니다. 파일 상단 변경-

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

클라이언트에서 오는 모든 메시지에 대해 유형 필드 를 추가 할 것 입니다. 예를 들어 사용자가 로그인을 원하면 로그인 유형 메시지 를 보냅니다 . 그것을 정의합시다-

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   }
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login", 
               success: false
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
			
         break;
			
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break;
   } 
});

사용자가 로그인 유형 으로 메시지를 보내면 우리는-

  • 이 사용자 이름으로 이미 로그인 한 사람이 있는지 확인하십시오.
  • 그렇다면 사용자에게 성공적으로 로그인하지 않았다고 알립니다.
  • 이 사용자 이름을 사용하는 사람이 없으면 사용자 이름을 연결 개체에 키로 추가합니다.
  • 명령이 인식되지 않으면 오류를 보냅니다.

다음 코드는 연결에 메시지를 보내기위한 도우미 함수입니다. server.js 파일에 추가 하십시오-

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

사용자가 연결을 끊으면 연결을 정리해야합니다. 닫기 이벤트가 발생 하면 사용자를 삭제할 수 있습니다 . 연결 핸들러에 다음 코드를 추가하십시오 -

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

로그인에 성공하면 사용자는 다른 사람에게 전화를 걸고 싶어합니다. 그는 그것을 달성하기 위해 다른 사용자에게 제안 을 해야 합니다. 오퍼 핸들러 추가 -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name); 
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null){ 
   //setting that UserA connected with UserB 
   connection.otherName = data.name; 
	
      sendTo(conn, { 
         type: "offer", 
         offer: data.offer, 
         name: connection.name 
      }); 
     
   break;

먼저, 우리 가 호출하려는 사용자 의 연결 을 얻습니다 . 존재하는 경우 제안 세부 정보를 보냅니다 . 또한 연결 개체 에 otherName 을 추가 합니다. 이것은 나중에 쉽게 찾을 수 있도록 만들어졌습니다.

응답에 대한 응답에는 오퍼 핸들러 에서 사용한 것과 유사한 패턴이 있습니다. 우리 서버는 다른 사용자에 대한 응답 으로 모든 메시지를 통과 합니다. 오퍼 핸들러 뒤에 다음 코드를 추가하십시오 -

case "answer": 
   console.log("Sending answer to: ", data.name); 
	
   //for ex. UserB answers UserA 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name;
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
	
   break;

마지막 부분은 사용자 간의 ICE 후보를 처리하는 것입니다. 우리는 사용자간에 메시지를 전달하는 것과 동일한 기술을 사용합니다. 주요 차이점은 후보 메시지가 순서에 관계없이 사용자 당 여러 번 발생할 수 있다는 것입니다. 후보 핸들러 추가 -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
	
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
	
   break;

사용자가 다른 사용자와 연결을 끊을 수 있도록 끊기 기능을 구현해야합니다. 또한 모든 사용자 참조를 삭제하도록 서버에 지시합니다. 휴가 핸들러 추가 -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null;
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, { 
         type: "leave" 
      }); 
   }
	
   break;

또한 다른 사용자에게 나가기 이벤트를 보내서 그에 따라 피어 연결을 끊을 수 있습니다. 사용자가 시그널링 서버에서 연결을 끊는 경우도 처리해야합니다. 닫기 핸들러를 수정 해 보겠습니다.

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;  
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            }); 
         }  
      } 
   } 
});

다음은 시그널링 서버의 전체 코드입니다.

//require our websocket library 
var WebSocketServer = require('ws').Server;
 
//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
  
//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	 
   //when server gets a message from a connected user 
   connection.on('message', function(message) {
	 
      var data; 
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		  
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login": 
            console.log("User logged", data.name); 
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name; 
					
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            }
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
				
            break;
				
         case "candidate": 
            console.log("Sending candidate to:",data.name);
            var conn = users[data.name];  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave"
               });
            }  
				
            break;
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            }); 
				
            break;
				
      }  
   });
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
         delete users[connection.name]; 
			
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
         } 
      } 
   });
	
   connection.send("Hello world");
	
});
  
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

클라이언트 애플리케이션

이 애플리케이션을 테스트하는 한 가지 방법은 두 개의 브라우저 탭을 열고 서로 메시지를 보내는 것입니다.

먼저 부트 스트랩 라이브러리 를 설치해야합니다 . 부트 스트랩은 웹 애플리케이션 개발을위한 프런트 엔드 프레임 워크입니다. 자세한 내용은http://getbootstrap.com/.예를 들어 "textchat"이라는 폴더를 만듭니다. 이것이 우리의 루트 애플리케이션 폴더가 될 것입니다. 이 폴더 안에 package.json 파일을 만들고 (npm 종속성을 관리하는 데 필요함) 다음을 추가하십시오.

{ 
   "name": "webrtc-textochat", 
   "version": "0.1.0", 
   "description": "webrtc-textchat", 
   "author": "Author", 
   "license": "BSD-2-Clause" 
}

그런 다음 npm install bootstrap을 실행하십시오 . 그러면 textchat / node_modules 폴더 에 부트 스트랩 라이브러리가 설치됩니다 .

이제 기본 HTML 페이지를 만들어야합니다. 다음 코드를 사용하여 루트 폴더에 index.html 파일을 만듭니다.

<html> 

   <head> 
      <title>WebRTC Text Demo</title> 
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/> 
   </head>
 
   <style> 
      body { 
         background: #eee; 
         padding: 5% 0; 
      } 
   </style>
	
   <body> 
      <div id = "loginPage" class = "container text-center"> 
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4"> 
               <h2>WebRTC Text Demo. Please sign in</h2> 
               <label for = "usernameInput" class = "sr-only">Login</label> 
               <input type = "email" id = "usernameInput" 
                  class = "form-control formgroup" placeholder = "Login" 
                  required = "" autofocus = "">
               <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock">
                  Sign in</button> 
            </div> 
         </div> 
			
      </div>
		
      <div id = "callPage" class = "call-page container">
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4 text-center"> 
               <div class = "panel panel-primary"> 
                  <div class = "panel-heading">Text chat</div> 
                  <div id = "chatarea" class = "panel-body text-left"></div> 
               </div> 
            </div> 
         </div>
			
         <div class = "row text-center form-group"> 
            <div class = "col-md-12"> 
               <input id = "callToUsernameInput" type = "text" 
                  placeholder = "username to call" /> 
               <button id = "callBtn" class = "btn-success btn">Call</button> 
               <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
            </div> 
         </div> 
			
         <div class = "row text-center"> 
            <div class = "col-md-12"> 
               <input id = "msgInput" type = "text" placeholder = "message" /> 
               <button id = "sendMsgBtn" class = "btn-success btn">Send</button> 
            </div> 
         </div>
			
      </div> 
		
      <script src = "client.js"></script> 
		
   </body>
 
</html>

이 페이지는 익숙 할 것입니다. 부트 스트랩 CSS 파일 을 추가했습니다 . 또한 두 페이지를 정의했습니다. 마지막으로 사용자로부터 정보를 얻기위한 여러 텍스트 필드와 버튼을 만들었습니다. "채팅"페이지에서 모든 메시지가 표시되는 "chatarea"ID가있는 div 태그를 볼 수 있습니다. client.js 파일 에 대한 링크를 추가했습니다 .

이제 시그널링 서버와 연결을 설정해야합니다. 다음 코드를 사용하여 루트 폴더에 client.js 파일을 만듭니다.

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
}; 
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);
	
   var data = JSON.parse(msg.data);
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate":
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};

이제 노드 서버 를 통해 신호 서버를 실행 합니다 . 그런 다음 루트 폴더 내에서 정적 명령을 실행 하고 브라우저에서 페이지를 엽니 다. 다음 콘솔 출력이 표시되어야합니다.

다음 단계는 고유 한 사용자 이름으로 사용자 로그인을 구현하는 것입니다. 우리는 단순히 사용자 이름을 서버로 보낸 다음 가져 왔는지 여부를 알려줍니다. client.js 파일에 다음 코드를 추가 하십시오-

//****** 
//UI selectors block
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput'); 
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
callPage.style.display = "none"; 

// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value;
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 

   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
   } 
	
};

먼저 페이지의 요소에 대한 몇 가지 참조를 선택합니다. 우리는 호출 페이지를 숨 깁니다. 그런 다음 로그인 버튼에 이벤트 리스너를 추가합니다. 사용자가 클릭하면 사용자 이름을 서버로 보냅니다. 마지막으로 handleLogin 콜백을 구현합니다. 로그인에 성공하면 통화 페이지를 표시하고 피어 연결을 설정하고 데이터 채널을 만듭니다.

데이터 채널로 피어 연결을 시작하려면 다음이 필요합니다.

  • RTCPeerConnection 객체 만들기
  • RTCPeerConnection 객체 내부에 데이터 채널 생성

"UI 선택기 블록"에 다음 코드를 추가하십시오-

var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 
var chatArea = document.querySelector('#chatarea'); 

var yourConn; 
var dataChannel;

handleLogin 함수 수정 -

function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //**********************
		
      //using Google public stun server 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
      }; 
		
      yourConn = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]});
		
      // Setup ice handling 
      yourConn.onicecandidate = function (event) { 
         if (event.candidate) { 
            send({
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      };
		
      //creating data channel 
      dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
		
      dataChannel.onerror = function (error) { 
         console.log("Ooops...error:", error); 
      };
		
      //when we receive a message from the other peer, display it on the screen 
      dataChannel.onmessage = function (event) { 
         chatArea.innerHTML += connectedUser + ": " + event.data + "<br />"; 
      };
		
      dataChannel.onclose = function () { 
         console.log("data channel is closed"); 
      };  
   } 
};

로그인이 성공하면 응용 프로그램은 RTCPeerConnection 객체를 생성하고 발견 된 모든 icecandidate를 다른 피어로 보내는 onicecandidate 핸들러를 설정 합니다. 또한 dataChannel을 생성합니다. RTCPeerConnection 객체를 생성 할 때 생성자 옵션의 두 번째 인수 인 [{RtpDataChannels : true}]는 Chrome 또는 Opera를 사용하는 경우 필수입니다. 다음 단계는 다른 피어에 대한 오퍼를 작성하는 것입니다. 사용자가 제안을 받으면 답변 을 작성하고 ICE 후보자 거래를 시작합니다. client.js 파일에 다음 코드를 추가 하십시오-

//initiating a call
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value;
	
   if (callToUsername.length > 0) {
	
      connectedUser = callToUsername;
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
		
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      });  
   } 
});
  
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer));
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   });
};
  
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
  
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};

오퍼를 시작하는 Call 버튼에 클릭 핸들러를 추가 합니다. 그런 다음 onmessage 핸들러에서 예상하는 여러 핸들러를 구현 합니다. 두 사용자가 모두 연결될 때까지 비동기 적으로 처리됩니다.

다음 단계는 끊기 기능을 구현하는 것입니다. 이렇게하면 데이터 전송이 중지되고 다른 사용자에게 데이터 채널을 닫으라고 알립니다. 다음 코드를 추가하십시오-

//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
}); 
 
function handleLeave() { 
   connectedUser = null; 
   yourConn.close(); 
   yourConn.onicecandidate = null; 
};

사용자가 전화 끊기 버튼을 클릭하면-

  • 다른 사용자에게 "나가기"메시지를 보냅니다.
  • RTCPeerConnection과 데이터 채널이 닫힙니다.

마지막 단계는 다른 피어에게 메시지를 보내는 것입니다. "메시지 보내기"버튼에 "클릭"핸들러 추가-

//when user clicks the "send message" button 
sendMsgBtn.addEventListener("click", function (event) { 
   var val = msgInput.value; 
   chatArea.innerHTML += name + ": " + val + "<br />"; 
	
   //sending a message to a connected peer 
   dataChannel.send(val); 
   msgInput.value = ""; 
});

이제 코드를 실행하십시오. 두 개의 브라우저 탭을 사용하여 서버에 로그인 할 수 있어야합니다. 그런 다음 다른 사용자에 대한 피어 연결을 설정하고 그에게 메시지를 보낼 수있을뿐만 아니라 "전화 끊기"버튼을 클릭하여 데이터 채널을 닫을 수 있습니다.

다음은 전체 client.js 파일입니다.

//our username 
var name; 
var connectedUser; 

//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090'); 

conn.onopen = function () { 
   console.log("Connected to the signaling server");
};
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data); 
   var data = JSON.parse(msg.data); 
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
}; 

conn.onerror = function (err) { 
   console.log("Got error", err); 
}; 

//alias for sending JSON encoded messages 
function send(message) { 

   //attach the other peer username to our messages
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};
 
//****** 
//UI selectors block 
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 

var chatArea = document.querySelector('#chatarea'); 
var yourConn; 
var dataChannel; 
callPage.style.display = "none"; 

// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 

   if (success === false) {
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //using Google public stun server 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
      }; 
		
      yourConn = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); 
		
      // Setup ice handling 
      yourConn.onicecandidate = function (event) { 
         if (event.candidate) { 
            send({ 
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      }; 
		
      //creating data channel 
      dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
		
      dataChannel.onerror = function (error) { 
         console.log("Ooops...error:", error); 
      }; 
		
      //when we receive a message from the other peer, display it on the screen 
      dataChannel.onmessage = function (event) { 
         chatArea.innerHTML += connectedUser + ": " + event.data + "<br />"; 
      }; 
		
      dataChannel.onclose = function () { 
         console.log("data channel is closed"); 
      };
		
   } 
};
 
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
         yourConn.setLocalDescription(offer); 
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
	
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
      send({ 
         type: "answer", 
         answer: answer 
      }); 
   }, function (error) { 
      alert("Error when creating an answer"); 
   });
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
 
//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
}); 

function handleLeave() { 
   connectedUser = null; 
   yourConn.close(); 
   yourConn.onicecandidate = null; 
};
 
//when user clicks the "send message" button 
sendMsgBtn.addEventListener("click", function (event) { 
   var val = msgInput.value; 
   chatArea.innerHTML += name + ": " + val + "<br />"; 
	
   //sending a message to a connected peer 
   dataChannel.send(val); 
   msgInput.value = ""; 
});