Merge remote-tracking branch 'upstream/master'

This commit is contained in:
turint 2014-07-18 10:53:41 +03:00
commit f90c277a49
11 changed files with 351 additions and 124 deletions

2
app.js
View File

@ -562,7 +562,7 @@ $(document).bind('callactive.jingle', function (event, videoelem, sid) {
// Update the large video to the last added video only if there's no // Update the large video to the last added video only if there's no
// current active or focused speaker. // current active or focused speaker.
if (!focusedVideoSrc && !VideoLayout.getActiveSpeakerResourceJid()) if (!focusedVideoSrc && !VideoLayout.getDominantSpeakerResourceJid())
VideoLayout.updateLargeVideo(videoelem.attr('src'), 1); VideoLayout.updateLargeVideo(videoelem.attr('src'), 1);
VideoLayout.showFocusIndicator(); VideoLayout.showFocusIndicator();

33
chat.js
View File

@ -115,8 +115,7 @@ var Chat = (function (my) {
+ '</div>'); + '</div>');
$('#chatconversation').animate( $('#chatconversation').animate(
{ scrollTop: $('#chatconversation')[0].scrollHeight}, 1000); { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
};
}
/** /**
* Sets the subject to the UI * Sets the subject to the UI
@ -135,8 +134,7 @@ var Chat = (function (my) {
{ {
$("#subject").css({display: "block"}); $("#subject").css({display: "block"});
} }
} };
/** /**
* Opens / closes the chat area. * Opens / closes the chat area.
@ -159,6 +157,10 @@ var Chat = (function (my) {
var horizontalIndent = videoPosition[0]; var horizontalIndent = videoPosition[0];
var verticalIndent = videoPosition[1]; var verticalIndent = videoPosition[1];
var thumbnailSize = VideoLayout.calculateThumbnailSize(videospaceWidth);
var thumbnailsWidth = thumbnailSize[0];
var thumbnailsHeight = thumbnailSize[1];
if (chatspace.is(":visible")) { if (chatspace.is(":visible")) {
videospace.animate({right: chatSize[0], videospace.animate({right: chatSize[0],
width: videospaceWidth, width: videospaceWidth,
@ -166,6 +168,15 @@ var Chat = (function (my) {
{queue: false, {queue: false,
duration: 500}); duration: 500});
$('#remoteVideos').animate({height: thumbnailsHeight},
{queue: false,
duration: 500});
$('#remoteVideos>span').animate({height: thumbnailsHeight,
width: thumbnailsWidth},
{queue: false,
duration: 500});
$('#largeVideoContainer').animate({ width: videospaceWidth, $('#largeVideoContainer').animate({ width: videospaceWidth,
height: videospaceHeight}, height: videospaceHeight},
{queue: false, {queue: false,
@ -187,6 +198,9 @@ var Chat = (function (my) {
duration: 500}); duration: 500});
} }
else { else {
// Undock the toolbar when the chat is shown.
Toolbar.dockToolbar(false);
videospace.animate({right: chatSize[0], videospace.animate({right: chatSize[0],
width: videospaceWidth, width: videospaceWidth,
height: videospaceHeight}, height: videospaceHeight},
@ -198,6 +212,15 @@ var Chat = (function (my) {
} }
}); });
$('#remoteVideos').animate({height: thumbnailsHeight},
{queue: false,
duration: 500});
$('#remoteVideos>span').animate({height: thumbnailsHeight,
width: thumbnailsWidth},
{queue: false,
duration: 500});
$('#largeVideoContainer').animate({ width: videospaceWidth, $('#largeVideoContainer').animate({ width: videospaceWidth,
height: videospaceHeight}, height: videospaceHeight},
{queue: false, {queue: false,
@ -290,7 +313,7 @@ var Chat = (function (my) {
if (unreadMessages) { if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString(); unreadMsgElement.innerHTML = unreadMessages.toString();
Toolbar.showToolbar(); Toolbar.dockToolbar(true);
var chatButtonElement var chatButtonElement
= document.getElementById('chatButton').parentNode; = document.getElementById('chatButton').parentNode;

View File

@ -13,6 +13,7 @@ var config = {
chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
minChromeExtVersion: '0.1', // Required version of Chrome extension minChromeExtVersion: '0.1', // Required version of Chrome extension
enableRtpStats: false, // Enables RTP stats processing enableRtpStats: false, // Enables RTP stats processing
openSctp: true, //Toggle to enable/disable SCTP channels openSctp: true, // Toggle to enable/disable SCTP channels
// channelLastN: -1, // The default value of the channel attribute last-n.
enableRecording: false enableRecording: false
}; };

View File

@ -131,15 +131,24 @@ html, body{
} }
#chatButton { #chatButton {
-webkit-transition: all .5s ease-in-out;; -webkit-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;; -moz-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;; transition: all .5s ease-in-out;
} }
/*#ffde00*/
#chatButton.active { #chatButton.active {
-webkit-text-shadow: 0 0 10px #ffffff; -webkit-text-shadow: -1px 0 10px #00ccff,
-moz-text-shadow: 0 0 10px #ffffff; 0 1px 10px #00ccff,
text-shadow: 0 0 10px #ffffff; 1px 0 10px #00ccff,
0 -1px 10px #00ccff;
-moz-text-shadow: 1px 0 10px #00ccff,
0 1px 10px #00ccff,
1px 0 10px #00ccff,
0 -1px 10px #00ccff;
text-shadow: -1px 0 10px #00ccff,
0 1px 10px #00ccff,
1px 0 10px #00ccff,
0 -1px 10px #00ccff;
} }
a.button:hover { a.button:hover {

View File

@ -94,7 +94,7 @@
height: 100%; height: 100%;
} }
.activespeaker { .dominantspeaker {
background: #000 !important; background: #000 !important;
} }

