Refactors VideoLayout.js.
This commit is contained in:
parent
38b180ad81
commit
6a492d96c2
|
@ -19,7 +19,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=88"></script>
|
||||
<script src="libs/app.bundle.js?v=89"></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">
|
||||
|
|
4396
libs/app.bundle.js
4396
libs/app.bundle.js
File diff suppressed because it is too large
Load Diff
|
@ -36,7 +36,7 @@ var DataChannels =
|
|||
// selections so that it can do adaptive simulcast,
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
var userJid = APP.UI.getLargeVideoState().userResourceJid;
|
||||
var userJid = APP.UI.getLargeVideoJid();
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
onSelectedEndpointChanged(userJid);
|
||||
|
|
|
@ -143,7 +143,7 @@ function registerListeners() {
|
|||
}
|
||||
|
||||
AudioLevels.updateAudioLevel(resourceJid, audioLevel,
|
||||
UI.getLargeVideoState().userResourceJid);
|
||||
UI.getLargeVideoJid());
|
||||
});
|
||||
APP.desktopsharing.addListener(function () {
|
||||
ToolbarToggler.showDesktopSharingButton();
|
||||
|
@ -215,8 +215,6 @@ function registerListeners() {
|
|||
break;
|
||||
case 'recvonly':
|
||||
el.hide();
|
||||
// FIXME: Check if we have to change large video
|
||||
//VideoLayout.updateLargeVideo(el);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -265,6 +263,11 @@ function setVideoMute(mute, options) {
|
|||
options);
|
||||
}
|
||||
|
||||
function onResize()
|
||||
{
|
||||
Chat.resizeChat();
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
}
|
||||
|
||||
function bindEvents()
|
||||
{
|
||||
|
@ -272,16 +275,9 @@ function bindEvents()
|
|||
* Resizes and repositions videos in full screen mode.
|
||||
*/
|
||||
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
|
||||
function () {
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
VideoLayout.positionLarge();
|
||||
}
|
||||
);
|
||||
onResize);
|
||||
|
||||
$(window).resize(function () {
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
VideoLayout.positionLarge();
|
||||
});
|
||||
$(window).resize(onResize);
|
||||
}
|
||||
|
||||
UI.start = function (init) {
|
||||
|
@ -322,7 +318,6 @@ UI.start = function (init) {
|
|||
|
||||
$("#welcome_page").hide();
|
||||
|
||||
VideoLayout.resizeLargeVideoContainer();
|
||||
$("#videospace").mousemove(function () {
|
||||
return ToolbarToggler.showToolbar();
|
||||
});
|
||||
|
@ -550,9 +545,8 @@ function onMucMemberJoined(jid, id, displayName) {
|
|||
VideoLayout.ensurePeerContainerExists(jid,id);
|
||||
}
|
||||
|
||||
function onMucPresenceStatus( jid, info) {
|
||||
VideoLayout.setPresenceStatus(
|
||||
'participant_' + Strophe.getResourceFromJid(jid), info.status);
|
||||
function onMucPresenceStatus(jid, info) {
|
||||
VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status);
|
||||
}
|
||||
|
||||
function onMucRoleChanged(role, displayName) {
|
||||
|
@ -615,9 +609,9 @@ UI.inputDisplayNameHandler = function (value) {
|
|||
};
|
||||
|
||||
|
||||
UI.getLargeVideoState = function()
|
||||
UI.getLargeVideoJid = function()
|
||||
{
|
||||
return VideoLayout.getLargeVideoState();
|
||||
return VideoLayout.getLargeVideoJid();
|
||||
};
|
||||
|
||||
UI.generateRoomName = function() {
|
||||
|
@ -656,9 +650,9 @@ UI.generateRoomName = function() {
|
|||
};
|
||||
|
||||
|
||||
UI.connectionIndicatorShowMore = function(id)
|
||||
UI.connectionIndicatorShowMore = function(jid)
|
||||
{
|
||||
return VideoLayout.connectionIndicators[id].showMore();
|
||||
return VideoLayout.showMore(jid);
|
||||
};
|
||||
|
||||
UI.showLoginPopup = function(callback)
|
||||
|
|
|
@ -13,14 +13,13 @@ function setVisibility(selector, show) {
|
|||
function isUserMuted(jid) {
|
||||
// XXX(gp) we may want to rename this method to something like
|
||||
// isUserStreaming, for example.
|
||||
if (jid && jid != APP.xmpp.myJid()) {
|
||||
if (jid != APP.xmpp.myJid()) {
|
||||
var resource = Strophe.getResourceFromJid(jid);
|
||||
if (!require("../videolayout/VideoLayout").isInLastN(resource)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(jid && jid == APP.xmpp.myJid())
|
||||
else
|
||||
{
|
||||
var localVideo = APP.RTC.localVideo;
|
||||
return (!localVideo || localVideo.isMuted());
|
||||
|
@ -117,7 +116,7 @@ var Avatar = {
|
|||
//if the user is the currently focused, the dominant speaker or if
|
||||
//there is no focused and no dominant speaker and the large video is
|
||||
//currently shown
|
||||
if (activeSpeakerJid === jid && require("../videolayout/VideoLayout").isLargeVideoOnTop()) {
|
||||
if (activeSpeakerJid === jid && require("../videolayout/LargeVideo").isLargeVideoOnTop()) {
|
||||
setVisibility($("#largeVideo"), !show);
|
||||
setVisibility($('#activeSpeaker'), show);
|
||||
setVisibility(avatar, false);
|
||||
|
@ -137,10 +136,6 @@ var Avatar = {
|
|||
* @param jid of the current active speaker
|
||||
*/
|
||||
updateActiveSpeakerAvatarSrc: function (jid) {
|
||||
if (!jid) {
|
||||
jid = APP.xmpp.findJidFromResource(
|
||||
require("../videolayout/VideoLayout").getLargeVideoState().userResourceJid);
|
||||
}
|
||||
var avatar = $("#activeSpeakerAvatar")[0];
|
||||
var url = getGravatarUrl(users[jid],
|
||||
interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE);
|
||||
|
|
|
@ -165,10 +165,10 @@ function presentationAdded(event, jid, presUrl, currentSlide) {
|
|||
+ Strophe.getResourceFromJid(jid)
|
||||
+ '_' + presId;
|
||||
|
||||
// We explicitly don't specify the peer jid here, because we don't want
|
||||
// this video to be dealt with as a peer related one (for example we
|
||||
// don't want to show a mute/kick menu for this one, etc.).
|
||||
VideoLayout.addRemoteVideoContainer(null, elementId);
|
||||
|
||||
|
||||
|
||||
VideoLayout.addPreziContainer(elementId);
|
||||
VideoLayout.resizeThumbnails();
|
||||
|
||||
var controlsEnabled = false;
|
||||
|
|
|
@ -5,6 +5,7 @@ var SettingsMenu = require("./settings/SettingsMenu");
|
|||
var VideoLayout = require("../videolayout/VideoLayout");
|
||||
var ToolbarToggler = require("../toolbars/ToolbarToggler");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
var LargeVideo = require("../videolayout/LargeVideo");
|
||||
|
||||
/**
|
||||
* Toggler for the chat, contact list, settings menu, etc..
|
||||
|
@ -18,90 +19,6 @@ var PanelToggler = (function(my) {
|
|||
'#settingsmenu': '#settingsButton'
|
||||
};
|
||||
|
||||
/**
|
||||
* Resizes the video area
|
||||
* @param isClosing whether the side panel is going to be closed or is going to open / remain opened
|
||||
* @param completeFunction a function to be called when the video space is resized
|
||||
*/
|
||||
var resizeVideoArea = function(isClosing, completeFunction) {
|
||||
var videospace = $('#videospace');
|
||||
|
||||
var panelSize = isClosing ? [0, 0] : PanelToggler.getPanelSize();
|
||||
var videospaceWidth = window.innerWidth - panelSize[0];
|
||||
var videospaceHeight = window.innerHeight;
|
||||
var videoSize
|
||||
= VideoLayout.getVideoSize(null, null, videospaceWidth, videospaceHeight);
|
||||
var videoWidth = videoSize[0];
|
||||
var videoHeight = videoSize[1];
|
||||
var videoPosition = VideoLayout.getVideoPosition(videoWidth,
|
||||
videoHeight,
|
||||
videospaceWidth,
|
||||
videospaceHeight);
|
||||
var horizontalIndent = videoPosition[0];
|
||||
var verticalIndent = videoPosition[1];
|
||||
|
||||
var thumbnailSize = VideoLayout.calculateThumbnailSize(videospaceWidth);
|
||||
var thumbnailsWidth = thumbnailSize[0];
|
||||
var thumbnailsHeight = thumbnailSize[1];
|
||||
//for chat
|
||||
|
||||
videospace.animate({
|
||||
right: panelSize[0],
|
||||
width: videospaceWidth,
|
||||
height: videospaceHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500,
|
||||
complete: completeFunction
|
||||
});
|
||||
|
||||
$('#remoteVideos').animate({
|
||||
height: thumbnailsHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
|
||||
$('#remoteVideos>span').animate({
|
||||
height: thumbnailsHeight,
|
||||
width: thumbnailsWidth
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500,
|
||||
complete: function () {
|
||||
$(document).trigger(
|
||||
"remotevideo.resized",
|
||||
[thumbnailsWidth,
|
||||
thumbnailsHeight]);
|
||||
}
|
||||
});
|
||||
|
||||
$('#largeVideoContainer').animate({
|
||||
width: videospaceWidth,
|
||||
height: videospaceHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
|
||||
$('#largeVideo').animate({
|
||||
width: videoWidth,
|
||||
height: videoHeight,
|
||||
top: verticalIndent,
|
||||
bottom: verticalIndent,
|
||||
left: horizontalIndent,
|
||||
right: horizontalIndent
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the windows in the side panel
|
||||
* @param object the window that should be shown
|
||||
|
@ -135,7 +52,7 @@ var PanelToggler = (function(my) {
|
|||
else {
|
||||
// Undock the toolbar when the chat is shown and if we're in a
|
||||
// video mode.
|
||||
if (VideoLayout.isLargeVideoVisible()) {
|
||||
if (LargeVideo.isLargeVideoVisible()) {
|
||||
ToolbarToggler.dockToolbar(false);
|
||||
}
|
||||
|
||||
|
@ -180,7 +97,7 @@ var PanelToggler = (function(my) {
|
|||
$('#chatspace').trigger('shown');
|
||||
};
|
||||
|
||||
resizeVideoArea(Chat.isVisible(), chatCompleteFunction);
|
||||
VideoLayout.resizeVideoArea(!Chat.isVisible(), chatCompleteFunction);
|
||||
|
||||
toggle(Chat,
|
||||
'#chatspace',
|
||||
|
@ -203,7 +120,7 @@ var PanelToggler = (function(my) {
|
|||
my.toggleContactList = function () {
|
||||
var completeFunction = ContactList.isVisible() ?
|
||||
function() {} : function () { $('#contactlist').trigger('shown');};
|
||||
resizeVideoArea(ContactList.isVisible(), completeFunction);
|
||||
VideoLayout.resizeVideoArea(!ContactList.isVisible(), completeFunction);
|
||||
|
||||
toggle(ContactList,
|
||||
'#contactlist',
|
||||
|
@ -218,7 +135,7 @@ var PanelToggler = (function(my) {
|
|||
* Opens / closes the settings menu
|
||||
*/
|
||||
my.toggleSettingsMenu = function() {
|
||||
resizeVideoArea(SettingsMenu.isVisible(), function (){});
|
||||
VideoLayout.resizeVideoArea(!SettingsMenu.isVisible(), function (){});
|
||||
toggle(SettingsMenu,
|
||||
'#settingsmenu',
|
||||
null,
|
||||
|
|
|
@ -5,10 +5,12 @@ module.exports = {
|
|||
/**
|
||||
* Returns the available video width.
|
||||
*/
|
||||
getAvailableVideoWidth: function () {
|
||||
getAvailableVideoWidth: function (isVisible) {
|
||||
var PanelToggler = require("../side_pannels/SidePanelToggler");
|
||||
if(typeof isVisible === "undefined" || isVisible === null)
|
||||
isVisible = PanelToggler.isVisible();
|
||||
var rightPanelWidth
|
||||
= PanelToggler.isVisible() ? PanelToggler.getPanelSize()[0] : 0;
|
||||
= isVisible ? PanelToggler.getPanelSize()[0] : 0;
|
||||
|
||||
return window.innerWidth - rightPanelWidth;
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ var JitsiPopover = require("../util/JitsiPopover");
|
|||
* @param videoContainer the video container associated with the indicator.
|
||||
* @constructor
|
||||
*/
|
||||
function ConnectionIndicator(videoContainer, jid, VideoLayout)
|
||||
function ConnectionIndicator(videoContainer, jid)
|
||||
{
|
||||
this.videoContainer = videoContainer;
|
||||
this.bandwidth = null;
|
||||
|
@ -17,7 +17,6 @@ function ConnectionIndicator(videoContainer, jid, VideoLayout)
|
|||
this.popover = null;
|
||||
this.jid = jid;
|
||||
this.create();
|
||||
this.videoLayout = VideoLayout;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,10 +152,10 @@ ConnectionIndicator.prototype.generateText = function () {
|
|||
translate("connectionindicator.resolution") + "</span></td>" +
|
||||
"<td>" + resolution + "</td></tr></table>";
|
||||
|
||||
if(this.videoContainer.id == "localVideoContainer") {
|
||||
if(this.videoContainer.videoSpanId == "localVideoContainer") {
|
||||
result += "<div class=\"jitsipopover_showmore\" " +
|
||||
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
|
||||
this.videoContainer.id + "')\" data-i18n='connectionindicator." +
|
||||
this.jid + "')\" data-i18n='connectionindicator." +
|
||||
(this.showMoreValue ? "less" : "more") + "'>" +
|
||||
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
|
||||
"</div><br />";
|
||||
|
@ -318,9 +317,9 @@ ConnectionIndicator.prototype.create = function () {
|
|||
this.connectionIndicatorContainer = document.createElement("div");
|
||||
this.connectionIndicatorContainer.className = "connectionindicator";
|
||||
this.connectionIndicatorContainer.style.display = "none";
|
||||
this.videoContainer.appendChild(this.connectionIndicatorContainer);
|
||||
this.videoContainer.container.appendChild(this.connectionIndicatorContainer);
|
||||
this.popover = new JitsiPopover(
|
||||
$("#" + this.videoContainer.id + " > .connectionindicator"),
|
||||
$("#" + this.videoContainer.videoSpanId + " > .connectionindicator"),
|
||||
{content: "<div class=\"connection_info\" data-i18n='connectionindicator.na'>" +
|
||||
APP.translation.translateString("connectionindicator.na") + "</div>",
|
||||
skin: "black"});
|
||||
|
@ -360,7 +359,7 @@ function (percent, object) {
|
|||
{
|
||||
if(this.connectionIndicatorContainer.style.display == "none") {
|
||||
this.connectionIndicatorContainer.style.display = "block";
|
||||
this.videoLayout.updateMutePosition(this.videoContainer.id);
|
||||
this.videoContainer.updateIconPositions();
|
||||
}
|
||||
}
|
||||
this.bandwidth = object.bandwidth;
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
var Avatar = require("../avatar/Avatar");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
var UIEvents = require("../../../service/UI/UIEvents");
|
||||
var xmpp = require("../../xmpp/xmpp");
|
||||
|
||||
var video = $('#largeVideo');
|
||||
|
||||
var currentVideoWidth = null;
|
||||
var currentVideoHeight = null;
|
||||
// By default we use camera
|
||||
var getVideoSize = getCameraVideoSize;
|
||||
var getVideoPosition = getCameraVideoPosition;
|
||||
var currentSmallVideo = null;
|
||||
var oldSmallVideo = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the size and position of the given video element.
|
||||
*
|
||||
* @param video the video element to position
|
||||
* @param width the desired video width
|
||||
* @param height the desired video height
|
||||
* @param horizontalIndent the left and right indent
|
||||
* @param verticalIndent the top and bottom indent
|
||||
*/
|
||||
function positionVideo(video,
|
||||
width,
|
||||
height,
|
||||
horizontalIndent,
|
||||
verticalIndent,
|
||||
animate) {
|
||||
if(animate)
|
||||
{
|
||||
video.animate({
|
||||
width: width,
|
||||
height: height,
|
||||
top: verticalIndent,
|
||||
bottom: verticalIndent,
|
||||
left: horizontalIndent,
|
||||
right: horizontalIndent
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
video.width(width);
|
||||
video.height(height);
|
||||
video.css({ top: verticalIndent + 'px',
|
||||
bottom: verticalIndent + 'px',
|
||||
left: horizontalIndent + 'px',
|
||||
right: horizontalIndent + 'px'});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of the video dimensions, so that it keeps it's aspect
|
||||
* ratio and fits available area with it's larger dimension. This method
|
||||
* ensures that whole video will be visible and can leave empty areas.
|
||||
*
|
||||
* @return an array with 2 elements, the video width and the video height
|
||||
*/
|
||||
function getDesktopVideoSize(videoWidth,
|
||||
videoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight) {
|
||||
if (!videoWidth)
|
||||
videoWidth = currentVideoWidth;
|
||||
if (!videoHeight)
|
||||
videoHeight = currentVideoHeight;
|
||||
|
||||
var aspectRatio = videoWidth / videoHeight;
|
||||
|
||||
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
||||
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
||||
|
||||
videoSpaceHeight -= $('#remoteVideos').outerHeight();
|
||||
|
||||
if (availableWidth / aspectRatio >= videoSpaceHeight)
|
||||
{
|
||||
availableHeight = videoSpaceHeight;
|
||||
availableWidth = availableHeight * aspectRatio;
|
||||
}
|
||||
|
||||
if (availableHeight * aspectRatio >= videoSpaceWidth)
|
||||
{
|
||||
availableWidth = videoSpaceWidth;
|
||||
availableHeight = availableWidth / aspectRatio;
|
||||
}
|
||||
|
||||
return [availableWidth, availableHeight];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of the video horizontal and vertical indents,
|
||||
* so that if fits its parent.
|
||||
*
|
||||
* @return an array with 2 elements, the horizontal indent and the vertical
|
||||
* indent
|
||||
*/
|
||||
function getCameraVideoPosition(videoWidth,
|
||||
videoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight) {
|
||||
// Parent height isn't completely calculated when we position the video in
|
||||
// full screen mode and this is why we use the screen height in this case.
|
||||
// Need to think it further at some point and implement it properly.
|
||||
var isFullScreen = document.fullScreen ||
|
||||
document.mozFullScreen ||
|
||||
document.webkitIsFullScreen;
|
||||
if (isFullScreen)
|
||||
videoSpaceHeight = window.innerHeight;
|
||||
|
||||
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
||||
var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
|
||||
|
||||
return [horizontalIndent, verticalIndent];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the video horizontal and vertical indents.
|
||||
* Centers horizontally and top aligns vertically.
|
||||
*
|
||||
* @return an array with 2 elements, the horizontal indent and the vertical
|
||||
* indent
|
||||
*/
|
||||
function getDesktopVideoPosition(videoWidth,
|
||||
videoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight) {
|
||||
|
||||
var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
||||
|
||||
var verticalIndent = 0;// Top aligned
|
||||
|
||||
return [horizontalIndent, verticalIndent];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of the video dimensions, so that it covers the screen.
|
||||
* It leaves no empty areas, but some parts of the video might not be visible.
|
||||
*
|
||||
* @return an array with 2 elements, the video width and the video height
|
||||
*/
|
||||
function getCameraVideoSize(videoWidth,
|
||||
videoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight) {
|
||||
if (!videoWidth)
|
||||
videoWidth = currentVideoWidth;
|
||||
if (!videoHeight)
|
||||
videoHeight = currentVideoHeight;
|
||||
|
||||
var aspectRatio = videoWidth / videoHeight;
|
||||
|
||||
var availableWidth = Math.max(videoWidth, videoSpaceWidth);
|
||||
var availableHeight = Math.max(videoHeight, videoSpaceHeight);
|
||||
|
||||
if (availableWidth / aspectRatio < videoSpaceHeight) {
|
||||
availableHeight = videoSpaceHeight;
|
||||
availableWidth = availableHeight * aspectRatio;
|
||||
}
|
||||
|
||||
if (availableHeight * aspectRatio < videoSpaceWidth) {
|
||||
availableWidth = videoSpaceWidth;
|
||||
availableHeight = availableWidth / aspectRatio;
|
||||
}
|
||||
|
||||
return [availableWidth, availableHeight];
|
||||
}
|
||||
|
||||
|
||||
function changeVideo(isVisible) {
|
||||
Avatar.updateActiveSpeakerAvatarSrc(currentSmallVideo.peerJid);
|
||||
|
||||
APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
|
||||
|
||||
var videoTransform = document.getElementById('largeVideo')
|
||||
.style.webkitTransform;
|
||||
|
||||
var flipX = currentSmallVideo.flipX;
|
||||
|
||||
if (flipX && videoTransform !== 'scaleX(-1)') {
|
||||
document.getElementById('largeVideo').style.webkitTransform
|
||||
= "scaleX(-1)";
|
||||
}
|
||||
else if (!flipX && videoTransform === 'scaleX(-1)') {
|
||||
document.getElementById('largeVideo').style.webkitTransform
|
||||
= "none";
|
||||
}
|
||||
|
||||
var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
|
||||
// Change the way we'll be measuring and positioning large video
|
||||
|
||||
getVideoSize = isDesktop
|
||||
? getDesktopVideoSize
|
||||
: getCameraVideoSize;
|
||||
getVideoPosition = isDesktop
|
||||
? getDesktopVideoPosition
|
||||
: getCameraVideoPosition;
|
||||
|
||||
|
||||
// Only if the large video is currently visible.
|
||||
// Disable previous dominant speaker video.
|
||||
if (oldSmallVideo) {
|
||||
oldSmallVideo.enableDominantSpeaker(false);
|
||||
}
|
||||
|
||||
// Enable new dominant speaker in the remote videos section.
|
||||
if (currentSmallVideo) {
|
||||
currentSmallVideo.enableDominantSpeaker(true);
|
||||
}
|
||||
|
||||
if (isVisible) {
|
||||
// using "this" should be ok because we're called
|
||||
// from within the fadeOut event.
|
||||
$(this).fadeIn(300);
|
||||
}
|
||||
|
||||
if(oldSmallVideo)
|
||||
Avatar.showUserAvatar(oldSmallVideo.peerJid);
|
||||
}
|
||||
|
||||
var LargeVideo = {
|
||||
|
||||
init: function (VideoLayout, emitter) {
|
||||
this.VideoLayout = VideoLayout;
|
||||
this.eventEmitter = emitter;
|
||||
var self = this;
|
||||
// Listen for large video size updates
|
||||
document.getElementById('largeVideo')
|
||||
.addEventListener('loadedmetadata', function (e) {
|
||||
currentVideoWidth = this.videoWidth;
|
||||
currentVideoHeight = this.videoHeight;
|
||||
self.position(currentVideoWidth, currentVideoHeight);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Indicates if the large video is currently visible.
|
||||
*
|
||||
* @return <tt>true</tt> if visible, <tt>false</tt> - otherwise
|
||||
*/
|
||||
isLargeVideoVisible: function() {
|
||||
return video.is(':visible');
|
||||
},
|
||||
/**
|
||||
* Updates the large video with the given new video source.
|
||||
*/
|
||||
updateLargeVideo: function(resourceJid, forceUpdate) {
|
||||
console.log('hover in', resourceJid);
|
||||
var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
|
||||
|
||||
if ((currentSmallVideo && currentSmallVideo.resourceJid !== resourceJid)
|
||||
|| forceUpdate) {
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
|
||||
if(currentSmallVideo) {
|
||||
oldSmallVideo = currentSmallVideo;
|
||||
} else {
|
||||
oldSmallVideo = null;
|
||||
}
|
||||
|
||||
currentSmallVideo = newSmallVideo;
|
||||
var oldJid = null;
|
||||
if(oldSmallVideo)
|
||||
oldJid = oldSmallVideo.peerJid;
|
||||
|
||||
if (oldJid !== resourceJid) {
|
||||
// we want the notification to trigger even if userJid is undefined,
|
||||
// or null.
|
||||
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT,
|
||||
resourceJid);
|
||||
}
|
||||
|
||||
video.fadeOut(300, changeVideo.bind(video, this.isLargeVideoVisible()));
|
||||
} else {
|
||||
var jid = null;
|
||||
if(currentSmallVideo)
|
||||
jid = currentSmallVideo.peerJid;
|
||||
Avatar.showUserAvatar(jid);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides the large video.
|
||||
*/
|
||||
setLargeVideoVisible: function(isVisible) {
|
||||
if (isVisible) {
|
||||
$('#largeVideo').css({visibility: 'visible'});
|
||||
$('.watermark').css({visibility: 'visible'});
|
||||
if(currentSmallVideo)
|
||||
currentSmallVideo.enableDominantSpeaker(true);
|
||||
}
|
||||
else {
|
||||
$('#largeVideo').css({visibility: 'hidden'});
|
||||
$('#activeSpeaker').css('visibility', 'hidden');
|
||||
$('.watermark').css({visibility: 'hidden'});
|
||||
if(currentSmallVideo)
|
||||
currentSmallVideo.enableDominantSpeaker(false);
|
||||
}
|
||||
},
|
||||
onVideoTypeChanged: function (jid) {
|
||||
if(jid && currentSmallVideo && jid === currentSmallVideo.peerJid)
|
||||
{
|
||||
var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
|
||||
getVideoSize = isDesktop
|
||||
? getDesktopVideoSize
|
||||
: getCameraVideoSize;
|
||||
getVideoPosition = isDesktop
|
||||
? getDesktopVideoPosition
|
||||
: getCameraVideoPosition;
|
||||
this.position(null, null);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Positions the large video.
|
||||
*
|
||||
* @param videoWidth the stream video width
|
||||
* @param videoHeight the stream video height
|
||||
*/
|
||||
position: function (videoWidth, videoHeight,
|
||||
videoSpaceWidth, videoSpaceHeight, animate) {
|
||||
if(!videoSpaceWidth)
|
||||
videoSpaceWidth = $('#videospace').width();
|
||||
if(!videoSpaceHeight)
|
||||
videoSpaceHeight = window.innerHeight;
|
||||
|
||||
var videoSize = getVideoSize(videoWidth,
|
||||
videoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight);
|
||||
|
||||
var largeVideoWidth = videoSize[0];
|
||||
var largeVideoHeight = videoSize[1];
|
||||
|
||||
var videoPosition = getVideoPosition(largeVideoWidth,
|
||||
largeVideoHeight,
|
||||
videoSpaceWidth,
|
||||
videoSpaceHeight);
|
||||
|
||||
var horizontalIndent = videoPosition[0];
|
||||
var verticalIndent = videoPosition[1];
|
||||
|
||||
positionVideo($('#largeVideo'),
|
||||
largeVideoWidth,
|
||||
largeVideoHeight,
|
||||
horizontalIndent, verticalIndent, animate);
|
||||
},
|
||||
|
||||
isLargeVideoOnTop: function () {
|
||||
var Etherpad = require("../etherpad/Etherpad");
|
||||
var Prezi = require("../prezi/Prezi");
|
||||
return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
|
||||
},
|
||||
resize: function (animate, isVisible, completeFunction) {
|
||||
var availableHeight = window.innerHeight;
|
||||
var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
|
||||
|
||||
if (availableWidth < 0 || availableHeight < 0) return;
|
||||
|
||||
var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
|
||||
var top = availableHeight / 2 - avatarSize / 4 * 3;
|
||||
$('#activeSpeaker').css('top', top);
|
||||
|
||||
if(animate)
|
||||
{
|
||||
$('#videospace').animate({
|
||||
right: window.innerWidth - availableWidth,
|
||||
width: availableWidth,
|
||||
height: availableHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500,
|
||||
complete: completeFunction
|
||||
});
|
||||
|
||||
$('#largeVideoContainer').animate({
|
||||
width: availableWidth,
|
||||
height: availableHeight
|
||||
},
|
||||
{
|
||||
queue: false,
|
||||
duration: 500
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#videospace').width(availableWidth);
|
||||
$('#videospace').height(availableHeight);
|
||||
$('#largeVideoContainer').width(availableWidth);
|
||||
$('#largeVideoContainer').height(availableHeight);
|
||||
}
|
||||
return [availableWidth, availableHeight];
|
||||
|
||||
},
|
||||
resizeVideoAreaAnimated: function (isVisible, completeFunction) {
|
||||
var size = this.resize(true, isVisible, completeFunction);
|
||||
this.position(null, null, size[0], size[1], true);
|
||||
},
|
||||
getResourceJid: function () {
|
||||
if(!currentSmallVideo)
|
||||
return null;
|
||||
return currentSmallVideo.peerJid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = LargeVideo;
|
|
@ -0,0 +1,227 @@
|
|||
var SmallVideo = require("./SmallVideo");
|
||||
var ConnectionIndicator = require("./ConnectionIndicator");
|
||||
var NicknameHandler = require("../util/NicknameHandler");
|
||||
var UIUtil = require("../util/UIUtil");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
|
||||
function LocalVideo(VideoLayout)
|
||||
{
|
||||
this.videoSpanId = "localVideoContainer";
|
||||
this.container = $("#localVideoContainer").get(0);
|
||||
this.VideoLayout = VideoLayout;
|
||||
this.flipX = true;
|
||||
this.peerJid = null;
|
||||
this.resourceJid = null;
|
||||
}
|
||||
|
||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
LocalVideo.prototype.constructor = LocalVideo;
|
||||
|
||||
/**
|
||||
* Creates the edit display name button.
|
||||
*
|
||||
* @returns the edit button
|
||||
*/
|
||||
function createEditDisplayNameButton() {
|
||||
var editButton = document.createElement('a');
|
||||
editButton.className = 'displayname';
|
||||
UIUtil.setTooltip(editButton,
|
||||
"videothumbnail.editnickname",
|
||||
"top");
|
||||
editButton.innerHTML = '<i class="fa fa-pencil"></i>';
|
||||
|
||||
return editButton;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
*/
|
||||
LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
if (!this.container) {
|
||||
console.warn(
|
||||
"Unable to set displayName - " + this.videoSpanId + " does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
var defaultLocalDisplayName = APP.translation.generateTranslatonHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
|
||||
if (nameSpan.text() !== displayName) {
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
$('#localDisplayName').html(displayName + ' (' + meHTML + ')');
|
||||
}
|
||||
else
|
||||
$('#localDisplayName').html(defaultLocalDisplayName);
|
||||
}
|
||||
} else {
|
||||
var editButton = createEditDisplayNameButton();
|
||||
|
||||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
||||
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
nameSpan.innerHTML = displayName + meHTML;
|
||||
}
|
||||
else {
|
||||
nameSpan.innerHTML = defaultLocalDisplayName;
|
||||
}
|
||||
|
||||
|
||||
nameSpan.id = 'localDisplayName';
|
||||
this.container.appendChild(editButton);
|
||||
//translates popover of edit button
|
||||
APP.translation.translateElement($("a.displayname"));
|
||||
|
||||
var editableText = document.createElement('input');
|
||||
editableText.className = 'displayname';
|
||||
editableText.type = 'text';
|
||||
editableText.id = 'editDisplayName';
|
||||
|
||||
if (displayName && displayName.length) {
|
||||
editableText.value = displayName;
|
||||
}
|
||||
|
||||
var defaultNickname = APP.translation.translateString(
|
||||
"defaultNickname", {name: "Jane Pink"});
|
||||
editableText.setAttribute('style', 'display:none;');
|
||||
editableText.setAttribute('data-18n',
|
||||
'[placeholder]defaultNickname');
|
||||
editableText.setAttribute("data-i18n-options",
|
||||
JSON.stringify({name: "Jane Pink"}));
|
||||
editableText.setAttribute("placeholder", defaultNickname);
|
||||
|
||||
this.container.appendChild(editableText);
|
||||
|
||||
var self = this;
|
||||
$('#localVideoContainer .displayname')
|
||||
.bind("click", function (e) {
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$('#localDisplayName').hide();
|
||||
$('#editDisplayName').show();
|
||||
$('#editDisplayName').focus();
|
||||
$('#editDisplayName').select();
|
||||
|
||||
$('#editDisplayName').one("focusout", function (e) {
|
||||
self.VideoLayout.inputDisplayNameHandler(this.value);
|
||||
});
|
||||
|
||||
$('#editDisplayName').on('keydown', function (e) {
|
||||
if (e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
self.VideoLayout.inputDisplayNameHandler(this.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LocalVideo.prototype.inputDisplayNameHandler = function (name) {
|
||||
NicknameHandler.setNickname(name);
|
||||
|
||||
if (!$('#localDisplayName').is(":visible")) {
|
||||
if (NicknameHandler.getNickname())
|
||||
{
|
||||
var meHTML = APP.translation.generateTranslatonHTML("me");
|
||||
$('#localDisplayName').html(NicknameHandler.getNickname() + " (" + meHTML + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
var defaultHTML = APP.translation.generateTranslatonHTML(
|
||||
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
||||
$('#localDisplayName')
|
||||
.html(defaultHTML);
|
||||
}
|
||||
$('#localDisplayName').show();
|
||||
}
|
||||
|
||||
$('#editDisplayName').hide();
|
||||
}
|
||||
|
||||
LocalVideo.prototype.createConnectionIndicator = function()
|
||||
{
|
||||
if(this.connectionIndicator)
|
||||
return;
|
||||
|
||||
this.connectionIndicator
|
||||
= new ConnectionIndicator(this, null);
|
||||
}
|
||||
|
||||
LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||
var self = this;
|
||||
|
||||
function localVideoClick(event) {
|
||||
event.stopPropagation();
|
||||
self.VideoLayout.handleVideoThumbClicked(
|
||||
false,
|
||||
APP.xmpp.myResource());
|
||||
}
|
||||
|
||||
$('#localVideoContainer').click(localVideoClick);
|
||||
|
||||
// Add hover handler
|
||||
$('#localVideoContainer').hover(
|
||||
function() {
|
||||
self.showDisplayName(true);
|
||||
},
|
||||
function() {
|
||||
if (!LargeVideo.isLargeVideoVisible()
|
||||
|| APP.xmpp.myResource() !== LargeVideo.getResourceJid())
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
|
||||
if(isMuted)
|
||||
{
|
||||
APP.UI.setVideoMute(true);
|
||||
return;
|
||||
}
|
||||
this.flipX = (stream.videoType == "screen")? false : true;
|
||||
var localVideo = document.createElement('video');
|
||||
localVideo.id = 'localVideo_' +
|
||||
APP.RTC.getStreamID(stream.getOriginalStream());
|
||||
localVideo.autoplay = true;
|
||||
localVideo.volume = 0; // is it required if audio is separated ?
|
||||
localVideo.oncontextmenu = function () { return false; };
|
||||
|
||||
var localVideoContainer = document.getElementById('localVideoWrapper');
|
||||
localVideoContainer.appendChild(localVideo);
|
||||
|
||||
var localVideoSelector = $('#' + localVideo.id);
|
||||
|
||||
// Add click handler to both video and video wrapper elements in case
|
||||
// there's no video.
|
||||
localVideoSelector.click(localVideoClick);
|
||||
|
||||
if (this.flipX) {
|
||||
localVideoSelector.addClass("flipVideoX");
|
||||
}
|
||||
|
||||
// Attach WebRTC stream
|
||||
APP.RTC.attachMediaStream(localVideoSelector, stream.getOriginalStream());
|
||||
|
||||
// Add stream ended handler
|
||||
stream.getOriginalStream().onended = function () {
|
||||
localVideoContainer.removeChild(localVideo);
|
||||
self.VideoLayout.updateRemovedVideo(APP.RTC.getVideoSrc(localVideo));
|
||||
};
|
||||
}
|
||||
|
||||
LocalVideo.prototype.joined = function (jid) {
|
||||
this.peerJid = jid;
|
||||
this.resourceJid = Strophe.getResourceFromJid(jid);
|
||||
}
|
||||
|
||||
module.exports = LocalVideo;
|
|
@ -0,0 +1,398 @@
|
|||
var ConnectionIndicator = require("./ConnectionIndicator");
|
||||
var SmallVideo = require("./SmallVideo");
|
||||
var AudioLevels = require("../audio_levels/AudioLevels");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
var Avatar = require("../avatar/Avatar");
|
||||
|
||||
function RemoteVideo(peerJid, VideoLayout)
|
||||
{
|
||||
this.peerJid = peerJid;
|
||||
this.resourceJid = Strophe.getResourceFromJid(peerJid);
|
||||
this.videoSpanId = 'participant_' + this.resourceJid;
|
||||
this.VideoLayout = VideoLayout;
|
||||
this.addRemoteVideoContainer();
|
||||
this.connectionIndicator = new ConnectionIndicator(
|
||||
this, this.peerJid);
|
||||
this.setDisplayName();
|
||||
var nickfield = document.createElement('span');
|
||||
nickfield.className = "nick";
|
||||
nickfield.appendChild(document.createTextNode(this.resourceJid));
|
||||
this.container.appendChild(nickfield);
|
||||
this.flipX = false;
|
||||
}
|
||||
|
||||
RemoteVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
RemoteVideo.prototype.constructor = RemoteVideo;
|
||||
|
||||
RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||
this.container = RemoteVideo.createContainer(this.videoSpanId);
|
||||
if (APP.xmpp.isModerator())
|
||||
this.addRemoteVideoMenu();
|
||||
AudioLevels.updateAudioLevelCanvas(this.peerJid, this.VideoLayout);
|
||||
|
||||
return this.container;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the remote video menu element for the given <tt>jid</tt> in the
|
||||
* given <tt>parentElement</tt>.
|
||||
*
|
||||
* @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);
|
||||
|
||||
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.resourceJid;
|
||||
spanElement.appendChild(popupmenuElement);
|
||||
|
||||
var muteMenuItem = document.createElement('li');
|
||||
var muteLinkItem = document.createElement('a');
|
||||
|
||||
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';
|
||||
}
|
||||
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'> </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"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the remote stream element corresponding to the given stream and
|
||||
* parent container.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param isVideo <tt>true</tt> if given <tt>stream</tt> is a video one.
|
||||
* @param container
|
||||
*/
|
||||
RemoteVideo.prototype.removeRemoteStreamElement = function (stream, isVideo, id) {
|
||||
if (!this.container)
|
||||
return false;
|
||||
|
||||
var select = null;
|
||||
var removedVideoSrc = null;
|
||||
if (isVideo) {
|
||||
select = $('#' + id);
|
||||
removedVideoSrc = APP.RTC.getVideoSrc(select.get(0));
|
||||
}
|
||||
else
|
||||
select = $('#' + this.videoSpanId + '>audio');
|
||||
|
||||
|
||||
// Mark video as removed to cancel waiting loop(if video is removed
|
||||
// before has started)
|
||||
select.removed = true;
|
||||
select.remove();
|
||||
|
||||
var audioCount = $('#' + this.videoSpanId + '>audio').length;
|
||||
var videoCount = $('#' + this.videoSpanId + '>video').length;
|
||||
|
||||
if (!audioCount && !videoCount) {
|
||||
console.log("Remove whole user", this.videoSpanId);
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.remove();
|
||||
// Remove whole container
|
||||
this.container.remove();
|
||||
|
||||
this.VideoLayout.resizeThumbnails();
|
||||
}
|
||||
|
||||
if (removedVideoSrc)
|
||||
this.VideoLayout.updateRemovedVideo(removedVideoSrc);
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.addRemoteStreamElement = function (sid, stream, thessrc) {
|
||||
var isVideo = stream.getVideoTracks().length > 0;
|
||||
if(!this.container)
|
||||
return;
|
||||
|
||||
var streamElement = SmallVideo.createStreamElement(sid, stream);
|
||||
var newElementId = streamElement.id;
|
||||
|
||||
this.container.appendChild(streamElement);
|
||||
|
||||
var sel = $('#' + newElementId);
|
||||
sel.hide();
|
||||
|
||||
// If the container is currently visible we attach the stream.
|
||||
if (!isVideo
|
||||
|| (this.container.offsetParent !== null && isVideo)) {
|
||||
APP.RTC.attachMediaStream(sel, stream);
|
||||
|
||||
if (isVideo)
|
||||
this.waitForRemoteVideo(sel, thessrc, stream);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
stream.onended = function () {
|
||||
console.log('stream ended', this);
|
||||
|
||||
self.removeRemoteStreamElement(stream, isVideo, newElementId);
|
||||
|
||||
};
|
||||
|
||||
// Add click handler.
|
||||
this.container.onclick = function (event) {
|
||||
/*
|
||||
* FIXME It turns out that videoThumb may not exist (if there is
|
||||
* no actual video).
|
||||
*/
|
||||
var videoThumb = $('#' + self.videoSpanId + '>video').get(0);
|
||||
if (videoThumb) {
|
||||
self.VideoLayout.handleVideoThumbClicked(
|
||||
false,
|
||||
self.resourceJid);
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return false;
|
||||
};
|
||||
|
||||
//FIXME
|
||||
// Add hover handler
|
||||
$(this.container).hover(
|
||||
function() {
|
||||
self.showDisplayName(true);
|
||||
},
|
||||
function() {
|
||||
var videoSrc = null;
|
||||
if ($('#' + self.videoSpanId + '>video')
|
||||
&& $('#' + self.videoSpanId + '>video').length > 0) {
|
||||
videoSrc = APP.RTC.getVideoSrc($('#' + self.videoSpanId + '>video').get(0));
|
||||
}
|
||||
|
||||
// If the video has been "pinned" by the user we want to
|
||||
// keep the display name on place.
|
||||
if (!LargeVideo.isLargeVideoVisible()
|
||||
|| videoSrc !== APP.RTC.getVideoSrc($('#largeVideo')[0]))
|
||||
self.showDisplayName(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
RemoteVideo.prototype.waitForRemoteVideo = function(selector, ssrc, stream) {
|
||||
if (selector.removed || !selector.parent().is(":visible")) {
|
||||
console.warn("Media removed before had started", selector);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream.id === 'mixedmslabel') return;
|
||||
|
||||
if (selector[0].currentTime > 0) {
|
||||
APP.RTC.attachMediaStream(selector, stream); // FIXME: why do i have to do this for FF?
|
||||
this.VideoLayout.videoactive(selector, this.resourceJid);
|
||||
} else {
|
||||
var self = this;
|
||||
setTimeout(function () {
|
||||
self.waitForRemoteVideo(selector, ssrc, stream);
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/hide peer container for the given resourceJid.
|
||||
*/
|
||||
RemoteVideo.prototype.showPeerContainer = function (state) {
|
||||
if (!this.container)
|
||||
return;
|
||||
|
||||
var isHide = state === 'hide';
|
||||
var resizeThumbnails = false;
|
||||
|
||||
if (!isHide) {
|
||||
if (!$(this.container).is(':visible')) {
|
||||
resizeThumbnails = true;
|
||||
$(this.container).show();
|
||||
}
|
||||
|
||||
Avatar.showUserAvatar(this.peerJid, (state !== 'show'));
|
||||
}
|
||||
else if ($(this.container).is(':visible') && isHide)
|
||||
{
|
||||
resizeThumbnails = true;
|
||||
$(this.container).hide();
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.hide();
|
||||
}
|
||||
|
||||
if (resizeThumbnails) {
|
||||
this.VideoLayout.resizeThumbnails();
|
||||
}
|
||||
|
||||
// We want to be able to pin a participant from the contact list, even
|
||||
// if he's not in the lastN set!
|
||||
// ContactList.setClickable(resourceJid, !isHide);
|
||||
|
||||
};
|
||||
|
||||
RemoteVideo.prototype.removeConnectionIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.remove();
|
||||
}
|
||||
|
||||
RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the remote video menu.
|
||||
*
|
||||
* @param jid the jid indicating the video for which we're adding a menu.
|
||||
* @param isMuted indicates the current mute state
|
||||
*/
|
||||
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
||||
var muteMenuItem
|
||||
= $('#remote_popupmenu_'
|
||||
+ this.resourceJid
|
||||
+ '>li>a.mutelink');
|
||||
|
||||
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
|
||||
|
||||
if (muteMenuItem.length) {
|
||||
var muteLink = muteMenuItem.get(0);
|
||||
|
||||
if (isMuted === 'true') {
|
||||
muteLink.innerHTML = mutedIndicator + ' Muted';
|
||||
muteLink.className = 'mutelink disabled';
|
||||
}
|
||||
else {
|
||||
muteLink.innerHTML = mutedIndicator + ' Mute';
|
||||
muteLink.className = 'mutelink';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the display name for the given video span id.
|
||||
*/
|
||||
RemoteVideo.prototype.setDisplayName = function(displayName, key) {
|
||||
|
||||
if (!this.container) {
|
||||
console.warn(
|
||||
"Unable to set displayName - " + this.videoSpanId + " does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
|
||||
// If we already have a display name for this video.
|
||||
if (nameSpan.length > 0) {
|
||||
if (displayName && displayName.length > 0)
|
||||
{
|
||||
$('#' + this.videoSpanId + '_name').html(displayName);
|
||||
}
|
||||
else if (key && key.length > 0)
|
||||
{
|
||||
var nameHtml = APP.translation.generateTranslatonHTML(key);
|
||||
$('#' + this.videoSpanId + '_name').html(nameHtml);
|
||||
}
|
||||
else
|
||||
$('#' + this.videoSpanId + '_name').text(
|
||||
interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME);
|
||||
} else {
|
||||
nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'displayname';
|
||||
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
||||
|
||||
|
||||
if (displayName && displayName.length > 0) {
|
||||
|
||||
nameSpan.innerText = displayName;
|
||||
}
|
||||
else
|
||||
nameSpan.innerText = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
|
||||
|
||||
nameSpan.id = this.videoSpanId + '_name';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes remote video menu element from video element identified by
|
||||
* given <tt>videoElementId</tt>.
|
||||
*
|
||||
* @param videoElementId the id of local or remote video element.
|
||||
*/
|
||||
RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||
var menuSpan = $('#' + this.videoSpanId + '>span.remotevideomenu');
|
||||
if (menuSpan.length) {
|
||||
menuSpan.remove();
|
||||
}
|
||||
}
|
||||
|
||||
RemoteVideo.createContainer = function (spanId) {
|
||||
var container = document.createElement('span');
|
||||
container.id = spanId;
|
||||
container.className = 'videocontainer';
|
||||
var remotes = document.getElementById('remoteVideos');
|
||||
return remotes.appendChild(container);
|
||||
};
|
||||
|
||||
module.exports = RemoteVideo;
|
|
@ -0,0 +1,285 @@
|
|||
var UIUtil = require("../util/UIUtil");
|
||||
var LargeVideo = require("./LargeVideo");
|
||||
|
||||
function SmallVideo(){
|
||||
this.isMuted = false;
|
||||
}
|
||||
|
||||
SmallVideo.prototype.showDisplayName = function(isShow) {
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname').get(0);
|
||||
if (isShow) {
|
||||
if (nameSpan && nameSpan.innerHTML && nameSpan.innerHTML.length)
|
||||
nameSpan.setAttribute("style", "display:inline-block;");
|
||||
}
|
||||
else {
|
||||
if (nameSpan)
|
||||
nameSpan.setAttribute("style", "display:none;");
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.setDeviceAvailabilityIcons = function (devices) {
|
||||
if(!this.container)
|
||||
return;
|
||||
|
||||
$("#" + this.videoSpanId + " > .noMic").remove();
|
||||
$("#" + this.videoSpanId + " > .noVideo").remove();
|
||||
if(!devices.audio)
|
||||
{
|
||||
this.container.appendChild(
|
||||
document.createElement("div")).setAttribute("class","noMic");
|
||||
}
|
||||
|
||||
if(!devices.video)
|
||||
{
|
||||
this.container.appendChild(
|
||||
document.createElement("div")).setAttribute("class","noVideo");
|
||||
}
|
||||
|
||||
if(!devices.audio && !devices.video)
|
||||
{
|
||||
$("#" + this.videoSpanId + " > .noMic").css("background-position", "75%");
|
||||
$("#" + this.videoSpanId + " > .noVideo").css("background-position", "25%");
|
||||
$("#" + this.videoSpanId + " > .noVideo").css("background-color", "transparent");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the presence status message for the given video.
|
||||
*/
|
||||
SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
|
||||
|
||||
if (!this.container) {
|
||||
// No container
|
||||
return;
|
||||
}
|
||||
|
||||
var statusSpan = $('#' + this.videoSpanId + '>span.status');
|
||||
if (!statusSpan.length) {
|
||||
//Add status span
|
||||
statusSpan = document.createElement('span');
|
||||
statusSpan.className = 'status';
|
||||
statusSpan.id = this.videoSpanId + '_status';
|
||||
$('#' + this.videoSpanId)[0].appendChild(statusSpan);
|
||||
|
||||
statusSpan = $('#' + this.videoSpanId + '>span.status');
|
||||
}
|
||||
|
||||
// Display status
|
||||
if (statusMsg && statusMsg.length) {
|
||||
$('#' + this.videoSpanId + '_status').text(statusMsg);
|
||||
statusSpan.get(0).setAttribute("style", "display:inline-block;");
|
||||
}
|
||||
else {
|
||||
// Hide
|
||||
statusSpan.get(0).setAttribute("style", "display:none;");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an audio or video stream element.
|
||||
*/
|
||||
|
||||
SmallVideo.createStreamElement = function (sid, stream) {
|
||||
var isVideo = stream.getVideoTracks().length > 0;
|
||||
|
||||
var element = isVideo
|
||||
? document.createElement('video')
|
||||
: document.createElement('audio');
|
||||
var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_')
|
||||
+ sid + '_' + APP.RTC.getStreamID(stream);
|
||||
|
||||
element.id = id;
|
||||
element.autoplay = true;
|
||||
element.oncontextmenu = function () { return false; };
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the data for the indicator
|
||||
* @param id the id of the indicator
|
||||
* @param percent the percent for connection quality
|
||||
* @param object the data
|
||||
*/
|
||||
SmallVideo.prototype.updateStatsIndicator = function (percent, object) {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.updateConnectionQuality(percent, object);
|
||||
}
|
||||
|
||||
SmallVideo.prototype.hideIndicator = function () {
|
||||
if(this.connectionIndicator)
|
||||
this.connectionIndicator.hideIndicator();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows audio muted indicator over small videos.
|
||||
* @param {string} isMuted
|
||||
*/
|
||||
SmallVideo.prototype.showAudioIndicator = function(isMuted) {
|
||||
var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted');
|
||||
|
||||
if (isMuted === 'false') {
|
||||
if (audioMutedSpan.length > 0) {
|
||||
audioMutedSpan.popover('hide');
|
||||
audioMutedSpan.remove();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(audioMutedSpan.length == 0 ) {
|
||||
audioMutedSpan = document.createElement('span');
|
||||
audioMutedSpan.className = 'audioMuted';
|
||||
UIUtil.setTooltip(audioMutedSpan,
|
||||
"videothumbnail.mute",
|
||||
"top");
|
||||
|
||||
this.container.appendChild(audioMutedSpan);
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + " > span"));
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-mic-disabled';
|
||||
audioMutedSpan.appendChild(mutedIndicator);
|
||||
|
||||
}
|
||||
this.updateIconPositions();
|
||||
}
|
||||
this.isMuted = isMuted;
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows video muted indicator over small videos.
|
||||
*/
|
||||
SmallVideo.prototype.showVideoIndicator = function(isMuted) {
|
||||
var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted');
|
||||
|
||||
if (isMuted === false) {
|
||||
if (videoMutedSpan.length > 0) {
|
||||
videoMutedSpan.remove();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(videoMutedSpan.length == 0) {
|
||||
videoMutedSpan = document.createElement('span');
|
||||
videoMutedSpan.className = 'videoMuted';
|
||||
|
||||
this.container.appendChild(videoMutedSpan);
|
||||
|
||||
var mutedIndicator = document.createElement('i');
|
||||
mutedIndicator.className = 'icon-camera-disabled';
|
||||
UIUtil.setTooltip(mutedIndicator,
|
||||
"videothumbnail.videomute",
|
||||
"top");
|
||||
videoMutedSpan.appendChild(mutedIndicator);
|
||||
//translate texts for muted indicator
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + " > span > i"));
|
||||
}
|
||||
|
||||
this.updateIconPositions();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.enableDominantSpeaker = function (isEnable)
|
||||
{
|
||||
var displayName = this.resourceJid;
|
||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||
if (nameSpan.length > 0)
|
||||
displayName = nameSpan.html();
|
||||
|
||||
console.log("UI enable dominant speaker",
|
||||
displayName,
|
||||
this.resourceJid,
|
||||
isEnable);
|
||||
|
||||
|
||||
if (!this.container) {
|
||||
return;
|
||||
}
|
||||
|
||||
var video = $('#' + this.videoSpanId + '>video');
|
||||
|
||||
if (video && video.length > 0) {
|
||||
if (isEnable) {
|
||||
this.showDisplayName(LargeVideo.isLargeVideoOnTop());
|
||||
|
||||
if (!this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.add("dominantspeaker");
|
||||
}
|
||||
else {
|
||||
this.showDisplayName(false);
|
||||
|
||||
if (this.container.classList.contains("dominantspeaker"))
|
||||
this.container.classList.remove("dominantspeaker");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SmallVideo.prototype.updateIconPositions = function () {
|
||||
var audioMutedSpan = $('#' + this.videoSpanId + '>span.audioMuted');
|
||||
var connectionIndicator = $('#' + this.videoSpanId + '>div.connectionindicator');
|
||||
var videoMutedSpan = $('#' + this.videoSpanId + '>span.videoMuted');
|
||||
if(connectionIndicator.length > 0
|
||||
&& connectionIndicator[0].style.display != "none") {
|
||||
audioMutedSpan.css({right: "23px"});
|
||||
videoMutedSpan.css({right: ((audioMutedSpan.length > 0? 23 : 0) + 30) + "px"});
|
||||
}
|
||||
else
|
||||
{
|
||||
audioMutedSpan.css({right: "0px"});
|
||||
videoMutedSpan.css({right: (audioMutedSpan.length > 0? 30 : 0) + "px"});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the element indicating the moderator(owner) of the conference.
|
||||
*
|
||||
* @param parentElement the parent element where the owner indicator will
|
||||
* be added
|
||||
*/
|
||||
SmallVideo.prototype.createModeratorIndicatorElement = function () {
|
||||
// Show moderator indicator
|
||||
var indicatorSpan = $('#' + this.videoSpanId + ' .focusindicator');
|
||||
|
||||
if (!indicatorSpan || indicatorSpan.length === 0) {
|
||||
indicatorSpan = document.createElement('span');
|
||||
indicatorSpan.className = 'focusindicator';
|
||||
|
||||
this.container.appendChild(indicatorSpan);
|
||||
indicatorSpan = $('#' + this.videoSpanId + ' .focusindicator');
|
||||
}
|
||||
|
||||
if (indicatorSpan.children().length !== 0)
|
||||
return;
|
||||
var moderatorIndicator = document.createElement('i');
|
||||
moderatorIndicator.className = 'fa fa-star';
|
||||
indicatorSpan[0].appendChild(moderatorIndicator);
|
||||
|
||||
UIUtil.setTooltip(indicatorSpan[0],
|
||||
"videothumbnail.moderator",
|
||||
"top");
|
||||
|
||||
//translates text in focus indicators
|
||||
APP.translation.translateElement($('#' + this.videoSpanId + ' .focusindicator'));
|
||||
}
|
||||
|
||||
|
||||
SmallVideo.prototype.getSrc = function () {
|
||||
return APP.RTC.getVideoSrc($('#' + this.videoSpanId).find("video").get(0));
|
||||
}
|
||||
|
||||
SmallVideo.prototype.focus = function(isFocused)
|
||||
{
|
||||
if(!isFocused) {
|
||||
this.container.classList.remove("videoContainerFocused");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.container.classList.add("videoContainerFocused");
|
||||
}
|
||||
}
|
||||
|
||||
SmallVideo.prototype.hasVideo = function () {
|
||||
return $("#" + this.videoSpanId).find("video").length !== 0;
|
||||
}
|
||||
|
||||
module.exports = SmallVideo;
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue