call.htm 52 KB

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