View File

@ -10,7 +10,7 @@ function onDataChannel(event)
dataChannel.onopen = function () dataChannel.onopen = function ()
{ {
console.info("Data channel opened by the bridge !!!", dataChannel); console.info("Data channel opened by the Videobridge!", dataChannel);
// Code sample for sending string and/or binary data // Code sample for sending string and/or binary data
// Sends String message to the bridge // Sends String message to the bridge
@ -26,18 +26,56 @@ function onDataChannel(event)
dataChannel.onmessage = function (event) dataChannel.onmessage = function (event)
{ {
var msgData = event.data; var data = event.data;
console.info("Got Data Channel Message:", msgData, dataChannel); // JSON
var obj;
// Active speaker event try
if (msgData.indexOf('activeSpeaker') === 0)
{ {
// Endpoint ID from the bridge obj = JSON.parse(data);
var resourceJid = msgData.split(":")[1]; }
catch (e)
{
console.error(
"Failed to parse data channel message as JSON: ",
data,
dataChannel);
}
if (('undefined' !== typeof(obj)) && (null !== obj))
{
var colibriClass = obj.colibriClass;
console.info( if ("DominantSpeakerEndpointChangeEvent" === colibriClass)
"Data channel new active speaker event: " + resourceJid); {
$(document).trigger('activespeakerchanged', [resourceJid]); // Endpoint ID from the Videobridge.
var dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint;
console.info(
"Data channel new dominant speaker event: ",
dominantSpeakerEndpoint);
$(document).trigger(
'dominantspeakerchanged',
[dominantSpeakerEndpoint]);
}
else if ("LastNEndpointsChangeEvent" === colibriClass)
{
// The new/latest list of last-n endpoint IDs.
var lastNEndpoints = obj.lastNEndpoints;
/*
* The list of endpoint IDs which are entering the list of
* last-n at this time i.e. were not in the old list of last-n
* endpoint IDs.
*/
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
console.debug(
"Data channel new last-n event: ",
lastNEndpoints);
}
else
{
console.debug("Data channel JSON-formatted message: ", obj);
}
} }
}; };
@ -78,3 +116,4 @@ function bindDataChannelListener(peerConnection)
console.info("Got My Data Channel Message:", msgData, dataChannel); console.info("Got My Data Channel Message:", msgData, dataChannel);
};*/ };*/
} }

View File

