cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - Activehtmlicon-personicon-teamoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External CSS

These stylesheets will be added in this order and before the code you write in the CSS editor. You can also add another Pen here, and it will pull the CSS from it. Try typing "font" or "ribbon" below.

Quick-add: + add another resource

Add External JavaScript

These scripts will run in this order and before the code in the JavaScript editor. You can also link to another Pen here, and it will run the JavaScript from it. Also try typing the name of any popular library.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
              <h3>Group Call (audio & video)</h3>
<p>
  New to <a href="https://www.circuit.com/" target="_blank">Circuit</a>? <a href="https://developers.circuit.com/registration"  target="_blank">Register</a> for a <a href="https://developers.circuit.com"  target="_blank">developer</a> account.
</p>
In this example you can start, join or leave a conference call, toggle video. Only up to two remote participants are shown in this example.<br>

Screenshare example can be viewed at <a href="https://rawgit.com/yourcircuit/js-sdk/master/screenshare.html" target="_blank">https://rawgit.com/yourcircuit/js-sdk/master/screenshare.html</a>
<div id="mainWrapper" style="display: none">
  <section id="domainSection">
    <span>Connect to:</span>
    <select id="domain">
      <option value="sdk.circuitsandbox.net">sdk.circuitsandbox.net</option>
    </select>
  </section>
  <section id="connectSection">
    <input type="email" id="email" placeholder="Email address" value="circuitsdk01@gmail.com"/>
    <input type="password" id="password" value="GoCircuit!" placeholder="Password"/>
    <button id="logon" onclick="logon()" style="">Logon</button>
    <button id="logout" onclick="logout()" style="display: none">Logout</button>
    <span>(<span id="logonState">Disconnected</span>)</span>
  </section>
  <section>
    <select id="convList" onchange="onConversationSelected()"></select>
  </section>
  <section>
    <button onclick="start()" id="startButton" class="hide">Start Conference</button>
    <button onclick="join()" id="joinButton" class="hide">Join Conference</button>
    <button onclick="leave()" id="leaveButton" class="hide">Leave Conference</button>
    <button onclick="pull()" id="pullButton" class="hide">Pull Call</button>
  </section>
  <section>
    <hr>
    <input id="enableVideo" type="checkbox" onclick="onVideoChange(this)"/>Enable Video, or
    <input id="enableScreenShare" type="checkbox" onclick="onScreenShareChange(this)"/>Enable ScreenShare
    <div><input id="enableRecording" type="checkbox" onclick="onRecordingChange(this)"/>Record Session</div>
  </section>
  <section>
    <a id="download" download href="">Download recording</a>
  </section>
  <section id="output">
    <div>Call state: <span id="callState"></span></div>
    <div>Media: <span id="media"></span></div>
    <div>Conversation ID: <span id="convId"></span></div>
    <div>Call ID: <span id="callId"></span></div>
    <div>
      <video id="localVideo" width="200px" autoplay="true"></video>
      <video id="remoteVideo1" width="200px" autoplay="true"></video>
      <video id="remoteVideo2" width="200px" autoplay="true"></video>
    </div>
  </section>
</div>
<span id="errorMessage" style="display: none"></span>
<audio id="remoteAudio" autoplay="autoplay"></audio>


            
          
!
            
              body {
  font-family: sans-serif;
  font-size: 12px;
}
section { padding-top: 20px; }
input { margin-right: 10px; }
button {
  margin-right: 10px;
}
pre {padding: 5px; margin: 5px; }
.string { color: green; }
.number { color: darkorange; }
.boolean { color: blue; }
.null { color: magenta; }
.key { color: red; }
.error { color: red; }
#output span { color: blue; }
#errorMessage {
  margin-top: 10px;
  color: red;
}
.hide { display: none;}
            
          
!
            
              var _client;
var _localUser;
var _conversations = {};  // Hashtable of converastions with convId as key
var _calls = {};          // Hashtable of calls with convId as key

