mirror of https://github.com/jitsi/jitsi-meet
parent
38b180ad81
commit
6a492d96c2
File diff suppressed because it is too large
Load Diff
@ -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