@ -27,9 +27,9 @@
<script src="estos_log.js?v=2"></script><!-- simple stanza logger --> <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
<script src="desktopsharing.js?v=2"></script><!-- desktop sharing --> <script src="desktopsharing.js?v=2"></script><!-- desktop sharing -->
<script src="data_channels.js?v=2"></script><!-- data channels --> <script src="data_channels.js?v=2"></script><!-- data channels -->
<script src="app.js?v=29"></script><!-- application logic --> <script src="app.js?v=3"></script><!-- application logic -->
<script src="commands.js?v=1"></script><!-- application logic --> <script src="commands.js?v=1"></script><!-- application logic -->
<script src="chat.js?v=6"></script><!-- chat logic --> <script src="chat.js?v=7"></script><!-- chat logic -->
<script src="util.js?v=5"></script><!-- utility functions --> <script src="util.js?v=5"></script><!-- utility functions -->
<script src="etherpad.js?v=8"></script><!-- etherpad plugin --> <script src="etherpad.js?v=8"></script><!-- etherpad plugin -->
<script src="prezi.js?v=4"></script><!-- prezi plugin --> <script src="prezi.js?v=4"></script><!-- prezi plugin -->
@ -39,11 +39,11 @@
<script src="analytics.js?v=1"></script><!-- google analytics plugin --> <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<script src="rtp_stats.js?v=1"></script><!-- RTP stats processing --> <script src="rtp_stats.js?v=1"></script><!-- RTP stats processing -->
<script src="local_stats.js?v=1"></script><!-- Local stats processing --> <script src="local_stats.js?v=1"></script><!-- Local stats processing -->
<script src="videolayout.js?v=4"></script><!-- video ui --> <script src="videolayout.js?v=6"></script><!-- video ui -->
<script src="toolbar.js?v=2"></script><!-- toolbar ui --> <script src="toolbar.js?v=3"></script><!-- toolbar ui -->
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="css/font.css"/> <link rel="stylesheet" href="css/font.css"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=21"/> <link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=22"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=7" id="videolayout_default"/> <link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=7" id="videolayout_default"/>
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4"> <link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
<link rel="stylesheet" href="css/modaldialog.css?v=3"> <link rel="stylesheet" href="css/modaldialog.css?v=3">
@ -123,7 +123,7 @@
<!--<video id="localVideo" autoplay oncontextmenu="return false;" muted></video> - is now per stream generated --> <!--<video id="localVideo" autoplay oncontextmenu="return false;" muted></video> - is now per stream generated -->
</span> </span>
<audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio> <audio id="localAudio" autoplay oncontextmenu="return false;" muted></audio>
<span class="focusindicator" data-content="The owner of&#10;this conference" data-toggle="popover" data-placement="top"></span> <span class="focusindicator"></span>
</span> </span>
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio> <audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio> <audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>

View File

