@telegenta/webclient 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,564 +1,584 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
-
4
- <meta name="keywords" content="telegenta, phone client, webrtc, demo">
5
- <meta name="description" content="Telegenta Phone client demo application using telegenta client library">
6
-
7
- <head>
8
- <meta charset="UTF-8">
9
- <title>Telegenta Phone client Demo</title>
10
- <script src="../dist/telegenta.min.js"></script>
11
- <link rel="stylesheet" type="text/css" href="styles.css">
12
- </head>
13
-
14
- <body>
15
- <!-- Logo -->
16
- <div class="groupelementsnoline" id="Telegenta"></div>
17
- <!-- Logo end -->
18
- <div>
19
- <div class="groupelementsnoline">
20
- <h2>Telegenta Phone Client Demo</h2>
21
- </div>
22
- <div class="groupelements">
23
- <h3>STEP 1: Get account list</h3>
24
- <div class="inputelements">
25
- <label for="api_key">API Key</label>
26
- <input type="text" size="40" id="api_key" placeholder="Enter API Key"
27
- value="">
28
- <button onclick="populateSelectElements()">Get sip accounts</button>
29
- </div>
30
- </div>
31
-
32
- <div class="groupelements">
33
- <h3>STEP 2: Start phone</h3>
34
- <div class="inputelements">
35
- <label for="account_selector">SIP Accounts</label><span class="smallright"><small>Connection: <span
36
- id="conn_status">DISCONNECTED</span></small></span>
37
- <select id="account_selector">
38
- <option value="" disabled selected>Waiting for SIP accounts</option>
39
- </select>
40
- <button onclick="startPhoneClient()">Start phone and use selected account</button>
41
- <button onclick="stopPhoneClient()">Stop the phone client</button>
42
- </div>
43
- </div>
44
-
45
- <div class="groupelements">
46
- <h3>STEP 3: Add a new call</h3>
47
- <div class="inputelements">
48
- <label for="destination_number">Number to call</label><span class="smallright"><small>Status: <span
49
- id="call_status">IDLE</span>
50
- &nbsp;| Duration: <span id="call_duration">-</span></small></span>
51
- <input type="text" size="40" id="destination_number" placeholder="Enter phone number" value="">
52
- <button onclick="makeCall()">Call number now</button>
3
+ <meta name="keywords" content="telegenta, phone client, webrtc, demo" />
4
+ <meta name="description" content="Telegenta Phone client demo application using telegenta client library" />
5
+
6
+ <head>
7
+ <meta charset="UTF-8" />
8
+ <title>Telegenta Phone client Demo</title>
9
+ <script src="../dist/telegenta.min.js"></script>
10
+ <link rel="stylesheet" type="text/css" href="styles.css" />
11
+ </head>
12
+
13
+ <body>
14
+ <!-- Logo -->
15
+ <div class="groupelementsnoline" id="Telegenta"></div>
16
+ <!-- Logo end -->
17
+ <div>
18
+ <div class="groupelementsnoline">
19
+ <h2>Telegenta Phone Client Demo</h2>
20
+ </div>
21
+ <div class="groupelements">
22
+ <h3>STEP 1: Get account list</h3>
23
+ <div class="inputelements">
24
+ <label for="api_key">API Key</label>
25
+ <input type="text" size="40" id="api_key" placeholder="Enter API Key" value="" />
26
+ <button onclick="populateSelectElements()">Get sip accounts</button>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="groupelements">
31
+ <h3>STEP 2: Start phone</h3>
32
+ <div class="inputelements">
33
+ <label for="account_selector">SIP Accounts</label
34
+ ><span class="smallright"
35
+ ><small>Connection: <span id="conn_status">DISCONNECTED</span></small></span
36
+ >
37
+ <select id="account_selector">
38
+ <option value="" disabled selected>Waiting for SIP accounts</option>
39
+ </select>
40
+ <button onclick="startPhoneClient()">Start phone and use selected account</button>
41
+ <button onclick="stopPhoneClient()">Stop the phone client</button>
42
+ </div>
43
+ </div>
44
+
45
+ <div class="groupelements">
46
+ <h3>STEP 3: Add a new call</h3>
47
+ <div class="inputelements">
48
+ <label for="destination_number">Number to call</label
49
+ ><span class="smallright"
50
+ ><small
51
+ >Status: <span id="call_status">IDLE</span> &nbsp;| Duration:
52
+ <span id="call_duration">-</span></small
53
+ ></span
54
+ >
55
+ <input type="text" size="40" id="destination_number" placeholder="Enter phone number" value="" />
56
+ <button onclick="makeCall()">Call number now</button>
57
+ </div>
58
+ </div>
59
+
60
+ <div class="groupelements">
61
+ <h3>Transfer call</h3>
62
+
63
+ <div class="inputelements">
64
+ <h3 id="transfer_from"></h3>
65
+ <label for="transfer_selector">Select call: </label>
66
+ <select id="transfer_selector">
67
+ <option value="" disabled selected>No call to transfer</option>
68
+ </select>
69
+
70
+ <button onclick="doCallTransfer()">Transfer call</button>
71
+ </div>
72
+ </div>
73
+
74
+ <div class="groupelements">
75
+ <h3>Listen call</h3>
76
+
77
+ <div class="inputelements">
78
+ <h3>Listen to another call</h3>
79
+ <label for="listen_key">Call listen key</label>
80
+ <input type="text" size="40" id="listen_key" placeholder="Enter call listen key" value="" />
81
+ <button onclick="doListen()">Listen now</button>
82
+ </div>
83
+ </div>
84
+
85
+ <div class="groupelements">
86
+ <h3>Playback</h3>
87
+ <div class="inputelements">
88
+ <label for="media_file_selector">Media files</label>
89
+ <select id="media_file_selector">
90
+ <option value="" disabled selected>Select media file</option>
91
+ </select>
92
+ </div>
93
+ </div>
94
+
95
+ <div class="groupelementsnoline">
96
+ <h3>Active calls</h3>
97
+ <table id="session_table">
98
+ <tr>
99
+ <th>Direction</th>
100
+ <th>Destination number</th>
101
+ <th>State</th>
102
+ <th>Duration</th>
103
+ <th>Quality metrics</th>
104
+ <th>Mic muted</th>
105
+ <th>Recording</th>
106
+ <th>On hold</th>
107
+ <th>Actions</th>
108
+ </tr>
109
+ </table>
110
+ </div>
111
+ <div class="groupelementsnoline">
112
+ <small>Tip: Press F12 (developer console) to get additional information</small>
113
+ </div>
53
114
  </div>
