You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ngcp-panel/share/templates/subscriber/webphone.tt

631 lines
27 KiB

[% site_config.title = c.loc('Web Phone for ') _ subscriber.username _ '@' _ subscriber.domain.domain -%]
<script type="text/javascript" src="/js/libs/jssip-0.3.0.min.js"></script>
<!--<script type="text/javascript" src="/js/libs/stanzaio.bundle.min.js"></script>-->
<script type="text/javascript" src="/js/libs/stanzaio.bundle.js"></script>
<script type="text/javascript" src="/js/libs/bootstrap-select.min.js"></script>
<script type="text/javascript" src="/js/libs/bootstrap-switch.js"></script>
<script type="text/javascript" src="/js/libs/jquery.slimscroll.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/bootstrap-select/bootstrap-select.min.css"/>
<link rel="stylesheet" type="text/css" href="/css/bootstrap-switch/bootstrap-switch.css"/>
<link rel="stylesheet" type="text/css" href="/font/font-awesome/css/font-awesome.min.css"/>
<style>
#xmpp-roster {
list-style-type: none;
}
#xmpp-roster .popover {
max-width: 100%;
min-height: 150px;
}
.xmpp-roster-entry-col1.available {
background: #080;
}
.xmpp-roster-entry-col1.unavailable {
background: #ddd;
}
.xmpp-roster-entry-col1.chat {
background: #0f0;
}
.xmpp-roster-entry-col1.away,
.xmpp-roster-entry-col1.xa {
background: #fa0;
}
.xmpp-roster-entry-col1.dnd {
background: #f00;
}
.xmpp-roster-entry-ctrl {
margin-right:10px;
cursor: pointer;
}
.switch-left.switch-mini, .switch-right.switch-mini {
/* make more space on left/right */
padding-left: 5px;
padding-right: 5px;
}
.has-switch {
margin-right: 10px;
}
.xmpp-chat-time {
font-style:italic;
color: #999;
}
.xmpp-chat-sent {
float: right;
clear: both;
margin-right: 15px;
margin-left: 30px;
text-align: right;
color: #333;
}
.xmpp-chat-recv {
float: left;
clear: both;
margin-left: 0px;
margin-right: 30px;
text-align: left;
color: #666;
}
.has-switch {
min-width: 200px;
}
</style>
<div class="row">
<span class="pull-left" style="margin:0 5px 0 5px;">
<a class="btn btn-primary btn-large" href="[% c.uri_for('/back') %]"><i class="icon-arrow-left"></i> [% c.loc('Back') %]</a>
</span>
</div>
[% back_created = 1 -%]
<div class="ngcp-separator"></div>
<script type="text/javascript">
var phone = null;
var chat = null;
var sip_configuration = null;
var xmpp_configuration = null;
var xmpp_last_state = 'available';
var orig_page_title = document.title;
var window_focus = true;
var window_timeout;
var xmpp_show_offline = false;
var xmpp_last_time = "";
$.ajax({
url: "[% c.uri_for_action('/subscriber/webphone_ajax', c.req.captures) %]"
}).done(function(data) {
sip_configuration = data.aaData.sip;
sip_configuration.register = true;
sip_configuration.trace_sip = true;
phone = new JsSIP.UA(sip_configuration);
// ws connection events
phone.on('connected', function(e){
console.log("connected");
$("#sip-status").html("[% c.loc('connected - registering...') %]");
});
phone.on('disconnected', function(e){
console.log("disconnected");
$("#sip-status").html("[% c.loc('disconnected.') %]");
});
// in/out call event
phone.on('newRTCSession', function(e){
//console.log("newRTCSession", e.originator, e.session);
console.log("newRTCSession", e);
var session = e.data.session;
if(session.direction == 'incoming') {
session.answer({
mediaConstraints: { audio: true, video: $('#sip_toggle_video').is(':checked') }
});
// TODO: create_incall_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
session.on('started', function(e) {
console.log("+++++++ session started");
var rtcSession = e.sender;
if(rtcSession.getLocalStreams().length > 0) {
selfView.src = window.URL.createObjectURL(rtcSession.getLocalStreams()[0]);
selfView.volume = 0;
}
if(rtcSession.getRemoteStreams().length > 0) {
remoteView.src = window.URL.createObjectURL(rtcSession.getRemoteStreams()[0]);
}
});
}
});
// in/out im event
phone.on('newMessage', function(e){
console.log("newMessage");
});
// registration events
phone.on('registered', function(e){
console.log("registered");
$("#sip-status").html("[% c.loc('registered.') %]");
});
phone.on('unregistered', function(e){
console.log("unregistered");
$("#sip-status").html("[% c.loc('unregistered.') %]");
});
phone.on('registrationFailed', function(e){
console.log("registrationFailed", e.data.response);
$("#sip-status").html("[% c.loc('registration failed:') %] " + e.data.response.status_code + " - " + e.data.response.reason_phrase);
});
phone.start();
xmpp_configuration = data.aaData.xmpp;
// chat client modifies it, so make a copy to have the original
// one later on re-connects
var tmp_xmpp_configuration = jQuery.extend(true, {}, xmpp_configuration);
chat = XMPP.createClient(tmp_xmpp_configuration);
register_chat_callbacks();
chat.connect();
});
function register_chat_callbacks() {
var timer = null;
chat.on('disconnected', function() {
console.log("xmpp disconnection");
$("#xmpp-status").html("[% c.loc('disconnected.') %]");
if(timer)
return 1;
console.log("prepare re-connect timer");
$('#xmpp-roster').empty();
var xmpp_last_state_tmp = xmpp_last_state;
$('#xmpp-pres').val('unavailable');
$('#xmpp-pres').change();
xmpp_last_state = xmpp_last_state_tmp;
timer = window.setInterval(function(){
console.log("perform re-connect");
window.clearInterval(timer);
timer = null;
chat.disconnect();
console.log("create new client", xmpp_configuration);
var tmp_xmpp_configuration = jQuery.extend(true, {}, xmpp_configuration);
chat = XMPP.createClient(tmp_xmpp_configuration);
register_chat_callbacks();
chat.connect();
}, 3000);
});
chat.on('chatState', function(obj) {
console.log("got chat state", obj);
var jid = obj.from.bare;
var msg;
if(obj.chatState == "composing") {
msg = "[% c.loc('is typing...') %]";
} else if(obj.chatState == "paused") {
msg = "[% c.loc('has stopped typing.') %]";
} else if(obj.chatState == "active") {
// chat session started/closed
return;
}
var jidid = jid.replace(/[^a-zA-Z0-9_]/g, '-');
var chat_win = $('#xmpp-roster #' + jidid).find('.popover');
if(!chat_win.length) {
chat_win = create_chat_window($('#xmpp-roster #' + jidid), jidid, jid);
}
$(chat_win).find('.xmpp-chat-history').append('<li><em>' + msg + '</em></li>')
.slimScroll({ scrollBy: '50px' });
});
chat.on('chat', function(obj) {
console.log("got message ", obj);
var jid = obj.from.bare;
var jidid = jid.replace(/[^a-zA-Z0-9_]/g, '-');
var chat_win = $('#xmpp-roster #' + jidid).find('.popover');
if(!chat_win.length) {
chat_win = create_chat_window($('#xmpp-roster #' + jidid), jidid, jid);
}
var now = get_time_string();
if(now != xmpp_last_time) {
xmpp_last_time = now;
$(chat_win).find('.xmpp-chat-history').append('<li><span class="xmpp-chat-recv"><span class="xmpp-chat-time">' + now + '</span></span></li>')
}
$(chat_win).find('.xmpp-chat-history').append('<li><span class="xmpp-chat-recv">' + obj.body + '</span></li>')
.slimScroll({ scrollBy: '50px' });
raise_attention('chat');
});
chat.on('groupchat', function(message) {
console.log("got group message ", message);
});
chat.on('session:started', function() {
$("#xmpp-status").html("[% c.loc('online.') %]");
console.log("++++++++++++++++++ session started");
chat.enableCarbons();
chat.getRoster(function(err, resp) {
console.log(err, resp);
chat.sendPresence();
if(err == null) {
$.each(resp.roster.items, function(index, item) {
console.log(item.name);
var jidid = item.jid.bare.replace(/[^a-zA-Z0-9_]/g, '-');
var entry = create_xmpp_entry_dom(jidid, item.jid.bare, item.name || item.jid.bare);
$('#xmpp-roster').append(entry);
$('#xmpp-roster li').sort(roster_asc_sort).appendTo('#xmpp-roster');
equalHeights($('#' + jidid).find('.xmpp-roster-entry-col1'), $('#' + jidid).find('.xmpp-roster-entry-col2'));
$('#' + jidid)
.mouseenter(function(obj) {
$(obj.currentTarget).find('.xmpp-roster-entry-col3').show();
})
.mouseleave(function(obj) {
$(obj.currentTarget).find('.xmpp-roster-entry-col3').hide();
})
.find('[rel="tooltip"]').tooltip({'html': false});
$('#' + jidid).find('.xmpp-roster-entry-ctrl-chat').click(function(obj) {
console.log("start chat");
create_chat_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
});
$('#' + jidid).find('.xmpp-roster-entry-ctrl-phone').click(function(obj) {
console.log("start chat");
create_outcall_window($(obj.target).parents("li.xmpp-roster-entry"), jidid, item.jid.bare);
var session = call(item.jid.bare);
});
// we get a presence callback where we'll show it
$(entry).hide();
});
}
$('#xmpp-pres').val(xmpp_last_state);
$('#xmpp-pres').change();
});
});
chat.on('presence', function(pres) {
if(pres.from.bare == xmpp_configuration.jid) {
console.log("skip own presence info");
return 1;
}
var type = pres.type || 'available';
var show;
if(type == 'available') {
show = pres.show || 'available';
} else {
show = pres.show || 'unavailable';
}
var jidid = pres.from.bare.replace(/[^a-zA-Z0-9_]/g, '-');
$("#xmpp-roster #" + jidid + " .xmpp-roster-entry-col1").removeClass().addClass("xmpp-roster-entry-col1 " + show);
console.log("+++++++ type=" + pres.type + ", show=" + pres.show);
if(show != 'unavailable' || xmpp_show_offline) {
$("#xmpp-roster #" + jidid).show();
} else {
$("#xmpp-roster #" + jidid).hide();
}
});
}
function call(dest_uri) {
var eventHandlers = {
'progress': function(e) {
console.log("call in progress", e);
$("#sip-status").html("[% c.loc('in progress...') %]");
},
'failed': function(e) {
console.log("call failed");
$("#sip-status").html("[% c.loc('call failed:') %] ", e);
},
'started': function(e) {
console.log("call started");
$("#sip-status").html("[% c.loc('call started.') %]");
var rtcSession = e.sender;
if (rtcSession.getLocalStreams().length > 0) {
selfView.src = window.URL.createObjectURL(rtcSession.getLocalStreams()[0]);
}
if (rtcSession.getRemoteStreams().length > 0) {
remoteView.src = window.URL.createObjectURL(rtcSession.getRemoteStreams()[0]);
}
},
'ended': function(e){
console.log("call ended");
$("#sip-status").html("[% c.loc('call ended.') %]");
}
};
var options = {
'eventHandlers': eventHandlers,
'extraHeaders': [ 'X-Foo: foo', 'X-Bar: bar' ],
'mediaConstraints': { 'audio': true, 'video': $('#sip_toggle_video').is(':checked') }
};
phone.call('sip:' + dest_uri, options);
}
function roster_asc_sort(a, b) {
return ($(b).find('.xmpp-roster-entry-name').text().toLowerCase()) < ($(a).find('.xmpp-roster-entry-name').text().toLowerCase()) ? 1 : -1;
}
function get_time_string() {
var now = new Date();
//return ((now.getHours() < 10)?"0":"") + now.getHours() +":"+ ((now.getMinutes() < 10)?"0":"") + now.getMinutes() +":"+ ((now.getSeconds() < 10)?"0":"") + now.getSeconds();
return ((now.getHours() < 10)?"0":"") + now.getHours() +":"+ ((now.getMinutes() < 10)?"0":"") + now.getMinutes();
}
function create_xmpp_entry_dom(jidid, jid, name) {
var entry =
'<li id="' + jidid + '" class="xmpp-roster-entry row span6" style="clear:both; float:left; padding:0; margin:1px; background:#f0f0f0;">' +
' <div class="xmpp-roster-entry-col1 unavailable" style="float:left; width:10px; padding:0; margin:0; ">&nbsp;</div>' +
' <div class="xmpp-roster-entry-col2 span3" style="float:left; padding:20px; margin:0;">' +
' <div>' +
' <span class="xmpp-roster-entry-name" style="font-size:1.3em; font-weight:bold;">' + name + '</span>' +
' </div>' +
' <div>' +
' <span class="xmpp-roster-entry-details" style="font-size:1em; font-weight:normal;">' + jid + '</span>' +
' </div>' +
' </div>' +
' <div class="xmpp-roster-entry-col3" style="float:right; padding:20px 0 20px 0; margin:0; display:none;">' +
' <div style="float:right; font-size:1.5em">' +
' <span class="fa fa-comment xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-chat" rel="tooltip" title="Start Chat"></span>' +
' <span class="fa fa-phone xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-phone" rel="tooltip" title="Make Call"></span>' +
' <span class="fa fa-file-text xmpp-roster-entry-ctrl xmpp-roster-entry-ctrl-fax" rel="tooltip" title="Send Fax"></span>' +
' </div>' +
' </div>' +
'</li>';
return entry;
}
function create_chat_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><ul class="xmpp-chat-history" style="list-style-type:none; margin:0; min-height:100px;"></ul><input data-jid="'+ jid + '" type="text" class="xmpp-chat-input" style="width:100%; margin:10px 0 0 0;"/></div>'
});
$(parent).popover("show");
$(parent).find(".xmpp-chat-input").focus();
$(parent).find(".xmpp-chat-history").slimScroll({
height: '100px',
railVisible: true,
alwaysVisible: true,
start: 'bottom'
});
return $(parent).find(".popover");
}
function create_incall_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">[% c.loc('Call from') %] ' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><button class="sip-accept-call" style="margin-right:20px;">[% c.loc('Accept') %]</button><button class="sip-reject-call">[% c.loc('Reject') %]</button></div>'
});
$(parent).popover("show");
$(parent).find(".sip-accept-call").click(function(){
console.log("+++++++++++ answering call");
/*
var session = ???;
session.answer({
mediaConstraints: { audio: true, video: $('#sip_toggle_video').is(':checked') }
});
*/
});
$(parent).find(".sip-reject-call").click(function(){
console.log("+++++++++++ rejecting call");
// var session = ???; session.terminate({ status_code: 486 });
});
return $(parent).find(".popover");
}
function create_outcall_window(parent, jidid, jid) {
$(parent).popover("destroy");
$(parent).popover({
placement: 'right',
html: true,
container: '#' + jidid,
trigger: 'manual',
title: '<div>&nbsp;<span class="pull-left">[% c.loc('Calling') %] ' + jid + '</span><span class="xmpp-chat-close pull-right fa fa-times"></span></div>',
content: '<div class="span4" style="margin:10px;"><button class="sip-stop-call" style="margin-right:20px;">[% c.loc('Terminate Call') %]</button></div>'
});
$(parent).popover("show");
$(parent).find(".sip-stop-call").click(function(){
console.log("+++++++++++ stopping call");
/*
var session = ???;
session.terminate();
*/
});
return $(parent).find(".popover");
}
function equalHeights (element1, element2) {
var height;
if (element1.outerHeight() > element2.outerHeight())
{
height = element1.outerHeight();
element2.css('height', height);
}
else {
height = element2.outerHeight();
element1.css('height', height);
}
}
function raise_attention(type) {
if(!window_focus) {
if(type == "chat") {
flash_title('[% c.loc('NEW MESSAGE') %]');
} else {
flash_title(type + '! ' + orig_page_title);
}
}
}
function flash_title(title) {
function step() {
document.title = (document.title == orig_page_title) ? title : orig_page_title;
window_timeout = setTimeout(step, 800);
};
cancel_flash_title(window_timeout);
step();
};
function cancel_flash_title() {
clearTimeout(window_timeout);
document.title = orig_page_title;
};
$(function() {
$('.selectpicker').selectpicker();
$('#xmpp-toggle-offline').bootstrapSwitch();
$('#xmpp-toggle-offline').bootstrapSwitch('setSizeClass', 'switch-mini');
$('#xmpp-toggle-offline').on('switch-change', function(obj) {
if(obj.currentTarget.checked) {
console.log("show offline entries");
xmpp_show_offline = true;
$('.xmpp-roster-entry').show();
} else {
console.log("hide offline entries");
xmpp_show_offline = false;
$('.xmpp-roster-entry-col1.unavailable').parent().hide();
}
});
$('#sip_toggle_video').bootstrapSwitch();
$('#sip_toggle_video').bootstrapSwitch('setSizeClass', 'switch-mini');
$('#xmpp-pres').change(function(obj) {
var show = obj.currentTarget[obj.currentTarget.selectedIndex].value;
xmpp_last_state = show;
var type = (show == "unavailable" ? "unavailable" : "available");
console.log("changing xmpp presence status, show=" + show + ", type=" + type);
if(show == "available") {
chat.sendPresence();
} else {
chat.sendPresence({ type: type, show: show });
}
});
$(document).keypress(function(obj) {
if($(obj.target).hasClass("xmpp-chat-input")) {
// return esc and tab
if (obj.which == 9 || obj.which == 27) {
return false;
}
if(obj.which == 13 && $(obj.target).val().length) {
obj.preventDefault();
chat.sendMessage({ to: $(obj.target).data("jid"), body: $(obj.target).val() });
var chat_win = $(obj.target).parent().find(".xmpp-chat-history");
var now = get_time_string();
if(now != xmpp_last_time) {
xmpp_last_time = now;
$(chat_win).append('<li><span class="xmpp-chat-sent"><span class="xmpp-chat-time">' + now + '</span></span></li>')
}
$(chat_win).append('<li><span class="xmpp-chat-sent">' + $(obj.target).val() + '</span></li>')
.slimScroll({ scrollBy: '50px' });
$(obj.target).val("");
} else {
// TODO: send chatState message somehow
}
}
});
$(document).click(function(obj) {
if($(obj.target).hasClass("xmpp-chat-close")) {
console.log("hiding chat window");
$(obj.target).parents("li.xmpp-roster-entry").popover("destroy");
$(obj.target).parents(".popover").remove();
}
});
$(window).focus(function() {
window_focus = true;
document.title = orig_page_title;
clearTimeout(window_timeout);
}).blur(function() {
window_focus = false;
});
window.onbeforeunload = function(obj) {
if(phone.isRegistered()) {
console.log("++++ unregistering phone before leaving");
phone.unregister({'all': true});
}
};
$('#xmpp-buddy-add').click(function(obj) {
var jid = $('#xmpp-buddy-add-jid').val();
console.log(">>>>>>>>> adding jid " + jid);
chat.subscribe(jid);
});
});
</script>
<div class="row">
<div class="span6">[% c.loc('Phone Status:') %] <span id="sip-status">[% c.loc('connecting...') %]</span></div>
</div>
<div class="row">
<div class="span6">[% c.loc('Chat Status:') %] <span id="xmpp-status">[% c.loc('connecting...') %]</span></div>
</div>
<div>
<h3>[% c.loc('Buddy List') %]</h3>
<div class="row span6" style="margin:0; clear:both; padding:10px;">
<input id="xmpp-toggle-offline" style="float:left;" type="checkbox" data-on="success" data-off="default" data-on-label="[% c.loc('Show Offline') %]" data-off-label="[% c.loc('Hide Offline') %]">
<input id="sip_toggle_video" checked style="float:left" type="checkbox" data-on="success" data-off="default" data-on-label="[% c.loc('Audio&amp;Video') %]" data-off-label="[% c.loc('Audio Only') %]">
</div>
<div class="span6" style="margin:0; clear:both; padding:0;">
<div class="span4" style="margin:0;">
<input type="text" id="xmpp-buddy-add-jid" class="span4"/>
</div>
<div class="span2" style="margin:0; float:right;">
<button class="btn btn-primary btn-medium" id="xmpp-buddy-add"><i class="icon-plus"></i> Add Buddy</button>
</div>
</div>
<div class="row span6" style="margin:0; clear:both;">
<select id="xmpp-pres" class="selectpicker span6">
[% FOR opt IN
[
{ n = "unavailable", d = c.loc('Offline') },
{ n = "available", d = c.loc('Available') },
{ n = "away", d = c.loc('Away') },
{ n = "xa", d = c.loc('Extended Away') },
{ n = "dnd", d = c.loc('Do Not Disturb') },
]
-%]
<option value="[% opt.n %]" data-content="<span class='xmpp-roster-entry-col1 [% opt.n %]'>&nbsp;</span><span> [% opt.d %]</span>">[% opt.d %]</option>
[% END -%]
</select>
</div>
<ul id="xmpp-roster" class="span8" style="list-style-type:none; padding:0; margin:0;">
</ul>
</div>
<video id="selfView" autoplay hidden=true></video>
<video id="remoteView" autoplay hidden=true></video>
[% # vim: set tabstop=4 syntax=html expandtab: -%]