diff --git a/app.js b/app.js index f7ac1bd53..60a306387 100644 --- a/app.js +++ b/app.js @@ -8,6 +8,7 @@ var nickname = null; var sharedKey = ''; var recordingToken =''; var roomUrl = null; +var roomName = null; var ssrc2jid = {}; /** * The stats collector that process stats data and triggers updates to app.js. @@ -182,7 +183,9 @@ function doJoin() { } } - roomjid = roomnode + '@' + config.hosts.muc; + roomName = roomnode + '@' + config.hosts.muc; + + roomjid = roomName; if (config.useNicks) { var nick = window.prompt('Your nickname (optional)'); @@ -631,9 +634,15 @@ $(document).bind('joined.muc', function (event, jid, info) { focus.setEndpointDisplayName(connection.emuc.myroomjid, nickname); } + Toolbar.showSipCallButton(true); Toolbar.showRecordingButton(false); } + if (!focus) + { + Toolbar.showSipCallButton(false); + } + if (focus && config.etherpad_base) { Etherpad.init(); } @@ -714,6 +723,8 @@ $(document).bind('left.muc', function (event, jid) { nickname); } + Toolbar.showSipCallButton(true); + if (Object.keys(connection.emuc.members).length > 0) { focus.makeConference(Object.keys(connection.emuc.members)); Toolbar.showRecordingButton(true); @@ -730,6 +741,7 @@ $(document).bind('left.muc', function (event, jid) { focus.setEndpointDisplayName(connection.emuc.myroomjid, nickname); } + Toolbar.showSipCallButton(true); Toolbar.showRecordingButton(false); } if (connection.emuc.getPrezi(jid)) { @@ -781,7 +793,7 @@ $(document).bind('presence.muc', function (event, jid, info, pres) { VideoLayout.ensurePeerContainerExists(jid); VideoLayout.setDisplayName( 'participant_' + Strophe.getResourceFromJid(jid), - info.displayName); + info.displayName, info.status); } if (focus !== null && info.displayName !== null) { @@ -1279,3 +1291,32 @@ $(document).bind('fatalError.jingle', "Your browser version is too old. Please update and try again..."); } ); + +function callSipButtonClicked() +{ + $.prompt('

Enter SIP number

' + + '', + { + persistent: false, + buttons: { "Dial": true, "Cancel": false}, + defaultButton: 2, + loaded: function (event) + { + document.getElementById('sipNumber').focus(); + }, + submit: function (e, v, m, f) + { + if (v) + { + var numberInput = document.getElementById('sipNumber'); + if (numberInput.value) + { + connection.rayo.dial( + numberInput.value, 'fromnumber', roomName); + } + } + } + } + ); +} diff --git a/config.js b/config.js index 1074b03c7..f0fef9c2f 100644 --- a/config.js +++ b/config.js @@ -2,7 +2,8 @@ var config = { hosts: { domain: 'jitsi-meet.example.com', muc: 'conference.jitsi-meet.example.com', // FIXME: use XEP-0030 - bridge: 'jitsi-videobridge.jitsi-meet.example.com' // FIXME: use XEP-0030 + bridge: 'jitsi-videobridge.jitsi-meet.example.com', // FIXME: use XEP-0030 + //call_control: 'callcontrol.jitsi-meet.example.com' }, // getroomnode: function (path) { return 'someprefixpossiblybasedonpath'; }, // useStunTurn: true, // use XEP-0215 to fetch STUN and TURN server @@ -14,6 +15,7 @@ var config = { minChromeExtVersion: '0.1', // Required version of Chrome extension enableRtpStats: true, // Enables RTP stats processing openSctp: true, // Toggle to enable/disable SCTP channels + //defaultSipNumber: '20669', //Default SIP number used in call dialog // channelLastN: -1, // The default value of the channel attribute last-n. // useRtcpMux: true, enableRecording: false diff --git a/css/font.css b/css/font.css index 43bc19b47..b9ed14b00 100755 --- a/css/font.css +++ b/css/font.css @@ -71,6 +71,9 @@ .icon-share-doc:before { content: "\e605"; } +.icon-telephone:before { + content: "\e611"; +} .icon-security-locked:before { content: "\e607"; } diff --git a/css/videolayout_default.css b/css/videolayout_default.css index 3a33e0269..229ef6910 100644 --- a/css/videolayout_default.css +++ b/css/videolayout_default.css @@ -171,10 +171,31 @@ border-radius:20px; } +.videocontainer>span.status { + display: inline-block; + position: absolute; + color: #FFFFFF; + background: rgba(0,0,0,.7); + text-align: center; + text-overflow: ellipsis; + width: 70%; + height: 15%; + left: 15%; + bottom: 2%; + padding: 5px; + font-size: 10pt; + overflow: hidden; + white-space: nowrap; + z-index: 2; + border-radius:20px; +} + +#localVideoContainer>span.status:hover, #localVideoContainer>span.displayname:hover { cursor: text; } +.videocontainer>span.status, .videocontainer>span.displayname { pointer-events: none; } @@ -187,6 +208,7 @@ pointer-events: auto !important; } +.videocontainer>a.status, .videocontainer>a.displayname { display: inline-block; position: absolute; diff --git a/index.html b/index.html index 79d91363b..a86efcbd1 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,7 @@ + @@ -96,6 +97,11 @@ + +
+ + +
diff --git a/libs/rayo.js b/libs/rayo.js new file mode 100644 index 000000000..e036c6f9a --- /dev/null +++ b/libs/rayo.js @@ -0,0 +1,94 @@ +/* jshint -W117 */ +Strophe.addConnectionPlugin('rayo', + { + RAYO_XMLNS: 'urn:xmpp:rayo:1', + connection: null, + init: function (conn) + { + this.connection = conn; + if (this.connection.disco) + { + this.connection.disco.addFeature('urn:xmpp:rayo:client:1'); + } + + this.connection.addHandler( + this.onRayo.bind(this), this.RAYO_XMLNS, 'iq', 'set', null, null); + }, + onRayo: function (iq) + { + console.info("Rayo IQ", iq); + }, + dial: function (to, from, roomName) + { + var self = this; + var req = $iq( + { + type: 'set', + to: config.hosts.call_control + } + ); + req.c('dial', + { + xmlns: this.RAYO_XMLNS, + to: to, + from: from + }); + req.c('header', + { + name: 'JvbRoomName', + value: roomName + }); + + this.connection.sendIQ( + req, + function (result) + { + console.info('Dial result ', result); + + var resource = $(result).find('ref').attr('uri'); + this.call_resource = resource.substr('xmpp:'.length); + console.info( + "Received call resource: " + this.call_resource); + }, + function (error) + { + console.info('Dial error ', error); + } + ); + }, + hang_up: function () + { + if (!this.call_resource) + { + console.warn("No call in progress"); + return; + } + + var self = this; + var req = $iq( + { + type: 'set', + to: this.call_resource + } + ); + req.c('hangup', + { + xmlns: this.RAYO_XMLNS + }); + + this.connection.sendIQ( + req, + function (result) + { + console.info('Hangup result ', result); + self.call_resource = null; + }, + function (error) + { + console.info('Hangup error ', error); + self.call_resource = null; + } + ); + } + } +); \ No newline at end of file diff --git a/toolbar.js b/toolbar.js index 870dfa47b..c5e79a011 100644 --- a/toolbar.js +++ b/toolbar.js @@ -303,5 +303,18 @@ var Toolbar = (function (my) { $('#recordButton').toggleClass('active'); }; + // Shows or hides SIP calls button + my.showSipCallButton = function (show) + { + if (config.hosts.call_control && show) + { + $('#sipCallButton').css({display: "inline"}); + } + else + { + $('#sipCallButton').css({display: "none"}); + } + }; + return my; }(Toolbar || {})); diff --git a/videolayout.js b/videolayout.js index 0c94799f3..5665efa3f 100644 --- a/videolayout.js +++ b/videolayout.js @@ -323,11 +323,35 @@ var VideoLayout = (function (my) { /** * Shows the display name for the given video. */ - my.setDisplayName = function(videoSpanId, displayName) { + my.setDisplayName = function(videoSpanId, displayName, statusMsg) { var nameSpan = $('#' + videoSpanId + '>span.displayname'); var defaultLocalDisplayName = "Me"; var defaultRemoteDisplayName = "Speaker"; + var statusSpan = $('#' + videoSpanId + '>span.status'); + if (!statusSpan.length) + { + //Add status span + statusSpan = document.createElement('span'); + statusSpan.className = 'status'; + statusSpan.id = videoSpanId + '_status'; + $('#' + videoSpanId)[0].appendChild(statusSpan); + + statusSpan = $('#' + videoSpanId + '>span.status'); + } + + // Display status + if (statusMsg && statusMsg.length) + { + $('#' + videoSpanId + '_status').text(statusMsg); + statusSpan.get(0).setAttribute("style", "display:inline-block;"); + } + else + { + // Hide + statusSpan.get(0).setAttribute("style", "display:none;"); + } + // If we already have a display name for this video. if (nameSpan.length > 0) { var nameSpanElement = nameSpan.get(0);