Implements minimized mode - only the thumbnails are visible.

This commit is contained in:
hristoterezov 2015-08-06 18:34:40 -05:00
parent 4092d67853
commit b77791f4b2
9 changed files with 565 additions and 401 deletions

View File

@ -22,7 +22,7 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="libs/toastr.js?v=1"></script><!-- notifications lib -->
<script src="interface_config.js?v=5"></script>
<script src="libs/app.bundle.js?v=120"></script>
<script src="libs/app.bundle.js?v=121"></script>
<script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<link rel="stylesheet" href="css/font.css?v=7"/>
<link rel="stylesheet" href="css/toastr.css?v=1">

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,7 @@ var messageHandler = UI.messageHandler;
var Authentication = require("./authentication/Authentication");
var UIUtil = require("./util/UIUtil");
var NicknameHandler = require("./util/NicknameHandler");
var JitsiPopover = require("./util/JitsiPopover");
var CQEvents = require("../../service/connectionquality/CQEvents");
var DesktopSharingEventTypes
= require("../../service/desktopsharing/DesktopSharingEventTypes");
@ -169,7 +170,7 @@ function registerListeners() {
VideoLayout.setDeviceAvailabilityIcons(null, devices);
});
APP.RTC.addListener(RTCEvents.VIDEO_MUTE, UI.setVideoMuteButtonsState);
APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function() {
APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function () {
// when the data channel becomes available, tell the bridge about video
// selections so that it can do adaptive simulcast,
// we want the notification to trigger even if userJid is undefined,
@ -254,10 +255,8 @@ function registerListeners() {
APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged);
APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus);
APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject);
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft);
APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordRequired);
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad);
APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED,
onAuthenticationRequired);
@ -269,11 +268,11 @@ function registerListeners() {
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED, VideoLayout.onAudioMute);
APP.xmpp.addListener(XMPPEvents.VIDEO_MUTED, VideoLayout.onVideoMute);
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function(doMuteAudio) {
APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function (doMuteAudio) {
UI.setAudioMuted(doMuteAudio);
});
APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
onDtmfSupportChanged);
onDtmfSupportChanged);
APP.xmpp.addListener(XMPPEvents.START_MUTED_SETTING_CHANGED, function (audio, video) {
SettingsMenu.setStartMuted(audio, video);
});
@ -286,43 +285,43 @@ function registerListeners() {
"dialog.internalError");
});
APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function () {
messageHandler.showError("dialog.error",
"dialog.SLDFailure");
"dialog.SLDFailure");
});
APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function () {
messageHandler.showError("dialog.error",
"dialog.SRDFailure");
});
APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function() {
APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function () {
messageHandler.showError();
});
APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function() {
APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function () {
// FIXME: re-use LoginDialog which supports retries
UI.showLoginPopup(connect);
});
APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function(focusComponent, retrySec) {
APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focusComponent, retrySec) {
UI.messageHandler.notify(
null, "notify.focus",
'disconnected', "notify.focusFail",
{component: focusComponent, ms: retrySec});
});
APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function(pres) {
APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) {
UI.messageHandler.openReportDialog(null,
"dialog.joinError", pres);
});
APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function(pres) {
APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) {
UI.messageHandler.openReportDialog(null,
"dialog.connectError", pres);
});
APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function() {
APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function () {
var roomName = UI.generateRoomName();
APP.xmpp.allocateConferenceFocus(roomName, UI.checkForNicknameAndJoin);
});
//NicknameHandler emits this event
UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
APP.xmpp.addToPresence("displayName", nickname);
@ -332,10 +331,14 @@ function registerListeners() {
AudioLevels.init();
});
// Listens for video interruption events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted);
// Listens for video restores events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored);
if (!config.minimized) {
APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
// Listens for video interruption events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted);
// Listens for video restores events.
APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored);
}
}
@ -385,9 +388,6 @@ UI.start = function (init) {
$("#welcome_page").hide();
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
// Set the defaults for prompt dialogs.
$.prompt.setDefaults({persistent: false});
@ -399,34 +399,39 @@ UI.start = function (init) {
bindEvents();
setupPrezi();
setupToolbars();
setupChat();
if(!config.minimized) {
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
setupToolbars();
setupChat();
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
$('#notice').css({display: 'block'});
}
$("#downloadlog").click(function (event) {
dump(event.target);
});
}
else
{
$("#header").css("display", "none");
$("#bottomToolbar").css("display", "none");
$("#downloadlog").css("display", "none");
$("#remoteVideos").css("padding", "0px 0px 18px 0px");
$("#remoteVideos").css("right", "0px");
messageHandler.disableNotifications();
$('body').popover("disable");
// $("[data-toggle=popover]").popover("disable");
JitsiPopover.enabled = false;
}
document.title = interfaceConfig.APP_NAME;
$("#downloadlog").click(function (event) {
dump(event.target);
});
if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled ||
window.localStorage.welcomePageDisabled == "false")) {
$("#videoconference_page").hide();
if (!setupWelcomePage)
setupWelcomePage = require("./welcome_page/WelcomePage");
setupWelcomePage();
return;
}
$("#welcome_page").hide();
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
$('#notice').css({display: 'block'});
}
if(config.requireDisplayName) {
var currentSettings = Settings.getSettings();
@ -437,30 +442,33 @@ UI.start = function (init) {
init();
toastr.options = {
"closeButton": true,
"debug": false,
"positionClass": "notification-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "2000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut",
"reposition": function() {
if(PanelToggler.isVisible()) {
$("#toast-container").addClass("notification-bottom-right-center");
} else {
$("#toast-container").removeClass("notification-bottom-right-center");
}
},
"newestOnTop": false
};
if(!config.minimized) {
toastr.options = {
"closeButton": true,
"debug": false,
"positionClass": "notification-bottom-right",
"onclick": null,
"showDuration": "300",
"hideDuration": "1000",
"timeOut": "2000",
"extendedTimeOut": "1000",
"showEasing": "swing",
"hideEasing": "linear",
"showMethod": "fadeIn",
"hideMethod": "fadeOut",
"reposition": function () {
if (PanelToggler.isVisible()) {
$("#toast-container").addClass("notification-bottom-right-center");
} else {
$("#toast-container").removeClass("notification-bottom-right-center");
}
},
"newestOnTop": false
};
SettingsMenu.init();
SettingsMenu.init();
}
};

View File

@ -53,6 +53,8 @@ var ToolbarToggler = {
* Shows the main toolbar.
*/
showToolbar: function () {
if(config.minimized)
return;
var header = $("#header"),
bottomToolbar = $("#bottomToolbar");
if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
@ -88,6 +90,9 @@ var ToolbarToggler = {
* @param isDock indicates what operation to perform
*/
dockToolbar: function (isDock) {
if(config.minimized)
return;
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {

View File

@ -46,6 +46,8 @@ var JitsiPopover = (function () {
* Shows the popover
*/
JitsiPopover.prototype.show = function () {
if(!JitsiPopover.enabled)
return;
this.createPopover();
this.popoverShown = true;
};
@ -118,6 +120,8 @@ var JitsiPopover = (function () {
this.createPopover();
};
JitsiPopover.enabled = true;
return JitsiPopover;
})();

View File

@ -1,4 +1,11 @@
/* global $, APP, jQuery, toastr */
/**
* Flag for enable/disable of the notifications.
* @type {boolean}
*/
var notificationsEnabled = true;
var messageHandler = (function(my) {
/**
@ -172,8 +179,19 @@ var messageHandler = (function(my) {
messageHandler.openMessageDialog(titleKey, msgKey);
};
/**
* Displayes notification.
* @param displayName display name of the participant that is associated with the notification.
* @param displayNameKey the key from the language file for the display name.
* @param cls css class for the notification
* @param messageKey the key from the language file for the text of the message.
* @param messageArguments object with the arguments for the message.
* @param options object with language options.
*/
my.notify = function(displayName, displayNameKey,
cls, messageKey, messageArguments, options) {
if(!notificationsEnabled)
return;
var displayNameSpan = '<span class="nickname" ';
if (displayName) {
displayNameSpan += ">" + displayName;
@ -201,6 +219,20 @@ var messageHandler = (function(my) {
toasterElement.remove();
};
/**
* Disables notifications.
*/
my.disableNotifications = function () {
notificationsEnabled = false;
};
/**
* Enables notifications.
*/
my.enableNotifications = function () {
notificationsEnabled = true;
};
return my;
}(messageHandler || {}));

View File

@ -469,6 +469,14 @@ var LargeVideo = {
largeVideoHeight,
horizontalIndent, verticalIndent, animate);
},
/**
* Resizes the large html elements.
* @param animate boolean property that indicates whether the resize should be animated or not.
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
* If that parameter is null the method will check the chat pannel visibility.
* @param completeFunction a function to be called when the video space is resized
* @returns {*[]} array with the current width and height values of the largeVideo html element.
*/
resize: function (animate, isVisible, completeFunction) {
if(!isEnabled)
return;
@ -481,18 +489,8 @@ var LargeVideo = {
var top = availableHeight / 2 - avatarSize / 4 * 3;
$('#activeSpeaker').css('top', top);
this.VideoLayout.resizeVideoSpace(animate, isVisible, completeFunction);
if(animate) {
$('#videospace').animate({
right: window.innerWidth - availableWidth,
width: availableWidth,
height: availableHeight
},
{
queue: false,
duration: 500,
complete: completeFunction
});
$('#largeVideoContainer').animate({
width: availableWidth,
height: availableHeight
@ -502,8 +500,6 @@ var LargeVideo = {
duration: 500
});
} else {
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
$('#largeVideoContainer').width(availableWidth);
$('#largeVideoContainer').height(availableHeight);
}

View File

@ -42,85 +42,96 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() {
* @param jid the jid indicating the video for which we're adding a menu.
* @param parentElement the parent element where this menu will be added
*/
RemoteVideo.prototype.addRemoteVideoMenu = function () {
var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu';
this.container.appendChild(spanElement);
if(!config.minimized) {
RemoteVideo.prototype.addRemoteVideoMenu = function () {
var spanElement = document.createElement('span');
spanElement.className = 'remotevideomenu';
var menuElement = document.createElement('i');
menuElement.className = 'fa fa-angle-down';
menuElement.title = 'Remote user controls';
spanElement.appendChild(menuElement);
this.container.appendChild(spanElement);
var menuElement = document.createElement('i');
menuElement.className = 'fa fa-angle-down';
menuElement.title = 'Remote user controls';
spanElement.appendChild(menuElement);
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
spanElement.appendChild(popupmenuElement);
var popupmenuElement = document.createElement('ul');
popupmenuElement.className = 'popupmenu';
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
spanElement.appendChild(popupmenuElement);
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var muteMenuItem = document.createElement('li');
var muteLinkItem = document.createElement('a');
var mutedIndicator = "<i style='float:left;' class='icon-mic-disabled'></i>";
var mutedIndicator = "<i style='float:left;' " +
"class='icon-mic-disabled'></i>";
if (!this.isMuted) {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
var self = this;
muteLinkItem.onclick = function(){
if ($(this).attr('disabled') != undefined) {
event.preventDefault();
}
var isMute = self.isMuted == true;
APP.xmpp.setMute(self.peerJid, !isMute);
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
if (!this.isMuted) {
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.domute'></div>";
muteLinkItem.className = 'mutelink';
}
else {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
muteLinkItem.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.muted'></div>";
muteLinkItem.className = 'mutelink disabled';
}
var self = this;
muteLinkItem.onclick = function(){
if ($(this).attr('disabled') != undefined) {
event.preventDefault();
}
var isMute = self.isMuted == true;
APP.xmpp.setMute(self.peerJid, !isMute);
popupmenuElement.setAttribute('style', 'display:none;');
if (isMute) {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.muted'></div>";
this.className = 'mutelink disabled';
}
else {
this.innerHTML = mutedIndicator +
" <div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.domute'></div>";
this.className = 'mutelink';
}
};
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
var ejectText = "<div style='width: 90px;margin-left: 20px;' " +
"data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(self.peerJid);
popupmenuElement.setAttribute('style', 'display:none;');
};
ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem);
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement(
$("#" + popupmenuElement.id + " > li > a > div"));
};
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
var ejectText = "<div style='width: 90px;margin-left: 20px;' data-i18n='videothumbnail.kick'>&nbsp;</div>";
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.onclick = function(){
APP.xmpp.eject(self.peerJid);
popupmenuElement.setAttribute('style', 'display:none;');
};
ejectMenuItem.appendChild(ejectLinkItem);
popupmenuElement.appendChild(ejectMenuItem);
var paddingSpan = document.createElement('span');
paddingSpan.className = 'popupmenuPadding';
popupmenuElement.appendChild(paddingSpan);
APP.translation.translateElement(
$("#" + popupmenuElement.id + " > li > a > div"));
};
} else {
RemoteVideo.prototype.addRemoteVideoMenu = function() {}
}
/**
* Removes the remote stream element corresponding to the given stream and

View File

@ -3,6 +3,7 @@ var AudioLevels = require("../audio_levels/AudioLevels");
var ContactList = require("../side_pannels/contactlist/ContactList");
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
var UIEvents = require("../../../service/UI/UIEvents");
var UIUtil = require("../util/UIUtil");
var RTC = require("../../RTC/RTC");
var RTCBrowserType = require('../../RTC/RTCBrowserType');
@ -11,6 +12,7 @@ var RemoteVideo = require("./RemoteVideo");
var LargeVideo = require("./LargeVideo");
var LocalVideo = require("./LocalVideo");
var remoteVideos = {};
var remoteVideoTypes = {};
var localVideoThumbnail = null;
@ -34,8 +36,16 @@ var VideoLayout = (function (my) {
my.init = function (emitter) {
eventEmitter = emitter;
localVideoThumbnail = new LocalVideo(VideoLayout);
if(config.minimized)
{
showLargeVideo = false;
LargeVideo.disable();
}
else
{
LargeVideo.init(VideoLayout, emitter);
}
LargeVideo.init(VideoLayout, emitter);
VideoLayout.resizeLargeVideoContainer();
};
@ -192,7 +202,7 @@ var VideoLayout = (function (my) {
resourceJid) {
if(focusedVideoResourceJid) {
var oldSmallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
if(oldSmallVideo)
if(oldSmallVideo && !config.minimized)
oldSmallVideo.focus(false);
}
@ -219,7 +229,7 @@ var VideoLayout = (function (my) {
// Update focused/pinned interface.
if (resourceJid) {
if(smallVideo)
if(smallVideo && !config.minimized)
smallVideo.focus(true);
if (!noPinnedEndpointChangedEvent) {
@ -354,7 +364,11 @@ var VideoLayout = (function (my) {
* Resizes the large video container.
*/
my.resizeLargeVideoContainer = function () {
LargeVideo.resize();
if(LargeVideo.isEnabled()) {
LargeVideo.resize();
} else {
VideoLayout.resizeVideoSpace();
}
VideoLayout.resizeThumbnails();
LargeVideo.position();
};
@ -373,7 +387,7 @@ var VideoLayout = (function (my) {
if(animate) {
$('#remoteVideos').animate({
height: height
height: height + 2 // adds 2 px because of small video 1px border
},
{
queue: false,
@ -398,7 +412,7 @@ var VideoLayout = (function (my) {
} else {
// size videos so that while keeping AR and max height, we have a
// nice fit
$('#remoteVideos').height(height);
$('#remoteVideos').height(height + 2);// adds 2 px because of small video 1px border
$('#remoteVideos>span').width(width);
$('#remoteVideos>span').height(height);
@ -431,7 +445,7 @@ var VideoLayout = (function (my) {
var availableWidth = availableWinWidth / numvids;
var aspectRatio = 16.0 / 9.0;
var maxHeight = Math.min(160, availableHeight);
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio);
availableHeight = Math.min(maxHeight, availableWidth / aspectRatio, window.innerHeight - 18);
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = Math.floor(availableHeight * aspectRatio);
}
@ -886,6 +900,37 @@ var VideoLayout = (function (my) {
VideoLayout.resizeThumbnails(true);
};
/**
* Resizes the #videospace html element
* @param animate boolean property that indicates whether the resize should be animated or not.
* @param isChatVisible boolean property that indicates whether the chat area is displayed or not.
* If that parameter is null the method will check the chat pannel visibility.
* @param completeFunction a function to be called when the video space is resized
*/
my.resizeVideoSpace = function (animate, isChatVisible, completeFunction) {
var availableHeight = window.innerHeight;
var availableWidth = UIUtil.getAvailableVideoWidth(isChatVisible);
if (availableWidth < 0 || availableHeight < 0) return;
if(animate) {
$('#videospace').animate({
right: window.innerWidth - availableWidth,
width: availableWidth,
height: availableHeight
},
{
queue: false,
duration: 500,
complete: completeFunction
});
} else {
$('#videospace').width(availableWidth);
$('#videospace').height(availableHeight);
}
};
my.getSmallVideo = function (resourceJid) {
if(resourceJid == APP.xmpp.myResource()) {
return localVideoThumbnail;