WebRTC - Metin Demosu

Bu bölümde, ayrı cihazlardaki iki kullanıcının WebRTC kullanarak birbirlerine mesaj göndermesine izin veren bir istemci uygulaması oluşturacağız. Uygulamamız iki sayfadan oluşacaktır. Biri oturum açmak ve diğeri başka bir kullanıcıya mesaj göndermek için.

İki sayfa div etiketleri olacaktır. Girişlerin çoğu basit olay işleyicileri aracılığıyla yapılır.

Sinyal Sunucusu

Bir WebRTC bağlantısı oluşturmak için, istemcilerin bir WebRTC eş bağlantısı kullanmadan mesajları aktarabilmesi gerekir. Burada iki uç nokta arasında çift yönlü bir soket bağlantısı olan HTML5 WebSockets'i kullanacağız - bir web sunucusu ve bir web tarayıcısı. Şimdi WebSocket kitaplığını kullanmaya başlayalım. Server.js dosyasını oluşturun ve aşağıdaki kodu ekleyin -

//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"); 
});

İlk satır, önceden kurmuş olduğumuz WebSocket kitaplığını gerektirir. Ardından 9090 portunda bir soket sunucusu oluşturuyoruz. Ardından bağlantı olayını dinliyoruz . Bu kod, bir kullanıcı sunucuya bir WebSocket bağlantısı yaptığında çalıştırılacaktır. Daha sonra kullanıcı tarafından gönderilen mesajları dinliyoruz. Son olarak bağlanan kullanıcıya "Sunucudan merhaba" diyen bir yanıt gönderiyoruz.

Sinyal verme sunucumuzda, her bağlantı için dizeye dayalı bir kullanıcı adı kullanacağız, böylece mesajları nereye göndereceğimizi bileceğiz. En bizim değiştirelim bağlantı işleyicisi biraz -

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

Bu şekilde sadece JSON mesajlarını kabul ediyoruz. Sonra, tüm bağlı kullanıcıları bir yerde saklamamız gerekiyor. Bunun için basit bir Javascript nesnesi kullanacağız. Dosyamızın üstünü değiştirin -

//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 = {};

Müşteriden gelen her mesaj için bir tip alanı ekleyeceğiz . Örneğin, bir kullanıcı giriş yapmak isterse, giriş türü mesajını gönderir . Tanımlayalım -

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;
   } 
});

Kullanıcı, giriş türü ile bir mesaj gönderirse , biz -

  • Bu kullanıcı adıyla zaten giriş yapan biri olup olmadığını kontrol edin.
  • Öyleyse, kullanıcıya başarıyla oturum açmadığını söyleyin.
  • Bu kullanıcı adını kimse kullanmıyorsa, kullanıcı adını bağlantı nesnesine anahtar olarak ekleriz.
  • Bir komut tanınmazsa, bir hata göndeririz.

Aşağıdaki kod, bir bağlantıya mesaj göndermek için yardımcı bir işlevdir. Bunu server.js dosyasına ekleyin -

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

Kullanıcı bağlantısı kesildiğinde bağlantısını temizlemeliyiz. Kapanış olayı tetiklendiğinde kullanıcıyı silebiliriz . Aşağıdaki kodu bağlantı işleyicisine ekleyin -

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

Başarılı bir şekilde oturum açtıktan sonra, kullanıcı başka birini aramak ister. Bunu başarmak için başka bir kullanıcıya teklif vermelidir . Teklif işleyiciyi ekleyin -

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;

Öncelikle aramaya çalıştığımız kullanıcının bağlantısını alıyoruz. Varsa, ona teklif ayrıntılarını göndeririz . Biz de eklemek otherName için bağlantı nesnesi. Bu, daha sonra bulmanın basitliği için yapılmıştır.

Yanıtı yanıtlamak, teklif işleyicide kullandığımız benzer bir modele sahiptir . Sunucumuz tüm mesajları başka bir kullanıcıya cevap olarak geçirir . Teklif işleyiciden sonra aşağıdaki kodu ekleyin -

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;

Son kısım, kullanıcılar arasında ICE adayını ele almaktır. Mesajları kullanıcılar arasında iletmekle aynı tekniği kullanıyoruz. Temel fark, aday mesajlarının herhangi bir sırada kullanıcı başına birden çok kez olabilmesidir. Aday işleyiciyi ekleyin -

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;

Kullanıcılarımızın başka bir kullanıcıyla bağlantısını kesmelerine izin vermek için, kapatma işlevini uygulamalıyız. Ayrıca sunucuya tüm kullanıcı referanslarını silmesini söyleyecektir. İzin işleyicisini ekleyin -

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;

Bu aynı zamanda diğer kullanıcıya ayrılma olayını da gönderecek, böylece eş bağlantısını uygun şekilde kesebilecektir. Bir kullanıcı sinyal sunucusundan bağlantısını kestiğinde de durumu ele almalıyız. Yakın işleyicimizi değiştirelim -

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" 
            }); 
         }  
      } 
   } 
});

Aşağıdaki, sinyal sunucumuzun kodunun tamamıdır -

//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)); 
}

İstemci Uygulaması

Bu uygulamayı test etmenin bir yolu, iki tarayıcı sekmesi açıp birbirlerine mesaj göndermeye çalışmaktır.