var $domain = document.getElementById('domain');
var $email = document.getElementById('email');
var $password = document.getElementById('password');
var $logonButton = document.getElementById('logon');
var $logoutButton = document.getElementById('logout');
var $logonState = document.getElementById('logonState');
var $mediaType = document.getElementById('mediaType');
var $localVideo = document.getElementById('localVideo');
var $remoteVideo1 = document.getElementById('remoteVideo1');
var $remoteVideo2 = document.getElementById('remoteVideo2');
var $remoteAudio = document.getElementById('remoteAudio');
var $enableVideo = document.getElementById('enableVideo');
var $enableScreenShare = document.getElementById('enableScreenShare');
var $enableRecording = document.getElementById('enableRecording');
var $startButton = document.getElementById('startButton');
var $joinButton = document.getElementById('joinButton');
var $leaveButton = document.getElementById('leaveButton');
var $pullButton = document.getElementById('pullButton');
var $convList = document.getElementById('convList');
var $callState = document.getElementById('callState');
var $media = document.getElementById('media');
var $convId = document.getElementById('convId');
var $callId = document.getElementById('callId');
var $errorMessage = document.getElementById('errorMessage');
var $download = document.getElementById('download');

if (typeof Circuit === 'undefined') {
  $errorMessage.textContent = "Could not load SDK (circuit.js). Make sure Circuit is running on https://localhost:8094.";
  $errorMessage.style.display = 'block';
} else if (!Circuit.isCompatible()) {
  $errorMessage.textContent = "Sorry, your browser is not supported. Chrome works :)";
  $errorMessage.style.display = 'block';
} else {
  document.getElementById('mainWrapper').style.display = 'block';
}

Circuit.logger.setLevel(Circuit.Enums.LogLevel.Debug);

// Helper function to get the list element
function getListOption(convId) {
  for (var i = 0; i < $convList.options.length; i++) {
    if ($convList.options[i].value === convId) {
      return $convList.options[i];
    }
  }
}

// Reset the UI for the call section
function resetCallUI() {
  updateButtons();
  $callState.textContent = '';
  $media.textContent = '';
  $callId.textContent = '';
  $convId.textContent = '';
  $localVideo.src = '';
  $remoteVideo1.src = '';
  $remoteVideo2.src = '';
  $remoteAudio.src = '';
  $enableVideo.checked = false;
  $enableVideo.disabled = true;
  $enableScreenShare.checked = false;
  $enableScreenShare.disabled = true;
  $enableRecording.checked = false;
  $enableRecording.disabled = true;
  $download.style.display = 'none';
}

// Update the UI call section for the selected call
function setCallUI() {
  updateButtons();
  var convId = document.getElementById('convList').value;
  if (!_conversations[convId]) {
    return;
  }
  var call = _calls[convId];
  $convId.textContent = call.convId;
  $callState.textContent = call.state;
  $callId.textContent = call.callId;
  call.activeMediaType && ($media.textContent = (call.activeMediaType.audio ? 'audio' : '') + (call.activeMediaType.video ? '+video' : ''));
  $callId.textContent = call.callId;
  $convList.disabled = !call.isRemote && call.isEstablished;
  $enableVideo.disabled = !(!call.isRemote && call.isEstablished);
  $enableScreenShare.disabled = !(!call.isRemote && call.isEstablished);
  $enableRecording.disabled = !(!call.isRemote && call.isEstablished);

  // Set local video stream
  $localVideo.src = call.localVideoUrl || '';

  // Enable video checkbox
  if (call.isEstablished) {
    $remoteAudio.src = call.remoteAudioUrl;
    // Set remote video streams (up to two in this example)
    if (call.participants && call.participants.length > 0) {
      $remoteVideo1.src = call.participants[0].videoUrl;
    }
    if (call.participants && call.participants.length > 1) {
      $remoteVideo2.src = call.participants[1].videoUrl;
    }
  } else {
    $remoteAudio.src = '';
    $remoteVideo1.src = '';
    $remoteVideo2.src = '';
  }
}

