API Version: 6.4
<body>
HTML used for this project is as follows:<body>
<video id="phone" autoplay></video>
<div id="calls">
<div class="calls-title">Calls</div>
<input type="text" id="number" size="20" placeholder="Number to dial"></input>
<button type="button" id="call">Call</button>
<div class="call" style="display: none" id="clone">
<span>Caller name here</span>
<i class="material-icons call-hangup">call_end</i>
<i class="material-icons call-hold">phone_paused</i>
<i class="material-icons call-talk">call</i>
<i class="material-icons call-ring">ring_volume</i>
</div>
</div>
<script src="call-manage.js"></script>
</body>
<div class="call">
which is cloned as needed in order to display each call. Each 'call' div
has four buttons which will be displayed or hidden depending on the call state.function runApp() {
/* Grab references to DOM elements */
var numberTag = document.getElementById('number');
var callTag = document.getElementById('call');
/* Grab the first owned device for the logged in user */
var myPhone = IPCortex.PBX.owned[0];
/* Very simple error-check */
if ( !myPhone || !myPhone.webrtc ) {
throw(Error('Cannot find WebRTC capable device'));
}
/* Enable WebRTC connection */
myPhone.enableRTC();
/* Wait for new call events to arrive */
myPhone.addListener('update', function (device) {
device.calls.forEach(function(call) {
processCall(call);
});
});
numberTag.addEventListener('keyup', function(e) {
/* Clean up input box content */
if ( numberTag.value.search(/[^0-9\*\#]/) != -1 )
numberTag.value = numberTag.value.replace(/[^0-9\*\#]/, '');
});
callTag.addEventListener('click', function(e) {
if ( !numberTag.value )
return;
myPhone.dial(numberTag.value);
numberTag.value = '';
e.preventDefault();
});
}
processCall()
, and a very simple error-check is added to make sure we can find a keevio phone for the current user. Other than that, the same code is used to enable the Device update listener and to manage the number input box and the 'call' button events.processCall()
is actually split into two parts. A new call (the call's unique ID has not been seen before) drops through to newCall()
to have the DOM elements created, and it then returns to processCall()
so that its visual state can be updated.processCall()
itself can be broken down into smaller chunks:function processCall(call) {
/*
* 1 - If the call is not recognised, create it.
* 2 - If the call is going-away, delete it.
* 3 - If the call is newly 'up' then attach it's audio
* 4 - Update the buttons
* 5 - Update the caller name and save state for next time
*/
}
calls
storage object - As long as it is not an already-dead call, create it with newCall()
(see below) and then come back to continue processing.if ( !calls[call.uid] ) {
if( call.state === 'dead' )
return;
/* A new call has appeared, and is not 'dead' */
newCall(call);
}
if( call.state === 'dead' ) {
/* A known call is now dead, delete it */
calls[call.uid].elem.parentNode.removeChild(calls[call.uid].elem);
delete calls[call.uid];
return;
}
mute()
outbound media streams, and assume that the IPCortex Communication System will handle this cleanly for us. In a more complete application that may not be a safe assumption.if ( call.state === 'up' && calls[call.uid].state !== 'up' ) {
/* A call not previously 'up' is now 'up' so attach its media stream */
var videoTag = document.getElementById('phone');
if (call.remoteMedia)
attachMediaStream(videoTag, call.remoteMedia[0]);
}
if (call.state !== calls[call.uid].state) {
/* Call state has changed, so possibly update the control icons
* The control icons are in an array of
* [hangup, hold, talk, ringing]
*/
switch(call.state) {
case('dial'): /* Outbound call being dialled */
console.log(TAG, 'Call', call.uid, 'Outbound dial state');
calls[call.uid].buttons[0].style.display = 'block';
calls[call.uid].buttons[1].style.display = 'none';
calls[call.uid].buttons[2].style.display = 'none';
calls[call.uid].buttons[3].style.display = 'block';
break;
case('ring'): /* Inbound call ringing */
console.log(TAG, 'Call', call.uid, 'Inbound ring state');
calls[call.uid].buttons[0].style.display = 'block';
calls[call.uid].buttons[1].style.display = 'none';
calls[call.uid].buttons[2].style.display = 'block';
calls[call.uid].buttons[3].style.display = 'block';
break;
case('up'): /* Call is up and proceeding */
console.log(TAG, 'Call', call.uid, 'is up');
calls[call.uid].buttons[0].style.display = 'block';
calls[call.uid].buttons[1].style.display = 'block';
calls[call.uid].buttons[2].style.display = 'none';
calls[call.uid].buttons[3].style.display = 'none';
break;
case('hold'): /* Call is on hold */
console.log(TAG, 'Call', call.uid, 'is on hold');
calls[call.uid].buttons[0].style.display = 'block';
calls[call.uid].buttons[1].style.display = 'none';
calls[call.uid].buttons[2].style.display = 'block';
calls[call.uid].buttons[3].style.display = 'none';
break;
default:
calls[call.uid].buttons[0].style.display = 'block';
calls[call.uid].buttons[1].style.display = 'none';
calls[call.uid].buttons[2].style.display = 'none';
calls[call.uid].buttons[3].style.display = 'none';
break;
};
}
Call.label
attribute will update if the call is transferred, so needs to be updated regularly to be sure it is accurate./* Ensure that the caller name is up to date */
calls[call.uid].name.innerHTML = call.label;
/* Record the call's previous state to detect changes */
calls[call.uid].state = call.state;
newCall()
newCall()
function firstly clones the 'call' div
and adds the copied elements into the page, it then searches the for relevant child elements for later use so that the call recipient-name can be updated, and the buttons can be hidden or displayed based on the call state. Before it returns, it adds some on-click events to the relevant buttons to call the Call.talk()
, Call.hold()
or Call.hangup()
methods - These are created with the in-scope copy of the 'call' class, so each of these calls will operate on the relevant call object./* For a new call, clone the DOM element to make the call visible,
* and store the necessary data for later
*/
function newCall(call) {
var callsTag = document.getElementById('calls');
var elem = document.getElementById('clone').cloneNode(true);
elem.style.display = 'block';
elem.id = call.uid;
callsTag.appendChild(elem);
/* Store important call info for later */
calls[call.uid] = {
state: 'new',
elem: elem,
name: elem.getElementsByTagName('span')[0],
buttons: elem.getElementsByTagName('i')
};
/* Add quick and dirty handlers onto the buttons for this call
* The control icons are in an array of
* [hangup, hold, talk, ringing]
*/
var inputs = calls[call.uid].buttons;
inputs[0].addEventListener('click', function() {
console.log(TAG, 'Hangup call', call.uid);
call.hangup();
});
inputs[1].addEventListener('click', function() {
console.log(TAG, 'Hold call', call.uid);
call.hold();
});
inputs[2].addEventListener('click', function() {
console.log(TAG, 'Start/Unhold call', call.uid);
call.talk();
});
}