@telegenta/webclient 3.0.0 → 3.1.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,17 +1,17 @@
1
1
  <!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source: phone-client.js</title><!--[if lt IE 9]>
2
2
  <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
3
- <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="EventClient.html">EventClient</a></div><div class="sidebar-section-children"><a href="InvalidMediaDevice.html">InvalidMediaDevice</a></div><div class="sidebar-section-children"><a href="InvalidOptions.html">InvalidOptions</a></div><div class="sidebar-section-children"><a href="InvalidParameter.html">InvalidParameter</a></div><div class="sidebar-section-children"><a href="LogHandler.html">LogHandler</a></div><div class="sidebar-section-children"><a href="MediaNotPrepared.html">MediaNotPrepared</a></div><div class="sidebar-section-children"><a href="NoActiveSession.html">NoActiveSession</a></div><div class="sidebar-section-children"><a href="NotAllowed.html">NotAllowed</a></div><div class="sidebar-section-children"><a href="NotReady.html">NotReady</a></div><div class="sidebar-section-children"><a href="Phone.html">Phone</a></div><div class="sidebar-section-children"><a href="Request.html">Request</a></div><div class="sidebar-section-children"><a href="Requestor.html">Requestor</a></div><div class="sidebar-section-children"><a href="Session.html">Session</a></div><div class="sidebar-section-children"><a href="UnableToConnect.html">UnableToConnect</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:callDataInfo">callDataInfo</a></div><div class="sidebar-section-children"><a href="global.html#event:callDurationUpdate">callDurationUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:callEnded">callEnded</a></div><div class="sidebar-section-children"><a href="global.html#event:callQualityReportUpdate">callQualityReportUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:connectionStateChange">connectionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:holdStateChange">holdStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:incomingCall">incomingCall</a></div><div class="sidebar-section-children"><a href="global.html#event:muteStateChange">muteStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:recordingStateChange">recordingStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionCreated">sessionCreated</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionRemoved">sessionRemoved</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionStateChange">sessionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:whisperModeChange">whisperModeChange</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#CALL_DIRECTION">CALL_DIRECTION</a></div><div class="sidebar-section-children"><a href="global.html#CALL_DISPOSITION">CALL_DISPOSITION</a></div><div class="sidebar-section-children"><a href="global.html#CONNECTION_STATE">CONNECTION_STATE</a></div><div class="sidebar-section-children"><a href="global.html#LOGLVL">LOGLVL</a></div><div class="sidebar-section-children"><a href="global.html#SESSION_STATE">SESSION_STATE</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">phone-client.js</h1></header><article><pre class="prettyprint source lang-js"><code>// 'use strict';
4
- import EventEmitter from "events";
5
- import JsSIP from "jssip";
6
- import { LogHandler, LOGLVL } from "./logger";
7
- import * as Exceptions from "./exceptions";
8
- import * as Tools from "./tools";
9
- import { EventClient } from "./event-client";
10
- import camelcaseKeys from "camelcase-keys";
3
+ <![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="dark"><div class="sidebar-container"><div class="sidebar" id="sidebar"><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="EventClient.html">EventClient</a></div><div class="sidebar-section-children"><a href="EventServiceNotConnected.html">EventServiceNotConnected</a></div><div class="sidebar-section-children"><a href="InvalidMediaDevice.html">InvalidMediaDevice</a></div><div class="sidebar-section-children"><a href="InvalidOptions.html">InvalidOptions</a></div><div class="sidebar-section-children"><a href="InvalidParameter.html">InvalidParameter</a></div><div class="sidebar-section-children"><a href="LogHandler.html">LogHandler</a></div><div class="sidebar-section-children"><a href="MediaNotPrepared.html">MediaNotPrepared</a></div><div class="sidebar-section-children"><a href="NoActiveSession.html">NoActiveSession</a></div><div class="sidebar-section-children"><a href="NotAllowed.html">NotAllowed</a></div><div class="sidebar-section-children"><a href="NotReady.html">NotReady</a></div><div class="sidebar-section-children"><a href="Phone.html">Phone</a></div><div class="sidebar-section-children"><a href="Request.html">Request</a></div><div class="sidebar-section-children"><a href="Requestor.html">Requestor</a></div><div class="sidebar-section-children"><a href="Session.html">Session</a></div><div class="sidebar-section-children"><a href="UnableToConnect.html">UnableToConnect</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:callDataInfo">callDataInfo</a></div><div class="sidebar-section-children"><a href="global.html#event:callDurationUpdate">callDurationUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:callEnded">callEnded</a></div><div class="sidebar-section-children"><a href="global.html#event:callQualityReportUpdate">callQualityReportUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:connectionStateChange">connectionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:holdStateChange">holdStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:incomingCall">incomingCall</a></div><div class="sidebar-section-children"><a href="global.html#event:muteStateChange">muteStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:recordingStateChange">recordingStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionCreated">sessionCreated</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionRemoved">sessionRemoved</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionStateChange">sessionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:whisperModeChange">whisperModeChange</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#CALL_DIRECTION">CALL_DIRECTION</a></div><div class="sidebar-section-children"><a href="global.html#CALL_DISPOSITION">CALL_DISPOSITION</a></div><div class="sidebar-section-children"><a href="global.html#CONNECTION_STATE">CONNECTION_STATE</a></div><div class="sidebar-section-children"><a href="global.html#LOGLVL">LOGLVL</a></div><div class="sidebar-section-children"><a href="global.html#SESSION_STATE">SESSION_STATE</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">phone-client.js</h1></header><article><pre class="prettyprint source lang-js"><code>// 'use strict';
4
+ import EventEmitter from 'events';
5
+ import JsSIP from 'jssip';
6
+ import { LogHandler, LOGLVL } from './logger';
7
+ import * as Exceptions from './exceptions';
8
+ import * as Tools from './tools';
9
+ import { EventClient } from './event-client';
10
+ import camelcaseKeys from 'camelcase-keys';
11
11
 
12
12
  const options = {
13
- wssServer: "wss://api.telegenta.com/sip/",
14
- eventServer: "wss://api.telegenta.com/es/",
13
+ wssServer: 'wss://api.telegenta.com/sip/',
14
+ eventServer: 'wss://esvc.telegenta.com/',
15
15
  provisionToken: null,
16
16
  authorizationUsername: null,
17
17
  password: null,
@@ -44,9 +44,9 @@ const DTMF_FREQUENCY_TABLE = {
44
44
  8: [1336, 852],
45
45
  9: [1477, 852],
46
46
  c: [1633, 852],
47
- "*": [1209, 941],
47
+ '*': [1209, 941],
48
48
  0: [1336, 941],
49
- "#": [1477, 941],
49
+ '#': [1477, 941],
50
50
  d: [1633, 941],
51
51
  };
52
52
 
@@ -117,11 +117,11 @@ export class Phone extends EventEmitter {
117
117
  this._activeSessions = {};
118
118
 
119
119
  // Create remote audio element
120
- this._remoteAudioElement = document.createElement("AUDIO");
120
+ this._remoteAudioElement = document.createElement('AUDIO');
121
121
  this._remoteAudioElement.autoplay = true;
122
122
 
123
123
  // Tone generator
124
- this._toneAudioElement = document.createElement("AUDIO");
124
+ this._toneAudioElement = document.createElement('AUDIO');
125
125
  this._toneAudioElement.autoplay = true;
126
126
  this._toneTimer = null;
127
127
  this._repeatToneActive = false;
@@ -136,9 +136,9 @@ export class Phone extends EventEmitter {
136
136
 
137
137
  // Handle jssip debug logs
138
138
  if (this.log.level === LOGLVL.DEBUG) {
139
- JsSIP.debug.disable("JsSIP:*");
139
+ JsSIP.debug.disable('JsSIP:*');
140
140
  } else {
141
- JsSIP.debug.disable("JsSIP:*");
141
+ JsSIP.debug.disable('JsSIP:*');
142
142
  }
143
143
  }
144
144
 
@@ -150,7 +150,7 @@ export class Phone extends EventEmitter {
150
150
  *
151
151
  */
152
152
  prepareMedia() {
153
- this.log.info("Get media permission");
153
+ this.log.info('Get media permission');
154
154
  const self = this;
155
155
  const constraints = { audio: true, video: false };
156
156
  return new Promise(function (resolve, reject) {
@@ -158,13 +158,13 @@ export class Phone extends EventEmitter {
158
158
  () => {
159
159
  self._mediaPermission = true;
160
160
  self._mediaPermissionError = false;
161
- self.log.info("Media permission granted");
161
+ self.log.info('Media permission granted');
162
162
  resolve();
163
163
  },
164
164
  (err) => {
165
165
  self._mediaPermission = false;
166
166
  self._mediaPermissionError = true;
167
- self.log.error("Denied media permission: " + err);
167
+ self.log.error('Denied media permission: ' + err);
168
168
  reject(err);
169
169
  },
170
170
  );
@@ -199,7 +199,7 @@ export class Phone extends EventEmitter {
199
199
  this._checkMediaPermission();
200
200
  // Check phone is not running already, if so stop it first
201
201
  if (this._isStarted) {
202
- this.log.warning("Phone already started, stopping first");
202
+ this.log.warning('Phone already started, stopping first');
203
203
  this.stop();
204
204
  }
205
205
 
@@ -214,29 +214,29 @@ export class Phone extends EventEmitter {
214
214
  try {
215
215
  pt = JSON.parse(atob(this._options.provisionToken));
216
216
  } catch (err) {
217
- throw new Exceptions.InvalidOptions("provisionToken", "Invalid token");
217
+ throw new Exceptions.InvalidOptions('provisionToken', 'Invalid token');
218
218
  }
219
219
 
220
- this.log.info("Using provisioning token authorization");
220
+ this.log.info('Using provisioning token authorization');
221
221
  this._options.wssServer = pt.wss_server;
222
222
  this._options.eventServer = pt.event_server;
223
223
  this._options.authorizationUsername = pt.wss_username;
224
224
  this._options.password = pt.wss_password;
225
225
  this._options.callToken = pt.wss_token;
226
226
  } else {
227
- this.log.info("Using manual specified credentials");
227
+ this.log.info('Using manual specified credentials');
228
228
 
229
229
  // Check auth info is provided
230
230
  if (!this._options.authorizationUsername) {
231
- throw new Exceptions.InvalidOptions("authorizationUsername", "Missing username");
231
+ throw new Exceptions.InvalidOptions('authorizationUsername', 'Missing username');
232
232
  }
233
233
 
234
234
  if (!this._options.password) {
235
- throw new Exceptions.InvalidOptions("password", "Missing password");
235
+ throw new Exceptions.InvalidOptions('password', 'Missing password');
236
236
  }
237
237
 
238
238
  if (!this._options.callToken) {
239
- throw new Exceptions.InvalidOptions("callToken", "Missing call token");
239
+ throw new Exceptions.InvalidOptions('callToken', 'Missing call token');
240
240
  }
241
241
  }
242
242
 
@@ -259,18 +259,18 @@ export class Phone extends EventEmitter {
259
259
  if (this._options.useCallDataInfoEvent) {
260
260
  // Subscribe to calldata_available events
261
261
  let eventSpec = {
262
- event_name: "calldata_available",
262
+ event_name: 'calldata_available',
263
263
  call_token: this._options.callToken,
264
264
  };
265
265
 
266
266
  this._eventClient
267
267
  .subscribe(eventSpec, (data) => {
268
268
  try {
269
- this.log.debug("Received remote call data info");
269
+ this.log.debug('Received remote call data info');
270
270
  this.log.debug(data);
271
271
  this._fireCallDataInfoEvent(data);
272
272
  } catch (e) {
273
- this.log.error("Event callback failed: " + e);
273
+ this.log.error('Event callback failed: ' + e);
274
274
  }
275
275
  })
276
276
  .then(
@@ -293,10 +293,10 @@ export class Phone extends EventEmitter {
293
293
  try {
294
294
  wsSocket = new JsSIP.WebSocketInterface(this._options.wssServer);
295
295
  } catch (err) {
296
- throw new Exceptions.UnableToConnect("Unable to connect phone websocket: " + err);
296
+ throw new Exceptions.UnableToConnect('Unable to connect phone websocket: ' + err);
297
297
  }
298
298
 
299
- this.log.info("Starting phone");
299
+ this.log.info('Starting phone');
300
300
  this.log.debug(this._options);
301
301
 
302
302
  this._UA = new JsSIP.UA({
@@ -305,15 +305,15 @@ export class Phone extends EventEmitter {
305
305
  password: this._options.password,
306
306
  register: this._options.register,
307
307
  display_name: this._options.authorizationUsername,
308
- uri: this._options.authorizationUsername + "@telegenta.com",
308
+ uri: this._options.authorizationUsername + '@telegenta.com',
309
309
  session_timers: false,
310
310
  });
311
311
 
312
312
  // Setup remote media handler
313
313
  const self = this;
314
- this._UA.on("newRTCSession", function (data) {
315
- self.log.debug("New RTCSession");
316
- if (data.originator === "remote") {
314
+ this._UA.on('newRTCSession', function (data) {
315
+ self.log.debug('New RTCSession');
316
+ if (data.originator === 'remote') {
317
317
  // Create new session
318
318
  const session = new Session(self);
319
319
 
@@ -326,18 +326,20 @@ export class Phone extends EventEmitter {
326
326
  // Check if we have other sessions and reject call if autoBusyIncoming is true
327
327
  if (self._options.autoBusyIncoming) {
328
328
  if (Object.keys(self._activeSessions).length > 0) {
329
- self.log.info(`Autorejecting incoming call ${session.uuid} because there is already active calls`);
330
- session.reject(486, "Busy");
329
+ self.log.info(
330
+ `Autorejecting incoming call ${session.uuid} because there is already active calls`,
331
+ );
332
+ session.reject(486, 'Busy');
331
333
  return;
332
334
  } else {
333
- self.log.debug("There are no other sessions, adding incoming call");
335
+ self.log.debug('There are no other sessions, adding incoming call');
334
336
  }
335
337
  }
336
338
 
337
339
  self._addSession(session);
338
340
 
339
341
  // Set new session state
340
- session._setSessionState("INCOMING");
342
+ session._setSessionState('INCOMING');
341
343
 
342
344
  // Now fire incomingCall event
343
345
  self._fireIncomingCallEvent(session);
@@ -345,33 +347,33 @@ export class Phone extends EventEmitter {
345
347
  });
346
348
 
347
349
  // Setup connection state event subscriptions
348
- this._UA.on("connecting", function (/* data */) {
349
- self._setConnectionState("CONNECTING");
350
+ this._UA.on('connecting', function (/* data */) {
351
+ self._setConnectionState('CONNECTING');
350
352
  });
351
353
 
352
- this._UA.on("connected", function (/* data */) {
353
- self._setConnectionState("CONNECTED");
354
+ this._UA.on('connected', function (/* data */) {
355
+ self._setConnectionState('CONNECTED');
354
356
  });
355
357
 
356
- this._UA.on("disconnected", function (/* data */) {
357
- self._setConnectionState("DISCONNECTED");
358
+ this._UA.on('disconnected', function (/* data */) {
359
+ self._setConnectionState('DISCONNECTED');
358
360
  });
359
361
 
360
- this._UA.on("registered", function (/* data */) {
361
- self._setConnectionState("REGISTERED");
362
+ this._UA.on('registered', function (/* data */) {
363
+ self._setConnectionState('REGISTERED');
362
364
  });
363
365
 
364
- this._UA.on("unregistered", function (/* data */) {
365
- self._setConnectionState("UNREGISTERED");
366
+ this._UA.on('unregistered', function (/* data */) {
367
+ self._setConnectionState('UNREGISTERED');
366
368
  });
367
369
 
368
- this._UA.on("registrationFailed", function (/* data */) {
369
- self._setConnectionState("REGISTRATION_FAILED");
370
+ this._UA.on('registrationFailed', function (/* data */) {
371
+ self._setConnectionState('REGISTRATION_FAILED');
370
372
  });
371
373
 
372
374
  this._UA.start();
373
375
  this._isStarted = true;
374
- this.log.info("Successfully started phone");
376
+ this.log.info('Successfully started phone');
375
377
  }
376
378
 
377
379
  /**
@@ -381,7 +383,7 @@ export class Phone extends EventEmitter {
381
383
  stop() {
382
384
  // Check phone is started first
383
385
  if (!this._isStarted) {
384
- throw new Exceptions.NotReady("Phone is not started");
386
+ throw new Exceptions.NotReady('Phone is not started');
385
387
  }
386
388
 
387
389
  // Make sure events are not firing anymore, we can't rely on GC.
@@ -393,18 +395,18 @@ export class Phone extends EventEmitter {
393
395
 
394
396
  // Disconnect event client if needed
395
397
  if (!this._eventClient) {
396
- this.log.debug("Event client not running, not stopping");
398
+ this.log.debug('Event client not running, not stopping');
397
399
  return;
398
400
  }
399
401
  try {
400
402
  this._eventClient.disconnect();
401
403
  delete this._eventClient;
402
404
  } catch (e) {
403
- this.log.warning("Unable to disconnect event client");
405
+ this.log.warning('Unable to disconnect event client');
404
406
  }
405
407
 
406
408
  this._isStarted = false;
407
- this.log.info("Phone client successfully stopped");
409
+ this.log.info('Phone client successfully stopped');
408
410
  }
409
411
 
410
412
  /**
@@ -418,20 +420,22 @@ export class Phone extends EventEmitter {
418
420
  * @param {number} [params.answerCallTimeout=0] Hangup call with NO ANSWER if call is not picked up before specified interval (seconds). If 0, timeout is disabled.
419
421
  * @param {number} [params.maximumCallCost] Maximum call cost (in customer currency) allowed for call. The call will automatically be stopped when the specified cost has been reached. Setting this to 0 disables checking for maximum cost.
420
422
  * @param {string} [params.metaInfo] Add meta info to call, which will be available in server events and inside call complete webhook (max 256 chars)
423
+ * @param {object} [params.setHeader] Custom SIP headers to add to the INVITE, as {name: value}.
421
424
  * @return {Session} object for call
422
425
  * @throws NotReady - If phone is not ready to call
423
426
  * @throws NotAllowed - If there are calls currently connecting
427
+ * @throws InvalidParameter - If setHeader contains an invalid header name or container
424
428
  *
425
429
  */
426
430
  call(number, params = {}) {
427
431
  // Check phone is started first
428
432
  if (!this._isStarted) {
429
- throw new Exceptions.NotReady("Phone is not started");
433
+ throw new Exceptions.NotReady('Phone is not started');
430
434
  }
431
435
 
432
436
  // Check we dont have any other connecting calls
433
437
  if (this._checkForConnectingCalls()) {
434
- throw Exceptions.NotAllowed("Unable to make new calls while others are connecting");
438
+ throw new Exceptions.NotAllowed('Unable to make new calls while others are connecting');
435
439
  }
436
440
 
437
441
  // Hold other calls if needed
@@ -445,15 +449,16 @@ export class Phone extends EventEmitter {
445
449
  explicitCallerId: null,
446
450
  explicitShortCallerId: null,
447
451
  metaInfo: null,
452
+ setHeader: null,
448
453
  _isListenCall: false,
449
454
  },
450
455
  params,
451
456
  );
452
457
 
453
458
  // Trim phone number to remove spaces and '()'
454
- number = number.trim().replace(/ |\(|\)/g, "");
459
+ number = number.trim().replace(/ |\(|\)/g, '');
455
460
 
456
- this.log.info("Calling number: " + number);
461
+ this.log.info('Calling number: ' + number);
457
462
 
458
463
  let session = new Session(this);
459
464
  session.newOutboundSession(number, callParams);
@@ -469,12 +474,12 @@ export class Phone extends EventEmitter {
469
474
  * @throws NotAllowed - If there are calls currently connecting
470
475
  */
471
476
  listen(listenKey) {
472
- const s = listenKey.split("@");
477
+ const s = listenKey.split('@');
473
478
  let callUUID = s[0];
474
- let mediaSwitch = "sip:" + s[1];
479
+ let mediaSwitch = 'sip:' + s[1];
475
480
 
476
- this.log.debug("Initiating listen to call with uuid " + callUUID + " on media switch " + mediaSwitch);
477
- return this.call("listen-" + callUUID, {
481
+ this.log.debug('Initiating listen to call with uuid ' + callUUID + ' on media switch ' + mediaSwitch);
482
+ return this.call('listen-' + callUUID, {
478
483
  mediaSwitchURI: mediaSwitch,
479
484
  _isListenCall: true,
480
485
  });
@@ -600,7 +605,7 @@ export class Phone extends EventEmitter {
600
605
 
601
606
  // Begin repeating tones
602
607
  _startRepeatTone(sequences) {
603
- this.log.info("Repeating tone sequence started");
608
+ this.log.info('Repeating tone sequence started');
604
609
  this.log.debug(sequences);
605
610
  this._repeatToneSequence = sequences;
606
611
  this._repeatToneSequencePos = 0;
@@ -630,7 +635,7 @@ export class Phone extends EventEmitter {
630
635
  // End repeating tones
631
636
  _endRepeatTone() {
632
637
  if (this._repeatToneActive) {
633
- this.log.info("Repeating tone stopped");
638
+ this.log.info('Repeating tone stopped');
634
639
  this._repeatToneActive = false;
635
640
  this.stopTone();
636
641
  }
@@ -641,7 +646,7 @@ export class Phone extends EventEmitter {
641
646
  // Create audio context
642
647
  const AudioContext = window.AudioContext || window.webkitAudioContext || false;
643
648
  if (!AudioContext) {
644
- throw new Exceptions.NotAllowed("Audio API not supported by this browser");
649
+ throw new Exceptions.NotAllowed('Audio API not supported by this browser');
645
650
  }
646
651
 
647
652
  const ctx = new AudioContext();
@@ -669,17 +674,17 @@ export class Phone extends EventEmitter {
669
674
  * @throws NotAllowed If there are connection calls
670
675
  */
671
676
  holdAllCalls() {
672
- this.log.info("Holding all active sessions");
677
+ this.log.info('Holding all active sessions');
673
678
  // Check all session can be set on hold
674
679
  if (this._checkForConnectingCalls()) {
675
- throw Exceptions.NotAllowed("A session is currently connecting");
680
+ throw new Exceptions.NotAllowed('A session is currently connecting');
676
681
  }
677
682
 
678
683
  // Iterate through all active calls and hold them
679
684
  for (let key in this._activeSessions) {
680
685
  if (this._activeSessions.hasOwnProperty(key)) {
681
686
  let session = this._activeSessions[key];
682
- if (session.state === "ACTIVE") {
687
+ if (session.state === 'ACTIVE') {
683
688
  session.hold();
684
689
  }
685
690
  }
@@ -708,9 +713,9 @@ export class Phone extends EventEmitter {
708
713
 
709
714
  // Fire event
710
715
  try {
711
- this.emit("sessionCreated", { session: session });
716
+ this.emit('sessionCreated', { session: session });
712
717
  } catch (e) {
713
- this.log.error("Exception in sessionCreated event: " + e);
718
+ this.log.error('Exception in sessionCreated event: ' + e);
714
719
  }
715
720
  }
716
721
 
@@ -726,9 +731,9 @@ export class Phone extends EventEmitter {
726
731
 
727
732
  // Fire event
728
733
  try {
729
- this.emit("sessionRemoved", { session: session });
734
+ this.emit('sessionRemoved', { session: session });
730
735
  } catch (e) {
731
- this.log.error("Exception in sessionRemoved event: " + e);
736
+ this.log.error('Exception in sessionRemoved event: ' + e);
732
737
  }
733
738
 
734
739
  delete this._activeSessions[session.__containerId];
@@ -753,7 +758,12 @@ export class Phone extends EventEmitter {
753
758
  for (let key in this._activeSessions) {
754
759
  if (this._activeSessions.hasOwnProperty(key)) {
755
760
  let session = this._activeSessions[key];
756
- if (session.state === "INITIALIZING" || session.state === "CALLING" || session.state === "PROGRESS" || session.state === "INCOMING") {
761
+ if (
762
+ session.state === 'INITIALIZING' ||
763
+ session.state === 'CALLING' ||
764
+ session.state === 'PROGRESS' ||
765
+ session.state === 'INCOMING'
766
+ ) {
757
767
  return true;
758
768
  }
759
769
  }
@@ -778,12 +788,12 @@ export class Phone extends EventEmitter {
778
788
  * @param {CONNECTION_STATE} state - The current connection state
779
789
  */
780
790
 
781
- this.log.info("Connection state: " + state);
791
+ this.log.info('Connection state: ' + state);
782
792
  this._connectionState = state;
783
793
  try {
784
- this.emit("connectionStateChange", { state: state });
794
+ this.emit('connectionStateChange', { state: state });
785
795
  } catch (e) {
786
- this.log.error("Exception in connectionStateChange event: " + e);
796
+ this.log.error('Exception in connectionStateChange event: ' + e);
787
797
  }
788
798
  }
789
799
 
@@ -794,9 +804,9 @@ export class Phone extends EventEmitter {
794
804
  */
795
805
  _fireIncomingCallEvent(session) {
796
806
  try {
797
- this.emit("incomingCall", session);
807
+ this.emit('incomingCall', session);
798
808
  } catch (e) {
799
- this.log.error("Exception in incomingCall event: " + e);
809
+ this.log.error('Exception in incomingCall event: ' + e);
800
810
  }
801
811
  }
802
812
 
@@ -849,12 +859,12 @@ export class Phone extends EventEmitter {
849
859
 
850
860
  // Add some extra convenience fields
851
861
  e.durationStr = Tools.createDurationString(e.duration);
852
- e.answerDurationStr = e.answerDuration ? Tools.createDurationString(e.answerDuration) : "";
853
- e.activeDurationStr = e.activeDuration ? Tools.createDurationString(e.activeDuration) : "";
862
+ e.answerDurationStr = e.answerDuration ? Tools.createDurationString(e.answerDuration) : '';
863
+ e.activeDurationStr = e.activeDuration ? Tools.createDurationString(e.activeDuration) : '';
854
864
  try {
855
- this.emit("callDataInfo", e);
865
+ this.emit('callDataInfo', e);
856
866
  } catch (e) {
857
- this.log.error("Exception in callDataInfo event: " + e);
867
+ this.log.error('Exception in callDataInfo event: ' + e);
858
868
  }
859
869
  }
860
870
 
@@ -935,33 +945,37 @@ export class Session extends EventEmitter {
935
945
  let options = {
936
946
  mediaConstraints: { audio: true, video: false },
937
947
  pcConfig: {
938
- iceServers: [{ urls: ["stun:stun.l.google.com:19302"] }],
948
+ iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }],
939
949
  },
940
- extraHeaders: ["X-Call-token: " + this._phone.options.callToken, "X-P-UUID: " + this._callUUID, "X-Max-cost: " + callParams.maximumCallCost],
950
+ extraHeaders: [
951
+ 'X-Call-token: ' + this._phone.options.callToken,
952
+ 'X-P-UUID: ' + this._callUUID,
953
+ 'X-Max-cost: ' + callParams.maximumCallCost,
954
+ ],
941
955
  };
942
956
 
943
957
  // Append explicit caller id if needed
944
958
  if (callParams.explicitCallerId) {
945
- options.extraHeaders.push("X-CID-Number-Id: " + callParams.explicitCallerId);
959
+ options.extraHeaders.push('X-CID-Number-Id: ' + callParams.explicitCallerId);
946
960
  }
947
961
 
948
962
  // Append explicit short cid if needed
949
963
  if (callParams.explicitShortCallerId) {
950
- options.extraHeaders.push("X-Short-CID-Id: " + callParams.explicitShortCallerId);
964
+ options.extraHeaders.push('X-Short-CID-Id: ' + callParams.explicitShortCallerId);
951
965
  }
952
966
 
953
967
  // Append meta info
954
968
  if (callParams.metaInfo) {
955
- options.extraHeaders.push("X-Meta-Info: " + encodeURI(callParams.metaInfo.toString().substr(0, 255)));
969
+ options.extraHeaders.push('X-Meta-Info: ' + encodeURI(callParams.metaInfo.toString().substr(0, 255)));
956
970
  }
957
971
 
958
972
  // Enable recording?
959
973
  if (callParams.record) {
960
974
  if (isNaN(callParams.record)) {
961
- throw new Exceptions.InvalidParameter("Record parameter must be a number");
975
+ throw new Exceptions.InvalidParameter('Record parameter must be a number');
962
976
  }
963
977
  this.log.debug(`Recording enabled and kept for ${callParams.record} days`);
964
- options.extraHeaders.push("X-Record: " + callParams.record);
978
+ options.extraHeaders.push('X-Record: ' + callParams.record);
965
979
  this._isRecording = true;
966
980
  } else {
967
981
  this._isRecording = false;
@@ -972,8 +986,8 @@ export class Session extends EventEmitter {
972
986
 
973
987
  // Manually specified media switch
974
988
  if (callParams.mediaSwitchURI) {
975
- this.log.debug("Requesting specific media switch " + callParams.mediaSwitchURI);
976
- options.extraHeaders.push("X-RMS: " + callParams.mediaSwitchURI);
989
+ this.log.debug('Requesting specific media switch ' + callParams.mediaSwitchURI);
990
+ options.extraHeaders.push('X-RMS: ' + callParams.mediaSwitchURI);
977
991
  } else {
978
992
  // Request specific media switch to group calls on same server
979
993
  const otherSessions = this.getOtherSessions();
@@ -981,24 +995,41 @@ export class Session extends EventEmitter {
981
995
  if (otherSessions.length > 0) {
982
996
  if (otherSessions[0]._mediaSwitch) {
983
997
  // There are existing active calls, append first media switch address found and request call grouping to this
984
- this.log.debug("Requesting call grouping on media switch " + otherSessions[0]._mediaSwitch);
985
- options.extraHeaders.push("X-RMS: sip:" + otherSessions[0]._mediaSwitch);
998
+ this.log.debug('Requesting call grouping on media switch ' + otherSessions[0]._mediaSwitch);
999
+ options.extraHeaders.push('X-RMS: sip:' + otherSessions[0]._mediaSwitch);
986
1000
  } else {
987
- this.log.warning("Other sessions exist but media switch address is invalid");
1001
+ this.log.warning('Other sessions exist but media switch address is invalid');
988
1002
  }
989
1003
  } else {
990
- this.log.debug("No other active calls, no media switch grouping requested");
1004
+ this.log.debug('No other active calls, no media switch grouping requested');
991
1005
  }
992
1006
  }
993
1007
 
1008
+ // Custom headers last, so they cannot displace the built-in ones.
1009
+ // Must be before _UA.call() below: jssip clones extraHeaders synchronously
1010
+ // inside connect(), so anything pushed after that call never reaches the INVITE.
1011
+ let customHeaders;
1012
+ try {
1013
+ customHeaders = Tools.buildCustomHeaders(callParams.setHeader);
1014
+ } catch (error) {
1015
+ if (error instanceof TypeError) {
1016
+ throw new Exceptions.InvalidParameter(error.message);
1017
+ }
1018
+ throw error;
1019
+ }
1020
+ for (const header of customHeaders) {
1021
+ this.log.debug('Adding custom header: ' + header);
1022
+ options.extraHeaders.push(header);
1023
+ }
1024
+
994
1025
  // Prepare invite uri
995
- const uri = "sip:" + number + "@telegenta.com";
1026
+ const uri = 'sip:' + number + '@telegenta.com';
996
1027
 
997
1028
  // Not start call and create sip session
998
1029
  try {
999
1030
  this._initWebRTCSession(this._phone._UA.call(uri, options));
1000
1031
  } catch (error) {
1001
- this.log.error("Error starting call:", error);
1032
+ this.log.error('Error starting call:', error);
1002
1033
  return null;
1003
1034
  }
1004
1035
 
@@ -1009,16 +1040,16 @@ export class Session extends EventEmitter {
1009
1040
 
1010
1041
  // Create new inbound session
1011
1042
  newInboundSession(newSessionRequest) {
1012
- this._callUUID = newSessionRequest.request.getHeader("X-P-UUID", 0);
1013
- this._destinationNumber = newSessionRequest.request.getHeader("X-DID", 0);
1014
- this._localNumberName = unescape(newSessionRequest.request.getHeader("X-DID-NAME", 0));
1043
+ this._callUUID = newSessionRequest.request.getHeader('X-P-UUID', 0);
1044
+ this._destinationNumber = newSessionRequest.request.getHeader('X-DID', 0);
1045
+ this._localNumberName = unescape(newSessionRequest.request.getHeader('X-DID-NAME', 0));
1015
1046
  this._originName = newSessionRequest.request.from.display_name;
1016
1047
  this._originatingNumber = newSessionRequest.request.from.uri.user;
1017
1048
  this.log.info(`Incoming call from ${this._originatingNumber} [${this._callUUID}]`);
1018
1049
  this.log.debug(newSessionRequest);
1019
1050
 
1020
1051
  // Get media switch info
1021
- this._mediaSwitch = newSessionRequest.request.getHeader("X-MS");
1052
+ this._mediaSwitch = newSessionRequest.request.getHeader('X-MS');
1022
1053
  this.log.debug(`Media switch handling call: ${this._mediaSwitch}`);
1023
1054
 
1024
1055
  this._initWebRTCSession(newSessionRequest.session);
@@ -1052,10 +1083,10 @@ export class Session extends EventEmitter {
1052
1083
  const self = this;
1053
1084
 
1054
1085
  // ICE CANDIDATES
1055
- this._webRTCSession.on("icecandidate", function (event) {
1086
+ this._webRTCSession.on('icecandidate', function (event) {
1056
1087
  const c = event.candidate;
1057
1088
  self.log.debug(`Got ICE candidate: ${c.candidate}`);
1058
- if (c.type === "srflx" &amp;&amp; c.relatedAddress !== null &amp;&amp; c.relatedPort !== null) {
1089
+ if (c.type === 'srflx' &amp;&amp; c.relatedAddress !== null &amp;&amp; c.relatedPort !== null) {
1059
1090
  self.log.info(`Accepting ICE candidate: ${c.candidate}`);
1060
1091
 
1061
1092
  // Clear timer first
@@ -1078,24 +1109,24 @@ export class Session extends EventEmitter {
1078
1109
  }
1079
1110
  });
1080
1111
 
1081
- this._webRTCSession.on("connecting", function (/* data */) {
1112
+ this._webRTCSession.on('connecting', function (/* data */) {
1082
1113
  self._startDurationUpdateTimer();
1083
1114
  self._startRTCPStatsUpdateTimer();
1084
- self._setSessionState("INITIALIZING");
1115
+ self._setSessionState('INITIALIZING');
1085
1116
  });
1086
1117
 
1087
- this._webRTCSession.on("sending", function (/* data */) {
1118
+ this._webRTCSession.on('sending', function (/* data */) {
1088
1119
  // Play call initialized tone if needed
1089
1120
  if (self._phone.options.playCallInitializedTone) {
1090
1121
  self._phone._startRepeatTone(ST_CALLINIT);
1091
1122
  }
1092
1123
 
1093
- self._setSessionState("CALLING");
1124
+ self._setSessionState('CALLING');
1094
1125
  });
1095
1126
 
1096
- this._webRTCSession.on("progress", function (data) {
1097
- if (data.originator === "remote") {
1098
- self._setSessionState("PROGRESS", { code: data.response.status_code });
1127
+ this._webRTCSession.on('progress', function (data) {
1128
+ if (data.originator === 'remote') {
1129
+ self._setSessionState('PROGRESS', { code: data.response.status_code });
1099
1130
  self._phone._endRepeatTone();
1100
1131
 
1101
1132
  // Ringback handling on 180 RINGING
@@ -1110,14 +1141,14 @@ export class Session extends EventEmitter {
1110
1141
  try {
1111
1142
  self.hangup();
1112
1143
  } catch (e) {
1113
- self.log.info("No active session, ignoring answer call timeout");
1144
+ self.log.info('No active session, ignoring answer call timeout');
1114
1145
  }
1115
1146
  }, self._answerCallTimeout * 1000);
1116
1147
  }
1117
1148
  }
1118
1149
  });
1119
1150
 
1120
- this._webRTCSession.on("accepted", function (data) {
1151
+ this._webRTCSession.on('accepted', function (data) {
1121
1152
  // Always end ringsback
1122
1153
  self._phone._endRepeatTone();
1123
1154
 
@@ -1130,27 +1161,27 @@ export class Session extends EventEmitter {
1130
1161
  }
1131
1162
 
1132
1163
  // Get initial recording state
1133
- if (data.originator === "remote") {
1134
- const recState = data.response.getHeader("X-Recording");
1135
- if (recState === "true") {
1136
- self.log.debug("Call is being prerecorded");
1164
+ if (data.originator === 'remote') {
1165
+ const recState = data.response.getHeader('X-Recording');
1166
+ if (recState === 'true') {
1167
+ self.log.debug('Call is being prerecorded');
1137
1168
  // self._isRecording = true;
1138
1169
  // self.emit('recordingStateChange', {state: true, error: null});
1139
1170
  } else {
1140
- self.log.debug("No call recording started yet");
1171
+ self.log.debug('No call recording started yet');
1141
1172
  }
1142
1173
 
1143
1174
  // Get media switch info
1144
- self._mediaSwitch = data.response.getHeader("X-MS");
1175
+ self._mediaSwitch = data.response.getHeader('X-MS');
1145
1176
  self.log.debug(`Media switch handling call: ${self._mediaSwitch}`);
1146
1177
  }
1147
1178
  });
1148
1179
 
1149
- this._webRTCSession.on("confirmed", function (/* data */) {
1150
- self._setSessionState("ACTIVE");
1180
+ this._webRTCSession.on('confirmed', function (/* data */) {
1181
+ self._setSessionState('ACTIVE');
1151
1182
  });
1152
1183
 
1153
- this._webRTCSession.on("ended", function (/* data */) {
1184
+ this._webRTCSession.on('ended', function (/* data */) {
1154
1185
  // End any ringsbacks if needed
1155
1186
  self._phone._endRepeatTone();
1156
1187
 
@@ -1161,10 +1192,10 @@ export class Session extends EventEmitter {
1161
1192
  clearTimeout(self._answerCallTimer);
1162
1193
  }
1163
1194
 
1164
- self._setSessionState("TERMINATED");
1195
+ self._setSessionState('TERMINATED');
1165
1196
  });
1166
1197
 
1167
- this._webRTCSession.on("failed", function (data) {
1198
+ this._webRTCSession.on('failed', function (data) {
1168
1199
  // End any ringsbacks if needed
1169
1200
  self._phone._endRepeatTone();
1170
1201
 
@@ -1182,54 +1213,54 @@ export class Session extends EventEmitter {
1182
1213
  if (data.message) {
1183
1214
  m = `${data.message.status_code} ${data.message.reason_phrase}`;
1184
1215
  } else {
1185
- m = "";
1216
+ m = '';
1186
1217
  }
1187
1218
 
1188
- self._setSessionState("FAILED", { cause: data.cause, message: m });
1219
+ self._setSessionState('FAILED', { cause: data.cause, message: m });
1189
1220
  });
1190
1221
 
1191
- this._webRTCSession.on("dtmf", function (data) {
1192
- self.log.debug("Session DTMF", data);
1222
+ this._webRTCSession.on('dtmf', function (data) {
1223
+ self.log.debug('Session DTMF', data);
1193
1224
  });
1194
1225
 
1195
- this._webRTCSession.on("newInfo", function (data) {
1196
- self.log.debug("New session info", data);
1226
+ this._webRTCSession.on('newInfo', function (data) {
1227
+ self.log.debug('New session info', data);
1197
1228
  });
1198
1229
 
1199
- this._webRTCSession.on("hold", function (/* data */) {
1230
+ this._webRTCSession.on('hold', function (/* data */) {
1200
1231
  self._isOnHold = true;
1201
1232
  self._fireHoldStateEvent();
1202
- self._setSessionState("ON_HOLD");
1233
+ self._setSessionState('ON_HOLD');
1203
1234
  });
1204
1235
 
1205
- this._webRTCSession.on("unhold", function (/* data */) {
1236
+ this._webRTCSession.on('unhold', function (/* data */) {
1206
1237
  self._isOnHold = false;
1207
1238
  self._fireHoldStateEvent();
1208
- self._setSessionState("ACTIVE");
1239
+ self._setSessionState('ACTIVE');
1209
1240
  });
1210
1241
 
1211
- this._webRTCSession.on("muted", function (data) {
1212
- self.log.debug("Microphone muted", data);
1242
+ this._webRTCSession.on('muted', function (data) {
1243
+ self.log.debug('Microphone muted', data);
1213
1244
  self._isMuted = true;
1214
1245
  self._fireMuteStateEvent();
1215
1246
  });
1216
1247
 
1217
- this._webRTCSession.on("unmuted", function (data) {
1218
- self.log.debug("Microphone unmuted", data);
1248
+ this._webRTCSession.on('unmuted', function (data) {
1249
+ self.log.debug('Microphone unmuted', data);
1219
1250
  self._isMuted = false;
1220
1251
  self._fireMuteStateEvent();
1221
1252
  });
1222
1253
 
1223
- this._webRTCSession.on("reinvite", function (data) {
1224
- self.log.debug("Session reinvite", data);
1254
+ this._webRTCSession.on('reinvite', function (data) {
1255
+ self.log.debug('Session reinvite', data);
1225
1256
  });
1226
1257
 
1227
- this._webRTCSession.on("update", function (data) {
1228
- self.log.debug("Session update", data);
1258
+ this._webRTCSession.on('update', function (data) {
1259
+ self.log.debug('Session update', data);
1229
1260
  });
1230
1261
 
1231
- this._webRTCSession.on("getusermediafailed", function (data) {
1232
- self.log.error("Getusermedia failed", data);
1262
+ this._webRTCSession.on('getusermediafailed', function (data) {
1263
+ self.log.error('Getusermedia failed', data);
1233
1264
  });
1234
1265
 
1235
1266
  // If connection connect audio stream
@@ -1238,7 +1269,7 @@ export class Session extends EventEmitter {
1238
1269
  }
1239
1270
 
1240
1271
  // DEBUG STUFF
1241
- this.log.debug("WebRTCSession object:");
1272
+ this.log.debug('WebRTCSession object:');
1242
1273
  this.log.debug(this);
1243
1274
  }
1244
1275
 
@@ -1249,13 +1280,13 @@ export class Session extends EventEmitter {
1249
1280
  */
1250
1281
  hangup(disposition = null) {
1251
1282
  if (!disposition) {
1252
- this.log.info("Hanging up call");
1283
+ this.log.info('Hanging up call');
1253
1284
  this._webRTCSession.terminate();
1254
1285
  } else {
1255
1286
  this.log.info(`Hanging up call with disposition ${disposition}`);
1256
1287
  this._manualDisposition = disposition;
1257
1288
  this._webRTCSession.terminate({
1258
- extraHeaders: ["X-HDISP: " + disposition],
1289
+ extraHeaders: ['X-HDISP: ' + disposition],
1259
1290
  });
1260
1291
  }
1261
1292
  }
@@ -1268,14 +1299,14 @@ export class Session extends EventEmitter {
1268
1299
  let options = {
1269
1300
  mediaConstraints: { audio: true, video: false },
1270
1301
  pcConfig: {
1271
- iceServers: [{ urls: ["stun:stun.l.google.com:19302"] }],
1302
+ iceServers: [{ urls: ['stun:stun.l.google.com:19302'] }],
1272
1303
  },
1273
- extraHeaders: ["X-Call-token: " + this._phone.options.callToken],
1304
+ extraHeaders: ['X-Call-token: ' + this._phone.options.callToken],
1274
1305
  };
1275
1306
 
1276
1307
  // Append meta info if needed
1277
1308
  if (metaInfo) {
1278
- options.extraHeaders.push("X-Meta-Info: " + encodeURI(metaInfo.toString().substr(0, 255)));
1309
+ options.extraHeaders.push('X-Meta-Info: ' + encodeURI(metaInfo.toString().substr(0, 255)));
1279
1310
  }
1280
1311
 
1281
1312
  this._webRTCSession.answer(options);
@@ -1289,8 +1320,8 @@ export class Session extends EventEmitter {
1289
1320
  * @param statusCode - SIP Status code
1290
1321
  * @param reasonPhrase - Reason phrase
1291
1322
  */
1292
- reject(statusCode = 603, reasonPhrase = "Rejected call") {
1293
- this.log.info("Rejecting call");
1323
+ reject(statusCode = 603, reasonPhrase = 'Rejected call') {
1324
+ this.log.info('Rejecting call');
1294
1325
  this._webRTCSession.terminate({
1295
1326
  status_code: statusCode,
1296
1327
  reason_phrase: reasonPhrase,
@@ -1331,7 +1362,7 @@ export class Session extends EventEmitter {
1331
1362
 
1332
1363
  // Set destination number
1333
1364
  set destinationNumber(number) {
1334
- this.log.debug("Setting destination number to: " + number);
1365
+ this.log.debug('Setting destination number to: ' + number);
1335
1366
  this._destinationNumber = number;
1336
1367
  }
1337
1368
 
@@ -1349,53 +1380,77 @@ export class Session extends EventEmitter {
1349
1380
  */
1350
1381
  get direction() {
1351
1382
  if (this._isListenCall) {
1352
- this.log.debug("Call direction is LISTEN");
1353
- return "LISTEN";
1354
- } else if (this._webRTCSession.direction === "incoming") {
1355
- this.log.debug("Call direction is INBOUND");
1356
- return "INBOUND";
1357
- } else if (this._webRTCSession.direction === "outgoing") {
1358
- this.log.debug("Call direction is OUTBOUND");
1359
- return "OUTBOUND";
1383
+ this.log.debug('Call direction is LISTEN');
1384
+ return 'LISTEN';
1385
+ } else if (this._webRTCSession.direction === 'incoming') {
1386
+ this.log.debug('Call direction is INBOUND');
1387
+ return 'INBOUND';
1388
+ } else if (this._webRTCSession.direction === 'outgoing') {
1389
+ this.log.debug('Call direction is OUTBOUND');
1390
+ return 'OUTBOUND';
1360
1391
  } else {
1361
- this.log.error("Unknown call direction: " + this._webRTCSession.direction);
1362
- return "UNKNOWN";
1392
+ this.log.error('Unknown call direction: ' + this._webRTCSession.direction);
1393
+ return 'UNKNOWN';
1363
1394
  }
1364
1395
  }
1365
1396
 
1397
+ /**
1398
+ * Raw WebRTC statistics for this session's peer connection.
1399
+ *
1400
+ * Returns the browser's own `RTCStatsReport` untouched, so callers can read
1401
+ * any statistic the browser exposes - RTP packet and byte counters, the
1402
+ * DTLS/ICE transport state, codec and candidate-pair details.
1403
+ *
1404
+ * @returns {Promise&lt;RTCStatsReport>} The peer connection's statistics
1405
+ * @throws NoActiveSession - If the session has no established peer connection
1406
+ * (thrown synchronously, before the promise is created)
1407
+ *
1408
+ * @example
1409
+ * const report = await session.getRTCStats();
1410
+ * for (const stat of report.values()) {
1411
+ * if (stat.type === 'inbound-rtp') console.log(stat.packetsReceived);
1412
+ * }
1413
+ */
1414
+ getRTCStats() {
1415
+ if (!this._webRTCSession || !this._webRTCSession.connection) {
1416
+ throw new Exceptions.NoActiveSession('no peer connection for this session');
1417
+ }
1418
+ return this._webRTCSession.connection.getStats();
1419
+ }
1420
+
1366
1421
  // RECORDING
1367
1422
  /**
1368
1423
  * Start recording
1369
1424
  */
1370
1425
  startRecording() {
1371
1426
  const opt = {
1372
- extraHeaders: ["Record: on"],
1427
+ extraHeaders: ['Record: on'],
1373
1428
  };
1374
1429
 
1375
- this.log.debug("Request start recording");
1430
+ this.log.debug('Request start recording');
1376
1431
 
1377
1432
  const self = this;
1378
- this._webRTCSession.once("newInfo", (data) => {
1379
- if (data.originator === "local") {
1380
- data.info.on("succeeded", function (/* data */) {
1381
- self.log.info("Recording started successfully");
1433
+ this._webRTCSession.once('newInfo', (data) => {
1434
+ if (data.originator === 'local') {
1435
+ data.info.on('succeeded', function (/* data */) {
1436
+ self.log.info('Recording started successfully');
1382
1437
  self._isRecording = true;
1383
1438
  try {
1384
- self.emit("recordingStateChange", { state: true, error: null });
1439
+ self.emit('recordingStateChange', { state: true, error: null });
1385
1440
  } catch (e) {
1386
- self.log.error("Exception in recordingStateChange event: " + e);
1441
+ self.log.error('Exception in recordingStateChange event: ' + e);
1387
1442
  }
1388
1443
  });
1389
1444
 
1390
- data.info.on("failed", function (data) {
1445
+ data.info.on('failed', function (data) {
1391
1446
  self._recordingError = data.response.reason_phrase;
1392
- self.log.error("Recording start failed: " + self._recordingError);
1447
+ self.log.error('Recording start failed: ' + self._recordingError);
1393
1448
  self._fireRecordingStateEvent();
1394
1449
  });
1395
1450
  }
1396
1451
  });
1397
1452
 
1398
- this._webRTCSession.sendInfo("application/info", null, opt);
1453
+ this._webRTCSession.sendInfo('application/info', null, opt);
1399
1454
  }
1400
1455
 
1401
1456
  /**
@@ -1403,35 +1458,35 @@ export class Session extends EventEmitter {
1403
1458
  */
1404
1459
  stopRecording() {
1405
1460
  const opt = {
1406
- extraHeaders: ["Record: off"],
1461
+ extraHeaders: ['Record: off'],
1407
1462
  };
1408
1463
 
1409
- this.log.debug("Request stop recording");
1464
+ this.log.debug('Request stop recording');
1410
1465
 
1411
1466
  const self = this;
1412
1467
 
1413
- this._webRTCSession.once("newInfo", (data) => {
1414
- data.info.on("succeeded", function (/* data */) {
1415
- self.log.info("Recording stopped successfully");
1468
+ this._webRTCSession.once('newInfo', (data) => {
1469
+ data.info.on('succeeded', function (/* data */) {
1470
+ self.log.info('Recording stopped successfully');
1416
1471
  self._isRecording = false;
1417
1472
  try {
1418
- self.emit("recordingStateChange", { state: false, error: null });
1473
+ self.emit('recordingStateChange', { state: false, error: null });
1419
1474
  } catch (e) {
1420
- self.log.error("Exception in recordingStateChange event: " + e);
1475
+ self.log.error('Exception in recordingStateChange event: ' + e);
1421
1476
  }
1422
1477
  });
1423
1478
 
1424
- data.info.on("failed", function (data) {
1425
- self.log.error("Recording stopped failed: " + data.response.reason_phrase);
1479
+ data.info.on('failed', function (data) {
1480
+ self.log.error('Recording stopped failed: ' + data.response.reason_phrase);
1426
1481
  try {
1427
- self.emit("recordingStateChange", { state: self._isRecording, error: data.response.reason_phrase });
1482
+ self.emit('recordingStateChange', { state: self._isRecording, error: data.response.reason_phrase });
1428
1483
  } catch (e) {
1429
- self.log.error("Exception in recordingStateChange event: " + e);
1484
+ self.log.error('Exception in recordingStateChange event: ' + e);
1430
1485
  }
1431
1486
  });
1432
1487
  });
1433
1488
 
1434
- this._webRTCSession.sendInfo("application/info", null, opt);
1489
+ this._webRTCSession.sendInfo('application/info', null, opt);
1435
1490
  }
1436
1491
 
1437
1492
  /**
@@ -1460,7 +1515,7 @@ export class Session extends EventEmitter {
1460
1515
  * Mute microphone
1461
1516
  */
1462
1517
  muteMic() {
1463
- this.log.info("Mute microphone");
1518
+ this.log.info('Mute microphone');
1464
1519
  this._webRTCSession.mute();
1465
1520
  }
1466
1521
 
@@ -1468,7 +1523,7 @@ export class Session extends EventEmitter {
1468
1523
  * Unmute microphone
1469
1524
  */
1470
1525
  unmuteMic() {
1471
- this.log.info("Unmute microphone");
1526
+ this.log.info('Unmute microphone');
1472
1527
  this._webRTCSession.unmute();
1473
1528
  }
1474
1529
 
@@ -1496,7 +1551,7 @@ export class Session extends EventEmitter {
1496
1551
  * Hold call
1497
1552
  */
1498
1553
  hold() {
1499
- this.log.info("Hold call");
1554
+ this.log.info('Hold call');
1500
1555
  this._webRTCSession.hold();
1501
1556
  }
1502
1557
 
@@ -1504,7 +1559,7 @@ export class Session extends EventEmitter {
1504
1559
  * Unhold call
1505
1560
  */
1506
1561
  unhold() {
1507
- this.log.info("Uncall call");
1562
+ this.log.info('Uncall call');
1508
1563
  this._phone.holdAllCalls();
1509
1564
  this._webRTCSession.unhold();
1510
1565
  }
@@ -1537,10 +1592,10 @@ export class Session extends EventEmitter {
1537
1592
  sendDTMF(code, playLocal = true) {
1538
1593
  // Check for correct DTMF and get frequencies
1539
1594
  if (!(code in DTMF_FREQUENCY_TABLE)) {
1540
- throw new Exceptions.InvalidParameter("Invalid DTMF code");
1595
+ throw new Exceptions.InvalidParameter('Invalid DTMF code');
1541
1596
  }
1542
1597
 
1543
- this.log.info("Sending DTMF: " + code);
1598
+ this.log.info('Sending DTMF: ' + code);
1544
1599
  this._webRTCSession.sendDTMF(code);
1545
1600
 
1546
1601
  if (playLocal) {
@@ -1548,15 +1603,15 @@ export class Session extends EventEmitter {
1548
1603
 
1549
1604
  // First mute microphone before playing DTMF
1550
1605
  if (this._isMuted) {
1551
- this.log.debug("Mic already muted, just sending DTMF");
1606
+ this.log.debug('Mic already muted, just sending DTMF');
1552
1607
  this._phone.playTone(500, f[0], f[1]);
1553
1608
  } else {
1554
- this.log.debug("Mute mic while sending DTMF");
1609
+ this.log.debug('Mute mic while sending DTMF');
1555
1610
  this._webRTCSession.mute();
1556
1611
 
1557
1612
  const self = this;
1558
1613
  this._phone.playTone(500, f[0], f[1], function () {
1559
- self.log.debug("Unmute mic after DTMF");
1614
+ self.log.debug('Unmute mic after DTMF');
1560
1615
  self._webRTCSession.unmute();
1561
1616
  });
1562
1617
  }
@@ -1572,20 +1627,22 @@ export class Session extends EventEmitter {
1572
1627
  this.log.info(`Transferring ${this.uuid} => ${dstSession.uuid}`);
1573
1628
 
1574
1629
  // Create ReferSubscriber event class
1575
- const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, { replaces: dstSession._webRTCSession });
1630
+ const referSubscriber = this._webRTCSession.refer(this._webRTCSession._request.ruri, {
1631
+ replaces: dstSession._webRTCSession,
1632
+ });
1576
1633
 
1577
1634
  const self = this;
1578
1635
 
1579
1636
  // Setup some logging
1580
- referSubscriber.on("requestSucceeded", function () {
1581
- self.log.info("Transfer completed successfully");
1637
+ referSubscriber.on('requestSucceeded', function () {
1638
+ self.log.info('Transfer completed successfully');
1582
1639
  });
1583
1640
 
1584
- referSubscriber.on("requestFailed", function (cause) {
1641
+ referSubscriber.on('requestFailed', function (cause) {
1585
1642
  self.log.error(`Transfer rejected: ${cause}`);
1586
1643
  });
1587
1644
 
1588
- referSubscriber.on("failed", function (cause) {
1645
+ referSubscriber.on('failed', function (cause) {
1589
1646
  self.log.error(`Transfer failed: ${cause}`);
1590
1647
  });
1591
1648
  }
@@ -1603,14 +1660,14 @@ export class Session extends EventEmitter {
1603
1660
  playback(media_file_id, hangup_after = false) {
1604
1661
  // Check event service is enabled first
1605
1662
  if (!this._phone._options.connectEventService) {
1606
- throw new Exceptions.NotAllowed("Event service not enabled");
1663
+ throw new Exceptions.NotAllowed('Event service not enabled');
1607
1664
  }
1608
1665
 
1609
1666
  this.log.info(`Playback media file ${media_file_id} hangup after ${hangup_after}`);
1610
1667
 
1611
- const p = this._phone._eventClient.fireEvent("call_command", {
1668
+ const p = this._phone._eventClient.fireEvent('call_command', {
1612
1669
  call_uuid: this.uuid,
1613
- call_command: "playback",
1670
+ call_command: 'playback',
1614
1671
  media_file_id: media_file_id,
1615
1672
  hangup_after: hangup_after,
1616
1673
  });
@@ -1626,7 +1683,7 @@ export class Session extends EventEmitter {
1626
1683
  // When playback starts set state to playback
1627
1684
  p.then(() => {
1628
1685
  // Set session state
1629
- this._setSessionState("PLAYBACK");
1686
+ this._setSessionState('PLAYBACK');
1630
1687
  });
1631
1688
  }
1632
1689
 
@@ -1642,12 +1699,12 @@ export class Session extends EventEmitter {
1642
1699
  * @throws InvalidOptions - If invalid mode given
1643
1700
  */
1644
1701
  setWhisperMode(mode = 0) {
1645
- if (!this.direction === "LISTEN") {
1646
- throw new Exceptions.NotAllowed("Call is not correct type (listen)");
1702
+ if (!this.direction === 'LISTEN') {
1703
+ throw new Exceptions.NotAllowed('Call is not correct type (listen)');
1647
1704
  }
1648
1705
 
1649
1706
  if (mode &lt; 0 || mode > 2) {
1650
- throw new Exceptions.InvalidOptions("Invalid mode");
1707
+ throw new Exceptions.InvalidOptions('Invalid mode');
1651
1708
  }
1652
1709
 
1653
1710
  let t_dtmf_mode;
@@ -1705,13 +1762,13 @@ export class Session extends EventEmitter {
1705
1762
  duration: Date.now() - self._callBeginTime,
1706
1763
  durationStr: Tools.createDurationString(Date.now() - self._callBeginTime),
1707
1764
  activeDuration: activeDuration,
1708
- activeDurationStr: activeDuration ? Tools.createDurationString(activeDuration) : "",
1765
+ activeDurationStr: activeDuration ? Tools.createDurationString(activeDuration) : '',
1709
1766
  };
1710
1767
 
1711
1768
  try {
1712
- self.emit("callDurationUpdate", e);
1769
+ self.emit('callDurationUpdate', e);
1713
1770
  } catch (e) {
1714
- self.log.error("Exception in call duration update event: " + e);
1771
+ self.log.error('Exception in call duration update event: ' + e);
1715
1772
  }
1716
1773
  }, 1000);
1717
1774
  }
@@ -1775,40 +1832,46 @@ export class Session extends EventEmitter {
1775
1832
  for (let report of data.values()) {
1776
1833
  // this.log.debug(report);
1777
1834
  switch (report.type) {
1778
- case "outbound-rtp":
1835
+ case 'outbound-rtp':
1779
1836
  // this.log.debug('Got: outbound-rtp');
1780
1837
  // this.log.debug(report);
1781
1838
  // Packets
1782
1839
  totalReport.outboundPacketsSent = report.packetsSent;
1783
- deltaReport.outboundPacketsSent = report.packetsSent - this._currentReport.outboundPacketsSent;
1840
+ deltaReport.outboundPacketsSent =
1841
+ report.packetsSent - this._currentReport.outboundPacketsSent;
1784
1842
  // Bytes
1785
1843
  totalReport.outboundBytesSent = report.bytesSent;
1786
- deltaReport.outboundBytesSent = report.bytesSent - this._currentReport.outboundBytesSent;
1844
+ deltaReport.outboundBytesSent =
1845
+ report.bytesSent - this._currentReport.outboundBytesSent;
1787
1846
  break;
1788
1847
 
1789
- case "inbound-rtp":
1848
+ case 'inbound-rtp':
1790
1849
  // this.log.debug('Got: inbound-rtp');
1791
1850
  // this.log.debug(report);
1792
1851
  // Packets
1793
1852
  totalReport.inboundPacketsReceived = report.packetsReceived;
1794
- deltaReport.inboundPacketsReceived = report.packetsReceived - this._currentReport.inboundPacketsReceived;
1853
+ deltaReport.inboundPacketsReceived =
1854
+ report.packetsReceived - this._currentReport.inboundPacketsReceived;
1795
1855
  // Bytes
1796
1856
  totalReport.inboundBytesReceived = report.bytesReceived;
1797
- deltaReport.inboundBytesReceived = report.bytesReceived - this._currentReport.inboundBytesReceived;
1857
+ deltaReport.inboundBytesReceived =
1858
+ report.bytesReceived - this._currentReport.inboundBytesReceived;
1798
1859
  // Loss
1799
1860
  totalReport.inboundPacketsLost = report.packetsLost;
1800
- deltaReport.inboundPacketsLost = report.packetsLost - this._currentReport.inboundPacketsLost;
1861
+ deltaReport.inboundPacketsLost =
1862
+ report.packetsLost - this._currentReport.inboundPacketsLost;
1801
1863
  // Jitter
1802
1864
  totalReport.inboundJitter = report.jitter * 1000;
1803
1865
  deltaReport.inboundJitter = report.jitter * 1000;
1804
1866
  break;
1805
1867
 
1806
- case "remote-inbound-rtp":
1868
+ case 'remote-inbound-rtp':
1807
1869
  // this.log.debug('Got: remote-inbound-rtp');
1808
1870
  // this.log.debug(report);
1809
1871
  // Loss
1810
1872
  totalReport.outboundPacketsLost = report.packetsLost;
1811
- deltaReport.outboundPacketsLost = report.packetsLost - this._currentReport.outboundPacketsLost;
1873
+ deltaReport.outboundPacketsLost =
1874
+ report.packetsLost - this._currentReport.outboundPacketsLost;
1812
1875
  // Jitter
1813
1876
  totalReport.outboundJitter = report.jitter * 1000;
1814
1877
  deltaReport.outboundJitter = report.jitter * 1000;
@@ -1834,9 +1897,9 @@ export class Session extends EventEmitter {
1834
1897
  };
1835
1898
 
1836
1899
  try {
1837
- this.emit("callQualityReportUpdate", finalReport);
1900
+ this.emit('callQualityReportUpdate', finalReport);
1838
1901
  } catch (e) {
1839
- this.log.error("Exception in call quality report update event: " + e);
1902
+ this.log.error('Exception in call quality report update event: ' + e);
1840
1903
  }
1841
1904
  }
1842
1905
  });
@@ -1860,27 +1923,28 @@ export class Session extends EventEmitter {
1860
1923
  // this.log.debug(`R factor loss effect ${lossEffect} / latency effect ${latencyEffect}`);
1861
1924
 
1862
1925
  report.rFactor = Math.max(0, 100 - lossEffect - latencyEffect);
1863
- report.mos = 1 + 0.035 * report.rFactor + 0.000007 * report.rFactor * (report.rFactor - 60) * (100 - report.rFactor);
1926
+ report.mos =
1927
+ 1 + 0.035 * report.rFactor + 0.000007 * report.rFactor * (report.rFactor - 60) * (100 - report.rFactor);
1864
1928
 
1865
1929
  // Quality string
1866
1930
  switch (true) {
1867
1931
  case report.rFactor &lt; 50:
1868
- report.qualityString = "Bad";
1932
+ report.qualityString = 'Bad';
1869
1933
  break;
1870
1934
  case report.rFactor &lt; 70:
1871
- report.qualityString = "Poor";
1935
+ report.qualityString = 'Poor';
1872
1936
  break;
1873
1937
  case report.rFactor &lt; 80:
1874
- report.qualityString = "Fair";
1938
+ report.qualityString = 'Fair';
1875
1939
  break;
1876
1940
  case report.rFactor &lt; 90:
1877
- report.qualityString = "Good";
1941
+ report.qualityString = 'Good';
1878
1942
  break;
1879
1943
  case report.rFactor &lt;= 100:
1880
- report.qualityString = "Excellent";
1944
+ report.qualityString = 'Excellent';
1881
1945
  break;
1882
1946
  default:
1883
- report.qualityString = "Error";
1947
+ report.qualityString = 'Error';
1884
1948
  }
1885
1949
 
1886
1950
  return report;
@@ -1914,7 +1978,7 @@ export class Session extends EventEmitter {
1914
1978
  *
1915
1979
  */
1916
1980
 
1917
- this.log.debug("Session state: " + state);
1981
+ this.log.debug('Session state: ' + state);
1918
1982
  if (info) {
1919
1983
  this.log.debug(info);
1920
1984
  }
@@ -1922,7 +1986,7 @@ export class Session extends EventEmitter {
1922
1986
  this._sessionState = state;
1923
1987
  try {
1924
1988
  this.emit(
1925
- "sessionStateChange",
1989
+ 'sessionStateChange',
1926
1990
  Object.assign(
1927
1991
  {
1928
1992
  state: state,
@@ -1932,51 +1996,51 @@ export class Session extends EventEmitter {
1932
1996
  ),
1933
1997
  );
1934
1998
  } catch (e) {
1935
- this.log.error("Exception in sessionStateChange event:" + e);
1999
+ this.log.error('Exception in sessionStateChange event:' + e);
1936
2000
  }
1937
2001
 
1938
2002
  // Trigger callDataAvailable dummy event
1939
- if (state === "TERMINATED") {
1940
- this._callDisposition = this._manualDisposition || "NORMAL";
2003
+ if (state === 'TERMINATED') {
2004
+ this._callDisposition = this._manualDisposition || 'NORMAL';
1941
2005
  this._callFinished();
1942
2006
  }
1943
2007
 
1944
- if (state === "FAILED") {
1945
- if (info.cause === "Unavailable") {
1946
- this._callDisposition = "TEMP_UNAVAIL";
2008
+ if (state === 'FAILED') {
2009
+ if (info.cause === 'Unavailable') {
2010
+ this._callDisposition = 'TEMP_UNAVAIL';
1947
2011
  if (this._phone.options.playSignalTones) {
1948
2012
  this._phone._startRepeatTone(ST_TEMP_UNAVAIL);
1949
2013
  }
1950
2014
  }
1951
2015
 
1952
- if (info.cause === "Not Found") {
1953
- this._callDisposition = "INVALID_NUMBER";
2016
+ if (info.cause === 'Not Found') {
2017
+ this._callDisposition = 'INVALID_NUMBER';
1954
2018
  if (this._phone.options.playSignalTones) {
1955
2019
  this._phone._startRepeatTone(ST_INVALID_NUMBER);
1956
2020
  }
1957
2021
  }
1958
2022
 
1959
- if (info.cause === "Canceled") {
1960
- this._callDisposition = this._manualDisposition || "NO_ANSWER";
2023
+ if (info.cause === 'Canceled') {
2024
+ this._callDisposition = this._manualDisposition || 'NO_ANSWER';
1961
2025
  }
1962
2026
 
1963
- if (info.cause === "Rejected") {
1964
- if (this.direction === "INBOUND") {
1965
- this._callDisposition = "REJECTED";
2027
+ if (info.cause === 'Rejected') {
2028
+ if (this.direction === 'INBOUND') {
2029
+ this._callDisposition = 'REJECTED';
1966
2030
  } else {
1967
- this._callDisposition = "BARRED";
2031
+ this._callDisposition = 'BARRED';
1968
2032
  }
1969
2033
  }
1970
2034
 
1971
- if (info.cause === "Busy") {
1972
- this._callDisposition = "BUSY";
2035
+ if (info.cause === 'Busy') {
2036
+ this._callDisposition = 'BUSY';
1973
2037
  if (this._phone.options.playSignalTones) {
1974
2038
  this._phone._startRepeatTone(ST_BUSY);
1975
2039
  }
1976
2040
  }
1977
2041
 
1978
2042
  // If session is incoming and failed, we don't fire calldataAvailable event
1979
- if (this._webRTCSession.direction === "incoming") {
2043
+ if (this._webRTCSession.direction === 'incoming') {
1980
2044
  // return;
1981
2045
  }
1982
2046
 
@@ -2006,7 +2070,7 @@ export class Session extends EventEmitter {
2006
2070
  * @property {string} REJECTED Incoming call was rejected
2007
2071
  */
2008
2072
  _callFinished() {
2009
- this.log.info("Call finished");
2073
+ this.log.info('Call finished');
2010
2074
 
2011
2075
  // Fire callEnded event
2012
2076
  this._fireCallEndedEvent();
@@ -2023,7 +2087,7 @@ export class Session extends EventEmitter {
2023
2087
 
2024
2088
  // Set answer call timeout
2025
2089
  _setAnswerCallTimeout(timeout) {
2026
- this.log.debug("Setting answer call timeout to: " + timeout);
2090
+ this.log.debug('Setting answer call timeout to: ' + timeout);
2027
2091
  this._answerCallTimeout = timeout;
2028
2092
  }
2029
2093
 
@@ -2035,9 +2099,9 @@ export class Session extends EventEmitter {
2035
2099
  * @property {boolean} state - New recording state
2036
2100
  */
2037
2101
  try {
2038
- this.emit("recordingStateChange", { state: this._isRecording, error: this._recordingError });
2102
+ this.emit('recordingStateChange', { state: this._isRecording, error: this._recordingError });
2039
2103
  } catch (e) {
2040
- this.log.error("Exception in recording state change event: " + e);
2104
+ this.log.error('Exception in recording state change event: ' + e);
2041
2105
  }
2042
2106
  }
2043
2107
 
@@ -2049,9 +2113,9 @@ export class Session extends EventEmitter {
2049
2113
  * @property {boolean} state - Mute state (true = muted / false = unmuted)
2050
2114
  */
2051
2115
  try {
2052
- this.emit("muteStateChange", { state: this._isMuted });
2116
+ this.emit('muteStateChange', { state: this._isMuted });
2053
2117
  } catch (e) {
2054
- this.log.error("Exception in mute state change event: " + e);
2118
+ this.log.error('Exception in mute state change event: ' + e);
2055
2119
  }
2056
2120
  }
2057
2121
 
@@ -2063,9 +2127,9 @@ export class Session extends EventEmitter {
2063
2127
  * @property {boolean} state - New hold state
2064
2128
  */
2065
2129
  try {
2066
- this.emit("holdStateChange", { state: this._isOnHold });
2130
+ this.emit('holdStateChange', { state: this._isOnHold });
2067
2131
  } catch (e) {
2068
- this.log.error("Exception in hold state change event: " + e);
2132
+ this.log.error('Exception in hold state change event: ' + e);
2069
2133
  }
2070
2134
  }
2071
2135
 
@@ -2077,9 +2141,9 @@ export class Session extends EventEmitter {
2077
2141
  * @property {boolean} mode - New whisper mode (0 = No whisper / 1 = Local only / 2 = Three way)
2078
2142
  */
2079
2143
  try {
2080
- this.emit("whisperModeChange", { mode: this._whisperMode });
2144
+ this.emit('whisperModeChange', { mode: this._whisperMode });
2081
2145
  } catch (e) {
2082
- this.log.error("Exception in whisper mode change event: " + e);
2146
+ this.log.error('Exception in whisper mode change event: ' + e);
2083
2147
  }
2084
2148
  }
2085
2149
 
@@ -2128,14 +2192,14 @@ export class Session extends EventEmitter {
2128
2192
 
2129
2193
  // Add some extra convenience fields
2130
2194
  e.durationStr = Tools.createDurationString(e.duration);
2131
- e.answerDurationStr = e.answerDuration ? Tools.createDurationString(e.answerDuration) : "";
2132
- e.activeDurationStr = e.activeDuration ? Tools.createDurationString(e.activeDuration) : "";
2195
+ e.answerDurationStr = e.answerDuration ? Tools.createDurationString(e.answerDuration) : '';
2196
+ e.activeDurationStr = e.activeDuration ? Tools.createDurationString(e.activeDuration) : '';
2133
2197
  e.callQualityReport = this._currentReport;
2134
2198
 
2135
2199
  try {
2136
- this.emit("callEnded", e);
2200
+ this.emit('callEnded', e);
2137
2201
  } catch (e) {
2138
- this.log.error("Exception in callEnded event: " + e);
2202
+ this.log.error('Exception in callEnded event: ' + e);
2139
2203
  }
2140
2204
  }
2141
2205
 
@@ -2146,10 +2210,10 @@ export class Session extends EventEmitter {
2146
2210
 
2147
2211
  const self = this;
2148
2212
  session.connection.ontrack = function (event) {
2149
- self.log.debug("Start media output (track):");
2213
+ self.log.debug('Start media output (track):');
2150
2214
  self.log.debug(event);
2151
2215
  self._phone._remoteAudioElement.srcObject = event.streams[0];
2152
2216
  };
2153
2217
  }
2154
2218
  }
2155
- </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><div class="mobile-nav-links"></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="EventClient.html">EventClient</a></div><div class="sidebar-section-children"><a href="InvalidMediaDevice.html">InvalidMediaDevice</a></div><div class="sidebar-section-children"><a href="InvalidOptions.html">InvalidOptions</a></div><div class="sidebar-section-children"><a href="InvalidParameter.html">InvalidParameter</a></div><div class="sidebar-section-children"><a href="LogHandler.html">LogHandler</a></div><div class="sidebar-section-children"><a href="MediaNotPrepared.html">MediaNotPrepared</a></div><div class="sidebar-section-children"><a href="NoActiveSession.html">NoActiveSession</a></div><div class="sidebar-section-children"><a href="NotAllowed.html">NotAllowed</a></div><div class="sidebar-section-children"><a href="NotReady.html">NotReady</a></div><div class="sidebar-section-children"><a href="Phone.html">Phone</a></div><div class="sidebar-section-children"><a href="Request.html">Request</a></div><div class="sidebar-section-children"><a href="Requestor.html">Requestor</a></div><div class="sidebar-section-children"><a href="Session.html">Session</a></div><div class="sidebar-section-children"><a href="UnableToConnect.html">UnableToConnect</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:callDataInfo">callDataInfo</a></div><div class="sidebar-section-children"><a href="global.html#event:callDurationUpdate">callDurationUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:callEnded">callEnded</a></div><div class="sidebar-section-children"><a href="global.html#event:callQualityReportUpdate">callQualityReportUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:connectionStateChange">connectionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:holdStateChange">holdStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:incomingCall">incomingCall</a></div><div class="sidebar-section-children"><a href="global.html#event:muteStateChange">muteStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:recordingStateChange">recordingStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionCreated">sessionCreated</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionRemoved">sessionRemoved</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionStateChange">sessionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:whisperModeChange">whisperModeChange</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#CALL_DIRECTION">CALL_DIRECTION</a></div><div class="sidebar-section-children"><a href="global.html#CALL_DISPOSITION">CALL_DISPOSITION</a></div><div class="sidebar-section-children"><a href="global.html#CONNECTION_STATE">CONNECTION_STATE</a></div><div class="sidebar-section-children"><a href="global.html#LOGLVL">LOGLVL</a></div><div class="sidebar-section-children"><a href="global.html#SESSION_STATE">SESSION_STATE</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>
2219
+ </code></pre></article></section></div></div></div><div class="search-container" id="PkfLWpAbet" style="display:none"><div class="wrapper" id="iCxFxjkHbP"><button class="icon-button search-close-button" id="VjLlGakifb" aria-label="close search"><svg><use xlink:href="#close-icon"></use></svg></button><div class="search-box-c"><svg><use xlink:href="#search-icon"></use></svg> <input type="text" id="vpcKVYIppa" class="search-input" placeholder="Search..." autofocus></div><div class="search-result-c" id="fWwVHRuDuN"><span class="search-result-c-text">Type anything to view search result</span></div></div></div><div class="mobile-menu-icon-container"><button class="icon-button" id="mobile-menu" data-isopen="false" aria-label="menu"><svg><use xlink:href="#menu-icon"></use></svg></button></div><div id="mobile-sidebar" class="mobile-sidebar-container"><div class="mobile-sidebar-wrapper"><div class="mobile-nav-links"></div><div class="mobile-sidebar-items-c"><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-classes"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="EventClient.html">EventClient</a></div><div class="sidebar-section-children"><a href="EventServiceNotConnected.html">EventServiceNotConnected</a></div><div class="sidebar-section-children"><a href="InvalidMediaDevice.html">InvalidMediaDevice</a></div><div class="sidebar-section-children"><a href="InvalidOptions.html">InvalidOptions</a></div><div class="sidebar-section-children"><a href="InvalidParameter.html">InvalidParameter</a></div><div class="sidebar-section-children"><a href="LogHandler.html">LogHandler</a></div><div class="sidebar-section-children"><a href="MediaNotPrepared.html">MediaNotPrepared</a></div><div class="sidebar-section-children"><a href="NoActiveSession.html">NoActiveSession</a></div><div class="sidebar-section-children"><a href="NotAllowed.html">NotAllowed</a></div><div class="sidebar-section-children"><a href="NotReady.html">NotReady</a></div><div class="sidebar-section-children"><a href="Phone.html">Phone</a></div><div class="sidebar-section-children"><a href="Request.html">Request</a></div><div class="sidebar-section-children"><a href="Requestor.html">Requestor</a></div><div class="sidebar-section-children"><a href="Session.html">Session</a></div><div class="sidebar-section-children"><a href="UnableToConnect.html">UnableToConnect</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-events"><div>Events</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#event:callDataInfo">callDataInfo</a></div><div class="sidebar-section-children"><a href="global.html#event:callDurationUpdate">callDurationUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:callEnded">callEnded</a></div><div class="sidebar-section-children"><a href="global.html#event:callQualityReportUpdate">callQualityReportUpdate</a></div><div class="sidebar-section-children"><a href="global.html#event:connectionStateChange">connectionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:holdStateChange">holdStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:incomingCall">incomingCall</a></div><div class="sidebar-section-children"><a href="global.html#event:muteStateChange">muteStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:recordingStateChange">recordingStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionCreated">sessionCreated</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionRemoved">sessionRemoved</a></div><div class="sidebar-section-children"><a href="global.html#event:sessionStateChange">sessionStateChange</a></div><div class="sidebar-section-children"><a href="global.html#event:whisperModeChange">whisperModeChange</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="sidebar-global"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#CALL_DIRECTION">CALL_DIRECTION</a></div><div class="sidebar-section-children"><a href="global.html#CALL_DISPOSITION">CALL_DISPOSITION</a></div><div class="sidebar-section-children"><a href="global.html#CONNECTION_STATE">CONNECTION_STATE</a></div><div class="sidebar-section-children"><a href="global.html#LOGLVL">LOGLVL</a></div><div class="sidebar-section-children"><a href="global.html#SESSION_STATE">SESSION_STATE</a></div></div></div><div class="mobile-navbar-actions"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#light-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div></div></div><script type="text/javascript" src="scripts/core.min.js"></script><script src="scripts/search.min.js" defer="defer"></script><script src="scripts/third-party/fuse.js" defer="defer"></script><script type="text/javascript">var tocbotInstance=tocbot.init({tocSelector:"#eed4d2a0bfd64539bb9df78095dec881",contentSelector:".main-content",headingSelector:"h1, h2, h3",hasInnerContainers:!0,scrollContainer:".main-content",headingsOffset:130,onClick:bringLinkToView})</script></body></html>