// Show correct button based on selected conversation's call state
function updateButtons() {
  $startButton.classList.add('hide');
  $joinButton.classList.add('hide');
  $leaveButton.classList.add('hide');
  $pullButton.classList.add('hide');

  var convId = document.getElementById('convList').value;
  if (!_conversations[convId]) {
    return;
  }
  var call = _calls[convId];

  if (!call) {
    $startButton.classList.remove('hide');
  } else if (call.state === Circuit.Enums.CallStateName.Started) {
    $joinButton.classList.remove('hide');
  } else if (call.state === Circuit.Enums.CallStateName.ActiveRemote) {
    $pullButton.classList.remove('hide');
  } else {
    $leaveButton.classList.remove('hide');          
  }
}

// Register event listeners
function addClientEventListeners() {
  if (_client) {
    _client.addEventListener('connectionStateChanged', function onConnectionStateChanged(evt) {
      console.log('Received connectionStateChanged event - state = ', evt.state)
      $logonState.textContent = evt.state;
      if (evt.state === Circuit.Enums.ConnectionState.Disconnected) {
        resetCallUI();
      }
    });

    _client.addEventListener('callStatus', function (evt) {
      var call = _calls[evt.call.convId];
      if (!call || call.callId === evt.call.callId) {
        console.log('Received callStatus event. state: ' + evt.call.state + ', reason: ' + evt.reason, evt.call);
        _calls[evt.call.convId] = evt.call;
        updateList(evt.call);
        setCallUI();
      }
    });

    _client.addEventListener('callEnded', function (evt) {
      var call = _calls[evt.call.convId];
      if (call && call.callId === evt.call.callId) {
        console.log('Received callEnded event. state: ' + evt.call.state, evt.call);
        delete _calls[evt.call.convId];
        updateList(evt.call);
        resetCallUI();
      }
    });

    _client.addEventListener('itemAdded', function (evt) {
      var item = evt.item;
      if (item.rtc && item.rtc.type === 'ENDED' && item.attachments && item.attachments.length) {
        console.log('Received itemAdded event with a recording');
      }
    });

    _client.addEventListener('itemUpdated', function (evt) {
      var item = evt.item;
      if (item.rtc && item.rtc.type === 'ENDED' && item.attachments && item.attachments.length) {
        console.log('Received itemUpdated event with a recording');
        $download.style.display = 'block';
        $download.href = item.attachments[0].url;
      } else {
        $download.style.display = 'none';
      }
    });
  }
}

// Update call state in list for a call
function updateList(call) {
  if (call.state === Circuit.Enums.CallStateName.Terminated) {
    getListOption(call.convId).textContent = _conversations[call.convId].title;
  } else {
    getListOption(call.convId).textContent = _conversations[call.convId].title + ' (' + call.state + ')';          
  }
}

// Start a conference
function start() {
  if (!_client || !_localUser) { return alert('Caller is not logged in'); }

  var mediaType = {audio: true, video: enableVideo.checked};
  var selectedConvId = $convList.options[$convList.selectedIndex].value;

  _client.startConference(selectedConvId, mediaType)
    .then(setCallUI)
    .catch(function (err) {
    alert('Error starting conference. ' + err);
    console.error(err);
  });
}

// Join a conference
function join() {
  if (!_client || !_localUser) { return alert('Caller is not logged in'); }

  var mediaType = {audio: true, video: enableVideo.checked};
  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  _client.joinConference(call.callId, mediaType)
    .catch(function (err) {
    return alert('Failed to join the call: ' + err);
    console.error(err);
  });
}

// Leave a conference
function leave() {
  if (!_client || !_localUser) { return alert('Caller is not logged in'); }

  var mediaType = {audio: true, video: enableVideo.checked};
  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  _client.leaveConference(call.callId); 
}

// Pull a remote call
function pull() {
  if (!_client || !_localUser) { return alert('Caller is not logged in'); }

  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  _client.pullRemoteCall(call.callId); 
}

function cacheCalls(calls) {
  _calls = {};
  calls.forEach(function(call) {
    _calls[call.convId] = call;
  });
}