54
- </div>
115
+ </body>
116
+
117
+ <!-- Javascript stuff -->
118
+ <script>
119
+ const API_URL = 'https://api.telegenta.com/api';
120
+ let phoneClient = null;
121
+ let logger;
122
+ let transferSession;
123
+
124
+ // Start when DOM is ready
125
+ window.onload = function () {
126
+ // Create phone instance first
127
+ logger = new Telegenta.LogHandler(Telegenta.LOGLVL.DEBUG);
128
+ phoneClient = new Telegenta.Phone(logger);
129
+
130
+ // Check browser is supported first
131
+ if (!phoneClient.browserSupported) {
132
+ console.error('Your browser is not supported');
133
+ } else {
134
+ console.info('Your browser is supported');
135
+ }
136
+ };
137
+
138
+ /*
139
+ * STEP 1: Get a list of sip accounts from API server and populate select box with these accounts
140
+ */
141
+ function populateSelectElements() {
142
+ // SIP Accounts
143
+ jsonApiGetRequest('/sip-trunks/').then(
144
+ function (data) {
145
+ console.info(`Fetched ${data.results.length} sip accounts:`);
146
+ console.debug(data);
147
+
148
+ // Now reset and populate select box with accounts
149
+ document.getElementById('account_selector').innerHTML = '';
150
+ for (const account of data.results) {
151
+ document.getElementById('account_selector').appendChild(new Option(account.name, account.id));
152
+ }
153
+ },
154
+ function (reason) {
155
+ console.error(`Request failed ${reason}`);
156
+ },
157
+ );
158
+
159
+ // Media files
160
+ jsonApiGetRequest('/media-files/').then(
161
+ function (data) {
162
+ console.info(`Fetched ${data.results.length} media files:`);
163
+ console.debug(data);
164
+
165
+ // Now reset and populate select box with accounts
166
+ document.getElementById('media_file_selector').innerHTML = '';
167
+ for (const mfile of data.results) {
168
+ document.getElementById('media_file_selector').appendChild(new Option(mfile.name, mfile.id));
169
+ }
170
+ },
171
+ function (reason) {
172
+ console.error(`Request failed ${reason}`);
173
+ },
174
+ );
175
+ }
176
+
177
+ /*
178
+ * STEP 2: Get a new call_token from API and start phone client with account chosen in select box
179
+ */
180
+ function startPhoneClient() {
181
+ const accountId = document.getElementById('account_selector').value;
182
+ console.info(`Starting phone client with account id ${accountId}`);
183
+
184
+ // Create call session and get provision token
185
+ jsonApiGetRequest(`/get-call-session/?id=${accountId}`).then(
186
+ function (data) {
187
+ console.info('Fetched a new call session');
188
+ console.debug(data);
189
+
190
+ // Now prepare media and get access to microphone
191
+ phoneClient.prepareMedia().then(
192
+ function () {
193
+ // Media was prepared, now hook up events to display whats going on
194
+
195
+ // Connection state shows the client connection to the phone services
196
+ phoneClient.on('connectionStateChange', function (event) {
197
+ console.info(`Phone connection is now: ${event.state}`);
198
+ document.getElementById('conn_status').innerHTML = event.state;
199
+ });
200
+
201
+ // Get local information about call after it has ended
202
+ phoneClient.on('callEnded', function (data) {
203
+ console.info('Call ended event:');
204
+ console.info(data);
205
+ });
206
+
207
+ // Get remote information about call after it has ended
208
+ phoneClient.on('callDataInfo', function (data) {
209
+ console.info('Call data info event:');
210
+ console.info(data);
211
+ });
212
+
213
+ // New session created
214
+ phoneClient.on('sessionCreated', function (data) {
215
+ newSession(data.session);
216
+ });
217
+
218
+ // Session removed
219
+ phoneClient.on('sessionRemoved', function (data) {
220
+ console.log(data);
221
+ removeSession(data.session);
222
+ });
223
+
224
+ // Show incoming call
225
+ phoneClient.on('incomingCall', function (data) {
226
+ console.log(`Incoming call from ${data.originatingNumber}`);
227
+ });
228
+
229
+ // Now start phone with the following options
230
+ const opts = {
231
+ // Enable event client
232
+ connectEventService: true,
233
+
234
+ // Subscribe to detailed information about call after processing
235
+ useCallDataInfoEvent: true,
236
+
237
+ // Enable incoming calls (you must set a phone number to call the account also)
238
+ register: true,
239
+
240
+ // Configure client with provision token
241
+ provisionToken: data.provision_token,
242
+
243
+ // Enable signal tones (busy, invalid, etc)
244
+ playSignalTones: true,
245
+
246
+ // Enable call initialized tone
247
+ playCallInitializedTone: true,
248
+ };
249
+
250
+ try {
251
+ phoneClient.start(opts);
252
+ console.info('Media prepared and phone started');
253
+ } catch (e) {
254
+ console.error('Unable to start phone');
255
+ console.error(e);
256
+ }
257
+ },
258
+ function (reason) {
259
+ console.error('Unable to get media permission: ' + reason);
260
+ },
261
+ );
262
+ },
263
+ function (reason) {
264
+ console.error(`Request failed ${reason}`);
265
+ },
266
+ );
267
+ }
55
268
 
