26 changed files with 35276 additions and 0 deletions
-
41app/webphone/app_config.php
-
83app/webphone/app_languages.php
-
15app/webphone/app_menu.php
-
BINapp/webphone/img/bg.jpg
-
BINapp/webphone/img/favicon.ico
-
BINapp/webphone/img/screenshots/1.png
-
BINapp/webphone/img/screenshots/2.png
-
BINapp/webphone/img/screenshots/3.png
-
85app/webphone/phone/css/ctxSip.css
-
85app/webphone/phone/css/ctxSip.less
-
111app/webphone/phone/index.html
-
162app/webphone/phone/index.php
-
90app/webphone/phone/root.php
-
21553app/webphone/phone/scripts/SIP.js/sip.js
-
11860app/webphone/phone/scripts/SIP.js/sip.js.0.7.8
-
1app/webphone/phone/scripts/SIP.js/sip.min.js
-
40app/webphone/phone/scripts/SIP.js/sip.min.js.0.7.8
-
895app/webphone/phone/scripts/app.js
-
12app/webphone/phone/scripts/config-sample.js
-
12app/webphone/phone/scripts/config.js
-
7app/webphone/phone/scripts/moment.js/moment.min.js
-
BINapp/webphone/phone/sounds/dtmf.mp3
-
BINapp/webphone/phone/sounds/incoming.mp3
-
BINapp/webphone/phone/sounds/outgoing.mp3
-
90app/webphone/root.php
-
134app/webphone/webphone.php
@ -0,0 +1,41 @@ |
|||
<?php |
|||
|
|||
//application details
|
|||
$apps[$x]['name'] = "webphone"; |
|||
$apps[$x]['uuid'] = "29ad51b0-6ab0-4d65-9394-629d1a34580b"; |
|||
$apps[$x]['category'] = "Vendor"; |
|||
$apps[$x]['subcategory'] = ""; |
|||
$apps[$x]['version'] = "1.0"; |
|||
$apps[$x]['license'] = "Mozilla Public License 1.1"; |
|||
$apps[$x]['url'] = "http://www.fusionpbx.com"; |
|||
$apps[$x]['description']['en-us'] = "Web Phone"; |
|||
$apps[$x]['description']['en-gb'] = ""; |
|||
$apps[$x]['description']['ar-eg'] = ""; |
|||
$apps[$x]['description']['de-at'] = ""; |
|||
$apps[$x]['description']['de-ch'] = ""; |
|||
$apps[$x]['description']['de-de'] = ""; |
|||
$apps[$x]['description']['es-cl'] = ""; |
|||
$apps[$x]['description']['es-mx'] = ""; |
|||
$apps[$x]['description']['fr-ca'] = ""; |
|||
$apps[$x]['description']['fr-fr'] = ""; |
|||
$apps[$x]['description']['he-il'] = ""; |
|||
$apps[$x]['description']['it-it'] = ""; |
|||
$apps[$x]['description']['nl-nl'] = ""; |
|||
$apps[$x]['description']['pl-pl'] = ""; |
|||
$apps[$x]['description']['pt-br'] = ""; |
|||
$apps[$x]['description']['pt-pt'] = ""; |
|||
$apps[$x]['description']['ro-ro'] = ""; |
|||
$apps[$x]['description']['ru-ru'] = ""; |
|||
$apps[$x]['description']['sv-se'] = ""; |
|||
$apps[$x]['description']['uk-ua'] = ""; |
|||
|
|||
//permission details
|
|||
$y=0; |
|||
$apps[$x]['permissions'][$y]['name'] = "webphone_view"; |
|||
//$apps[$x]['permissions'][$y]['menu']['uuid'] = "000ed55e-7fbc-40b5-9631-506d0b6056b4";
|
|||
$apps[$x]['permissions'][$y]['groups'][] = "superadmin"; |
|||
$apps[$x]['permissions'][$y]['groups'][] = "admin"; |
|||
$apps[$x]['permissions'][$y]['groups'][] = "user"; |
|||
$apps[$x]['permissions'][$y]['groups'][] = "agent"; |
|||
|
|||
?>
|
@ -0,0 +1,83 @@ |
|||
<?php |
|||
$text['title-webphone']['en-us'] = "WebPhone"; |
|||
$text['title-webphone']['ar-eg'] = "ุฉ"; |
|||
$text['title-webphone']['de-at'] = ""; //copied from de-de
|
|||
$text['title-webphone']['de-ch'] = ""; //copied from de-de
|
|||
$text['title-webphone']['de-de'] = ""; |
|||
$text['title-webphone']['es-cl'] = ""; |
|||
$text['title-webphone']['es-mx'] = ""; //copied from es-cl
|
|||
$text['title-webphone']['fr-ca'] = ""; //copied from fr-fr
|
|||
$text['title-webphone']['fr-fr'] = ""; |
|||
$text['title-webphone']['he-il'] = ""; |
|||
$text['title-webphone']['it-it'] = ""; |
|||
$text['title-webphone']['nl-nl'] = ""; |
|||
$text['title-webphone']['pl-pl'] = ""; |
|||
$text['title-webphone']['pt-br'] = ""; //copied from pt-pt
|
|||
$text['title-webphone']['pt-pt'] = ""; |
|||
$text['title-webphone']['ro-ro'] = ""; |
|||
$text['title-webphone']['ru-ru'] = ""; |
|||
$text['title-webphone']['sv-se'] = ""; |
|||
$text['title-webphone']['uk-ua'] = ""; |
|||
|
|||
|
|||
$text['title-description-webphone']['en-us'] = "Webphone is a simple browser-based phone. Select an extension and click 'Launch'"; |
|||
$text['title-description-webphone']['ar-eg'] = "ุฉ"; |
|||
$text['title-description-webphone']['de-at'] = ""; //copied from de-de
|
|||
$text['title-description-webphone']['de-ch'] = ""; //copied from de-de
|
|||
$text['title-description-webphone']['de-de'] = ""; |
|||
$text['title-description-webphone']['es-cl'] = ""; |
|||
$text['title-description-webphone']['es-mx'] = ""; //copied from es-cl
|
|||
$text['title-description-webphone']['fr-ca'] = ""; //copied from fr-fr
|
|||
$text['title-description-webphone']['fr-fr'] = ""; |
|||
$text['title-description-webphone']['he-il'] = ""; |
|||
$text['title-description-webphone']['it-it'] = ""; |
|||
$text['title-description-webphone']['nl-nl'] = ""; |
|||
$text['title-description-webphone']['pl-pl'] = ""; |
|||
$text['title-description-webphone']['pt-br'] = ""; //copied from pt-pt
|
|||
$text['title-description-webphone']['pt-pt'] = ""; |
|||
$text['title-description-webphone']['ro-ro'] = ""; |
|||
$text['title-description-webphone']['ru-ru'] = ""; |
|||
$text['title-description-webphone']['sv-se'] = ""; |
|||
$text['title-description-webphone']['uk-ua'] = ""; |
|||
|
|||
$text['label-select_extension']['en-us'] = "Select Extension"; |
|||
$text['label-select_extension']['ar-eg'] = "ุฉ"; |
|||
$text['label-select_extension']['de-at'] = ""; //copied from de-de
|
|||
$text['label-select_extension']['de-ch'] = ""; //copied from de-de
|
|||
$text['label-select_extension']['de-de'] = ""; |
|||
$text['label-select_extension']['es-cl'] = ""; |
|||
$text['label-select_extension']['es-mx'] = ""; //copied from es-cl
|
|||
$text['label-select_extension']['fr-ca'] = ""; //copied from fr-fr
|
|||
$text['label-select_extension']['fr-fr'] = ""; |
|||
$text['label-select_extension']['he-il'] = ""; |
|||
$text['label-select_extension']['it-it'] = ""; |
|||
$text['label-select_extension']['nl-nl'] = ""; |
|||
$text['label-select_extension']['pl-pl'] = ""; |
|||
$text['label-select_extension']['pt-br'] = ""; //copied from pt-pt
|
|||
$text['label-select_extension']['pt-pt'] = ""; |
|||
$text['label-select_extension']['ro-ro'] = ""; |
|||
$text['label-select_extension']['ru-ru'] = ""; |
|||
$text['label-select_extension']['sv-se'] = ""; |
|||
$text['label-select_extension']['uk-ua'] = ""; |
|||
|
|||
|
|||
$text['label-webphone_launch']['en-us'] = "Launch"; |
|||
$text['label-webphone_launch']['ar-eg'] = "ุฉ"; |
|||
$text['label-webphone_launch']['de-at'] = ""; //copied from de-de
|
|||
$text['label-webphone_launch']['de-ch'] = ""; //copied from de-de
|
|||
$text['label-webphone_launch']['de-de'] = ""; |
|||
$text['label-webphone_launch']['es-cl'] = ""; |
|||
$text['label-webphone_launch']['es-mx'] = ""; //copied from es-cl
|
|||
$text['label-webphone_launch']['fr-ca'] = ""; //copied from fr-fr
|
|||
$text['label-webphone_launch']['fr-fr'] = ""; |
|||
$text['label-webphone_launch']['he-il'] = ""; |
|||
$text['label-webphone_launch']['it-it'] = ""; |
|||
$text['label-webphone_launch']['nl-nl'] = ""; |
|||
$text['label-webphone_launch']['pl-pl'] = ""; |
|||
$text['label-webphone_launch']['pt-br'] = ""; //copied from pt-pt
|
|||
$text['label-webphone_launch']['pt-pt'] = ""; |
|||
$text['label-webphone_launch']['ro-ro'] = ""; |
|||
$text['label-webphone_launch']['ru-ru'] = ""; |
|||
$text['label-webphone_launch']['sv-se'] = ""; |
|||
$text['label-webphone_launch']['uk-ua'] = ""; |
|||
?>
|
@ -0,0 +1,15 @@ |
|||
<?php |
|||
|
|||
$apps[$x]['menu'][0]['title']['en-us'] = "WebPhone"; |
|||
$apps[$x]['menu'][0]['uuid'] = "b08f5f87-8a66-4c29-b1e6-eb0c374d58f2"; |
|||
$apps[$x]['menu'][0]['parent_uuid'] = ""; |
|||
$apps[$x]['menu'][0]['category'] = "internal"; |
|||
$apps[$x]['menu'][0]['icon'] = "fa-phone"; |
|||
$apps[$x]['menu'][0]['path'] = "/app/webphone/webphone.php"; |
|||
$apps[$x]['menu'][0]['order'] = "28"; |
|||
$apps[$x]['menu'][0]['groups'][] = "admin"; |
|||
$apps[$x]['menu'][0]['groups'][] = "superadmin"; |
|||
$apps[$x]['menu'][0]['groups'][] = "user"; |
|||
$apps[$x]['menu'][0]['groups'][] = "agent"; |
|||
|
|||
?>
|
After Width: 1920 | Height: 514 | Size: 26 KiB |
After Width: 16 | Height: 16 | Size: 438 B |
After Width: 320 | Height: 479 | Size: 26 KiB |
After Width: 321 | Height: 478 | Size: 31 KiB |
After Width: 322 | Height: 481 | Size: 39 KiB |
@ -0,0 +1,85 @@ |
|||
#sipClient { |
|||
background-color: #333; |
|||
font-size: 13px; |
|||
line-height: 1.42857143; |
|||
/* firefox 19+ */ |
|||
/* ie */ |
|||
} |
|||
#sipClient p { |
|||
margin: 0; |
|||
padding: 0 0 10px 0; |
|||
line-height: 18px; |
|||
} |
|||
#sipClient .sipStatus { |
|||
margin: 0 -15px 15px -15px; |
|||
padding: 5px 15px; |
|||
background-color: #111; |
|||
color: #999; |
|||
} |
|||
#sipClient #txtCallStatus { |
|||
color: #fff; |
|||
} |
|||
#sipClient #sip-dialpad { |
|||
width: 292px; |
|||
padding: 16px 20px; |
|||
} |
|||
#sipClient #sip-dialpad .col-xs-4 { |
|||
text-align: center; |
|||
} |
|||
#sipClient .digit { |
|||
height: 66px; |
|||
width: 66px; |
|||
margin: 0 8px 14px !important; |
|||
background-color: #f8f8f8; |
|||
font-weight: 300; |
|||
font-size: 24px; |
|||
border-radius: 100px; |
|||
line-height: 21px; |
|||
padding-top: 9px; |
|||
float: left; |
|||
} |
|||
#sipClient .digit span { |
|||
display: block; |
|||
color: #999; |
|||
font-size: 10px; |
|||
font-weight: normal; |
|||
} |
|||
#sipClient .sip-panel h3 { |
|||
margin-top: 22px; |
|||
} |
|||
#sipClient #sip-splash { |
|||
height: 371px; |
|||
} |
|||
#sipClient #sip-splash .fa { |
|||
margin-bottom: 15px; |
|||
} |
|||
#sipClient #sip-splash .fa-circle { |
|||
color: #5cb85c; |
|||
} |
|||
#sipClient #sip-log .panel-heading { |
|||
padding: 10px 8px; |
|||
} |
|||
#sipClient #sip-logitems { |
|||
height: 332px; |
|||
overflow: auto; |
|||
} |
|||
#sipClient #sldVolume { |
|||
width: 140px; |
|||
margin: 0 auto; |
|||
box-shadow: none; |
|||
} |
|||
#sipClient .sip-logitem { |
|||
padding: 4px; |
|||
} |
|||
#sipClient #numDisplay::-webkit-input-placeholder { |
|||
color: #bbb; |
|||
} |
|||
#sipClient #numDisplay::-moz-placeholder { |
|||
color: #bbb; |
|||
} |
|||
#sipClient #numDisplay::-ms-input-placeholder { |
|||
color: #bbb; |
|||
} |
|||
#sipClient #numDisplay::-moz-placeholder { |
|||
color: #bbb; |
|||
} |
@ -0,0 +1,85 @@ |
|||
#sipClient { |
|||
|
|||
background-color : #333; |
|||
font-size : 13px; |
|||
line-height : 1.42857143; |
|||
|
|||
p { |
|||
margin : 0; |
|||
padding : 0 0 10px 0; |
|||
line-height : 18px; |
|||
} |
|||
|
|||
.sipStatus { |
|||
margin : 0 -15px 15px -15px; |
|||
padding : 5px 15px; |
|||
background-color : #111; |
|||
color : #999; |
|||
} |
|||
|
|||
#txtCallStatus { color : #fff; } |
|||
|
|||
#sip-dialpad { |
|||
width : 292px; |
|||
padding : 16px 20px; |
|||
} |
|||
|
|||
#sip-dialpad .col-xs-4 { text-align : center; } |
|||
|
|||
.digit { |
|||
height : 66px; |
|||
width : 66px; |
|||
margin : 0 8px 14px !important; |
|||
background-color : #f8f8f8; |
|||
font-weight : 300; |
|||
font-size : 24px; |
|||
border-radius : 100px; |
|||
line-height : 21px; |
|||
padding-top : 9px; |
|||
float : left; |
|||
|
|||
span { |
|||
display : block; |
|||
color : #999; |
|||
font-size : 10px; |
|||
font-weight : normal; |
|||
} |
|||
} |
|||
|
|||
.sip-panel h3 { margin-top : 22px; } |
|||
|
|||
#sip-splash { |
|||
height : 371px; |
|||
|
|||
.fa { |
|||
margin-bottom : 15px; |
|||
} |
|||
|
|||
.fa-circle { |
|||
color: #5cb85c; |
|||
} |
|||
} |
|||
|
|||
#sip-log .panel-heading { |
|||
padding : 10px 8px; |
|||
} |
|||
#sip-logitems { |
|||
height : 332px; |
|||
overflow : auto; |
|||
} |
|||
|
|||
#sldVolume { |
|||
width : 140px; |
|||
margin : 0 auto; |
|||
box-shadow : none; |
|||
} |
|||
|
|||
.sip-logitem { |
|||
padding : 4px; |
|||
} |
|||
|
|||
#numDisplay::-webkit-input-placeholder { color:#bbb; } |
|||
#numDisplay::-moz-placeholder { color:#bbb; } |
|||
#numDisplay::-ms-input-placeholder { color:#bbb; } |
|||
#numDisplay::-moz-placeholder { color:#bbb; } |
|||
} |
@ -0,0 +1,111 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
|||
<title>ctxSip</title> |
|||
<link rel="icon" type="image/gif" href="img/favicon.ico" /> |
|||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> |
|||
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css"> |
|||
<link href="css/ctxSip.css" rel="stylesheet" type="text/css" /> |
|||
</head> |
|||
<body id="sipClient"> |
|||
<div class="container-fluid"> |
|||
|
|||
<div class="clearfix sipStatus"> |
|||
<div id="txtCallStatus" class="pull-right"> </div> |
|||
<div id="txtRegStatus"></div> |
|||
</div> |
|||
|
|||
<div class="form-group" id="phoneUI"> |
|||
<div class="input-group"> |
|||
<div class="input-group-btn"> |
|||
<button class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" title="Show Keypad"> |
|||
<i class="fa fa-th"></i> |
|||
</button> |
|||
<div id="sip-dialpad" class="dropdown-menu"> |
|||
<button type="button" class="btn btn-default digit" data-digit="1">1<span> </span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="2">2<span>ABC</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="3">3<span>DEF</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="4">4<span>GHI</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="5">5<span>JKL</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="6">6<span>MNO</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="7">7<span>PQRS</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="8">8<span>TUV</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="9">9<span>WXYZ</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="*">*<span> </span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="0">0<span>+</span></button> |
|||
<button type="button" class="btn btn-default digit" data-digit="#">#<span> </span></button> |
|||
<div class="clearfix"> </div> |
|||
<button class="btn btn-success btn-block btnCall" title="Send"> |
|||
<i class="fa fa-play"></i> Send |
|||
</button> |
|||
</div> |
|||
</div> |
|||
<input type="text" name="number" id="numDisplay" class="form-control text-center input-sm" value="" placeholder="Enter number..." autocomplete="off" /> |
|||
<div class="input-group-btn input-group-btn-sm"> |
|||
<button class="btn btn-sm btn-primary dropdown-toggle" id="btnVol" data-toggle="dropdown" title="Volume"> |
|||
<i class="fa fa-fw fa-volume-up"></i> |
|||
</button> |
|||
<div class="dropdown-menu dropdown-menu-right"> |
|||
<input type="range" min="0" max="100" value="100" step="1" id="sldVolume" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="well-sip"> |
|||
<div id="sip-splash" class="text-muted text-center panel panel-default"> |
|||
<div class="panel-body"> |
|||
<h3 class="page-header"> |
|||
<span class="fa-stack fa-2x"> |
|||
<i class="fa fa-circle fa-stack-2x text-success"></i> |
|||
<i class="fa fa-phone fa-stack-1x fa-inverse"></i> |
|||
</span><br> |
|||
This is your phone.</h3> |
|||
<p class="lead">To make a call enter a number or SIP address in the box above.</p> |
|||
<small>Closing this window will cause calls to go to voicemail.</small> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="sip-log" class="panel panel-default hide"> |
|||
<div class="panel-heading"> |
|||
<h4 class="text-muted panel-title">Recent Calls <span class="pull-right"><i class="fa fa-trash text-muted sipLogClear" title="Clear Log"></i></span></h4> |
|||
</div> |
|||
<div id="sip-logitems" class="list-group"> |
|||
<p class="text-muted text-center">No recent calls from this browser.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="modal fade" id="mdlError" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static" data-keyboard="false"> |
|||
<div class="modal-dialog modal-sm"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h4 class="modal-title">Sip Error</h4> |
|||
</div> |
|||
<div class="modal-body text-center text-danger"> |
|||
<h3><i class="fa fa-3x fa-ban"></i></h3> |
|||
<p class="lead">Sip registration failed. No calls can be handled.</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<audio id="ringtone" src="sounds/incoming.mp3" loop></audio> |
|||
<audio id="ringbacktone" src="sounds/outgoing.mp3" loop></audio> |
|||
<audio id="dtmfTone" src="sounds/dtmf.mp3"></audio> |
|||
<audio id="audioRemote"></audio> |
|||
|
|||
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.min.js"></script> |
|||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> |
|||
<script type="text/javascript" src="scripts/moment.js/moment.min.js"></script> |
|||
|
|||
<script type="text/javascript" src="scripts/SIP.js/sip.min.js"></script> |
|||
<script type="text/javascript" src="scripts/config.js"></script> |
|||
<script type="text/javascript" src="scripts/app.js"></script> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,162 @@ |
|||
<?php |
|||
|
|||
//includes
|
|||
require_once "root.php"; |
|||
require_once "resources/require.php"; |
|||
require_once "resources/check_auth.php"; |
|||
|
|||
//check permissions
|
|||
if (permission_exists('webphone_view')) { |
|||
//access granted
|
|||
} |
|||
else { |
|||
echo "access denied"; |
|||
exit; |
|||
} |
|||
|
|||
//add multi-lingual support
|
|||
$language = new text; |
|||
$text = $language->get(); |
|||
|
|||
|
|||
if (is_uuid($_GET['id'])) { |
|||
$extension_uuid = $_GET['id']; |
|||
} |
|||
|
|||
//get the user ID
|
|||
$sql = "SELECT extension, password,effective_caller_id_name "; |
|||
$sql .= "FROM v_extensions "; |
|||
$sql .= "WHERE extension_uuid = '" . $extension_uuid . "' "; |
|||
$sql .= "AND v_extensions.domain_uuid = '" . $_SESSION["domain_uuid"] . "' LIMIT 1"; |
|||
|
|||
$prep_statement = $db->prepare($sql); |
|||
if ($prep_statement) { |
|||
$prep_statement->execute(); |
|||
$row = $prep_statement->fetch(PDO::FETCH_ASSOC); |
|||
$user_extension = $row['extension']; |
|||
$user_password = $row['password']; |
|||
$effective_caller_id_name = $row['effective_caller_id_name']; |
|||
} |
|||
|
|||
echo "<html lang='en'>\n"; |
|||
echo "<head>\n"; |
|||
echo " <meta charset='utf-8' />\n"; |
|||
echo " <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'>\n"; |
|||
echo " <title>ctxSip</title>\n"; |
|||
echo " <link rel='icon' type='image/gif' href='img/favicon.ico' />\n"; |
|||
echo " <link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css'>\n"; |
|||
echo " <link rel='stylesheet' href='//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css'>\n"; |
|||
echo " <link href='css/ctxSip.css' rel='stylesheet' type='text/css' />\n"; |
|||
echo "</head>\n"; |
|||
echo "<body id='sipClient'>\n"; |
|||
echo "<div class='container-fluid'>\n"; |
|||
|
|||
echo " <div class='clearfix sipStatus'>\n"; |
|||
echo " <div id='txtCallStatus' class='pull-right'> </div>\n"; |
|||
echo " <div id='txtRegStatus'></div>\n"; |
|||
echo " </div>\n"; |
|||
|
|||
echo " <div class='form-group' id='phoneUI'>\n"; |
|||
echo " <div class='input-group'>\n"; |
|||
echo " <div class='input-group-btn'>\n"; |
|||
echo " <button class='btn btn-sm btn-primary dropdown-toggle' data-toggle='dropdown' title='Show Keypad'>\n"; |
|||
echo " <i class='fa fa-th'></i>\n"; |
|||
echo " </button>\n"; |
|||
echo " <div id='sip-dialpad' class='dropdown-menu'>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='1'>1<span> </span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='2'>2<span>ABC</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='3'>3<span>DEF</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='4'>4<span>GHI</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='5'>5<span>JKL</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='6'>6<span>MNO</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='7'>7<span>PQRS</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='8'>8<span>TUV</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='9'>9<span>WXYZ</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='*'>*<span> </span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='0'>0<span>+</span></button>\n"; |
|||
echo " <button type='button' class='btn btn-default digit' data-digit='#'>#<span> </span></button>\n"; |
|||
echo " <div class='clearfix'> </div>\n"; |
|||
echo " <button class='btn btn-success btn-block btnCall' title='Send'>\n"; |
|||
echo " <i class='fa fa-play'></i> Send\n"; |
|||
echo " </button>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " <input type='text' name='number' id='numDisplay' class='form-control text-center input-sm' value='' placeholder='Enter number...' autocomplete='off' />\n"; |
|||
echo " <div class='input-group-btn input-group-btn-sm'>\n"; |
|||
echo " <button class='btn btn-sm btn-primary dropdown-toggle' id='btnVol' data-toggle='dropdown' title='Volume'>\n"; |
|||
echo " <i class='fa fa-fw fa-volume-up'></i>\n"; |
|||
echo " </button>\n"; |
|||
echo " <div class='dropdown-menu dropdown-menu-right'>\n"; |
|||
echo " <input type='range' min='0' max='100' value='100' step='1' id='sldVolume' />\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
|
|||
echo " <div class=well-sip'>\n"; |
|||
echo " <div id='sip-splash' class='text-muted text-center panel panel-default'>\n"; |
|||
echo " <div class='panel-body'>\n"; |
|||
echo " <h3 class='page-header'>\n"; |
|||
echo " <span class='fa-stack fa-2x'>\n"; |
|||
echo " <i class='fa fa-circle fa-stack-2x text-success'></i>\n"; |
|||
echo " <i class='fa fa-phone fa-stack-1x fa-inverse'></i>\n"; |
|||
echo " </span><br>\n"; |
|||
echo " This is your phone.</h3>\n"; |
|||
echo " <p class='lead'>To make a call enter a number in the box above.</p>\n"; |
|||
echo " <small>Closing this window will cause calls to go to voicemail.</small>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
|
|||
echo " <div id='sip-log' class='panel panel-default hide'>\n"; |
|||
echo " <div class='panel-heading'>\n"; |
|||
echo " <h4 class='text-muted panel-title'>Recent Calls <span class='pull-right'><i class='fa fa-trash text-muted sipLogClear' title='Clear Log'></i></span></h4>\n"; |
|||
echo " </div>\n"; |
|||
echo " <div id='sip-logitems' class='list-group'>\n"; |
|||
echo " <p class='text-muted text-center'>No recent calls from this browser.</p>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
|
|||
echo " <div class='modal fade' id='mdlError' tabindex='-1' role='dialog' aria-hidden='true' data-backdrop='static' data-keyboard='false'>\n"; |
|||
echo " <div class='modal-dialog modal-sm'>\n"; |
|||
echo " <div class='modal-content'>\n"; |
|||
echo " <div class='modal-header'>\n"; |
|||
echo " <h4 class='modal-title'>Sip Error</h4>\n"; |
|||
echo " </div>\n"; |
|||
echo " <div class='modal-body text-center text-danger'>\n"; |
|||
echo " <h3><i class='fa fa-3x fa-ban'></i></h3>\n"; |
|||
echo " <p class='lead'>Sip registration failed. No calls can be handled.</p>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
echo " </div>\n"; |
|||
|
|||
echo "</div>\n"; |
|||
|
|||
echo "<audio id='ringtone' src='sounds/incoming.mp3' loop></audio>\n"; |
|||
echo "<audio id='ringbacktone' src='sounds/outgoing.mp3' loop></audio>\n"; |
|||
echo "<audio id='dtmfTone' src='sounds/dtmf.mp3'></audio>\n"; |
|||
echo "<audio id='audioRemote'></audio>\n"; |
|||
|
|||
echo "<script type='text/javascript' src='https://code.jquery.com/jquery-1.11.3.min.js'></script>\n"; |
|||
echo "<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js'></script>\n"; |
|||
echo "<script type='text/javascript' src='scripts/moment.js/moment.min.js'></script>\n"; |
|||
|
|||
//echo "<script type='text/javascript' src='scripts/SIP.js/sip.min.js'></script>\n";
|
|||
echo "<script type='text/javascript' src='scripts/SIP.js/sip.js'></script>\n"; |
|||
//echo "<script type='text/javascript' src='scripts/config.js'></script>\n";
|
|||
|
|||
echo "<script type='text/javascript'>\n"; |
|||
echo "var user = {'User' : '" . $user_extension. "', "; |
|||
echo " 'Pass' : '".$user_password."', "; |
|||
echo " 'Realm' : '".$_SESSION["domain_name"]."', "; |
|||
echo " 'Display' : '".$effective_caller_id_name."', "; |
|||
echo " 'WSServer' : 'wss://".$_SESSION["domain_name"].":7443' "; |
|||
echo "};\n"; |
|||
echo "</script>\n"; |
|||
|
|||
echo "<script type='text/javascript' src='scripts/app.js'></script>\n"; |
|||
|
|||
echo "</body>\n"; |
|||
//</html>
|
|||
?>
|
@ -0,0 +1,90 @@ |
|||
<?php |
|||
/* |
|||
FusionPBX |
|||
Version: MPL 1.1 |
|||
|
|||
The contents of this file are subject to the Mozilla Public License Version |
|||
1.1 (the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
http://www.mozilla.org/MPL/ |
|||
|
|||
Software distributed under the License is distributed on an "AS IS" basis, |
|||
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
|||
for the specific language governing rights and limitations under the |
|||
License. |
|||
|
|||
The Original Code is FusionPBX |
|||
|
|||
The Initial Developer of the Original Code is |
|||
Mark J Crane <markjcrane@fusionpbx.com> |
|||
Portions created by the Initial Developer are Copyright (C) 2008-2012 |
|||
the Initial Developer. All Rights Reserved. |
|||
|
|||
Contributor(s): |
|||
Mark J Crane <markjcrane@fusionpbx.com> |
|||
*/ |
|||
|
|||
// make sure the PATH_SEPARATOR is defined
|
|||
umask(2); |
|||
if (!defined("PATH_SEPARATOR")) { |
|||
if (strpos($_ENV["OS"], "Win") !== false) { |
|||
define("PATH_SEPARATOR", ";"); |
|||
} else { |
|||
define("PATH_SEPARATOR", ":"); |
|||
} |
|||
} |
|||
|
|||
if (!isset($output_format)) $output_format = (PHP_SAPI == 'cli') ? 'text' : 'html'; |
|||
|
|||
// make sure the document_root is set
|
|||
$_SERVER["SCRIPT_FILENAME"] = str_replace("\\", '/', $_SERVER["SCRIPT_FILENAME"]); |
|||
if(PHP_SAPI == 'cli'){ |
|||
chdir(pathinfo(realpath($_SERVER["PHP_SELF"]), PATHINFO_DIRNAME)); |
|||
$script_full_path = str_replace("\\", '/', getcwd() . '/' . $_SERVER["SCRIPT_FILENAME"]); |
|||
$dirs = explode('/', pathinfo($script_full_path, PATHINFO_DIRNAME)); |
|||
if (file_exists('/project_root.php')) { |
|||
$path = '/'; |
|||
} else { |
|||
$i = 1; |
|||
$path = ''; |
|||
while ($i < count($dirs)) { |
|||
$path .= '/' . $dirs[$i]; |
|||
if (file_exists($path. '/project_root.php')) { |
|||
break; |
|||
} |
|||
$i++; |
|||
} |
|||
} |
|||
$_SERVER["DOCUMENT_ROOT"] = $path; |
|||
}else{ |
|||
$_SERVER["DOCUMENT_ROOT"] = str_replace($_SERVER["PHP_SELF"], "", $_SERVER["SCRIPT_FILENAME"]); |
|||
} |
|||
$_SERVER["DOCUMENT_ROOT"] = realpath($_SERVER["DOCUMENT_ROOT"]); |
|||
// try to detect if a project path is being used
|
|||
if (!defined('PROJECT_PATH')) { |
|||
if (is_dir($_SERVER["DOCUMENT_ROOT"]. '/fusionpbx')) { |
|||
define('PROJECT_PATH', '/fusionpbx'); |
|||
} elseif (file_exists($_SERVER["DOCUMENT_ROOT"]. '/project_root.php')) { |
|||
define('PROJECT_PATH', ''); |
|||
} else { |
|||
$dirs = explode('/', str_replace('\\', '/', pathinfo($_SERVER["PHP_SELF"], PATHINFO_DIRNAME))); |
|||
$i = 1; |
|||
$path = $_SERVER["DOCUMENT_ROOT"]; |
|||
while ($i < count($dirs)) { |
|||
$path .= '/' . $dirs[$i]; |
|||
if (file_exists($path. '/project_root.php')) { |
|||
break; |
|||
} |
|||
$i++; |
|||
} |
|||
if(!file_exists($path. '/project_root.php')){ |
|||
die("Failed to locate the Project Root by searching for project_root.php please contact support for assistance"); |
|||
} |
|||
$project_path = str_replace($_SERVER["DOCUMENT_ROOT"], "", $path); |
|||
define('PROJECT_PATH', $project_path); |
|||
} |
|||
$_SERVER["PROJECT_ROOT"] = realpath($_SERVER["DOCUMENT_ROOT"] . PROJECT_PATH); |
|||
set_include_path(get_include_path() . PATH_SEPARATOR . $_SERVER["PROJECT_ROOT"]); |
|||
} |
|||
|
|||
?>
|
21553
app/webphone/phone/scripts/SIP.js/sip.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
11860
app/webphone/phone/scripts/SIP.js/sip.js.0.7.8
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1
app/webphone/phone/scripts/SIP.js/sip.min.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
40
app/webphone/phone/scripts/SIP.js/sip.min.js.0.7.8
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,895 @@ |
|||
/* globals SIP,user,moment, Stopwatch */ |
|||
|
|||
var ctxSip; |
|||
|
|||
$(document).ready(function() { |
|||
|
|||
|
|||
if (typeof(user) === 'undefined') { |
|||
|
|||
|
|||
user = JSON.parse(localStorage.getItem('SIPCreds')); |
|||
} |
|||
ctxSip = { |
|||
|
|||
config : { |
|||
media : { |
|||
remote : { |
|||
video: document.getElementById('audioRemote'), |
|||
audio: document.getElementById('audioRemote') |
|||
} |
|||
}, |
|||
peerConnectionOptions : { |
|||
rtcConfiguration : { |
|||
iceServers : [{urls:"stun:stun.l.google.com:19302"}] |
|||
} |
|||
}, |
|||
password : user.Pass, |
|||
authorizationUser: user.User, |
|||
displayName : user.Display, |
|||
uri : user.User +'@'+user.Realm, |
|||
transportOptions: { |
|||
traceSip: true, |
|||
wsServers: user.WSServer, |
|||
}, |
|||
registerExpires : 30, |
|||
ua: {} |
|||
}, |
|||
ringtone : document.getElementById('ringtone'), |
|||
ringbacktone : document.getElementById('ringbacktone'), |
|||
dtmfTone : document.getElementById('dtmfTone'), |
|||
|
|||
Sessions : [], |
|||
callTimers : {}, |
|||
callActiveID : null, |
|||
callVolume : 1, |
|||
Stream : null, |
|||
|
|||
/** |
|||
* Parses a SIP uri and returns a formatted US phone number. |
|||
* |
|||
* @param {string} phone number or uri to format |
|||
* @return {string} formatted number |
|||
*/ |
|||
formatPhone : function(phone) { |
|||
|
|||
var num; |
|||
|
|||
if (phone.indexOf('@')) { |
|||
num = phone.split('@')[0]; |
|||
} else { |
|||
num = phone; |
|||
} |
|||
|
|||
num = num.toString().replace(/[^0-9]/g, ''); |
|||
|
|||
if (num.length === 10) { |
|||
return '(' + num.substr(0, 3) + ') ' + num.substr(3, 3) + '-' + num.substr(6,4); |
|||
} else if (num.length === 11) { |
|||
return '0' + num.substr(2); |
|||
} else { |
|||
return num; |
|||
} |
|||
}, |
|||
|
|||
// Sound methods
|
|||
startRingTone : function() { |
|||
try { ctxSip.ringtone.play(); } catch (e) { } |
|||
}, |
|||
|
|||
stopRingTone : function() { |
|||
try { ctxSip.ringtone.pause(); } catch (e) { } |
|||
}, |
|||
|
|||
startRingbackTone : function() { |
|||
try { ctxSip.ringbacktone.play(); } catch (e) { } |
|||
}, |
|||
|
|||
stopRingbackTone : function() { |
|||
try { ctxSip.ringbacktone.pause(); } catch (e) { } |
|||
}, |
|||
|
|||
// Genereates a rendom string to ID a call
|
|||
getUniqueID : function() { |
|||
return Math.random().toString(36).substr(2, 9); |
|||
}, |
|||
|
|||
newSession : function(newSess) { |
|||
|
|||
newSess.displayName = newSess.remoteIdentity.displayName || newSess.remoteIdentity.uri.user; |
|||
newSess.ctxid = ctxSip.getUniqueID(); |
|||
|
|||
var status; |
|||
|
|||
if (newSess.direction === 'incoming') { |
|||
status = "Incoming: "+ newSess.displayName; |
|||
ctxSip.startRingTone(); |
|||
|
|||
} else { |
|||
status = "Trying: "+ newSess.displayName; |
|||
ctxSip.startRingbackTone(); |
|||
} |
|||
|
|||
ctxSip.logCall(newSess, 'ringing'); |
|||
|
|||
ctxSip.setCallSessionStatus(status); |
|||
|
|||
// EVENT CALLBACKS
|
|||
|
|||
|
|||
var remoteVideo = document.getElementById('audioRemote'); |
|||
var localVideo = document.getElementById('audioRemote'); |
|||
|
|||
|
|||
|
|||
newSess.on('trackAdded', function() { |
|||
// We need to check the peer connection to determine which track was added
|
|||
|
|||
var pc = newSess.sessionDescriptionHandler.peerConnection; |
|||
|
|||
// Gets remote tracks
|
|||
var remoteStream = new MediaStream(); |
|||
pc.getReceivers().forEach(function(receiver) { |
|||
remoteStream.addTrack(receiver.track); |
|||
}); |
|||
remoteVideo.srcObject = remoteStream; |
|||
remoteVideo.play(); |
|||
|
|||
}); |
|||
|
|||
newSess.on('progress',function(e) { |
|||
if (e.direction === 'outgoing') { |
|||
ctxSip.setCallSessionStatus('Calling...'); |
|||
} |
|||
}); |
|||
|
|||
newSess.on('connecting',function(e) { |
|||
if (e.direction === 'outgoing') { |
|||
ctxSip.setCallSessionStatus('Connecting...'); |
|||
} |
|||
}); |
|||
|
|||
newSess.on('accepted',function(e) { |
|||
// If there is another active call, hold it
|
|||
if (ctxSip.callActiveID && ctxSip.callActiveID !== newSess.ctxid) { |
|||
ctxSip.phoneHoldButtonPressed(ctxSip.callActiveID); |
|||
} |
|||
|
|||
|
|||
ctxSip.stopRingbackTone(); |
|||
ctxSip.stopRingTone(); |
|||
ctxSip.setCallSessionStatus('Answered'); |
|||
ctxSip.logCall(newSess, 'answered'); |
|||
ctxSip.callActiveID = newSess.ctxid; |
|||
|
|||
}); |
|||
|
|||
newSess.on('hold', function(e) { |
|||
ctxSip.callActiveID = null; |
|||
ctxSip.logCall(newSess, 'holding'); |
|||
ctxSip.setCallSessionStatus("Holding"); |
|||
}); |
|||
|
|||
newSess.on('unhold', function(e) { |
|||
ctxSip.logCall(newSess, 'resumed'); |
|||
ctxSip.callActiveID = newSess.ctxid; |
|||
ctxSip.setCallSessionStatus("Answered"); |
|||
}); |
|||
|
|||
newSess.on('muted', function(e) { |
|||
ctxSip.Sessions[newSess.ctxid].isMuted = true; |
|||
ctxSip.setCallSessionStatus("Muted"); |
|||
}); |
|||
|
|||
newSess.on('unmuted', function(e) { |
|||
ctxSip.Sessions[newSess.ctxid].isMuted = false; |
|||
ctxSip.setCallSessionStatus("Answered"); |
|||
}); |
|||
|
|||
newSess.on('cancel', function(e) { |
|||
ctxSip.stopRingTone(); |
|||
ctxSip.stopRingbackTone(); |
|||
ctxSip.setCallSessionStatus("Canceled"); |
|||
if (this.direction === 'outgoing') { |
|||
ctxSip.callActiveID = null; |
|||
newSess = null; |
|||
ctxSip.logCall(this, 'ended'); |
|||
} |
|||
}); |
|||
|
|||
newSess.on('bye', function(e) { |
|||
ctxSip.stopRingTone(); |
|||
ctxSip.stopRingbackTone(); |
|||
ctxSip.setCallSessionStatus(""); |
|||
ctxSip.logCall(newSess, 'ended'); |
|||
ctxSip.callActiveID = null; |
|||
newSess = null; |
|||
}); |
|||
|
|||
newSess.on('failed',function(e) { |
|||
ctxSip.stopRingTone(); |
|||
ctxSip.stopRingbackTone(); |
|||
ctxSip.setCallSessionStatus('Terminated'); |
|||
}); |
|||
|
|||
newSess.on('rejected',function(e) { |
|||
ctxSip.stopRingTone(); |
|||
ctxSip.stopRingbackTone(); |
|||
ctxSip.setCallSessionStatus('Rejected'); |
|||
ctxSip.callActiveID = null; |
|||
ctxSip.logCall(this, 'ended'); |
|||
newSess = null; |
|||
}); |
|||
|
|||
|
|||
|
|||
ctxSip.Sessions[newSess.ctxid] = newSess; |
|||
|
|||
|
|||
}, |
|||
|
|||
// getUser media request refused or device was not present
|
|||
getUserMediaFailure : function(e) { |
|||
window.console.error('getUserMedia failed:', e); |
|||
ctxSip.setError(true, 'Media Error.', 'You must allow access to your microphone. Check the address bar.', true); |
|||
}, |
|||
|
|||
getUserMediaSuccess : function(stream) { |
|||
ctxSip.Stream = stream; |
|||
}, |
|||
|
|||
/** |
|||
* sets the ui call status field |
|||
* |
|||
* @param {string} status |
|||
*/ |
|||
setCallSessionStatus : function(status) { |
|||
$('#txtCallStatus').html(status); |
|||
}, |
|||
|
|||
/** |
|||
* sets the ui connection status field |
|||
* |
|||
* @param {string} status |
|||
*/ |
|||
setStatus : function(status) { |
|||
$("#txtRegStatus").html('<i class="fa fa-signal"></i> '+status); |
|||
}, |
|||
|
|||
/** |
|||
* logs a call to localstorage |
|||
* |
|||
* @param {object} session |
|||
* @param {string} status Enum 'ringing', 'answered', 'ended', 'holding', 'resumed' |
|||
*/ |
|||
logCall : function(session, status) { |
|||
|
|||
var log = { |
|||
clid : session.displayName, |
|||
uri : session.remoteIdentity.uri.toString(), |
|||
id : session.ctxid, |
|||
time : new Date().getTime() |
|||
}, |
|||
calllog = JSON.parse(localStorage.getItem('sipCalls')); |
|||
|
|||
if (!calllog) { calllog = {}; } |
|||
|
|||
if (!calllog.hasOwnProperty(session.ctxid)) { |
|||
calllog[log.id] = { |
|||
id : log.id, |
|||
clid : log.clid, |
|||
uri : log.uri, |
|||
start : log.time, |
|||
flow : session.direction |
|||
}; |
|||
} |
|||
|
|||
if (status === 'ended') { |
|||
calllog[log.id].stop = log.time; |
|||
} |
|||
|
|||
if (status === 'ended' && calllog[log.id].status === 'ringing') { |
|||
calllog[log.id].status = 'missed'; |
|||
} else { |
|||
calllog[log.id].status = status; |
|||
} |
|||
|
|||
localStorage.setItem('sipCalls', JSON.stringify(calllog)); |
|||
ctxSip.logShow(); |
|||
}, |
|||
|
|||
/** |
|||
* adds a ui item to the call log |
|||
* |
|||
* @param {object} item log item |
|||
*/ |
|||
logItem : function(item) { |
|||
|
|||
var callActive = (item.status !== 'ended' && item.status !== 'missed'), |
|||
callLength = (item.status !== 'ended')? '<span id="'+item.id+'"></span>': moment.duration(item.stop - item.start).humanize(), |
|||
callClass = '', |
|||
callIcon, |
|||
i; |
|||
|
|||
switch (item.status) { |
|||
case 'ringing' : |
|||
callClass = 'list-group-item-success'; |
|||
callIcon = 'fa-bell'; |
|||
break; |
|||
|
|||
case 'missed' : |
|||
callClass = 'list-group-item-danger'; |
|||
if (item.flow === "incoming") { callIcon = 'fa-chevron-left'; } |
|||
if (item.flow === "outgoing") { callIcon = 'fa-chevron-right'; } |
|||
break; |
|||
|
|||
case 'holding' : |
|||
callClass = 'list-group-item-warning'; |
|||
callIcon = 'fa-pause'; |
|||
break; |
|||
|
|||
case 'answered' : |
|||
case 'resumed' : |
|||
callClass = 'list-group-item-info'; |
|||
callIcon = 'fa-phone-square'; |
|||
break; |
|||
|
|||
case 'ended' : |
|||
if (item.flow === "incoming") { callIcon = 'fa-chevron-left'; } |
|||
if (item.flow === "outgoing") { callIcon = 'fa-chevron-right'; } |
|||
break; |
|||
} |
|||
|
|||
|
|||
i = '<div class="list-group-item sip-logitem clearfix '+callClass+'" data-uri="'+item.uri+'" data-sessionid="'+item.id+'" title="Double Click to Call">'; |
|||
i += '<div class="clearfix"><div class="pull-left">'; |
|||
i += '<i class="fa fa-fw '+callIcon+' fa-fw"></i> <strong>'+ctxSip.formatPhone(item.uri)+'</strong><br><small>'+moment(item.start).format('MM/DD hh:mm:ss a')+'</small>'; |
|||
i += '</div>'; |
|||
i += '<div class="pull-left text-left"><em>'+item.clid+'</em><br>' + callLength+'</div></div>'; |
|||
|
|||
if (callActive) { |
|||
i += '<div class="btn-group btn-group-xs pull-right">'; |
|||
if (item.status === 'ringing' && item.flow === 'incoming') { |
|||
i += '<button class="btn btn-xs btn-success btnCall" title="Call"><i class="fa fa-phone"></i></button>'; |
|||
} else { |
|||
i += '<button class="btn btn-xs btn-primary btnHoldResume" title="Hold"><i class="fa fa-pause"></i></button>'; |
|||
i += '<button class="btn btn-xs btn-info btnTransfer" title="Transfer"><i class="fa fa-random"></i></button>'; |
|||
i += '<button class="btn btn-xs btn-warning btnMute" title="Mute"><i class="fa fa-fw fa-microphone"></i></button>'; |
|||
} |
|||
i += '<button class="btn btn-xs btn-danger btnHangUp" title="Hangup"><i class="fa fa-stop"></i></button>'; |
|||
i += '</div>'; |
|||
} |
|||
i += '</div>'; |
|||
|
|||
$('#sip-logitems').append(i); |
|||
|
|||
|
|||
// Start call timer on answer
|
|||
if (item.status === 'answered') { |
|||
var tEle = document.getElementById(item.id); |
|||
ctxSip.callTimers[item.id] = new Stopwatch(tEle); |
|||
ctxSip.callTimers[item.id].start(); |
|||
} |
|||
|
|||
if (callActive && item.status !== 'ringing') { |
|||
ctxSip.callTimers[item.id].start({startTime : item.start}); |
|||
} |
|||
|
|||
$('#sip-logitems').scrollTop(0); |
|||
}, |
|||
|
|||
/** |
|||
* updates the call log ui |
|||
*/ |
|||
logShow : function() { |
|||
|
|||
var calllog = JSON.parse(localStorage.getItem('sipCalls')), |
|||
x = []; |
|||
|
|||
if (calllog !== null) { |
|||
|
|||
$('#sip-splash').addClass('hide'); |
|||
$('#sip-log').removeClass('hide'); |
|||
|
|||
// empty existing logs
|
|||
$('#sip-logitems').empty(); |
|||
|
|||
// JS doesn't guarantee property order so
|
|||
// create an array with the start time as
|
|||
// the key and sort by that.
|
|||
|
|||
// Add start time to array
|
|||
$.each(calllog, function(k,v) { |
|||
x.push(v); |
|||
}); |
|||
|
|||
// sort descending
|
|||
x.sort(function(a, b) { |
|||
return b.start - a.start; |
|||
}); |
|||
|
|||
$.each(x, function(k, v) { |
|||
ctxSip.logItem(v); |
|||
}); |
|||
|
|||
} else { |
|||
$('#sip-splash').removeClass('hide'); |
|||
$('#sip-log').addClass('hide'); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* removes log items from localstorage and updates the UI |
|||
*/ |
|||
logClear : function() { |
|||
|
|||
localStorage.removeItem('sipCalls'); |
|||
ctxSip.logShow(); |
|||
}, |
|||
|
|||
sipCall : function(target) { |
|||
|
|||
try { |
|||
var s = ctxSip.phone.invite(target, { |
|||
media : { |
|||
remote : { |
|||
video: document.getElementById('audioRemote'), |
|||
audio: document.getElementById('audioRemote') |
|||
} |
|||
}, |
|||
sessionDescriptionHandlerOptions : { |
|||
constraints : { audio : true, video : false }, |
|||
rtcConfiguration : { RTCConstraints : { "optional": [{ 'DtlsSrtpKeyAgreement': 'true'} ]}, stream : ctxSip.Stream } |
|||
|
|||
} |
|||
}); |
|||
s.direction = 'outgoing'; |
|||
ctxSip.newSession(s); |
|||
|
|||
var remoteVideo = document.getElementById('audioRemote'); |
|||
var localVideo = document.getElementById('audioRemote'); |
|||
|
|||
|
|||
|
|||
s.on('trackAdded', function() { |
|||
// We need to check the peer connection to determine which track was added
|
|||
|
|||
var pc = s.sessionDescriptionHandler.peerConnection; |
|||
|
|||
// Gets remote tracks
|
|||
var remoteStream = new MediaStream(); |
|||
pc.getReceivers().forEach(function(receiver) { |
|||
remoteStream.addTrack(receiver.track); |
|||
}); |
|||
remoteVideo.srcObject = remoteStream; |
|||
remoteVideo.play(); |
|||
}); |
|||
|
|||
|
|||
} catch(e) { |
|||
throw(e); |
|||
} |
|||
}, |
|||
|
|||
sipTransfer : function(sessionid) { |
|||
|
|||
var s = ctxSip.Sessions[sessionid], |
|||
target = window.prompt('Enter destination number', ''); |
|||
|
|||
ctxSip.setCallSessionStatus('<i>Transfering the call...</i>'); |
|||
s.refer(target); |
|||
}, |
|||
|
|||
sipHangUp : function(sessionid) { |
|||
|
|||
var s = ctxSip.Sessions[sessionid]; |
|||
// s.terminate();
|
|||
if (!s) { |
|||
return; |
|||
} else if (s.startTime) { |
|||
s.bye(); |
|||
} else if (s.reject) { |
|||
s.reject(); |
|||
} else if (s.cancel) { |
|||
s.cancel(); |
|||
} |
|||
|
|||
}, |
|||
|
|||
sipSendDTMF : function(digit) { |
|||
|
|||
try { ctxSip.dtmfTone.play(); } catch(e) { } |
|||
|
|||
var a = ctxSip.callActiveID; |
|||
if (a) { |
|||
var s = ctxSip.Sessions[a]; |
|||
s.dtmf(digit); |
|||
} |
|||
}, |
|||
|
|||
phoneCallButtonPressed : function(sessionid) { |
|||
|
|||
var s = ctxSip.Sessions[sessionid], |
|||
target = $("#numDisplay").val(); |
|||
|
|||
if (!s) { |
|||
|
|||
$("#numDisplay").val(""); |
|||
ctxSip.sipCall(target); |
|||
|
|||
} else if (s.accept && !s.startTime) { |
|||
|
|||
s.accept({ |
|||
media : { |
|||
remote : { |
|||
video: document.getElementById('audioRemote'), |
|||
audio: document.getElementById('audioRemote') |
|||
} |
|||
}, |
|||
sessionDescriptionHandlerOptions : { |
|||
constraints : { audio : true, video : false }, |
|||
rtcConfiguration : { RTCConstraints : { "optional": [{ 'DtlsSrtpKeyAgreement': 'true'} ]}, stream : ctxSip.Stream } |
|||
|
|||
} |
|||
}); |
|||
} |
|||
}, |
|||
|
|||
phoneMuteButtonPressed : function (sessionid) { |
|||
|
|||
var s = ctxSip.Sessions[sessionid]; |
|||
|
|||
if (!s.isMuted) { |
|||
s.mute(); |
|||
} else { |
|||
s.unmute(); |
|||
} |
|||
}, |
|||
|
|||
phoneHoldButtonPressed : function(sessionid) { |
|||
|
|||
var s = ctxSip.Sessions[sessionid]; |
|||
var direction = s.sessionDescriptionHandler.getDirection(); |
|||
|
|||
if (direction === 'sendrecv') { |
|||
console.log('Call is not on hold'); |
|||
s.hold(); |
|||
} else { |
|||
console.log('Call is on hold'); |
|||
s.unhold(); |
|||
} |
|||
}, |
|||
|
|||
|
|||
setError : function(err, title, msg, closable) { |
|||
|
|||
// Show modal if err = true
|
|||
if (err === true) { |
|||
$("#mdlError p").html(msg); |
|||
$("#mdlError").modal('show'); |
|||
|
|||
if (closable) { |
|||
var b = '<button type="button" class="close" data-dismiss="modal">×</button>'; |
|||
$("#mdlError .modal-header").find('button').remove(); |
|||
$("#mdlError .modal-header").prepend(b); |
|||
$("#mdlError .modal-title").html(title); |
|||
$("#mdlError").modal({ keyboard : true }); |
|||
} else { |
|||
$("#mdlError .modal-header").find('button').remove(); |
|||
$("#mdlError .modal-title").html(title); |
|||
$("#mdlError").modal({ keyboard : false }); |
|||
} |
|||
// $('#numDisplay').prop('disabled', 'disabled');
|
|||
} else { |
|||
$('#numDisplay').removeProp('disabled'); |
|||
$("#mdlError").modal('hide'); |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* Tests for a capable browser, return bool, and shows an |
|||
* error modal on fail. |
|||
*/ |
|||
hasWebRTC : function() { |
|||
|
|||
if (navigator.webkitGetUserMedia) { |
|||
return true; |
|||
} else if (navigator.mozGetUserMedia) { |
|||
return true; |
|||
} else if (navigator.getUserMedia) { |
|||
return true; |
|||
} else { |
|||
ctxSip.setError(true, 'Unsupported Browser.', 'Your browser does not support the features required for this phone.'); |
|||
window.console.error("WebRTC support not found"); |
|||
return false; |
|||
} |
|||
} |
|||
}; |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
// Throw an error if the browser can't hack it.
|
|||
if (!ctxSip.hasWebRTC()) { |
|||
return true; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
ctxSip.phone = new SIP.UA(ctxSip.config); |
|||
|
|||
|
|||
ctxSip.phone.on('connected', function(e) { |
|||
ctxSip.setStatus("Connected"); |
|||
ctxSip.setError(false) |
|||
}); |
|||
|
|||
ctxSip.phone.on('disconnected', function(e) { |
|||
ctxSip.setStatus("Disconnected"); |
|||
|
|||
// disable phone
|
|||
ctxSip.setError(true, 'Websocket Disconnected.', 'An Error occurred connecting to the websocket.'); |
|||
|
|||
}); |
|||
|
|||
ctxSip.phone.on('registered', function(e) { |
|||
ctxSip.setError(false) |
|||
var closeEditorWarning = function() { |
|||
return 'If you close this window, you will not be able to make or receive calls from your browser.'; |
|||
}; |
|||
|
|||
var closePhone = function() { |
|||
// stop the phone on unload
|
|||
localStorage.removeItem('ctxPhone'); |
|||
ctxSip.phone.stop(); |
|||
}; |
|||
|
|||
window.onbeforeunload = closeEditorWarning; |
|||
window.onunload = closePhone; |
|||
|
|||
// This key is set to prevent multiple windows.
|
|||
localStorage.setItem('ctxPhone', 'true'); |
|||
|
|||
$("#mldError").modal('hide'); |
|||
ctxSip.setStatus("Ready"); |
|||
|
|||
|
|||
// Get the userMedia and cache the stream
|
|||
// if (SIP.WebRTC.isSupported()) {
|
|||
// SIP.WebRTC.getUserMedia({ audio : true, video : false }, ctxSip.getUserMediaSuccess, ctxSip.getUserMediaFailure);
|
|||
// }
|
|||
}); |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
ctxSip.phone.on('registrationFailed', function(e) { |
|||
console.log(e) |
|||
ctxSip.setError(true, 'Registration Error.', 'An Error occurred registering your phone. Check your settings.'); |
|||
ctxSip.setStatus("Error: Registration Failed"); |
|||
}); |
|||
|
|||
ctxSip.phone.on('unregistered', function(e) { |
|||
ctxSip.setError(true, 'Registration Error.', 'An Error occurred registering your phone. Please wait while we try to connect it again.'); |
|||
ctxSip.setStatus("Error: Registration Failed"); |
|||
}); |
|||
|
|||
ctxSip.phone.on('invite', |
|||
|
|||
function (incomingSession) { |
|||
|
|||
var s = incomingSession; |
|||
|
|||
s.direction = 'incoming'; |
|||
ctxSip.newSession(s); |
|||
|
|||
|
|||
},); |
|||
|
|||
// Auto-focus number input on backspace.
|
|||
$('#sipClient').keydown(function(event) { |
|||
if (event.which === 8) { |
|||
$('#numDisplay').focus(); |
|||
} |
|||
}); |
|||
|
|||
$('#numDisplay').keypress(function(e) { |
|||
// Enter pressed? so Dial.
|
|||
if (e.which === 13) { |
|||
ctxSip.phoneCallButtonPressed(); |
|||
} |
|||
}); |
|||
|
|||
|
|||
$('#phoneIcon').click(function(eveny) { |
|||
// Enter pressed? so Dial.
|
|||
event.preventDefault(); |
|||
ctxSip.phoneCallButtonPressed(); |
|||
}); |
|||
|
|||
|
|||
$('.digit').click(function(event) { |
|||
event.preventDefault(); |
|||
var num = $('#numDisplay').val(), |
|||
dig = $(this).data('digit'); |
|||
|
|||
$('#numDisplay').val(num+dig); |
|||
|
|||
ctxSip.sipSendDTMF(dig); |
|||
return false; |
|||
}); |
|||
|
|||
$('#phoneUI .dropdown-menu').click(function(e) { |
|||
e.preventDefault(); |
|||
}); |
|||
|
|||
$('#phoneUI').delegate('.btnCall', 'click', function(event) { |
|||
ctxSip.phoneCallButtonPressed(); |
|||
// to close the dropdown
|
|||
return true; |
|||
}); |
|||
|
|||
$('.sipLogClear').click(function(event) { |
|||
event.preventDefault(); |
|||
ctxSip.logClear(); |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem .btnCall', 'click', function(event) { |
|||
var sessionid = $(this).closest('.sip-logitem').data('sessionid'); |
|||
ctxSip.phoneCallButtonPressed(sessionid); |
|||
return false; |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem .btnHoldResume', 'click', function(event) { |
|||
var sessionid = $(this).closest('.sip-logitem').data('sessionid'); |
|||
ctxSip.phoneHoldButtonPressed(sessionid); |
|||
return false; |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem .btnHangUp', 'click', function(event) { |
|||
var sessionid = $(this).closest('.sip-logitem').data('sessionid'); |
|||
ctxSip.sipHangUp(sessionid); |
|||
return false; |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem .btnTransfer', 'click', function(event) { |
|||
var sessionid = $(this).closest('.sip-logitem').data('sessionid'); |
|||
ctxSip.sipTransfer(sessionid); |
|||
return false; |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem .btnMute', 'click', function(event) { |
|||
var sessionid = $(this).closest('.sip-logitem').data('sessionid'); |
|||
ctxSip.phoneMuteButtonPressed(sessionid); |
|||
return false; |
|||
}); |
|||
|
|||
$('#sip-logitems').delegate('.sip-logitem', 'dblclick', function(event) { |
|||
event.preventDefault(); |
|||
|
|||
var uri = $(this).data('uri'); |
|||
$('#numDisplay').val(uri); |
|||
ctxSip.phoneCallButtonPressed(); |
|||
}); |
|||
|
|||
$('#sldVolume').on('change', function() { |
|||
|
|||
var v = $(this).val() / 100, |
|||
// player = $('audio').get()[0],
|
|||
btn = $('#btnVol'), |
|||
icon = $('#btnVol').find('i'), |
|||
active = ctxSip.callActiveID; |
|||
|
|||
// Set the object and media stream volumes
|
|||
if (ctxSip.Sessions[active]) { |
|||
ctxSip.Sessions[active].player.volume = v; |
|||
ctxSip.callVolume = v; |
|||
} |
|||
|
|||
// Set the others
|
|||
$('audio').each(function() { |
|||
$(this).get()[0].volume = v; |
|||
}); |
|||
|
|||
if (v < 0.1) { |
|||
btn.removeClass(function (index, css) { |
|||
return (css.match (/(^|\s)btn\S+/g) || []).join(' '); |
|||
}) |
|||
.addClass('btn btn-sm btn-danger'); |
|||
icon.removeClass().addClass('fa fa-fw fa-volume-off'); |
|||
} else if (v < 0.8) { |
|||
btn.removeClass(function (index, css) { |
|||
return (css.match (/(^|\s)btn\S+/g) || []).join(' '); |
|||
}).addClass('btn btn-sm btn-info'); |
|||
icon.removeClass().addClass('fa fa-fw fa-volume-down'); |
|||
} else { |
|||
btn.removeClass(function (index, css) { |
|||
return (css.match (/(^|\s)btn\S+/g) || []).join(' '); |
|||
}).addClass('btn btn-sm btn-primary'); |
|||
icon.removeClass().addClass('fa fa-fw fa-volume-up'); |
|||
} |
|||
return false; |
|||
}); |
|||
|
|||
// Hide the spalsh after 3 secs.
|
|||
setTimeout(function() { |
|||
ctxSip.logShow(); |
|||
} |