title: Quickblox Javascript SDK + Angular + webRTC - Cannot read property 'send' of undefined
body: <p>I have stood a fully functional webRTC Video Conferencing client using Quickblox Javascript SDK + Angular + webRTC. A strange thing is happening, Every time I clear cache and log in from scratch, when I initiate a call I am getting the following error:</p>
  
  <pre><code>MediaStream {id: "iAmALongalphanumericStringThatGoesHere", active: true, onaddtrack: null, onremovetrack: null, onactive: null…}
      quickblox.min.js:86149 [QBWebRTC]: Call, extension: {"name":"Erik Grosskurth","id":6184}
      quickblox.min.js:86149 [QBWebRTC]: _createPeer, iceServers: {"iceServers":[{"url":"stun:stun.l.google.com:19302","urls":"stun:stun.l.google.com:19302"},{"url":"stun:turn.quickblox.com","username":"quickblox","credential":"iAmALongalphanumericStringThatGoesHere","urls":"stun:turn.quickblox.com"},{"url":"turn:turn.quickblox.com:3478?transport=udp","username":"quickblox","credential":"iAmALongalphanumericStringThatGoesHere","urls":"turn:turn.quickblox.com:3478?transport=udp"},{"url":"turn:turn.quickblox.com:3478?transport=tcp","username":"quickblox","credential":"iAmALongalphanumericStringThatGoesHere","urls":"turn:turn.quickblox.com:3478?transport=tcp"}]}
      quickblox.min.js:86149 [QBWebRTC]: RTCPeerConnection init. userID: 6184, sessionID: 73eabb0a-21f1-4aa4-b928-f669090041d3, type: offer
      telemed.js:467 null
      quickblox.min.js:86149 [QBWebRTC]: getAndSetLocalSessionDescription success
      quickblox.min.js:86149 [QBWebRTC]: _startDialingTimer, dialingTimeInterval: 5000
      quickblox.min.js:86149 [QBWebRTC]: _dialingCallback, answerTimeInterval: 0
      quickblox.min.js:73302 Uncaught TypeError: Cannot read property 'send' of undefined
          at Strophe.Websocket._onIdle (quickblox.min.js:73302)
          at Strophe.Connection._onIdle (quickblox.min.js:71559)
          at Strophe.Connection.flush (quickblox.min.js:70444)
          at Strophe.Websocket._send (quickblox.min.js:73407)
          at Strophe.Connection.send (quickblox.min.js:70429)
          at WebRTCSignalingProvider.sendMessage (quickblox.min.js:87369)
          at WebRTCSession.processCall (quickblox.min.js:86798)
          at _dialingCallback (quickblox.min.js:85422)
          at RTCPeerConnection._startDialingTimer (quickblox.min.js:85429)
          at quickblox.min.js:86422
  </code></pre>
  
  <p>Funny thing is that if I refresh the page, It works fine with no problem at all. I had seen this during development when I had a $scope issue but I have backtracked and cannot identify when it started happening. Can someone identify what the cause of this error is?</p>
  
  <p>Here is my controller code:</p>
  
  <pre><code>app.controller('patientCtrl', function($scope, $http, $location) {
  
          QB.init(QBApp.appId, QBApp.authKey, QBApp.authSecret, config);
          $scope.peers = [];
          $scope.occupants = [];
          $scope.recipient = {};
          $scope.recipients = {};
          $scope.session = {};
          $scope.dialogs = {};
          $scope.modal = false;
          $scope.callWaiting = false;
          $scope.$watch('peers');
          $scope.$watch('occupants');
          $scope.$watch('session');
          $scope.$watch('modal');
          $scope.$watch('callOptions');
          $scope.$watch('callWaiting');
          $scope.$watch('toggleConnCtrl');
          $scope.user = JSON.parse(sessionStorage.getItem('userParams'));
          var patient = {
              userId: $scope.user.id,
              password: $scope.user.password,
              login: $scope.user.full_name
          };
          $scope.localMediaParams = {
              audio: true,
              video: true,
              options: {
                  muted: true,
                  mirror: true
              },
              elemId: 'localVideoEl',
              optional: {
                  minWidth: 240,
                  maxWidth: 320,
                  minHeight: 160,
                  maxHeight: 240
              }
          };
  
      // HANDLE VISIT DATA AND SET UP CHAT
  
          $scope.reqVisit = {
              sKey : sessionStorage.getItem('key'),
              sType: 'visit',
              iObjectId: 1142606//sessionStorage.getItem('sessionId')
          };
          $http.post('/ws/Util.asmx/returnObject',$scope.reqVisit).then(function(response) {
              $scope.visit = response.data.d;
              QB.createSession(function(err,result){
                  if (result) {
                      QB.login($scope.user, function(loginErr, loginUser){
                          if (loginErr) {
                              console.log('log in error');
                              console.log(loginErr);
                          }else {
                              $scope.user = loginUser;
                              console.log($scope.user);
                              QB.chat.connect(patient, function(err, result) {
                                  if (result) {   
                                      $scope.roomData = JSON.parse(sessionStorage.getItem('userParams'));
                                      $scope.user.user_tags = $scope.roomData.tag_list;
                                      QB.users.update($scope.user.id, {tag_list: $scope.roomData.tag_list}, function(err, user){
                                          if (user) {
                                              console.log('updated room');
                                          } else  {
                                              console.log(err); 
                                          }
                                      });
                                      $scope.updatePeerList($scope);
                                      QB.chat.dialog.list({name: $scope.user.user_tags}, function(err, resDialogs) {
                                          if (resDialogs) {
                                              if (resDialogs.total_entries === 0) {
                                                  var chatParams = {
                                                      type: 2,
                                                      occupants_ids: $scope.occupants,
                                                      name: $scope.user.user_tags
                                                  };
                                                  QB.chat.dialog.create(chatParams, function(err, createdDialog) {
                                                      if (createdDialog) {
                                                          console.log(createdDialog);
                                                      } else {
                                                          console.log(err);
                                                      }
                                                  });
                                              }else {
                                                  angular.forEach(resDialogs.items, function(item, i, arr) {
                                                      console.log('item found');
                                                      $scope.chatSession = item;
  
                                                      // join room
                                                      if ($scope.chatSession.type !== 3) {
                                                          QB.chat.muc.join($scope.chatSession.xmpp_room_jid, function() {
  
                                                          });
                                                      }
                                                      $scope.occupants = [];
                                                      $scope.chatSession.occupants_ids.map(function(userId) {
                                                          if ($scope.user.id !== userId &amp;&amp; $scope.occupants.indexOf(userId) &lt; 1) {
                                                              $scope.occupants.push(userId);
                                                              $scope.$apply($scope.occupants);
                                                          }
                                                      });
  
                                                      angular.forEach($scope.occupants, function (user_id) {
                                                          if (user_id !== $scope.user.id) {
                                                              var msg = {
                                                                  type: 'chat',
                                                                  extension: {
                                                                      notification_type: 1,
                                                                      _id: $scope.chatSession.xmpp_room_jid,
                                                                      name: $scope.user.full_name,
                                                                      occupant: $scope.user.id
                                                                  }
                                                              };
                                                              console.log(user_id);
                                                              QB.chat.send(user_id, msg);
                                                          }
                                                      });
                                                  });
                                              }
                                          } else {
                                              console.log('error with chat.dialog.list');
                                              console.log(err);
                                          }
                                      });                                                 
                                  } else { 
                                      console.log('chat.connect failed');
                                      console.log(res); 
                                  }
                              });
                          }
                      });
                  }else if (err) {
                      console.log(err);
                  }
              });
          },function(errorHandler) {
              console.log(errorHandler);
              $scope.logout();
          });
  
      // HANDLE VIDEO CALLING
  
          $scope.startCall = function() {
              if (angular.equals($scope.recipients, {})) {
                  $scope.flyOutPeers = !$scope.flyOutPeers;
                  alert('Please choose a person to call');
              }else {
                  if (!angular.equals($scope.session, {}) &amp;&amp; !angular.equals($scope.session, undefined)) {
                      console.log('session hasn\'t been started');
                      $scope.session.stop({});
                      $scope.session = {};
                      return false;
                  }else {
                      $scope.session = QB.webrtc.createNewSession($scope.occupants, QB.webrtc.CallType.VIDEO);
                      $scope.modal = true;
                      $scope.callWaiting = true;
                      $scope.session.getUserMedia($scope.localMediaParams, function(err, stream) {
                          if (err){
                              console.log(err);
                          }else{
                              console.log(stream);
                              $scope.session.call($scope.recipient, function(error) {
                                  console.log(error);
                              });
                          }
                      });
                  }
              }
          };
          $scope.answerCall = function() {
              $scope.modal = false;
              $scope.callOptions = false;
              $scope.toggleConnCtrl = true;
              $scope.session.getUserMedia($scope.localMediaParams, function(err, stream) {
                  if (err){
                      console.log(err);
                      $scope.session.stop({});
                  }else{
                      console.log(stream);
                      $scope.session.accept({});
                  }
              });
          };
          $scope.declineCall = function() {
              $scope.session.reject({});
              //$scope.session.stop({});
              $scope.modal = false;
              $scope.callOptions = false;
              $scope.toggleConnCtrl = false;
              $scope.session = {};
          };
          $scope.endCall = function() {
              $scope.session.stop({});
              $scope.modal = false;
              $scope.callWaiting = false;
              $scope.toggleConnCtrl = false;
              $scope.session = {};
          };
  
      // HANDLE LISTENERS
  
          QB.webrtc.getMediaDevices('videoinput').then(function(devices) {
              if(devices.length &gt; 1) {
                  //console.log(devices);
                  console.log('you have more than one media device')
              }
          }).catch(function(error) {
              console.warn('getMediaDevices', error);
          });
  // Call was placed
          QB.webrtc.onCallListener = function(session, extension) {
              $scope.callerData = extension;
              $scope.modal = true;$scope.$apply($scope.modal);
              $scope.callOptions = true;$scope.$apply($scope.callOptions);
              $scope.session = {};$scope.$apply($scope.session);
          };
  // No answer
          QB.webrtc.onUserNotAnswerListener = function(session, userId) {
              console.log('User '+session.currentUserID+' is not answering');
              $scope.toggleConnCtrl = true;$scope.$apply($scope.toggleConnCtrl);
              $scope.modal = false;$scope.$apply($scope.modal);
              $scope.callWaiting = false;$scope.$apply($scope.callWaiting);
          };
  // Call was answered
          QB.webrtc.onAcceptCallListener = function(session, userId, extension) {
              console.log('User '+session.currentUserID+' just answered');
              $scope.toggleConnCtrl = true;$scope.$apply($scope.toggleConnCtrl);
              $scope.modal = false;$scope.$apply($scope.modal);
              $scope.callWaiting = false;$scope.$apply($scope.callWaiting);
          };  
  // Call was declined
          QB.webrtc.onRejectCallListener = function(session, userId, extension) {
              console.log('User '+session.currentUserID+' sent you to voicemail');
              $scope.toggleConnCtrl = false;$scope.$apply($scope.toggleConnCtrl);
              $scope.modal = false;$scope.$apply($scope.modal);
              $scope.callWaiting = false;$scope.$apply($scope.callWaiting);
              $scope.session = {};$scope.$apply($scope.session);
          };  
  // End call
          QB.webrtc.onStopCallListener = function(session, userId, extension) {
              console.log('User '+session.currentUserID+' hung up');
              $scope.toggleConnCtrl = false;$scope.$apply($scope.toggleConnCtrl);
              $scope.modal = false;$scope.$apply($scope.modal);
              $scope.callOptions = false;$scope.$apply($scope.callOptions);
              $scope.session = {};$scope.$apply($scope.session);
          };                                  
  
          QB.webrtc.onRemoteStreamListener = function(session, userID, remoteStream) {
              $scope.session.attachMediaStream('remoteVideoEl', remoteStream);
          };                              
  
          QB.webrtc.onSessionConnectionStateChangedListener = function(session, userID, connectionState) {
  
          };                                                                              
  
          QB.chat.onMessageListener = function onMessage(userId, message) {
              if (message.extension &amp;&amp; message.extension.notification_type === '1') {
                  //console.log(message);
                  console.log(message.extension.name+' just logged on');
                  $scope.updatePeerList($scope);  
              }else if (message.extension &amp;&amp; message.extension.notification_type === '2') {
                  //console.log(message);
                  console.log(message.extension.name+' just logged out');
                  $scope.updatePeerList($scope);  
              }
          };
  
      // HANDLE USERS
  
          $scope.updatePeerList = function($scope) {
              QB.users.get({tags: [$scope.user.user_tags]}, function(err, result){
                  if (result) {
                      var newObj = {};
                      $scope.peers = [];
                      $scope.occupants = [];
                      angular.forEach(result.items, function(e) {
                          if ($scope.user.id !== e.user.id &amp;&amp; $scope.occupants.indexOf(e.user.id) &lt; 1) {
                              $scope.occupants.push(e.user.id);
                              $scope.$apply($scope.occupants);
                          }
                          if (e.user.full_name !== $scope.user.full_name) {
                              var ONE_HOUR = 60 * 60 * 1000,
                                  d = new Date(e.user.last_request_at);
                              if (((new Date) - d) &lt; ONE_HOUR) { 
                                  newObj.name = e.user.full_name;
                                  newObj.userData = e.user;
                                  newObj.status = true;
                                  $scope.peers.push(newObj);
                              }else {
                                  newObj.name = e.user.full_name;
                                  newObj.userData = e.user;
                                  newObj.status = false;
                                  $scope.peers.push(newObj);
                              }   
                          }
                      });
                      $scope.$apply($scope.peers);
                  }else {
                      console.log('error getting peer list');
                      console.log(err);
                  }
              });
          }       
  
          $scope.setRecipient = function(ele, name, index) {
              if (angular.equals($scope.recipients, {})) { 
                  $scope.recipients[index] = false;
              }else if (!angular.equals($scope.recipients, {}) &amp;&amp; $scope.recipients[index]) {
                  $scope.recipients[index] = true;
              }else {
                  angular.forEach($scope.recipients, function(value, key) {
                      if (key === index) {
                          $scope.recipients[index] = true;
                      }else {
                          $scope.recipients[key] = false;
                      }
                  });
              }
              if($scope.recipients[index]) {
                  $scope.recipients[index] = false;
                  $scope.recipient = "";
              } else {
                  $scope.recipients[index] = true;
                  $scope.recipient = {
                      name: name,
                      id: ele.peer.userData.id
                  }
              }
          };
  
      // HANDLE LOG OUT and UNLOAD
  
          $scope.logout = function() {
              QB.logout(function(err, result){
                  if (result) {
                      // success
                  } else {
                      // error
                  }
              });
              QB.users.update($scope.user.id, {tag_list: ""}, function(err, user){
                  if (user) {
                      console.log('changed rooms');
                      console.log(user); 
                  } else  {
                      console.log(err); 
                  }
              });
              console.log('change path');
              $location.path('/');
          }
  
          $scope.$on('onBeforeUnload', function (e, confirmation) {
              confirmation.message = "All data will be lost.";
              e.preventDefault();
              QB.users.update($scope.user.id, {tag_list: ""}, function(err, user){
                  if (user) {
                      console.log('changed rooms');
                      console.log(user); 
                  } else  {
                      console.log(err); 
                  }
              });
              var msg = {
                  type: 'chat',
                  extension: {
                      notification_type: 2,
                      _id: $scope.chatSession.xmpp_room_jid,
                      name: $scope.user.full_name,
                      occupant: $scope.user.id
                  }
              };
              angular.forEach($scope.occupants, function(e) {
                  if (e !== $scope.user.id) {
                      QB.chat.send(e, msg);
                  }
              });
  
          });
          $scope.$on('onUnload', function () {
              console.log('onUnload'); // Use 'Preserve Log' option in Console
              //$scope.logout();
          });
  
  
  
  
  
      });
  </code></pre>
  
  <p>Here is the block of code from the SDK where the error is occurring:</p>
  
  <pre><code> _onIdle: function () {
          var data = this._conn._data;
          if (data.length &gt; 0 &amp;&amp; !this._conn.paused) {
              for (var i = 0; i &lt; data.length; i++) {
                  if (data[i] !== null) {
                      var stanza, rawStanza;
                      if (data[i] === "restart") {
                          stanza = this._buildStream().tree();
                      } else {
                          stanza = data[i];
                      }
                      rawStanza = Strophe.serialize(stanza);
                      console.log(rawStanza);
                      this._conn.xmlOutput(stanza);
                      this._conn.rawOutput(rawStanza);
  // HERE IS WHERE I GET THE ERROR
                      this.socket.send(rawStanza);
  // HERE IS WHERE I GET THE ERROR
                  }
              }
              this._conn._data = [];
          }
      }
  </code></pre>
  
  <p>When the call initiates a message is sent to the opponent you are trying to connect with.</p>
  
  <p>Here is the message from the call that doesn't work:</p>
  
  <pre><code>&lt;message to="6184-5@chatcaduceustelemed.quickblox.com" type="headline" id="58d15d3311d18b4a8d000001" xmlns="jabber:client"&gt;
    &lt;extraParams xmlns="jabber:client"&gt;
      &lt;name&gt;Erik Grosskurth&lt;/name&gt;
      &lt;id&gt;6184&lt;/id&gt;
      &lt;sessionID&gt;5ce4c0e0-02cc-4baa-86b0-91dfe28ac0d4&lt;/sessionID&gt;
      &lt;callType&gt;1&lt;/callType&gt;
      &lt;callerID&gt;NaN&lt;/callerID&gt;
      &lt;opponentsIDs&gt;
        &lt;opponentID&gt;6184&lt;/opponentID&gt;
      &lt;/opponentsIDs&gt;
      &lt;sdp&gt; &lt;/sdp&gt;
      &lt;moduleIdentifier&gt;WebRTCVideoChat&lt;/moduleIdentifier&gt;
      &lt;signalType&gt;call&lt;/signalType&gt;
      &lt;platform&gt;web&lt;/platform&gt;
    &lt;/extraParams&gt;
  &lt;/message&gt;
  </code></pre>
  
  <p>And here is the message after I refresh and the call initiates:</p>
  
  <pre><code>&lt;message to="6184-5@chatcaduceustelemed.quickblox.com" type="headline" id="58d15c8cd15a7a2513000001" xmlns="jabber:client"&gt;
    &lt;extraParams xmlns="jabber:client"&gt;
      &lt;name&gt;Erik Grosskurth&lt;/name&gt;
      &lt;id&gt;6184&lt;/id&gt;
      &lt;sessionID&gt;1a37ad44-c408-4b1d-bda8-436f3322d7e9&lt;/sessionID&gt;
      &lt;callType&gt;1&lt;/callType&gt;
      &lt;callerID&gt;6186&lt;/callerID&gt;
      &lt;opponentsIDs&gt;
        &lt;opponentID&gt;6184&lt;/opponentID&gt;
      &lt;/opponentsIDs&gt;
      &lt;sdp&gt;&lt;/sdp&gt;
      &lt;moduleIdentifier&gt;WebRTCVideoChat&lt;/moduleIdentifier&gt;
      &lt;signalType&gt;call&lt;/signalType&gt;
      &lt;platform&gt;web&lt;/platform&gt;
    &lt;/extraParams&gt;
  &lt;/message&gt;
  </code></pre>
  
  <p>As you can see the CallerID is NaN on the call that doesn't work and is correct on the one that does work. I tried manually setting this initiatorID like this:</p>
  
  <pre><code>$scope.session = QB.webrtc.createNewSession($scope.occupants, QB.webrtc.CallType.VIDEO);
  
  $scope.session.initiatorID = $scope.user.id;
  $scope.modal = true;
  $scope.callWaiting = true;
  $scope.session.getUserMedia($scope.localMediaParams, function(err, stream) {
      if (err){
          console.log(err);
      }else{
          console.log(stream);
          console.log('placing a call to '+$scope.recipient.name);
          $scope.session.call($scope.recipient, function(error) {
              if(error) {
                  console.log(error);
              } else {
                  console.log('successfully placed call with no errors');
              }
          });
      }
  });
  </code></pre>
  
  <p>But that doesn't work. Please can someone explain why this is occurring??</p>