56
- <div class="groupelements">
57
- <h3>Transfer call</h3>
269
+ // Stop phone client
270
+ function stopPhoneClient() {
271
+ phoneClient.stop();
272
+ }
58
273
 
59
- <div class="inputelements">
60
- <h3 id="transfer_from"></h3>
61
- <label for="transfer_selector">Select call: </label>
62
- <select id="transfer_selector">
63
- <option value="" disabled selected>No call to transfer</option>
64
- </select>
274
+ /*
275
+ * STEP 3: Make a test call
276
+ */
277
+ function makeCall() {
278
+ const destination_number = document.getElementById('destination_number').value;
279
+ phoneClient.call(destination_number, {
280
+ // Change this to number of days to keep recording (enables recording)
281
+ record: 0,
65
282
 
66
- <button onclick="doCallTransfer()">Transfer call</button>
67
- </div>
68
- </div>
283
+ // Change this maximum amount in customer currency the call must cost before hanging up (0 = no checking)
284
+ maximumCallCost: 0,
69
285
 
70
- <div class="groupelements">
71
- <h3>Listen call</h3>
286
+ // Include this to force a specific caller id (use id of phone number from api service)
287
+ // explicitCallerId: 35,
72
288
 
73
- <div class="inputelements">
74
- <h3>Listen to another call</h3>
75
- <label for="listen_key">Call listen key</label>
76
- <input type="text" size="40" id="listen_key" placeholder="Enter call listen key" value="">
77
- <button onclick="doListen()">Listen now</button>
78
- </div>
79
- </div>
80
-
81
- <div class="groupelements">
82
- <h3>Playback</h3>
83
- <div class="inputelements">
84
- <label for="media_file_selector">Media files</label>
85
- <select id="media_file_selector">
86
- <option value="" disabled selected>Select media file</option>
87
- </select>
88
- </div>
89
- </div>
90
-
91
- <div class="groupelementsnoline">
92
- <h3>Active calls</h3>
93
- <table id="session_table">
94
- <tr>
95
- <th>Direction</th>
96
- <th>Destination number</th>
97
- <th>State</th>
98
- <th>Duration</th>
99
- <th>Quality metrics</th>
100
- <th>Mic muted</th>
101
- <th>Recording</th>
102
- <th>On hold</th>
103
- <th>Actions</th>
104
- </tr>
105
- </table>
106
- </div>
107
- <div class="groupelementsnoline">
108
- <small>Tip: Press F12 (developer console) to get additional information</small>
109
- </div>
110
- </div>
111
- </body>
112
-
113
- <!-- Javascript stuff -->
114
- <script>
115
- const API_URL = 'https://api.telegenta.com/api';
116
- let phoneClient = null;
117
- let logger;
118
- let transferSession;
119
-
120
- // Start when DOM is ready
121
- window.onload = function () {
122
- // Create phone instance first
123
- logger = new Telegenta.LogHandler(Telegenta.LOGLVL.DEBUG);
124
- phoneClient = new Telegenta.Phone(logger);
125
-
126
- // Check browser is supported first
127
- if (!phoneClient.browserSupported) {
128
- console.error('Your browser is not supported');
129
- } else {
130
- console.info('Your browser is supported');
131
- }
132
- };
133
-
134
-
135
- /*
136
- * STEP 1: Get a list of sip accounts from API server and populate select box with these accounts
137
- */
138
- function populateSelectElements() {
139
- // SIP Accounts
140
- jsonApiGetRequest('/sip-trunks/').then(function (data) {
141
- console.info(`Fetched ${data.results.length} sip accounts:`);
142
- console.debug(data);
143
-
144
- // Now reset and populate select box with accounts
145
- document.getElementById('account_selector').innerHTML = '';
146
- for (const account of data.results) {
147
- document.getElementById('account_selector').appendChild(new Option(account.name, account.id));
148
- }
149
- }, function (reason) {
150
- console.error(`Request failed ${reason}`);
151
- });
152
-
153
- // Media files
154
- jsonApiGetRequest('/media-files/').then(function (data) {
155
- console.info(`Fetched ${data.results.length} media files:`);
156
- console.debug(data);
157
-
158
- // Now reset and populate select box with accounts
159
- document.getElementById('media_file_selector').innerHTML = '';
160
- for (const mfile of data.results) {
161
- document.getElementById('media_file_selector').appendChild(new Option(mfile.name, mfile.id));
162
- }
163
- }, function (reason) {
164
- console.error(`Request failed ${reason}`);
165
- });
166
- }
167
-
168
- /*
169
- * STEP 2: Get a new call_token from API and start phone client with account chosen in select box
170
- */
171
- function startPhoneClient() {
172
- const accountId = document.getElementById('account_selector').value;
173
- console.info(`Starting phone client with account id ${accountId}`);
174
-
175
- // Create call session and get provision token
176
- jsonApiGetRequest(`/get-call-session/?id=${accountId}`).then(function (data) {
177
- console.info('Fetched a new call session');
178
- console.debug(data);
179
-
180
- // Now prepare media and get access to microphone
181
- phoneClient.prepareMedia().then(function () {
182
- // Media was prepared, now hook up events to display whats going on
183
-
184
- // Connection state shows the client connection to the phone services
185
- phoneClient.on('connectionStateChange', function (event) {
186
- console.info(`Phone connection is now: ${event.state}`);
187
- document.getElementById('conn_status').innerHTML = event.state;
188
- });
189
-
190
- // Get local information about call after it has ended
191
- phoneClient.on('callEnded', function (data) {
192
- console.info('Call ended event:');
193
- console.info(data);
194
- });
195
-
196
- // Get remote information about call after it has ended
197
- phoneClient.on('callDataInfo', function (data) {
198
- console.info('Call data info event:');
199
- console.info(data);
200
- });
201
-
202
- // New session created
203
- phoneClient.on('sessionCreated', function (data) {
204
- newSession(data.session);
205
- });
206
-
207
- // Session removed
208
- phoneClient.on('sessionRemoved', function (data) {
209
- console.log(data);
210
- removeSession(data.session);
211
- });
212
-
213
- // Show incoming call
214
- phoneClient.on('incomingCall', function (data) {
215
- console.log(`Incoming call from ${data.originatingNumber}`);
216
- });
217
-
218
- // Now start phone with the following options
219
- const opts = {
220
- // Enable event client
221
- connectEventService: true,
222
-
223
- // Subscribe to detailed information about call after processing
224
- useCallDataInfoEvent: true,
225
-
226
- // Enable incoming calls (you must set a phone number to call the account also)
227
- register: true,
228
-
229
- // Configure client with provision token
230
- provisionToken: data.provision_token,
231
-
232
- // Enable signal tones (busy, invalid, etc)
233
- playSignalTones: true,
234
-
235
- // Enable call initialized tone
236
- playCallInitializedTone: true
237
- };
289
+ // Include this to force a specific smart cid (use id of shot-cid from api service)
290
+ // explicitShortCallerId: 1,
238
291
 
239
- try {
240
- phoneClient.start(opts);
241
- console.info('Media prepared and phone started');
242
- } catch (e) {
243
- console.error('Unable to start phone');
244
- console.error(e);
245
- }
246
- }, function (reason) {
247
- console.error('Unable to get media permission: ' + reason);
292
+ // Configure answer call timeout
293
+ answerCallTimeout: 0,
294
+
295
+ // Add meta info to call
296
+ metaInfo:
297
+ 'You can add up to 256 bytes of meta info for your call (including json like {"type": "json"})',
248
298
  });
249
- }, function (reason) {
250
- console.error(`Request failed ${reason}`);
251
- });
252
- }
253
-
254
- // Stop phone client
255
- function stopPhoneClient() {
256
- phoneClient.stop();
257
- }
258
-
259
- /*
260
- * STEP 3: Make a test call
261
- */
262
- function makeCall() {
263
- const destination_number = document.getElementById('destination_number').value;
264
- phoneClient.call(destination_number, {
265
- // Change this to number of days to keep recording (enables recording)
266
- record: 0,
267
-
268
- // Change this maximum amount in customer currency the call must cost before hanging up (0 = no checking)
269
- maximumCallCost: 0,
270
-
271
- // Include this to force a specific caller id (use id of phone number from api service)
272
- // explicitCallerId: 35,
273
-
274
- // Include this to force a specific smart cid (use id of shot-cid from api service)
275
- // explicitShortCallerId: 1,
276
-
277
- // Configure answer call timeout
278
- answerCallTimeout: 0,
279
-
280
- // Add meta info to call
281
- metaInfo: 'You can add up to 256 bytes of meta info for your call (including json like {"type": "json"})'
282
- });
283
- }
284
-
285
- // Set the source of call transfer and populate transfer destination selector
286
- function setTransferSession(session) {
287
- transferSession = session;
288
-
289
- var otherSessions = session.getOtherSessions();
290
- if (otherSessions.length < 1) {
291
- document.getElementById('transfer_from').innerHTML = 'Requires two active sessions to perform transfer!';
292
- return;
293
299
  }
294
300
 
295
- // Show source call destination number
296
- document.getElementById('transfer_from').innerHTML = session.destinationNumber + ' => ';
301
+ // Set the source of call transfer and populate transfer destination selector
302
+ function setTransferSession(session) {
303
+ transferSession = session;
297
304
 
298
- // Add all other sessions to transfer selector
299
- document.getElementById('transfer_selector').innerHTML = '';
305
+ var otherSessions = session.getOtherSessions();
306
+ if (otherSessions.length < 1) {
307
+ document.getElementById('transfer_from').innerHTML =
308
+ 'Requires two active sessions to perform transfer!';
309
+ return;
310
+ }
300
311
 
301
- for (const tSession of otherSessions) {
302
- document.getElementById('transfer_selector').appendChild(new Option(tSession.destinationNumber, tSession.uuid));
303
- }
304
- }
312
+ // Show source call destination number
313
+ document.getElementById('transfer_from').innerHTML = session.destinationNumber + ' => ';
305
314
 
306
- // Transfer call from "transferSession" to item in transfer_selector
307
- function doCallTransfer() {
308
- if (!transferSession) {
309
- console.error('No session selected');
310
- return;
311
- }
315
+ // Add all other sessions to transfer selector
316
+ document.getElementById('transfer_selector').innerHTML = '';
312
317
 
313
- // Use session uuid from selectbox and get session object
314
- const dstSession = phoneClient.getSessionByUUID(document.getElementById('transfer_selector').value);
315
-
316
- // Now do transfer
317
- transferSession.transfer(dstSession);
318
- }
319
-
320
- // Create listen session
321
- function doListen() {
322
- phoneClient.listen(document.getElementById('listen_key').value);
323
- }
324
-
325
- // Playback message
326
- function playback(session) {
327
- const hangup_after = false;
328
- session.playback(document.getElementById('media_file_selector').value, hangup_after).then(() => {
329
- console.info('Playback started');
330
- })
331
- }
332
-
333
- /*
334
- * Helper functions
335
- */
336
-
337
- // Create new session
338
- // Add table row for session using session_<uuid> as element id,
339
- // (We do this so we can find and remove row when session ends)
340
- function newSession(session) {
341
- const rowId = `session_${session.uuid}`;
342
-
343
- // Create row in table for session
344
- const table = document.getElementById("session_table");
345
- const newRow = table.insertRow();
346
- newRow.id = rowId;
347
- for (let i = 0; i < table.rows[0].cells.length; i++)
348
- newRow.insertCell();
349
-
350
- // Set cells
351
- newRow.cells[0].innerText = session.direction;
352
-
353
- // Show name of local number called for inbound
354
- if (session.localNumberName) {
355
- newRow.cells[1].innerText = session.destinationNumber + ' (' + session.localNumberName + ')';
356
- } else {
357
- newRow.cells[1].innerText = session.destinationNumber;
318
+ for (const tSession of otherSessions) {
319
+ document
320
+ .getElementById('transfer_selector')
321
+ .appendChild(new Option(tSession.destinationNumber, tSession.uuid));
322
+ }
358
323
  }
359
324
 
360
- // Hookup events
361
- // Session state show the current calling state
362
- session.on('sessionStateChange', function (event) {
363
- console.info(`Phone session is now: ${event.state}`);
364
- newRow.cells[2].innerHTML = event.state;
365
- // Add failure cause
366
- if (event.state === 'FAILED') {
367
- newRow.cells[2].innerHTML = newRow.cells[2].innerHTML + ` (${event.cause})`;
325
+ // Transfer call from "transferSession" to item in transfer_selector
326
+ function doCallTransfer() {
327
+ if (!transferSession) {
328
+ console.error('No session selected');
329
+ return;
368
330
  }
369
- updateButtons(newRow.cells[8], session);
370
- });
371
-
372
- // Get record state events
373
- newRow.cells[6].innerHTML = session.isRecording;
374
- session.on('recordingStateChange', function (data) {
375
- console.info(`Recording state changed to ${data.state}`);
376
- newRow.cells[6].innerHTML = data.state;
377
- });
378
-
379
- // Get mute state events
380
- newRow.cells[5].innerHTML = session.isMuted;
381
- session.on('muteStateChange', function (data) {
382
- console.info(`Microphone mute state changed to ${data.state}`);
383
- newRow.cells[5].innerHTML = data.state;
384
- });
385
-
386
- // Get hold state events
387
- newRow.cells[7].innerHTML = session.isOnHold;
388
- session.on('holdStateChange', function (data) {
389
- console.info(`Call hold state changed to ${data.state}`);
390
- newRow.cells[7].innerHTML = data.state;
391
- });
392
-
393
- // Get call duration update events
394
- session.on('callDurationUpdate', function (data) {
395
- newRow.cells[3].innerHTML = `${data.durationStr} (${data.activeDurationStr})`;
396
- });
397
-
398
- // Get call duration update events
399
- session.on('callQualityReportUpdate', function (data) {
400
- console.info('Call quality report:');
401
- console.info(data);
402
- newRow.cells[4].innerHTML = `${data.delta.effectiveLatency.toFixed(1)} ms (${data.delta.rFactor.toFixed(1)} R)`;
403
- });
404
-
405
- // Get local information about call after it has ended
406
- session.on('callEnded', function (data) {
407
- console.info('Call ended event:');
408
- console.debug(data);
409
- });
410
-
411
- session.on('whisperModeChange', function (data) {
412
- console.info('Whisper mode: ' + data.mode);
413
- });
414
-
415
- }
416
-
417
- // Remove table row for session
418
- function removeSession(session) {
419
- const row = document.getElementById(`session_${session.uuid}`);
420
- if (row) {
421
- row.parentNode.removeChild(row);
331
+
332
+ // Use session uuid from selectbox and get session object
333
+ const dstSession = phoneClient.getSessionByUUID(document.getElementById('transfer_selector').value);
334
+
335
+ // Now do transfer
336
+ transferSession.transfer(dstSession);
422
337
  }
423
- }
424
-
425
- // Add buttons based on session state on element (the table row)
426
- function updateButtons(element, session) {
427
- // Remove all buttons first
428
- let e = element.firstElementChild;
429
- while (e) {
430
- e.remove();
431
- e = element.firstElementChild;
338
+
339
+ // Create listen session
340
+ function doListen() {
341
+ phoneClient.listen(document.getElementById('listen_key').value);
432
342
  }
433
343
 
434
- // Put session on TD element, so buttons can use it
435
- element._session = session;
436
-
437
- // NON TERMINATED/INCOMING/FAILED BUTTONS
438
- if (session.state !== 'TERMINATED' && session.state !== 'INCOMING' && session.state !== 'FAILED') {
439
- const btn = document.createElement("BUTTON");
440
- btn.innerHTML = 'Hangup';
441
- btn.onclick = function () {
442
- this.parentNode._session.hangup();
443
- };
444
- element.appendChild(btn)
344
+ // Playback message
345
+ function playback(session) {
346
+ const hangup_after = false;
347
+ session.playback(document.getElementById('media_file_selector').value, hangup_after).then(() => {
348
+ console.info('Playback started');
349
+ });
445
350
  }
446
351
 
447
- if (session.direction === 'OUTBOUND' || session.direction === 'INBOUND') {
448
- // ACTIVE CALL BUTTONS
449
- if (session.state === 'ACTIVE' || session.state === 'ON_HOLD') {
450
- const btn = document.createElement("BUTTON");
451
- btn.innerHTML = 'Play tone';
452
- btn.onclick = function () {
453
- phoneClient.playTone(1000, 440, 880, function () {
454
- console.info('Play tone complete!')
455
- });
456
- };
457
- element.appendChild(btn);
352
+ /*
353
+ * Helper functions
354
+ */
355
+
356
+ // Create new session
357
+ // Add table row for session using session_<uuid> as element id,
358
+ // (We do this so we can find and remove row when session ends)
359
+ function newSession(session) {
360
+ const rowId = `session_${session.uuid}`;
361
+
362
+ // Create row in table for session
363
+ const table = document.getElementById('session_table');
364
+ const newRow = table.insertRow();
365
+ newRow.id = rowId;
366
+ for (let i = 0; i < table.rows[0].cells.length; i++) newRow.insertCell();
367
+
368
+ // Set cells
369
+ newRow.cells[0].innerText = session.direction;
370
+
371
+ // Show name of local number called for inbound
372
+ if (session.localNumberName) {
373
+ newRow.cells[1].innerText = session.destinationNumber + ' (' + session.localNumberName + ')';
374
+ } else {
375
+ newRow.cells[1].innerText = session.destinationNumber;
376
+ }
458
377
 
459
- const btn2 = document.createElement("BUTTON");
460
- btn2.innerHTML = 'Send DTMF 1';
461
- btn2.onclick = function () {
462
- this.parentNode._session.sendDTMF(1);
463
- };
464
- element.appendChild(btn2);
378
+ // Hookup events
379
+ // Session state show the current calling state
380
+ session.on('sessionStateChange', function (event) {
381
+ console.info(`Phone session is now: ${event.state}`);
382
+ newRow.cells[2].innerHTML = event.state;
383
+ // Add failure cause
384
+ if (event.state === 'FAILED') {
385
+ newRow.cells[2].innerHTML = newRow.cells[2].innerHTML + ` (${event.cause})`;
386
+ }
387
+ updateButtons(newRow.cells[8], session);
388
+ });
465
389
 
466
- const btn3 = document.createElement("BUTTON");
467
- btn3.innerHTML = 'Toggle mute mic';
468
- btn3.onclick = function () {
469
- this.parentNode._session.toggleMute();
470
- };
471
- element.appendChild(btn3);
390
+ // Get record state events
391
+ newRow.cells[6].innerHTML = session.isRecording;
392
+ session.on('recordingStateChange', function (data) {
393
+ console.info(`Recording state changed to ${data.state}`);
394
+ newRow.cells[6].innerHTML = data.state;
395
+ });
472
396
 
473
- const btn4 = document.createElement("BUTTON");
474
- btn4.innerHTML = 'Toggle recording';
475
- btn4.onclick = function () {
476
- this.parentNode._session.toggleRecording();
477
- };
478
- element.appendChild(btn4);
397
+ // Get mute state events
398
+ newRow.cells[5].innerHTML = session.isMuted;
399
+ session.on('muteStateChange', function (data) {
400
+ console.info(`Microphone mute state changed to ${data.state}`);
401
+ newRow.cells[5].innerHTML = data.state;
402
+ });
479
403
 
480
- const btn5 = document.createElement("BUTTON");
481
- btn5.innerHTML = 'Toggle hold';
482
- btn5.onclick = function () {
483
- this.parentNode._session.toggleHold();
484
- };
485
- element.appendChild(btn5);
404
+ // Get hold state events
405
+ newRow.cells[7].innerHTML = session.isOnHold;
406
+ session.on('holdStateChange', function (data) {
407
+ console.info(`Call hold state changed to ${data.state}`);
408
+ newRow.cells[7].innerHTML = data.state;
409
+ });
486
410
 
487
- const btn6 = document.createElement("BUTTON");
488
- btn6.innerHTML = 'Transfer';
489
- btn6.onclick = function () {
490
- setTransferSession(this.parentNode._session);
491
- };
492
- element.appendChild(btn6);
411
+ // Get call duration update events
412
+ session.on('callDurationUpdate', function (data) {
413
+ newRow.cells[3].innerHTML = `${data.durationStr} (${data.activeDurationStr})`;
414
+ });
493
415
 
494
- const btn7 = document.createElement("BUTTON");
495
- btn7.innerHTML = 'Playback';
496
- btn7.onclick = function () {
497
- playback(this.parentNode._session);
416
+ // Get call duration update events
417
+ session.on('callQualityReportUpdate', function (data) {
418
+ console.info('Call quality report:');
419
+ console.info(data);
420
+ newRow.cells[4].innerHTML = `${data.delta.effectiveLatency.toFixed(1)} ms (${data.delta.rFactor.toFixed(
421
+ 1,
422
+ )} R)`;
423
+ });
424
+
425
+ // Get local information about call after it has ended
426
+ session.on('callEnded', function (data) {
427
+ console.info('Call ended event:');
428
+ console.debug(data);
429
+ });
430
+
431
+ session.on('whisperModeChange', function (data) {
432
+ console.info('Whisper mode: ' + data.mode);
433
+ });
434
+ }
435
+
436
+ // Remove table row for session
437
+ function removeSession(session) {
438
+ const row = document.getElementById(`session_${session.uuid}`);
439
+ if (row) {
440
+ row.parentNode.removeChild(row);
441
+ }
442
+ }
443
+
444
+ // Add buttons based on session state on element (the table row)
445
+ function updateButtons(element, session) {
446
+ // Remove all buttons first
447
+ let e = element.firstElementChild;
448
+ while (e) {
449
+ e.remove();
450
+ e = element.firstElementChild;
451
+ }
452
+
453
+ // Put session on TD element, so buttons can use it
454
+ element._session = session;
455
+
456
+ // NON TERMINATED/INCOMING/FAILED BUTTONS
457
+ if (session.state !== 'TERMINATED' && session.state !== 'INCOMING' && session.state !== 'FAILED') {
458
+ const btn = document.createElement('BUTTON');
459
+ btn.innerHTML = 'Hangup';
460
+ btn.onclick = function () {
461
+ this.parentNode._session.hangup();
498
462
  };
499
- element.appendChild(btn7);
463
+ element.appendChild(btn);
500
464
  }
501
465
 
502
- // INCOMING BUTTONS
503
- if (session.state === 'INCOMING') {
504
- const btn = document.createElement("BUTTON");
505
- btn.innerHTML = 'Answer';
466
+ if (session.direction === 'OUTBOUND' || session.direction === 'INBOUND') {
467
+ // ACTIVE CALL BUTTONS
468
+ if (session.state === 'ACTIVE' || session.state === 'ON_HOLD') {
469
+ const btn = document.createElement('BUTTON');
470
+ btn.innerHTML = 'Play tone';
471
+ btn.onclick = function () {
472
+ phoneClient.playTone(1000, 440, 880, function () {
473
+ console.info('Play tone complete!');
474
+ });
475
+ };
476
+ element.appendChild(btn);
477
+
478
+ const btn2 = document.createElement('BUTTON');
479
+ btn2.innerHTML = 'Send DTMF 1';
480
+ btn2.onclick = function () {
481
+ this.parentNode._session.sendDTMF(1);
482
+ };
483
+ element.appendChild(btn2);
484
+
485
+ const btn3 = document.createElement('BUTTON');
486
+ btn3.innerHTML = 'Toggle mute mic';
487
+ btn3.onclick = function () {
488
+ this.parentNode._session.toggleMute();
489
+ };
490
+ element.appendChild(btn3);
491
+
492
+ const btn4 = document.createElement('BUTTON');
493
+ btn4.innerHTML = 'Toggle recording';
494
+ btn4.onclick = function () {
495
+ this.parentNode._session.toggleRecording();
496
+ };
497
+ element.appendChild(btn4);
498
+
499
+ const btn5 = document.createElement('BUTTON');
500
+ btn5.innerHTML = 'Toggle hold';
501
+ btn5.onclick = function () {
502
+ this.parentNode._session.toggleHold();
503
+ };
504
+ element.appendChild(btn5);
505
+
506
+ const btn6 = document.createElement('BUTTON');
507
+ btn6.innerHTML = 'Transfer';
508
+ btn6.onclick = function () {
509
+ setTransferSession(this.parentNode._session);
510
+ };
511
+ element.appendChild(btn6);
512
+
513
+ const btn7 = document.createElement('BUTTON');
514
+ btn7.innerHTML = 'Playback';
515
+ btn7.onclick = function () {
516
+ playback(this.parentNode._session);
517
+ };
518
+ element.appendChild(btn7);
519
+ }
520
+
521
+ // INCOMING BUTTONS
522
+ if (session.state === 'INCOMING') {
523
+ const btn = document.createElement('BUTTON');
524
+ btn.innerHTML = 'Answer';
525
+ btn.onclick = function () {
526
+ this.parentNode._session.answer(
527
+ (metaInfo = 'When answering calls you can add meta info as well'),
528
+ );
529
+ };
530
+ element.appendChild(btn);
531
+
532
+ const btn2 = document.createElement('BUTTON');
533
+ btn2.innerHTML = 'Reject';
534
+ btn2.onclick = function () {
535
+ this.parentNode._session.reject();
536
+ };
537
+ element.appendChild(btn2);
538
+ }
539
+ }
540
+
541
+ // LISTEN BUTTONS
542
+ if (session.direction === 'LISTEN' && session.state === 'ACTIVE') {
543
+ const btn = document.createElement('BUTTON');
544
+ btn.innerHTML = 'Start whisper';
506
545
  btn.onclick = function () {
507
- this.parentNode._session.answer(metaInfo = 'When answering calls you can add meta info as well');
546
+ this.parentNode._session.setWhisperMode(1);
508
547
  };
509
548
  element.appendChild(btn);
510
549
 
511
- const btn2 = document.createElement("BUTTON");
512
- btn2.innerHTML = 'Reject';
550
+ const btn2 = document.createElement('BUTTON');
551
+ btn2.innerHTML = 'End whisper';
513
552
  btn2.onclick = function () {
514
- this.parentNode._session.reject();
553
+ this.parentNode._session.setWhisperMode(0);
515
554
  };
516
555
  element.appendChild(btn2);
517
556
  }
518
557
  }
519
558
 
520
- // LISTEN BUTTONS
521
- if (session.direction === 'LISTEN' && session.state === 'ACTIVE') {
522
- const btn = document.createElement("BUTTON");
523
- btn.innerHTML = 'Start whisper';
524
- btn.onclick = function () {
525
- this.parentNode._session.setWhisperMode(1);
526
- };
527
- element.appendChild(btn);
528
-
529
- const btn2 = document.createElement("BUTTON");
530
- btn2.innerHTML = 'End whisper';
531
- btn2.onclick = function () {
532
- this.parentNode._session.setWhisperMode(0);
533
- };
534
- element.appendChild(btn2);
535
- }
536
- }
537
-
538
- // Perform authenticated ajax request to api server and return json payload on success
539
- function jsonApiGetRequest(path) {
540
- const xhr = new XMLHttpRequest();
541
- const url = API_URL + path;
542
- xhr.open('GET', url, true);
543
- xhr.responseType = 'json';
544
- const apiToken = document.getElementById('api_key').value;
545
- xhr.setRequestHeader('Authorization', 'Bearer ' + apiToken);
546
- xhr.setRequestHeader('Accept', 'application/json');
547
-
548
- return new Promise(function (resolve, reject) {
549
- xhr.onreadystatechange = function () {
550
- if (xhr.readyState === 4) {
551
- if (xhr.status !== 200) {
552
- reject(xhr.status + ' ' + xhr.statusText);
553
- return;
559
+ // Perform authenticated ajax request to api server and return json payload on success
560
+ function jsonApiGetRequest(path) {
561
+ const xhr = new XMLHttpRequest();
562
+ const url = API_URL + path;
563
+ xhr.open('GET', url, true);
564
+ xhr.responseType = 'json';
565
+ const apiToken = document.getElementById('api_key').value;
566
+ xhr.setRequestHeader('Authorization', 'Bearer ' + apiToken);
567
+ xhr.setRequestHeader('Accept', 'application/json');
568
+
569
+ return new Promise(function (resolve, reject) {
570
+ xhr.onreadystatechange = function () {
571
+ if (xhr.readyState === 4) {
572
+ if (xhr.status !== 200) {
573
+ reject(xhr.status + ' ' + xhr.statusText);
574
+ return;
575
+ }
576
+ resolve(xhr.response);
554
577
  }
555
- resolve(xhr.response);
556
- }
557
- };
558
- console.debug(`API Request: ${url}`);
559
- xhr.send();
560
- })
561
- }
562
- </script>
563
-
578
+ };
579
+ console.debug(`API Request: ${url}`);
580
+ xhr.send();
581
+ });
582
+ }
583
+ </script>
564
584
  </html>