Öncelikle bootstrap kütüphanesini kurmamız gerekiyor . Bootstrap, web uygulamaları geliştirmek için bir ön uç çerçevesidir. Daha fazla bilgi edinebilirsinizhttp://getbootstrap.com/.Örneğin "textchat" adlı bir klasör oluşturun. Bu bizim kök uygulama klasörümüz olacak. Bu klasörün içinde package.json adlı bir dosya oluşturun (npm bağımlılıklarını yönetmek için gereklidir) ve aşağıdakileri ekleyin -

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

Ardından npm install bootstrap komutunu çalıştırın . Bu, bootstrap kitaplığını textchat / node_modules klasörüne yükleyecektir .

Şimdi temel bir HTML sayfası oluşturmamız gerekiyor. Aşağıdaki kodla kök klasörde bir index.html dosyası oluşturun -

<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>

Bu sayfa size tanıdık gelmelidir. Bootstrap css dosyasını ekledik . Ayrıca iki sayfa tanımladık. Son olarak, kullanıcıdan bilgi almak için birkaç metin alanı ve düğme oluşturduk. "Sohbet" sayfasında, tüm mesajlarımızın görüntüleneceği "sohbet alanı" kimliğine sahip div etiketini görmelisiniz. Client.js dosyasına bir bağlantı eklediğimize dikkat edin .

Şimdi sinyal sunucumuzla bir bağlantı kurmamız gerekiyor. Oluştur client.js aşağıdaki kodla kök klasöre -

//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)); 
};

Şimdi sinyal sunucumuzu düğüm sunucusu aracılığıyla çalıştırın . Ardından, kök klasörün içinde statik komutu çalıştırın ve tarayıcı içindeki sayfayı açın. Aşağıdaki konsol çıktısını görmelisiniz -

Bir sonraki adım, benzersiz bir kullanıcı adıyla bir kullanıcı girişi uygulamaktır. Sunucuya sadece bir kullanıcı adı göndeririz, bu da bize bunun alınıp alınmadığını söyler. Aşağıdaki kodu client.js dosyanıza ekleyin -

//****** 
//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 
      //********************** 
   } 
	
};

İlk olarak, sayfadaki elemanlara bazı referanslar seçiyoruz. Çağrı sayfasını gizleriz. Ardından, giriş butonuna bir olay dinleyicisi ekliyoruz. Kullanıcı tıkladığında, kullanıcı adını sunucuya göndeririz. Son olarak, handleLogin geri aramasını gerçekleştiriyoruz. Giriş başarılı olduysa, çağrı sayfasını gösteririz, bir eş bağlantı kurarız ve bir veri kanalı oluştururuz.

Bir veri kanalıyla eş bağlantı başlatmak için ihtiyacımız olan -

  • RTCPeerConnection nesnesini oluşturun
  • RTCPeerConnection nesnemiz içinde bir veri kanalı oluşturun

Aşağıdaki kodu "UI seçici bloğuna" ekleyin -

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

var yourConn; 
var dataChannel;

HandleLogin işlevini değiştirin -

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"); 
      };  
   } 
};

Oturum açma başarılı olursa, uygulama RTCPeerConnection nesnesini oluşturur ve bulunan tüm icecandidates'i diğer eşe gönderen onicecandidate işleyicisini ayarlar . Ayrıca bir dataChannel oluşturur. RTCPeerConnection nesnesini oluştururken, yapıcıdaki ikinci bağımsız değişkenin isteğe bağlı olduğuna dikkat edin: [{RtpDataChannels: true}], Chrome veya Opera kullanıyorsanız zorunludur. Bir sonraki adım, diğer akran için bir teklif oluşturmaktır. Bir kullanıcı teklifi aldıktan sonra bir cevap oluşturur ve ICE adaylarının ticaretine başlar. Aşağıdaki kodu client.js dosyasına ekleyin -

//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)); 
};

Çağrı düğmesine bir teklif başlatan bir tıklama işleyici ekliyoruz . Ardından onmessage işleyicisinin beklediği birkaç işleyiciyi uygularız . Her iki kullanıcı da bir bağlantı kurana kadar eşzamansız olarak işleneceklerdir.

Bir sonraki adım, kapatma özelliğini uygulamaktır. Bu, veri aktarımını durduracak ve diğer kullanıcıya veri kanalını kapatmasını söyleyecektir. Aşağıdaki kodu ekleyin -

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

Kullanıcı Kapat düğmesini tıkladığında -

  • Diğer kullanıcıya bir "ayrılma" mesajı gönderecektir.
  • RTCPeerConnection'ı ve ayrıca veri kanalını kapatacaktır.

Son adım, başka bir akrana mesaj göndermektir. "Mesaj gönder" düğmesine "tıklama" işleyicisini ekleyin -

//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 = ""; 
});

Şimdi kodu çalıştırın. İki tarayıcı sekmesi kullanarak sunucuda oturum açabilmelisiniz. Daha sonra diğer kullanıcıyla bir eş bağlantı kurabilir ve ona bir mesaj gönderebilir, ayrıca “Kapat” düğmesine tıklayarak veri kanalını kapatabilirsiniz.

Aşağıdaki tüm client.js dosyasıdır -

//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 = ""; 
});