function logon() {
  if (_client || !$email.value || !$password.value) {
    return;
  }
  _client = new Circuit.Client({domain: $domain.value});
  addClientEventListeners();

  _client.logon($email.value, $password.value).then(function (user) {
    _localUser = user;
    $domain.disabled = true;
    $email.disabled = true;
    $password.disabled = true;
    $logonButton.style.display = 'none';
    $logoutButton.style.display = '';
  })
    .then(_client.getConversations)
    .then(populateDropdown)
    .then(_client.getCalls)
    .then(cacheCalls)
    .catch(function (e) {
    console.error(e);
    alert('Unable to logon. Error: ' + e);
    _client.removeAllListeners();
    _client = null;
  });
}

function logout() {
  if (!_client) {
    return;
  }
  _client.logout().then(_client.removeAllListeners);

  _client = null;
  _localUser = null;
  _conversations = {};
  _calls = {};

  $convList.options.length = 0;
  $domain.disabled = false;
  $email.disabled = false;
  $password.disabled = false;
  $logonButton.style.display = '';
  $logoutButton.style.display = 'none';
  $logonState.textContent = 'Disconnected';
}

function onVideoChange() {
  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  if (call && call.localMediaType.video !== enableVideo.checked) {
    _client.toggleVideo(call.callId).then(function () {
      console.log('Local video was successfully toggled');
    }).catch(function (err) {
      console.error('Failed to toggle video. ', err);
    })
  }
}

function onScreenShareChange() {
  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  if (call && call.localMediaType.desktop !== enableScreenShare.checked) {
    _client.toggleScreenShare(call.callId).then(function () {
      console.log('Screenshare was successfully toggled');
    }).catch(function (err) {
      console.error('Failed to toggle screenshare. ', err);
    })
  }
}

function onRecordingChange() {
  var selectedConvId = $convList.options[$convList.selectedIndex].value;
  var call = _calls[selectedConvId];

  if (!enableRecording.checked) {
    _client.stopRecording(call.callId).then(function () {
      console.log('Recording stopped');
    }).catch(function (err) {
      console.error('Failed to stop recording. ', err);
    })
  } else {
    _client.startRecording(call.callId, true).then(function () {
      console.log('Recording started');
    }).catch(function (err) {
      console.error('Failed to record session. ', err);
    })
  }
}

function onConversationSelected() {
  var convId = document.getElementById('convList').value;
  var call = _calls[convId];

  if (call) {
    setCallUI();
  } else {
    resetCallUI();
  }
}

function populateDropdown(conversations) {
  _conversations = {};
  conversations.filter(function (c) {
    if (c && c.type === Circuit.Enums.ConversationType.GROUP) {
      _conversations[c.convId] = c;
      return true;
    }
    return false;
  }).forEach(function (c) {
    if (c.type !== 'DIRECT') {
      $convList.options[$convList.options.length] = new Option(c.title, c.convId);
    }
  });
  $convList.size = Math.min($convList.options.length, 5);
  $convList.selectedIndex = -1;
}

Circuit.Injectors.conversationInjector = function (conversation) {
  return new Promise(function (resolve, reject) {
    var userIds = conversation.participants.filter(function (p) {
      return p !== _client.loggedOnUser.userId;
    });
    _client.getUsersById(userIds).then(function (users) {
      // Set conversation.otherUsers
      conversation.otherUsers = users;

      // Set conversation.title
      if (conversation.type === 'DIRECT') {
        conversation.title = conversation.otherUsers[0].displayName;
      } else {
        conversation.title = conversation.topic || conversation.otherUsers.map(function (u) {
          return u.firstName;
        }).join(', ');
      }

      // Find telephony conversation to be excluded
      var telephonyConv = conversation.otherUsers.find(function (user) {
        return user.roles && user.roles.indexOf(Circuit.Enums.UserRole.VIRTUAL_TELEPHONY_CONNECTOR) !== -1;
      });

      resolve(telephonyConv ? null : conversation);
    }, function (err) {
      reject(err);
    });
  });
}

resetCallUI();
            
          
!
999px
Close

Asset uploading is a PRO feature.

As a PRO member, you can drag-and-drop upload files here to use as resources. Images, Libraries, JSON data... anything you want. You can even edit them anytime, like any other code on CodePen.

Go PRO

Loading ..................

Console