@ -54,17 +54,22 @@ function ColibriFocus(connection, bridgejid) {
* Default channel expire value in seconds. * Default channel expire value in seconds.
* @type {number} * @type {number}
*/ */
this.channelExpire = 60; this.channelExpire
= ('number' === typeof(config.channelExpire))
? config.channelExpire
: 15;
/**
* Default channel last-n value.
* @type {number}
*/
this.channelLastN
= ('number' === typeof(config.channelLastN)) ? config.channelLastN : -1;
// media types of the conference // media types of the conference
if (config.openSctp) if (config.openSctp)
{
this.media = ['audio', 'video', 'data']; this.media = ['audio', 'video', 'data'];
}
else else
{
this.media = ['audio', 'video']; this.media = ['audio', 'video'];
}
this.connection.jingle.sessions[this.sid] = this; this.connection.jingle.sessions[this.sid] = this;
this.mychannel = []; this.mychannel = [];
@ -202,29 +207,33 @@ ColibriFocus.prototype._makeConference = function () {
elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri'}); elem.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri'});
this.media.forEach(function (name) { this.media.forEach(function (name) {
var isData = name === 'data'; var elemName;
var channel = isData ? 'sctpconnection' : 'channel'; var elemAttrs = { initiator: 'true', expire: self.channelExpire };
if ('data' === name)
{
elemName = 'sctpconnection';
elemAttrs['port'] = 5000;
}
else
{
elemName = 'channel';
if (('video' === name) && (this.channelLastN >= 0))
elemAttrs['last-n'] = this.channelLastN;
}
elem.c('content', {name: name}); elem.c('content', {name: name});
elem.c(channel, { elem.c(elemName, elemAttrs);
initiator: 'true', elem.attrs({ endpoint: self.myMucResource });
expire: '15', elem.up();// end of channel/sctpconnection
endpoint: self.myMucResource
});
if (isData)
elem.attrs({port: 5000});
elem.up();// end of channel
for (var j = 0; j < self.peers.length; j++) { for (var j = 0; j < self.peers.length; j++) {
elem.c(channel, { var peer = self.peers[j];
initiator: 'true',
expire: '15', elem.c(elemName, elemAttrs);
endpoint: self.peers[j].substr(1 + self.peers[j].lastIndexOf('/')) elem.attrs({ endpoint: peer.substr(1 + peer.lastIndexOf('/')) });
}); elem.up(); // end of channel/sctpconnection
if (isData)
elem.attrs({port: 5000});
elem.up(); // end of channel
} }
elem.up(); // end of content elem.up(); // end of content
}); });
@ -233,7 +242,7 @@ ColibriFocus.prototype._makeConference = function () {
localSDP.media.forEach(function (media, channel) { localSDP.media.forEach(function (media, channel) {
var name = SDPUtil.parse_mline(media.split('\r\n')[0]).media; var name = SDPUtil.parse_mline(media.split('\r\n')[0]).media;
elem.c('content', {name: name}); elem.c('content', {name: name});
elem.c('channel', {initiator: 'false', expire: '15'}); elem.c('channel', {initiator: 'false', expire: self.channelExpire});
// FIXME: should reuse code from .toJingle // FIXME: should reuse code from .toJingle
var mline = SDPUtil.parse_mline(media.split('\r\n')[0]); var mline = SDPUtil.parse_mline(media.split('\r\n')[0]);
@ -247,7 +256,7 @@ ColibriFocus.prototype._makeConference = function () {
elem.up(); // end of channel elem.up(); // end of channel
for (j = 0; j < self.peers.length; j++) { for (j = 0; j < self.peers.length; j++) {
elem.c('channel', {initiator: 'true', expire:'15' }).up(); elem.c('channel', {initiator: 'true', expire: self.channelExpire }).up();
} }
elem.up(); // end of content elem.up(); // end of content
}); });
@ -540,7 +549,8 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) {
if (1) { //i > 0) { // not for audio FIXME: does not work as intended if (1) { //i > 0) { // not for audio FIXME: does not work as intended
// re-add all remote a=ssrcs // re-add all remote a=ssrcs
for (var jid in this.remotessrc) { for (var jid in this.remotessrc) {
if (jid == peer) continue; if (jid == peer || !this.remotessrc[jid][i])
continue;
sdp.media[i] += this.remotessrc[jid][i]; sdp.media[i] += this.remotessrc[jid][i];
} }
// and local a=ssrc lines // and local a=ssrc lines
@ -661,24 +671,28 @@ ColibriFocus.prototype.addNewParticipant = function (peer) {
var localSDP = new SDP(this.peerconnection.localDescription.sdp); var localSDP = new SDP(this.peerconnection.localDescription.sdp);
localSDP.media.forEach(function (media, channel) { localSDP.media.forEach(function (media, channel) {
var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:')); var name = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
elem.c('content', {name: name}); var elemName;
if (name !== 'data') var elemAttrs
{ = {
elem.c('channel', {
initiator: 'true', initiator: 'true',
expire: self.channelExpire, expire: self.channelExpire,
endpoint: peer.substr(1 + peer.lastIndexOf('/')) endpoint: peer.substr(1 + peer.lastIndexOf('/'))
}); };
if ('data' == name)
{
elemName = 'sctpconnection';
elemAttrs['port'] = 5000;
} }
else else
{ {
elem.c('sctpconnection', { elemName = 'channel';
endpoint: peer.substr(1 + peer.lastIndexOf('/')), if (('video' === name) && (this.channelLastN >= 0))
initiator: 'true', elemAttrs['last-n'] = this.channelLastN;
expire: self.channelExpire,
port: 5000
});
} }
elem.c('content', {name: name});
elem.c(elemName, elemAttrs);
elem.up(); // end of channel/sctpconnection elem.up(); // end of channel/sctpconnection
elem.up(); // end of content elem.up(); // end of content
}); });
@ -714,6 +728,9 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid}); change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid});
for (channel = 0; channel < this.channels[participant].length; channel++) for (channel = 0; channel < this.channels[participant].length; channel++)
{ {
if (!remoteSDP.media[channel])
continue;
var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:')); var name = SDPUtil.parse_mid(SDPUtil.find_line(remoteSDP.media[channel], 'a=mid:'));
change.c('content', {name: name}); change.c('content', {name: name});
if (name !== 'data') if (name !== 'data')
@ -894,6 +911,9 @@ ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype)
this.remotessrc[session.peerjid] = []; this.remotessrc[session.peerjid] = [];
for (channel = 0; channel < this.channels[participant].length; channel++) { for (channel = 0; channel < this.channels[participant].length; channel++) {
//if (channel == 0) continue; FIXME: does not work as intended //if (channel == 0) continue; FIXME: does not work as intended
if (!remoteSDP.media[channel])
continue;
if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length)
{ {
this.remotessrc[session.peerjid][channel] = this.remotessrc[session.peerjid][channel] =
@ -905,6 +925,9 @@ ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype)
// ACT 4: add new a=ssrc lines to local remotedescription // ACT 4: add new a=ssrc lines to local remotedescription
for (channel = 0; channel < this.channels[participant].length; channel++) { for (channel = 0; channel < this.channels[participant].length; channel++) {
//if (channel == 0) continue; FIXME: does not work as intended //if (channel == 0) continue; FIXME: does not work as intended
if (!remoteSDP.media[channel])
continue;
if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) { if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) {
this.peerconnection.enqueueAddSsrc( this.peerconnection.enqueueAddSsrc(
channel, channel,
@ -1153,3 +1176,73 @@ ColibriFocus.prototype.sendTerminate = function (session, reason, text) {
this.statsinterval = null; this.statsinterval = null;
} }
}; };
ColibriFocus.prototype.setRTCPTerminationStrategy = function (strategyFQN) {
var self = this;
var strategyIQ = $iq({to: this.bridgejid, type: 'set'});
strategyIQ.c('conference', {
xmlns: 'http://jitsi.org/protocol/colibri',
id: this.confid,
});
strategyIQ.c('rtcp-termination-strategy', {name: strategyFQN });
strategyIQ.c('content', {name: "video"});
strategyIQ.up(); // end of content
console.log('setting RTCP termination strategy', strategyFQN);
this.connection.sendIQ(strategyIQ,
function (res) {
console.log('got result');
},
function (err) {
console.error('got error', err);
}
);
};
/**
* Sets the default value of the channel last-n attribute in this conference and
* updates/patches the existing channels.
*/
ColibriFocus.prototype.setChannelLastN = function (channelLastN) {
if (('number' === typeof(channelLastN))
&& (this.channelLastN !== channelLastN))
{
this.channelLastN = channelLastN;
// Update/patch the existing channels.
var patch = $iq({ to:this.bridgejid, type:'set' });
patch.c(
'conference',
{ xmlns:'http://jitsi.org/protocol/colibri', id:this.confid });
patch.c('content', { name:'video' });
patch.c(
'channel',
{
id:$(this.mychannel[1 /* video */]).attr('id'),
'last-n':this.channelLastN
});
patch.up(); // end of channel
for (var p = 0; p < this.channels.length; p++)
{
patch.c(
'channel',
{
id:$(this.channels[p][1 /* video */]).attr('id'),
'last-n':this.channelLastN
});
patch.up(); // end of channel
}
this.connection.sendIQ(
patch,
function (res) {
console.info('Set channel last-n succeeded: ', res);
},
function (err) {
console.error('Set channel last-n failed: ', err);
});
}
};

View File

@ -519,6 +519,7 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res
constraints.video = { constraints.video = {
mandatory: { mandatory: {
chromeMediaSource: "screen", chromeMediaSource: "screen",
googLeakyBucket: true,
maxWidth: window.screen.width, maxWidth: window.screen.width,
maxHeight: window.screen.height, maxHeight: window.screen.height,
maxFrameRate: 3 maxFrameRate: 3
@ -530,6 +531,7 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res
mandatory: { mandatory: {
chromeMediaSource: "desktop", chromeMediaSource: "desktop",
chromeMediaSourceId: desktopStream, chromeMediaSourceId: desktopStream,
googLeakyBucket: true,
maxWidth: window.screen.width, maxWidth: window.screen.width,
maxHeight: window.screen.height, maxHeight: window.screen.height,
maxFrameRate: 3 maxFrameRate: 3

View File

@ -5,7 +5,7 @@ var Toolbar = (function (my) {
/** /**
* Opens the lock room dialog. * Opens the lock room dialog.
*/ */
my.openLockDialog = function() { my.openLockDialog = function () {
// Only the focus is able to set a shared key. // Only the focus is able to set a shared key.
if (focus === null) { if (focus === null) {
if (sharedKey) if (sharedKey)
@ -70,24 +70,75 @@ var Toolbar = (function (my) {
/** /**
* Opens the invite link dialog. * Opens the invite link dialog.
*/ */
my.openLinkDialog = function() { my.openLinkDialog = function () {
var inviteLink;
if (roomUrl == null)
inviteLink = "Your conference is currently being created...";
else
inviteLink = encodeURI(roomUrl);
$.prompt('<input id="inviteLinkRef" type="text" value="' + $.prompt('<input id="inviteLinkRef" type="text" value="' +
encodeURI(roomUrl) + '" onclick="this.select();" readonly>', inviteLink + '" onclick="this.select();" readonly>',
{ {
title: "Share this link with everyone you want to invite", title: "Share this link with everyone you want to invite",
persistent: false, persistent: false,
buttons: { "Cancel": false}, buttons: { "Invite": true, "Cancel": false},
loaded: function (event) { defaultButton: 1,
document.getElementById('inviteLinkRef').select(); loaded: function (event) {
if (roomUrl)
document.getElementById('inviteLinkRef').select();
else
document.getElementById('jqi_state0_buttonInvite')
.disabled = true;
},
submit: function (e, v, m, f) {
if (v) {
if (roomUrl) {
inviteParticipants();
}
}
}
} }
} );
);
}; };
/**
* Invite participants to conference.
*/
function inviteParticipants() {
if (roomUrl == null)
return;
var sharedKeyText = "";
if (sharedKey && sharedKey.length > 0)
sharedKeyText
= "This conference is password protected. Please use the "
+ "following pin when joining:%0D%0A%0D%0A"
+ sharedKey + "%0D%0A%0D%0A";
var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1);
var subject = "Invitation to a Jitsi Meet (" + conferenceName + ")";
var body = "Hey there, I%27d like to invite you to a Jitsi Meet"
+ " conference I%27ve just set up.%0D%0A%0D%0A"
+ "Please click on the following link in order"
+ " to join the conference.%0D%0A%0D%0A"
+ roomUrl + "%0D%0A%0D%0A"
+ sharedKeyText
+ "Note that Jitsi Meet is currently only supported by Chromim,"
+ " Google Chrome and Opera, so you need"
+ " to be using one of these browsers.%0D%0A%0D%0A"
+ "Talk to you in a sec!";
if (window.localStorage.displayname)
body += "%0D%0A%0D%0A" + window.localStorage.displayname;
window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
}
/** /**
* Opens the settings dialog. * Opens the settings dialog.
*/ */
my.openSettingsDialog = function() { my.openSettingsDialog = function () {
$.prompt('<h2>Configure your conference</h2>' + $.prompt('<h2>Configure your conference</h2>' +
'<input type="checkbox" id="initMuted"> Participants join muted<br/>' + '<input type="checkbox" id="initMuted"> Participants join muted<br/>' +
'<input type="checkbox" id="requireNicknames"> Require nicknames<br/><br/>' + '<input type="checkbox" id="requireNicknames"> Require nicknames<br/><br/>' +
@ -185,9 +236,12 @@ var Toolbar = (function (my) {
if (!$('#header').is(':visible')) { if (!$('#header').is(':visible')) {
Toolbar.showToolbar(); Toolbar.showToolbar();
} }
// Then clear the time out, to dock the toolbar. // Then clear the time out, to dock the toolbar.
clearTimeout(toolbarTimeout); if (toolbarTimeout) {
toolbarTimeout = null; clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
} }
else { else {
if (!$('#header').is(':visible')) { if (!$('#header').is(':visible')) {

View File

@ -1,6 +1,6 @@
var VideoLayout = (function (my) { var VideoLayout = (function (my) {
var preMuted = false; var preMuted = false;
var currentActiveSpeaker = null; var currentDominantSpeaker = null;
my.changeLocalAudio = function(stream) { my.changeLocalAudio = function(stream) {
connection.jingle.localAudio = stream; connection.jingle.localAudio = stream;
@ -139,19 +139,19 @@ var VideoLayout = (function (my) {
if (isVisible) { if (isVisible) {
// Only if the large video is currently visible. // Only if the large video is currently visible.
// Disable previous active speaker video. // Disable previous dominant speaker video.
var oldJid = getJidFromVideoSrc(oldSrc); var oldJid = getJidFromVideoSrc(oldSrc);
if (oldJid) { if (oldJid) {
var oldResourceJid = Strophe.getResourceFromJid(oldJid); var oldResourceJid = Strophe.getResourceFromJid(oldJid);
VideoLayout.enableActiveSpeaker(oldResourceJid, false); VideoLayout.enableDominantSpeaker(oldResourceJid, false);
} }
// Enable new active speaker in the remote videos section. // Enable new dominant speaker in the remote videos section.
var userJid = getJidFromVideoSrc(newSrc); var userJid = getJidFromVideoSrc(newSrc);
if (userJid) if (userJid)
{ {
var resourceJid = Strophe.getResourceFromJid(userJid); var resourceJid = Strophe.getResourceFromJid(userJid);
VideoLayout.enableActiveSpeaker(resourceJid, true); VideoLayout.enableDominantSpeaker(resourceJid, true);
} }
$(this).fadeIn(300); $(this).fadeIn(300);
@ -173,15 +173,15 @@ var VideoLayout = (function (my) {
if (focusedVideoSrc === videoSrc) if (focusedVideoSrc === videoSrc)
{ {
focusedVideoSrc = null; focusedVideoSrc = null;
var activeSpeakerVideo = null; var dominantSpeakerVideo = null;
// Enable the currently set active speaker. // Enable the currently set dominant speaker.
if (currentActiveSpeaker) { if (currentDominantSpeaker) {
activeSpeakerVideo dominantSpeakerVideo
= $('#participant_' + currentActiveSpeaker + '>video') = $('#participant_' + currentDominantSpeaker + '>video')
.get(0); .get(0);
if (activeSpeakerVideo) if (dominantSpeakerVideo)
VideoLayout.updateLargeVideo(activeSpeakerVideo.src, 1); VideoLayout.updateLargeVideo(dominantSpeakerVideo.src, 1);
} }
return; return;
@ -254,12 +254,12 @@ var VideoLayout = (function (my) {
if (isVisible) { if (isVisible) {
$('#largeVideo').css({visibility: 'visible'}); $('#largeVideo').css({visibility: 'visible'});
$('.watermark').css({visibility: 'visible'}); $('.watermark').css({visibility: 'visible'});
VideoLayout.enableActiveSpeaker(resourceJid, true); VideoLayout.enableDominantSpeaker(resourceJid, true);
} }
else { else {
$('#largeVideo').css({visibility: 'hidden'}); $('#largeVideo').css({visibility: 'hidden'});
$('.watermark').css({visibility: 'hidden'}); $('.watermark').css({visibility: 'hidden'});
VideoLayout.enableActiveSpeaker(resourceJid, false); VideoLayout.enableDominantSpeaker(resourceJid, false);
} }
}; };
@ -477,9 +477,7 @@ var VideoLayout = (function (my) {
if (!indicatorSpan || indicatorSpan.length === 0) { if (!indicatorSpan || indicatorSpan.length === 0) {
indicatorSpan = document.createElement('span'); indicatorSpan = document.createElement('span');
indicatorSpan.className = 'focusindicator'; indicatorSpan.className = 'focusindicator';
Util.setTooltip(indicatorSpan,
"The owner of<br/>this conference",
"top");
focusContainer.appendChild(indicatorSpan); focusContainer.appendChild(indicatorSpan);
createFocusIndicatorElement(indicatorSpan); createFocusIndicatorElement(indicatorSpan);
@ -570,7 +568,9 @@ var VideoLayout = (function (my) {
* Resizes thumbnails. * Resizes thumbnails.
*/ */
my.resizeThumbnails = function() { my.resizeThumbnails = function() {
var thumbnailSize = calculateThumbnailSize(); var videoSpaceWidth = $('#remoteVideos').width();
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
var width = thumbnailSize[0]; var width = thumbnailSize[0];
var height = thumbnailSize[1]; var height = thumbnailSize[1];
@ -582,20 +582,20 @@ var VideoLayout = (function (my) {
}; };
/** /**
* Enables the active speaker UI. * Enables the dominant speaker UI.
* *
* @param resourceJid the jid indicating the video element to * @param resourceJid the jid indicating the video element to
* activate/deactivate * activate/deactivate
* @param isEnable indicates if the active speaker should be enabled or * @param isEnable indicates if the dominant speaker should be enabled or
* disabled * disabled
*/ */
my.enableActiveSpeaker = function(resourceJid, isEnable) { my.enableDominantSpeaker = function(resourceJid, isEnable) {
var displayName = resourceJid; var displayName = resourceJid;
var nameSpan = $('#participant_' + resourceJid + '>span.displayname'); var nameSpan = $('#participant_' + resourceJid + '>span.displayname');
if (nameSpan.length > 0) if (nameSpan.length > 0)
displayName = nameSpan.text(); displayName = nameSpan.text();
console.log("UI enable active speaker", console.log("UI enable dominant speaker",
displayName, displayName,
resourceJid, resourceJid,
isEnable); isEnable);
@ -625,16 +625,16 @@ var VideoLayout = (function (my) {
if (isEnable) { if (isEnable) {
VideoLayout.showDisplayName(videoContainerId, true); VideoLayout.showDisplayName(videoContainerId, true);
if (!videoSpan.classList.contains("activespeaker")) if (!videoSpan.classList.contains("dominantspeaker"))
videoSpan.classList.add("activespeaker"); videoSpan.classList.add("dominantspeaker");
video.css({visibility: 'hidden'}); video.css({visibility: 'hidden'});
} }
else { else {
VideoLayout.showDisplayName(videoContainerId, false); VideoLayout.showDisplayName(videoContainerId, false);
if (videoSpan.classList.contains("activespeaker")) if (videoSpan.classList.contains("dominantspeaker"))
videoSpan.classList.remove("activespeaker"); videoSpan.classList.remove("dominantspeaker");
video.css({visibility: 'visible'}); video.css({visibility: 'visible'});
} }
@ -682,7 +682,7 @@ var VideoLayout = (function (my) {
/** /**
* Calculates the thumbnail size. * Calculates the thumbnail size.
*/ */
var calculateThumbnailSize = function () { my.calculateThumbnailSize = function (videoSpaceWidth) {
// Calculate the available height, which is the inner window height minus // Calculate the available height, which is the inner window height minus
// 39px for the header minus 2px for the delimiter lines on the top and // 39px for the header minus 2px for the delimiter lines on the top and
// bottom of the large video, minus the 36px space inside the remoteVideos // bottom of the large video, minus the 36px space inside the remoteVideos
@ -691,8 +691,10 @@ var VideoLayout = (function (my) {
var numvids = $('#remoteVideos>span:visible').length; var numvids = $('#remoteVideos>span:visible').length;
// Remove the 1px borders arround videos and the chat width. // Remove the 3px borders arround videos and border around the remote
var availableWinWidth = $('#remoteVideos').width() - 2 * numvids - 50; // videos area
var availableWinWidth = videoSpaceWidth - 2 * 3 * numvids - 50;
var availableWidth = availableWinWidth / numvids; var availableWidth = availableWinWidth / numvids;
var aspectRatio = 16.0 / 9.0; var aspectRatio = 16.0 / 9.0;
var maxHeight = Math.min(160, availableHeight); var maxHeight = Math.min(160, availableHeight);
@ -768,6 +770,10 @@ var VideoLayout = (function (my) {
var focusIndicator = document.createElement('i'); var focusIndicator = document.createElement('i');
focusIndicator.className = 'fa fa-star'; focusIndicator.className = 'fa fa-star';
parentElement.appendChild(focusIndicator); parentElement.appendChild(focusIndicator);
Util.setTooltip(parentElement,
"The owner of<br/>this conference",
"top");
} }
/** /**
@ -799,10 +805,10 @@ var VideoLayout = (function (my) {
}; };
/** /**
* Returns the current active speaker resource jid. * Returns the current dominant speaker resource jid.
*/ */
my.getActiveSpeakerResourceJid = function () { my.getDominantSpeakerResourceJid = function () {
return currentActiveSpeaker; return currentDominantSpeaker;
}; };
/** /**
@ -920,21 +926,21 @@ var VideoLayout = (function (my) {
}); });
/** /**
* On active speaker changed event. * On dominant speaker changed event.
*/ */
$(document).bind('activespeakerchanged', function (event, resourceJid) { $(document).bind('dominantspeakerchanged', function (event, resourceJid) {
// We ignore local user events. // We ignore local user events.
if (resourceJid if (resourceJid
=== Strophe.getResourceFromJid(connection.emuc.myroomjid)) === Strophe.getResourceFromJid(connection.emuc.myroomjid))
return; return;
// Obtain container for new active speaker. // Obtain container for new dominant speaker.
var container = document.getElementById( var container = document.getElementById(
'participant_' + resourceJid); 'participant_' + resourceJid);
// Update the current active speaker. // Update the current dominant speaker.
if (resourceJid !== currentActiveSpeaker) if (resourceJid !== currentDominantSpeaker)
currentActiveSpeaker = resourceJid; currentDominantSpeaker = resourceJid;
else else
return; return;