call.htm 58 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327
  1. <!DOCTYPE html>
  2. <!--
  3. * Copyright (C) 2012 Doubango Telecom <http://www.doubango.org>
  4. * License: BSD
  5. * This file is part of Open Source sipML5 solution <http://www.sipml5.org>
  6. -->
  7. <html>
  8. <!-- head -->
  9. <head>
  10. <meta charset="utf-8" />
  11. <title>sipML5 live demo</title>
  12. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  13. <meta name="Keywords" content="doubango, sipML5, VoIP, HTML5, WebRTC, RTCWeb, SIP, IMS, Video chat, VP8, live demo " />
  14. <meta name="Description" content="HTML5 SIP client using WebRTC framework" />
  15. <meta name="author" content="Doubango Telecom" />
  16. <!-- SIPML5 API:
  17. DEBUG VERSION: 'SIPml-api.js'
  18. RELEASE VERSION: 'release/SIPml-api.js'
  19. -->
  20. <script src="SIPml-api.js?svn=224" type="text/javascript"> </script>
  21. <!-- Styles -->
  22. <link href="./assets/css/bootstrap.css" rel="stylesheet" />
  23. <style type="text/css">
  24. body{
  25. padding-top: 80px;
  26. padding-bottom: 40px;
  27. }
  28. .navbar-inner-red {
  29. background-color: #600000;
  30. background-image: none;
  31. background-repeat: no-repeat;
  32. filter: none;
  33. }
  34. .full-screen{
  35. position: absolute;
  36. left: 0;
  37. top: 0;
  38. width: 100%;
  39. height: 100%;
  40. overflow: hidden;
  41. }
  42. .normal-screen{
  43. position: relative;
  44. }
  45. .call-options {
  46. padding: 5px;
  47. background-color: #f0f0f0;
  48. border: 1px solid #eee;
  49. border: 1px solid rgba(0, 0, 0, 0.08);
  50. -webkit-border-radius: 4px;
  51. -moz-border-radius: 4px;
  52. border-radius: 4px;
  53. -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
  54. -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
  55. box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
  56. -webkit-transition-property: opacity;
  57. -moz-transition-property: opacity;
  58. -o-transition-property: opacity;
  59. -webkit-transition-duration: 2s;
  60. -moz-transition-duration: 2s;
  61. -o-transition-duration: 2s;
  62. }
  63. .tab-video,
  64. .div-video{
  65. width: 100%;
  66. height: 0px;
  67. -webkit-transition-property: height;
  68. -moz-transition-property: height;
  69. -o-transition-property: height;
  70. -webkit-transition-duration: 2s;
  71. -moz-transition-duration: 2s;
  72. -o-transition-duration: 2s;
  73. }
  74. .label-align {
  75. display: block;
  76. padding-left: 15px;
  77. text-indent: -15px;
  78. }
  79. .input-align {
  80. width: 13px;
  81. height: 13px;
  82. padding: 0;
  83. margin:0;
  84. vertical-align: bottom;
  85. position: relative;
  86. top: -1px;
  87. *overflow: hidden;
  88. }
  89. .glass-panel{
  90. z-index: 99;
  91. position: fixed;
  92. width: 100%;
  93. height: 100%;
  94. margin: 0;
  95. padding: 0;
  96. top: 0;
  97. left: 0;
  98. opacity: 0.8;
  99. background-color: Gray;
  100. }
  101. .div-keypad {
  102. z-index: 100;
  103. position: fixed;
  104. -moz-transition-property: left top;
  105. -o-transition-property: left top;
  106. -webkit-transition-duration: 2s;
  107. -moz-transition-duration: 2s;
  108. -o-transition-duration: 2s;
  109. }
  110. </style>
  111. <link href="./assets/css/bootstrap-responsive.css" rel="stylesheet" />
  112. <!-- Le fav and touch icons -->
  113. <link rel="shortcut icon" href="./assets/ico/favicon.ico" />
  114. <link rel="apple-touch-icon-precomposed" sizes="114x114" href="./assets/ico/apple-touch-icon-114-precomposed.png" />
  115. <link rel="apple-touch-icon-precomposed" sizes="72x72" href="./assets/ico/apple-touch-icon-72-precomposed.png" />
  116. <link rel="apple-touch-icon-precomposed" href="./assets/ico/apple-touch-icon-57-precomposed.png" />
  117. </head>
  118. <!-- Javascript code -->
  119. <script type="text/javascript">
  120. // to avoid caching
  121. //if (window.location.href.indexOf("svn=") == -1) {
  122. // window.location.href += (window.location.href.indexOf("?") == -1 ? "?svn=224" : "&svn=224");
  123. //}
  124. var sTransferNumber;
  125. var oRingTone, oRingbackTone;
  126. var oSipStack, oSipSessionRegister, oSipSessionCall, oSipSessionTransferCall;
  127. var videoRemote, videoLocal, audioRemote;
  128. var bFullScreen = false;
  129. var oNotifICall;
  130. var bDisableVideo = false;
  131. var viewVideoLocal, viewVideoRemote; // <video> (webrtc) or <div> (webrtc4all)
  132. var oConfigCall;
  133. var oReadyStateTimer;
  134. C =
  135. {
  136. divKeyPadWidth: 220
  137. };
  138. window.onload = function () {
  139. if(window.console) {
  140. window.console.info("location=" + window.location);
  141. }
  142. videoLocal = document.getElementById("video_local");
  143. videoRemote = document.getElementById("video_remote");
  144. audioRemote = document.getElementById("audio_remote");
  145. document.onkeyup = onKeyUp;
  146. document.body.onkeyup = onKeyUp;
  147. divCallCtrl.onmousemove = onDivCallCtrlMouseMove;
  148. // set debug level
  149. SIPml.setDebugLevel((window.localStorage && window.localStorage.getItem('org.doubango.expert.disable_debug') == "true") ? "error" : "info");
  150. loadCredentials();
  151. loadCallOptions();
  152. // Initialize call button
  153. uiBtnCallSetText("Call");
  154. var getPVal = function (PName) {
  155. var query = window.location.search.substring(1);
  156. var vars = query.split('&');
  157. for (var i = 0; i < vars.length; i++) {
  158. var pair = vars[i].split('=');
  159. if (decodeURIComponent(pair[0]) === PName) {
  160. return decodeURIComponent(pair[1]);
  161. }
  162. }
  163. return null;
  164. }
  165. var preInit = function() {
  166. // set default webrtc type (before initialization)
  167. var s_webrtc_type = getPVal("wt");
  168. if (s_webrtc_type) {
  169. if(window.console) {
  170. window.console.info("s_webrtc_type=" + s_webrtc_type);
  171. }
  172. SIPml.setWebRtcType(s_webrtc_type);
  173. }
  174. // initialize SIPML5
  175. SIPml.init(postInit);
  176. }
  177. oReadyStateTimer = setInterval(function () {
  178. if (document.readyState === "complete") {
  179. clearInterval(oReadyStateTimer);
  180. // initialize SIPML5
  181. preInit();
  182. }
  183. },
  184. 500);
  185. /*if (document.readyState === "complete") {
  186. preInit();
  187. }
  188. else {
  189. document.onreadystatechange = function () {
  190. if (document.readyState === "complete") {
  191. preInit();
  192. }
  193. }
  194. }*/
  195. };
  196. function postInit() {
  197. // check webrtc4all version
  198. if (SIPml.isWebRtc4AllSupported() && SIPml.isWebRtc4AllPluginOutdated()) {
  199. if (confirm("Your WebRtc4all extension is outdated ("+SIPml.getWebRtc4AllVersion()+"). A new version with critical bug fix is available. Do you want to install it?\nIMPORTANT: You must restart your browser after the installation.")) {
  200. window.location = 'http://code.google.com/p/webrtc4all/downloads/list';
  201. return;
  202. }
  203. }
  204. // check for WebRTC support
  205. if (!SIPml.isWebRtcSupported()) {
  206. // is it chrome?
  207. if (SIPml.getNavigatorFriendlyName() == 'chrome') {
  208. if (confirm("You're using an old Chrome version or WebRTC is not enabled.\nDo you want to see how to enable WebRTC?")) {
  209. window.location = 'http://www.webrtc.org/running-the-demos';
  210. }
  211. else {
  212. window.location = "index.html";
  213. }
  214. return;
  215. }
  216. // for now the plugins (WebRTC4all only works on Windows)
  217. if (SIPml.getSystemFriendlyName() == 'windows') {
  218. // Internet explorer
  219. if (SIPml.getNavigatorFriendlyName() == 'ie') {
  220. // Check for IE version
  221. if (parseFloat(SIPml.getNavigatorVersion()) < 9.0) {
  222. if (confirm("You are using an old IE version. You need at least version 9. Would you like to update IE?")) {
  223. window.location = 'http://windows.microsoft.com/en-us/internet-explorer/products/ie/home';
  224. }
  225. else {
  226. window.location = "index.html";
  227. }
  228. }
  229. // check for WebRTC4all extension
  230. if (!SIPml.isWebRtc4AllSupported()) {
  231. if (confirm("webrtc4all extension is not installed. Do you want to install it?\nIMPORTANT: You must restart your browser after the installation.")) {
  232. window.location = 'http://code.google.com/p/webrtc4all/downloads/list';
  233. }
  234. else {
  235. // Must do nothing: give the user the chance to accept the extension
  236. // window.location = "index.html";
  237. }
  238. }
  239. // break page loading ('window.location' won't stop JS execution)
  240. if (!SIPml.isWebRtc4AllSupported()) {
  241. return;
  242. }
  243. }
  244. else if (SIPml.getNavigatorFriendlyName() == "safari" || SIPml.getNavigatorFriendlyName() == "firefox" || SIPml.getNavigatorFriendlyName() == "opera") {
  245. if (confirm("Your browser don't support WebRTC.\nDo you want to install WebRTC4all extension to enjoy audio/video calls?\nIMPORTANT: You must restart your browser after the installation.")) {
  246. window.location = 'http://code.google.com/p/webrtc4all/downloads/list';
  247. }
  248. else {
  249. window.location = "index.html";
  250. }
  251. return;
  252. }
  253. }
  254. // OSX, Unix, Android, iOS...
  255. else {
  256. if (confirm('WebRTC not supported on your browser.\nDo you want to download a WebRTC-capable browser?')) {
  257. window.location = 'https://www.google.com/intl/en/chrome/browser/';
  258. }
  259. else {
  260. window.location = "index.html";
  261. }
  262. return;
  263. }
  264. }
  265. // checks for WebSocket support
  266. if (!SIPml.isWebSocketSupported() && !SIPml.isWebRtc4AllSupported()) {
  267. if (confirm('Your browser don\'t support WebSockets.\nDo you want to download a WebSocket-capable browser?')) {
  268. window.location = 'https://www.google.com/intl/en/chrome/browser/';
  269. }
  270. else {
  271. window.location = "index.html";
  272. }
  273. return;
  274. }
  275. // FIXME: displays must be per session
  276. // attachs video displays
  277. if (SIPml.isWebRtc4AllSupported()) {
  278. viewVideoLocal = document.getElementById("divVideoLocal");
  279. viewVideoRemote = document.getElementById("divVideoRemote");
  280. WebRtc4all_SetDisplays(viewVideoLocal, viewVideoRemote); // FIXME: move to SIPml.* API
  281. }
  282. else{
  283. viewVideoLocal = videoLocal;
  284. viewVideoRemote = videoRemote;
  285. }
  286. if (!SIPml.isWebRtc4AllSupported() && !SIPml.isWebRtcSupported()) {
  287. if (confirm('Your browser don\'t support WebRTC.\naudio/video calls will be disabled.\nDo you want to download a WebRTC-capable browser?')) {
  288. window.location = 'https://www.google.com/intl/en/chrome/browser/';
  289. }
  290. }
  291. btnRegister.disabled = false;
  292. document.body.style.cursor = 'default';
  293. oConfigCall = {
  294. audio_remote: audioRemote,
  295. video_local: viewVideoLocal,
  296. video_remote: viewVideoRemote,
  297. bandwidth: { audio:undefined, video:undefined },
  298. video_size: { minWidth:undefined, minHeight:undefined, maxWidth:undefined, maxHeight:undefined },
  299. events_listener: { events: '*', listener: onSipEventSession },
  300. sip_caps: [
  301. { name: '+g.oma.sip-im' },
  302. { name: '+sip.ice' },
  303. { name: 'language', value: '\"en,fr\"' }
  304. ]
  305. };
  306. }
  307. function loadCallOptions() {
  308. if (window.localStorage) {
  309. var s_value;
  310. if ((s_value = window.localStorage.getItem('org.doubango.call.phone_number'))) txtPhoneNumber.value = s_value;
  311. bDisableVideo = (window.localStorage.getItem('org.doubango.expert.disable_video') == "true");
  312. txtCallStatus.innerHTML = '<i>Video ' + (bDisableVideo ? 'disabled' : 'enabled') + '</i>';
  313. }
  314. }
  315. function saveCallOptions() {
  316. if (window.localStorage) {
  317. window.localStorage.setItem('org.doubango.call.phone_number', txtPhoneNumber.value);
  318. window.localStorage.setItem('org.doubango.expert.disable_video', bDisableVideo ? "true" : "false");
  319. }
  320. }
  321. function loadCredentials() {
  322. if (window.localStorage) {
  323. // IE retuns 'null' if not defined
  324. var s_value;
  325. if ((s_value = window.localStorage.getItem('org.doubango.identity.display_name'))) txtDisplayName.value = s_value;
  326. if ((s_value = window.localStorage.getItem('org.doubango.identity.impi'))) txtPrivateIdentity.value = s_value;
  327. if ((s_value = window.localStorage.getItem('org.doubango.identity.impu'))) txtPublicIdentity.value = s_value;
  328. if ((s_value = window.localStorage.getItem('org.doubango.identity.password'))) txtPassword.value = s_value;
  329. if ((s_value = window.localStorage.getItem('org.doubango.identity.realm'))) txtRealm.value = s_value;
  330. }
  331. else {
  332. /*txtDisplayName.value = "005";
  333. txtPrivateIdentity.value = "005";
  334. txtPublicIdentity.value = "sip:[email protected]";
  335. txtPassword.value = "005";
  336. txtRealm.value = "192.168.0.28";
  337. txtPhoneNumber.value = "005";*/
  338. }
  339. };
  340. function saveCredentials() {
  341. if (window.localStorage) {
  342. window.localStorage.setItem('org.doubango.identity.display_name', txtDisplayName.value);
  343. window.localStorage.setItem('org.doubango.identity.impi', txtPrivateIdentity.value);
  344. window.localStorage.setItem('org.doubango.identity.impu', txtPublicIdentity.value);
  345. window.localStorage.setItem('org.doubango.identity.password', txtPassword.value);
  346. window.localStorage.setItem('org.doubango.identity.realm', txtRealm.value);
  347. }
  348. };
  349. // sends SIP REGISTER request to login
  350. function sipRegister() {
  351. // catch exception for IE (DOM not ready)
  352. try {
  353. btnRegister.disabled = true;
  354. if (!txtRealm.value || !txtPrivateIdentity.value || !txtPublicIdentity.value) {
  355. txtRegStatus.innerHTML = '<b>Please fill madatory fields (*)</b>';
  356. btnRegister.disabled = false;
  357. return;
  358. }
  359. var o_impu = tsip_uri.prototype.Parse(txtPublicIdentity.value);
  360. if (!o_impu || !o_impu.s_user_name || !o_impu.s_host) {
  361. txtRegStatus.innerHTML = "<b>[" + txtPublicIdentity.value + "] is not a valid Public identity</b>";
  362. btnRegister.disabled = false;
  363. return;
  364. }
  365. // enable notifications if not already done
  366. if (window.webkitNotifications && window.webkitNotifications.checkPermission() != 0) {
  367. window.webkitNotifications.requestPermission();
  368. }
  369. // save credentials
  370. saveCredentials();
  371. // update debug level to be sure new values will be used if the user haven't updated the page
  372. SIPml.setDebugLevel((window.localStorage && window.localStorage.getItem('org.doubango.expert.disable_debug') == "true") ? "error" : "info");
  373. // create SIP stack
  374. oSipStack = new SIPml.Stack({
  375. realm: txtRealm.value,
  376. impi: txtPrivateIdentity.value,
  377. impu: txtPublicIdentity.value,
  378. password: txtPassword.value,
  379. display_name: txtDisplayName.value,
  380. websocket_proxy_url: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.websocket_server_url') : null),
  381. outbound_proxy_url: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.sip_outboundproxy_url') : null),
  382. ice_servers: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.ice_servers') : null),
  383. enable_rtcweb_breaker: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.enable_rtcweb_breaker') == "true" : false),
  384. events_listener: { events: '*', listener: onSipEventStack },
  385. enable_early_ims: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.disable_early_ims') != "true" : true), // Must be true unless you're using a real IMS network
  386. enable_media_stream_cache: (window.localStorage ? window.localStorage.getItem('org.doubango.expert.enable_media_caching') == "true" : false),
  387. bandwidth: (window.localStorage ? tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.bandwidth')) : null), // could be redefined a session-level
  388. video_size: (window.localStorage ? tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.video_size')) : null), // could be redefined a session-level
  389. sip_headers: [
  390. { name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.2014.04.18' },
  391. { name: 'Organization', value: 'Doubango Telecom' }
  392. ]
  393. }
  394. );
  395. if (oSipStack.start() != 0) {
  396. txtRegStatus.innerHTML = '<b>Failed to start the SIP stack</b>';
  397. }
  398. else return;
  399. }
  400. catch (e) {
  401. txtRegStatus.innerHTML = "<b>2:" + e + "</b>";
  402. }
  403. btnRegister.disabled = false;
  404. }
  405. // sends SIP REGISTER (expires=0) to logout
  406. function sipUnRegister() {
  407. if (oSipStack) {
  408. oSipStack.stop(); // shutdown all sessions
  409. }
  410. }
  411. // makes a call (SIP INVITE)
  412. function sipCall(s_type) {
  413. if (oSipStack && !oSipSessionCall && !tsk_string_is_null_or_empty(txtPhoneNumber.value)) {
  414. if(s_type == 'call-screenshare') {
  415. if(!SIPml.isScreenShareSupported()) {
  416. alert('Screen sharing not supported. Are you using chrome 26+?');
  417. return;
  418. }
  419. if (!location.protocol.match('https')){
  420. if (confirm("Screen sharing requires https://. Do you want to be redirected?")) {
  421. sipUnRegister();
  422. window.location = 'https://ns313841.ovh.net/call.htm';
  423. }
  424. return;
  425. }
  426. }
  427. btnCall.disabled = true;
  428. btnHangUp.disabled = false;
  429. if(window.localStorage) {
  430. oConfigCall.bandwidth = tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.bandwidth')); // already defined at stack-level but redifined to use latest values
  431. oConfigCall.video_size = tsk_string_to_object(window.localStorage.getItem('org.doubango.expert.video_size')); // already defined at stack-level but redifined to use latest values
  432. }
  433. // create call session
  434. oSipSessionCall = oSipStack.newSession(s_type, oConfigCall);
  435. // make call
  436. if (oSipSessionCall.call(txtPhoneNumber.value) != 0) {
  437. oSipSessionCall = null;
  438. txtCallStatus.value = 'Failed to make call';
  439. btnCall.disabled = false;
  440. btnHangUp.disabled = true;
  441. return;
  442. }
  443. saveCallOptions();
  444. }
  445. else if (oSipSessionCall) {
  446. txtCallStatus.innerHTML = '<i>Connecting...</i>';
  447. oSipSessionCall.accept(oConfigCall);
  448. }
  449. }
  450. // transfers the call
  451. function sipTransfer() {
  452. if (oSipSessionCall) {
  453. var s_destination = prompt('Enter destination number', '');
  454. if (!tsk_string_is_null_or_empty(s_destination)) {
  455. btnTransfer.disabled = true;
  456. if (oSipSessionCall.transfer(s_destination) != 0) {
  457. txtCallStatus.innerHTML = '<i>Call transfer failed</i>';
  458. btnTransfer.disabled = false;
  459. return;
  460. }
  461. txtCallStatus.innerHTML = '<i>Transfering the call...</i>';
  462. }
  463. }
  464. }
  465. // holds or resumes the call
  466. function sipToggleHoldResume() {
  467. if (oSipSessionCall) {
  468. var i_ret;
  469. btnHoldResume.disabled = true;
  470. txtCallStatus.innerHTML = oSipSessionCall.bHeld ? '<i>Resuming the call...</i>' : '<i>Holding the call...</i>';
  471. i_ret = oSipSessionCall.bHeld ? oSipSessionCall.resume() : oSipSessionCall.hold();
  472. if (i_ret != 0) {
  473. txtCallStatus.innerHTML = '<i>Hold / Resume failed</i>';
  474. btnHoldResume.disabled = false;
  475. return;
  476. }
  477. }
  478. }
  479. // terminates the call (SIP BYE or CANCEL)
  480. function sipHangUp() {
  481. if (oSipSessionCall) {
  482. txtCallStatus.innerHTML = '<i>Terminating the call...</i>';
  483. oSipSessionCall.hangup({events_listener: { events: '*', listener: onSipEventSession }});
  484. }
  485. }
  486. function sipSendDTMF(c){
  487. if(oSipSessionCall && c){
  488. if(oSipSessionCall.dtmf(c) == 0){
  489. try { dtmfTone.play(); } catch(e){ }
  490. }
  491. }
  492. }
  493. function startRingTone() {
  494. try { ringtone.play(); }
  495. catch (e) { }
  496. }
  497. function stopRingTone() {
  498. try { ringtone.pause(); }
  499. catch (e) { }
  500. }
  501. function startRingbackTone() {
  502. try { ringbacktone.play(); }
  503. catch (e) { }
  504. }
  505. function stopRingbackTone() {
  506. try { ringbacktone.pause(); }
  507. catch (e) { }
  508. }
  509. function toggleFullScreen() {
  510. if (videoRemote.webkitSupportsFullscreen) {
  511. fullScreen(!videoRemote.webkitDisplayingFullscreen);
  512. }
  513. else {
  514. fullScreen(!bFullScreen);
  515. }
  516. }
  517. function openKeyPad(){
  518. divKeyPad.style.visibility = 'visible';
  519. divKeyPad.style.left = ((document.body.clientWidth - C.divKeyPadWidth) >> 1) + 'px';
  520. divKeyPad.style.top = '70px';
  521. divGlassPanel.style.visibility = 'visible';
  522. }
  523. function closeKeyPad(){
  524. divKeyPad.style.left = '0px';
  525. divKeyPad.style.top = '0px';
  526. divKeyPad.style.visibility = 'hidden';
  527. divGlassPanel.style.visibility = 'hidden';
  528. }
  529. function fullScreen(b_fs) {
  530. bFullScreen = b_fs;
  531. if (tsk_utils_have_webrtc4native() && bFullScreen && videoRemote.webkitSupportsFullscreen) {
  532. if (bFullScreen) {
  533. videoRemote.webkitEnterFullScreen();
  534. }
  535. else {
  536. videoRemote.webkitExitFullscreen();
  537. }
  538. }
  539. else {
  540. if (tsk_utils_have_webrtc4npapi()) {
  541. try { if(window.__o_display_remote) window.__o_display_remote.setFullScreen(b_fs); }
  542. catch (e) { divVideo.setAttribute("class", b_fs ? "full-screen" : "normal-screen"); }
  543. }
  544. else {
  545. divVideo.setAttribute("class", b_fs ? "full-screen" : "normal-screen");
  546. }
  547. }
  548. }
  549. function showNotifICall(s_number) {
  550. // permission already asked when we registered
  551. if (window.webkitNotifications && window.webkitNotifications.checkPermission() == 0) {
  552. if (oNotifICall) {
  553. oNotifICall.cancel();
  554. }
  555. oNotifICall = window.webkitNotifications.createNotification('images/sipml-34x39.png', 'Incaming call', 'Incoming call from ' + s_number);
  556. oNotifICall.onclose = function () { oNotifICall = null; };
  557. oNotifICall.show();
  558. }
  559. }
  560. function onKeyUp(evt) {
  561. evt = (evt || window.event);
  562. if (evt.keyCode == 27) {
  563. fullScreen(false);
  564. }
  565. else if (evt.ctrlKey && evt.shiftKey) { // CTRL + SHIFT
  566. if (evt.keyCode == 65 || evt.keyCode == 86) { // A (65) or V (86)
  567. bDisableVideo = (evt.keyCode == 65);
  568. txtCallStatus.innerHTML = '<i>Video ' + (bDisableVideo ? 'disabled' : 'enabled') + '</i>';
  569. window.localStorage.setItem('org.doubango.expert.disable_video', bDisableVideo);
  570. }
  571. }
  572. }
  573. function onDivCallCtrlMouseMove(evt) {
  574. try { // IE: DOM not ready
  575. if (tsk_utils_have_stream()) {
  576. btnCall.disabled = (!tsk_utils_have_stream() || !oSipSessionRegister || !oSipSessionRegister.is_connected());
  577. document.getElementById("divCallCtrl").onmousemove = null; // unsubscribe
  578. }
  579. }
  580. catch (e) { }
  581. }
  582. function uiOnConnectionEvent(b_connected, b_connecting) { // should be enum: connecting, connected, terminating, terminated
  583. btnRegister.disabled = b_connected || b_connecting;
  584. btnUnRegister.disabled = !b_connected && !b_connecting;
  585. btnCall.disabled = !(b_connected && tsk_utils_have_webrtc() && tsk_utils_have_stream());
  586. btnHangUp.disabled = !oSipSessionCall;
  587. }
  588. function uiVideoDisplayEvent(b_local, b_added) {
  589. var o_elt_video = b_local ? videoLocal : videoRemote;
  590. if (b_added) {
  591. if (SIPml.isWebRtc4AllSupported()) {
  592. if (b_local){ if(window.__o_display_local) window.__o_display_local.style.visibility = "visible"; }
  593. else { if(window.__o_display_remote) window.__o_display_remote.style.visibility = "visible"; }
  594. }
  595. else {
  596. o_elt_video.style.opacity = 1;
  597. }
  598. uiVideoDisplayShowHide(true);
  599. }
  600. else {
  601. if (SIPml.isWebRtc4AllSupported()) {
  602. if (b_local){ if(window.__o_display_local) window.__o_display_local.style.visibility = "hidden"; }
  603. else { if(window.__o_display_remote) window.__o_display_remote.style.visibility = "hidden"; }
  604. }
  605. else{
  606. o_elt_video.style.opacity = 0;
  607. }
  608. fullScreen(false);
  609. }
  610. }
  611. function uiVideoDisplayShowHide(b_show) {
  612. if (b_show) {
  613. tdVideo.style.height = '340px';
  614. divVideo.style.height = navigator.appName == 'Microsoft Internet Explorer' ? '100%' : '340px';
  615. }
  616. else {
  617. tdVideo.style.height = '0px';
  618. divVideo.style.height = '0px';
  619. }
  620. btnFullScreen.disabled = !b_show;
  621. }
  622. function uiDisableCallOptions() {
  623. if(window.localStorage) {
  624. window.localStorage.setItem('org.doubango.expert.disable_callbtn_options', 'true');
  625. uiBtnCallSetText('Call');
  626. alert('Use expert view to enable the options again (/!\\requires re-loading the page)');
  627. }
  628. }
  629. function uiBtnCallSetText(s_text) {
  630. switch(s_text) {
  631. case "Call":
  632. {
  633. var bDisableCallBtnOptions = (window.localStorage && window.localStorage.getItem('org.doubango.expert.disable_callbtn_options') == "true");
  634. btnCall.value = btnCall.innerHTML = bDisableCallBtnOptions ? 'Call' : 'Call <span id="spanCaret" class="caret">';
  635. btnCall.setAttribute("class", bDisableCallBtnOptions ? "btn btn-primary" : "btn btn-primary dropdown-toggle");
  636. btnCall.onclick = bDisableCallBtnOptions ? function(){ sipCall(bDisableVideo ? 'call-audio' : 'call-audiovideo'); } : null;
  637. ulCallOptions.style.visibility = bDisableCallBtnOptions ? "hidden" : "visible";
  638. if(!bDisableCallBtnOptions && ulCallOptions.parentNode != divBtnCallGroup){
  639. divBtnCallGroup.appendChild(ulCallOptions);
  640. }
  641. else if(bDisableCallBtnOptions && ulCallOptions.parentNode == divBtnCallGroup) {
  642. document.body.appendChild(ulCallOptions);
  643. }
  644. break;
  645. }
  646. default:
  647. {
  648. btnCall.value = btnCall.innerHTML = s_text;
  649. btnCall.setAttribute("class", "btn btn-primary");
  650. btnCall.onclick = function(){ sipCall(bDisableVideo ? 'call-audio' : 'call-audiovideo'); };
  651. ulCallOptions.style.visibility = "hidden";
  652. if(ulCallOptions.parentNode == divBtnCallGroup){
  653. document.body.appendChild(ulCallOptions);
  654. }
  655. break;
  656. }
  657. }
  658. }
  659. function uiCallTerminated(s_description){
  660. uiBtnCallSetText("Call");
  661. btnHangUp.value = 'HangUp';
  662. btnHoldResume.value = 'hold';
  663. btnCall.disabled = false;
  664. btnHangUp.disabled = true;
  665. oSipSessionCall = null;
  666. stopRingbackTone();
  667. stopRingTone();
  668. txtCallStatus.innerHTML = "<i>" + s_description + "</i>";
  669. uiVideoDisplayShowHide(false);
  670. divCallOptions.style.opacity = 0;
  671. if (oNotifICall) {
  672. oNotifICall.cancel();
  673. oNotifICall = null;
  674. }
  675. uiVideoDisplayEvent(true, false);
  676. uiVideoDisplayEvent(false, false);
  677. setTimeout(function () { if (!oSipSessionCall) txtCallStatus.innerHTML = ''; }, 2500);
  678. }
  679. // Callback function for SIP Stacks
  680. function onSipEventStack(e /*SIPml.Stack.Event*/) {
  681. tsk_utils_log_info('==stack event = ' + e.type);
  682. switch (e.type) {
  683. case 'started':
  684. {
  685. // catch exception for IE (DOM not ready)
  686. try {
  687. // LogIn (REGISTER) as soon as the stack finish starting
  688. oSipSessionRegister = this.newSession('register', {
  689. expires: 200,
  690. events_listener: { events: '*', listener: onSipEventSession },
  691. sip_caps: [
  692. { name: '+g.oma.sip-im', value: null },
  693. { name: '+audio', value: null },
  694. { name: 'language', value: '\"en,fr\"' }
  695. ]
  696. });
  697. oSipSessionRegister.register();
  698. }
  699. catch (e) {
  700. txtRegStatus.value = txtRegStatus.innerHTML = "<b>1:" + e + "</b>";
  701. btnRegister.disabled = false;
  702. }
  703. break;
  704. }
  705. case 'stopping': case 'stopped': case 'failed_to_start': case 'failed_to_stop':
  706. {
  707. var bFailure = (e.type == 'failed_to_start') || (e.type == 'failed_to_stop');
  708. oSipStack = null;
  709. oSipSessionRegister = null;
  710. oSipSessionCall = null;
  711. uiOnConnectionEvent(false, false);
  712. stopRingbackTone();
  713. stopRingTone();
  714. uiVideoDisplayShowHide(false);
  715. divCallOptions.style.opacity = 0;
  716. txtCallStatus.innerHTML = '';
  717. txtRegStatus.innerHTML = bFailure ? "<i>Disconnected: <b>" + e.description + "</b></i>" : "<i>Disconnected</i>";
  718. break;
  719. }
  720. case 'i_new_call':
  721. {
  722. if (oSipSessionCall) {
  723. // do not accept the incoming call if we're already 'in call'
  724. e.newSession.hangup(); // comment this line for multi-line support
  725. }
  726. else {
  727. oSipSessionCall = e.newSession;
  728. // start listening for events
  729. oSipSessionCall.setConfiguration(oConfigCall);
  730. uiBtnCallSetText('Answer');
  731. btnHangUp.value = 'Reject';
  732. btnCall.disabled = false;
  733. btnHangUp.disabled = false;
  734. startRingTone();
  735. var sRemoteNumber = (oSipSessionCall.getRemoteFriendlyName() || 'unknown');
  736. txtCallStatus.innerHTML = "<i>Incoming call from [<b>" + sRemoteNumber + "</b>]</i>";
  737. showNotifICall(sRemoteNumber);
  738. }
  739. break;
  740. }
  741. case 'm_permission_requested':
  742. {
  743. divGlassPanel.style.visibility = 'visible';
  744. break;
  745. }
  746. case 'm_permission_accepted':
  747. case 'm_permission_refused':
  748. {
  749. divGlassPanel.style.visibility = 'hidden';
  750. if(e.type == 'm_permission_refused'){
  751. uiCallTerminated('Media stream permission denied');
  752. }
  753. break;
  754. }
  755. case 'starting': default: break;
  756. }
  757. };
  758. // Callback function for SIP sessions (INVITE, REGISTER, MESSAGE...)
  759. function onSipEventSession(e /* SIPml.Session.Event */) {
  760. tsk_utils_log_info('==session event = ' + e.type);
  761. switch (e.type) {
  762. case 'connecting': case 'connected':
  763. {
  764. var bConnected = (e.type == 'connected');
  765. if (e.session == oSipSessionRegister) {
  766. uiOnConnectionEvent(bConnected, !bConnected);
  767. txtRegStatus.innerHTML = "<i>" + e.description + "</i>";
  768. }
  769. else if (e.session == oSipSessionCall) {
  770. btnHangUp.value = 'HangUp';
  771. btnCall.disabled = true;
  772. btnHangUp.disabled = false;
  773. btnTransfer.disabled = false;
  774. if (bConnected) {
  775. stopRingbackTone();
  776. stopRingTone();
  777. if (oNotifICall) {
  778. oNotifICall.cancel();
  779. oNotifICall = null;
  780. }
  781. }
  782. txtCallStatus.innerHTML = "<i>" + e.description + "</i>";
  783. divCallOptions.style.opacity = bConnected ? 1 : 0;
  784. if (SIPml.isWebRtc4AllSupported()) { // IE don't provide stream callback
  785. uiVideoDisplayEvent(true, true);
  786. uiVideoDisplayEvent(false, true);
  787. }
  788. }
  789. break;
  790. } // 'connecting' | 'connected'
  791. case 'terminating': case 'terminated':
  792. {
  793. if (e.session == oSipSessionRegister) {
  794. uiOnConnectionEvent(false, false);
  795. oSipSessionCall = null;
  796. oSipSessionRegister = null;
  797. txtRegStatus.innerHTML = "<i>" + e.description + "</i>";
  798. }
  799. else if (e.session == oSipSessionCall) {
  800. uiCallTerminated(e.description);
  801. }
  802. break;
  803. } // 'terminating' | 'terminated'
  804. case 'm_stream_video_local_added':
  805. {
  806. if (e.session == oSipSessionCall) {
  807. uiVideoDisplayEvent(true, true);
  808. }
  809. break;
  810. }
  811. case 'm_stream_video_local_removed':
  812. {
  813. if (e.session == oSipSessionCall) {
  814. uiVideoDisplayEvent(true, false);
  815. }
  816. break;
  817. }
  818. case 'm_stream_video_remote_added':
  819. {
  820. if (e.session == oSipSessionCall) {
  821. uiVideoDisplayEvent(false, true);
  822. }
  823. break;
  824. }
  825. case 'm_stream_video_remote_removed':
  826. {
  827. if (e.session == oSipSessionCall) {
  828. uiVideoDisplayEvent(false, false);
  829. }
  830. break;
  831. }
  832. case 'm_stream_audio_local_added':
  833. case 'm_stream_audio_local_removed':
  834. case 'm_stream_audio_remote_added':
  835. case 'm_stream_audio_remote_removed':
  836. {
  837. break;
  838. }
  839. case 'i_ect_new_call':
  840. {
  841. oSipSessionTransferCall = e.session;
  842. break;
  843. }
  844. case 'i_ao_request':
  845. {
  846. if(e.session == oSipSessionCall){
  847. var iSipResponseCode = e.getSipResponseCode();
  848. if (iSipResponseCode == 180 || iSipResponseCode == 183) {
  849. startRingbackTone();
  850. txtCallStatus.innerHTML = '<i>Remote ringing...</i>';
  851. }
  852. }
  853. break;
  854. }
  855. case 'm_early_media':
  856. {
  857. if(e.session == oSipSessionCall){
  858. stopRingbackTone();
  859. stopRingTone();
  860. txtCallStatus.innerHTML = '<i>Early media started</i>';
  861. }
  862. break;
  863. }
  864. case 'm_local_hold_ok':
  865. {
  866. if(e.session == oSipSessionCall){
  867. if (oSipSessionCall.bTransfering) {
  868. oSipSessionCall.bTransfering = false;
  869. // this.AVSession.TransferCall(this.transferUri);
  870. }
  871. btnHoldResume.value = 'Resume';
  872. btnHoldResume.disabled = false;
  873. txtCallStatus.innerHTML = '<i>Call placed on hold</i>';
  874. oSipSessionCall.bHeld = true;
  875. }
  876. break;
  877. }
  878. case 'm_local_hold_nok':
  879. {
  880. if(e.session == oSipSessionCall){
  881. oSipSessionCall.bTransfering = false;
  882. btnHoldResume.value = 'Hold';
  883. btnHoldResume.disabled = false;
  884. txtCallStatus.innerHTML = '<i>Failed to place remote party on hold</i>';
  885. }
  886. break;
  887. }
  888. case 'm_local_resume_ok':
  889. {
  890. if(e.session == oSipSessionCall){
  891. oSipSessionCall.bTransfering = false;
  892. btnHoldResume.value = 'Hold';
  893. btnHoldResume.disabled = false;
  894. txtCallStatus.innerHTML = '<i>Call taken off hold</i>';
  895. oSipSessionCall.bHeld = false;
  896. if (SIPml.isWebRtc4AllSupported()) { // IE don't provide stream callback yet
  897. uiVideoDisplayEvent(true, true);
  898. uiVideoDisplayEvent(false, true);
  899. }
  900. }
  901. break;
  902. }
  903. case 'm_local_resume_nok':
  904. {
  905. if(e.session == oSipSessionCall){
  906. oSipSessionCall.bTransfering = false;
  907. btnHoldResume.disabled = false;
  908. txtCallStatus.innerHTML = '<i>Failed to unhold call</i>';
  909. }
  910. break;
  911. }
  912. case 'm_remote_hold':
  913. {
  914. if(e.session == oSipSessionCall){
  915. txtCallStatus.innerHTML = '<i>Placed on hold by remote party</i>';
  916. }
  917. break;
  918. }
  919. case 'm_remote_resume':
  920. {
  921. if(e.session == oSipSessionCall){
  922. txtCallStatus.innerHTML = '<i>Taken off hold by remote party</i>';
  923. }
  924. break;
  925. }
  926. case 'o_ect_trying':
  927. {
  928. if(e.session == oSipSessionCall){
  929. txtCallStatus.innerHTML = '<i>Call transfer in progress...</i>';
  930. }
  931. break;
  932. }
  933. case 'o_ect_accepted':
  934. {
  935. if(e.session == oSipSessionCall){
  936. txtCallStatus.innerHTML = '<i>Call transfer accepted</i>';
  937. }
  938. break;
  939. }
  940. case 'o_ect_completed':
  941. case 'i_ect_completed':
  942. {
  943. if(e.session == oSipSessionCall){
  944. txtCallStatus.innerHTML = '<i>Call transfer completed</i>';
  945. btnTransfer.disabled = false;
  946. if (oSipSessionTransferCall) {
  947. oSipSessionCall = oSipSessionTransferCall;
  948. }
  949. oSipSessionTransferCall = null;
  950. }
  951. break;
  952. }
  953. case 'o_ect_failed':
  954. case 'i_ect_failed':
  955. {
  956. if(e.session == oSipSessionCall){
  957. txtCallStatus.innerHTML = '<i>Call transfer failed</i>';
  958. btnTransfer.disabled = false;
  959. }
  960. break;
  961. }
  962. case 'o_ect_notify':
  963. case 'i_ect_notify':
  964. {
  965. if(e.session == oSipSessionCall){
  966. txtCallStatus.innerHTML = "<i>Call Transfer: <b>" + e.getSipResponseCode() + " " + e.description + "</b></i>";
  967. if (e.getSipResponseCode() >= 300) {
  968. if (oSipSessionCall.bHeld) {
  969. oSipSessionCall.resume();
  970. }
  971. btnTransfer.disabled = false;
  972. }
  973. }
  974. break;
  975. }
  976. case 'i_ect_requested':
  977. {
  978. if(e.session == oSipSessionCall){
  979. var s_message = "Do you accept call transfer to [" + e.getTransferDestinationFriendlyName() + "]?";//FIXME
  980. if (confirm(s_message)) {
  981. txtCallStatus.innerHTML = "<i>Call transfer in progress...</i>";
  982. oSipSessionCall.acceptTransfer();
  983. break;
  984. }
  985. oSipSessionCall.rejectTransfer();
  986. }
  987. break;
  988. }
  989. }
  990. }
  991. </script>
  992. <body style="cursor:wait">
  993. <div class="navbar navbar-fixed-top">
  994. <div id="divNavbarInner" class="navbar-inner">
  995. <div class="container">
  996. <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"><span
  997. class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span>
  998. </a>
  999. <img alt="sipML5" class="brand" src="./images/sipml-34x39.png" />
  1000. <div class="nav-collapse">
  1001. <ul class="nav">
  1002. <li class="active"><a href="index.html?svn=224">Home</a></li>
  1003. </ul>
  1004. </div>
  1005. <!--/.nav-collapse -->
  1006. </div>
  1007. </div>
  1008. </div>
  1009. <div class="container">
  1010. <div class="row-fluid">
  1011. <div class="span4 well">
  1012. <label style="width: 100%;" align="center" id="txtRegStatus">
  1013. </label>
  1014. <h2>
  1015. Registration</h2>
  1016. <br />
  1017. <table style='width: 100%'>
  1018. <tr>
  1019. <td>
  1020. <label style="height: 100%">
  1021. Display Name:</label>
  1022. </td>
  1023. <td>
  1024. <input type="text" style="width: 100%; height: 100%" id="txtDisplayName" value=""
  1025. placeholder="e.g. John Doe" />
  1026. </td>
  1027. </tr>
  1028. <tr>
  1029. <td>
  1030. <label style="height: 100%">
  1031. Private Identity<sup>*</sup>:</label>
  1032. </td>
  1033. <td>
  1034. <input type="text" style="width: 100%; height: 100%" id="txtPrivateIdentity" value=""
  1035. placeholder="e.g. +33600000000" />
  1036. </td>
  1037. </tr>
  1038. <tr>
  1039. <td>
  1040. <label style="height: 100%">
  1041. Public Identity<sup>*</sup>:</label>
  1042. </td>
  1043. <td>
  1044. <input type="text" style="width: 100%; height: 100%" id="txtPublicIdentity" value=""
  1045. placeholder="e.g. sip:[email protected]" />
  1046. </td>
  1047. </tr>
  1048. <tr>
  1049. <td>
  1050. <label style="height: 100%">Password:</label>
  1051. </td>
  1052. <td>
  1053. <input type="password" style="width: 100%; height: 100%" id="txtPassword" value="" />
  1054. </td>
  1055. </tr>
  1056. <tr>
  1057. <td>
  1058. <label style="height: 100%">Realm<sup>*</sup>:</label>
  1059. </td>
  1060. <td>
  1061. <input type="text" style="width: 100%; height: 100%" id="txtRealm" value="" placeholder="e.g. doubango.org" />
  1062. </td>
  1063. </tr>
  1064. <tr>
  1065. <td colspan="2" align="right">
  1066. <input type="button" class="btn btn-success" id="btnRegister" value="LogIn" disabled onclick='sipRegister();' />
  1067. &nbsp;
  1068. <input type="button" class="btn btn-danger" id="btnUnRegister" value="LogOut" disabled onclick='sipUnRegister();' />
  1069. </td>
  1070. </tr>
  1071. <tr>
  1072. <td colspan="3">
  1073. <p class="small"><sup>*</sup> <i>Mandatory Field</i></p>
  1074. </td>
  1075. </tr>
  1076. <tr>
  1077. <td colspan="3">
  1078. <a class="btn" href="http://code.google.com/p/sipml5/wiki/Public_SIP_Servers" target="_blank">Need SIP account?</a>
  1079. </td>
  1080. </tr>
  1081. <tr>
  1082. <td colspan="3">
  1083. <a class="btn" href="./expert.htm" target="_blank">Expert mode?</a>
  1084. </td>
  1085. </tr>
  1086. </table>
  1087. </div>
  1088. <div id="divCallCtrl" class="span7 well" style='display:table-cell; vertical-align:middle'>
  1089. <label style="width: 100%;" align="center" id="txtCallStatus">
  1090. </label>
  1091. <h2>
  1092. Call control
  1093. </h2>
  1094. <br />
  1095. <table style='width: 100%;'>
  1096. <tr>
  1097. <td style="white-space:nowrap;">
  1098. <input type="text" style="width: 100%; height:100%" id="txtPhoneNumber" value="" placeholder="Enter phone number to call" />
  1099. </td>
  1100. </tr>
  1101. <tr>
  1102. <td colspan="1" align="right">
  1103. <div class="btn-toolbar" style="margin: 0; vertical-align:middle">
  1104. <div id="divBtnCallGroup" class="btn-group">
  1105. <button id="btnCall" disabled class="btn btn-primary" data-toggle="dropdown">Call</button>
  1106. </div>&nbsp;&nbsp;
  1107. <div class="btn-group">
  1108. <input type="button" id="btnHangUp" style="margin: 0; vertical-align:middle; height: 100%;" class="btn btn-primary" value="HangUp" onclick='sipHangUp();' disabled />
  1109. </div>
  1110. </div>
  1111. </td>
  1112. </tr>
  1113. <tr>
  1114. <td id="tdVideo" class='tab-video'>
  1115. <div id="divVideo" class='div-video'>
  1116. <div id="divVideoRemote" style='border:1px solid #000; height:100%; width:100%'>
  1117. <video class="video" width="100%" height="100%" id="video_remote" autoplay="autoplay" style="opacity: 0;
  1118. background-color: #000000; -webkit-transition-property: opacity; -webkit-transition-duration: 2s;">
  1119. </video>
  1120. </div>
  1121. <div id="divVideoLocal" style='border:0px solid #000'>
  1122. <video class="video" width="88px" height="72px" id="video_local" autoplay="autoplay" muted="true" style="opacity: 0;
  1123. margin-top: -80px; margin-left: 5px; background-color: #000000; -webkit-transition-property: opacity;
  1124. -webkit-transition-duration: 2s;">
  1125. </video>
  1126. </div>
  1127. </div>
  1128. </td>
  1129. </tr>
  1130. <tr>
  1131. <td align='center'>
  1132. <div id='divCallOptions' class='call-options' style='opacity: 0; margin-top: 3px'>
  1133. <input type="button" class="btn" style="" id="btnFullScreen" value="FullScreen" disabled onclick='toggleFullScreen();' /> &nbsp;
  1134. <input type="button" class="btn" style="" id="btnHoldResume" value="Hold" onclick='sipToggleHoldResume();' /> &nbsp;
  1135. <input type="button" class="btn" style="" id="btnTransfer" value="Transfer" onclick='sipTransfer();' /> &nbsp;
  1136. <input type="button" class="btn" style="" id="btnKeyPad" value="KeyPad" onclick='openKeyPad();' />
  1137. </div>
  1138. </td>
  1139. </tr>
  1140. </table>
  1141. </div>
  1142. </div>
  1143. <br />
  1144. <footer>
  1145. <p>&copy; Doubango Telecom 2012-2013 <br />
  1146. <i>Inspiring the future</i>
  1147. </p>
  1148. <!-- Creates all ATL/COM objects right now
  1149. Will open confirmation dialogs if not already done
  1150. -->
  1151. <object id="fakeVideoDisplay" classid="clsid:5C2C407B-09D9-449B-BB83-C39B7802A684" style="visibility:hidden;"> </object>
  1152. <object id="fakeLooper" classid="clsid:7082C446-54A8-4280-A18D-54143846211A" style="visibility:visible; width:0px; height:0px"> </object>
  1153. <object id="fakeSessionDescription" classid="clsid:DBA9F8E2-F9FB-47CF-8797-986A69A1CA9C" style="visibility:hidden;"> </object>
  1154. <object id="fakeNetTransport" classid="clsid:5A7D84EC-382C-4844-AB3A-9825DBE30DAE" style="visibility:hidden;"> </object>
  1155. <object id="fakePeerConnection" classid="clsid:56D10AD3-8F52-4AA4-854B-41F4D6F9CEA3" style="visibility:hidden;"> </object>
  1156. <!--
  1157. NPAPI browsers: Safari, Opera and Firefox
  1158. -->
  1159. <!--embed id="WebRtc4npapi" type="application/w4a" width='1' height='1' style='visibility:hidden;' /-->
  1160. </footer>
  1161. </div>
  1162. <!-- /container -->
  1163. <!-- Glass Panel -->
  1164. <div id='divGlassPanel' class='glass-panel' style='visibility:hidden'></div>
  1165. <!-- KeyPad Div -->
  1166. <div id='divKeyPad' class='span2 well div-keypad' style="left:0px; top:0px; width:250; height:240; visibility:hidden">
  1167. <table style="width: 100%; height: 100%">
  1168. <tr><td><input type="button" style="width: 33%" class="btn" value="1" onclick="sipSendDTMF('1');"/><input type="button" style="width: 33%" class="btn" value="2" onclick="sipSendDTMF('2');"/><input type="button" style="width: 33%" class="btn" value="3" onclick="sipSendDTMF('3');"/></td></tr>
  1169. <tr><td><input type="button" style="width: 33%" class="btn" value="4" onclick="sipSendDTMF('4');"/><input type="button" style="width: 33%" class="btn" value="5" onclick="sipSendDTMF('5');"/><input type="button" style="width: 33%" class="btn" value="6" onclick="sipSendDTMF('6');"/></td></tr>
  1170. <tr><td><input type="button" style="width: 33%" class="btn" value="7" onclick="sipSendDTMF('7');"/><input type="button" style="width: 33%" class="btn" value="8" onclick="sipSendDTMF('8');"/><input type="button" style="width: 33%" class="btn" value="9" onclick="sipSendDTMF('9');"/></td></tr>
  1171. <tr><td><input type="button" style="width: 33%" class="btn" value="*" onclick="sipSendDTMF('*');"/><input type="button" style="width: 33%" class="btn" value="0" onclick="sipSendDTMF('0');"/><input type="button" style="width: 33%" class="btn" value="#" onclick="sipSendDTMF('#');"/></td></tr>
  1172. <tr><td colspan=3><input type="button" style="width: 100%" class="btn btn-medium btn-danger" value="close" onclick="closeKeyPad();" /></td></tr>
  1173. </table>
  1174. </div>
  1175. <!-- Call button options -->
  1176. <ul id="ulCallOptions" class="dropdown-menu" style="visibility:hidden">
  1177. <li><a href="#" onclick='sipCall("call-audio");'>Audio</a></li>
  1178. <li><a href="#" onclick='sipCall("call-audiovideo");'>Video</a></li>
  1179. <li id='liScreenShare' ><a href="#" onclick='sipCall("call-screenshare");'>Screen Share</a></li>
  1180. <li class="divider"></li>
  1181. <li><a href="#" onclick='uiDisableCallOptions();'><b>Disable these options</b></a></li>
  1182. </ul>
  1183. <!-- Le javascript
  1184. ================================================== -->
  1185. <!-- Placed at the end of the document so the pages load faster -->
  1186. <script type="text/javascript" src="./assets/js/jquery.js"></script>
  1187. <script type="text/javascript" src="./assets/js/bootstrap-transition.js"></script>
  1188. <script type="text/javascript" src="./assets/js/bootstrap-alert.js"></script>
  1189. <script type="text/javascript" src="./assets/js/bootstrap-modal.js"></script>
  1190. <script type="text/javascript" src="./assets/js/bootstrap-dropdown.js"></script>
  1191. <script type="text/javascript" src="./assets/js/bootstrap-scrollspy.js"></script>
  1192. <script type="text/javascript" src="./assets/js/bootstrap-tab.js"></script>
  1193. <script type="text/javascript" src="./assets/js/bootstrap-tooltip.js"></script>
  1194. <script type="text/javascript" src="./assets/js/bootstrap-popover.js"></script>
  1195. <script type="text/javascript" src="./assets/js/bootstrap-button.js"></script>
  1196. <script type="text/javascript" src="./assets/js/bootstrap-collapse.js"></script>
  1197. <script type="text/javascript" src="./assets/js/bootstrap-carousel.js"></script>
  1198. <script type="text/javascript" src="./assets/js/bootstrap-typeahead.js"></script>
  1199. <!-- Audios -->
  1200. <audio id="audio_remote" autoplay="autoplay" />
  1201. <audio id="ringtone" loop src="sounds/ringtone.wav" />
  1202. <audio id="ringbacktone" loop src="sounds/ringbacktone.wav" />
  1203. <audio id="dtmfTone" src="sounds/dtmf.wav" />
  1204. <!--
  1205. Microsoft Internet Explorer extension
  1206. For now we use msi installer as we don't have trusted certificate yet :(
  1207. -->
  1208. <!--object id="webrtc4ieLooper" classid="clsid:7082C446-54A8-4280-A18D-54143846211A" CODEBASE="http://sipml5.org/deploy/webrtc4all.CAB"> </object-->
  1209. <!-- GOOGLE ANALYTICS -->
  1210. <script type="text/javascript">
  1211. var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
  1212. document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
  1213. </script>
  1214. <script type="text/javascript">
  1215. try {
  1216. var pageTracker = _gat._getTracker("UA-6868621-19");
  1217. pageTracker._trackPageview();
  1218. } catch (err) { }
  1219. </script>
  1220. </body>
  1221. </html>