diff --git a/css/jitsi_popover.css b/css/jitsi_popover.css index bdf8a09a9c..5936c87b26 100644 --- a/css/jitsi_popover.css +++ b/css/jitsi_popover.css @@ -71,7 +71,7 @@ height: 35px; width: 100px; position: absolute; - bottom: -35; + bottom: -35px; } .jitsipopover_green diff --git a/css/popup_menu.css b/css/popup_menu.css index 041e04229b..90c9dc752e 100644 --- a/css/popup_menu.css +++ b/css/popup_menu.css @@ -1,27 +1,10 @@ /*Initialize*/ ul.popupmenu { - display:none; - position: absolute; - padding:10px; + padding: 0px 10px 0px 10px; margin: 0; bottom: 0; - margin-bottom: 35px; - padding-bottom: 10px; - padding-top: 10px; - right: 10px; - left: -5px; width: 100px; - background-color: rgba(0,0,0,0.9); - border: 1px solid rgba(256, 256, 256, 0.2); - border-radius:3px; -} - -ul.popupmenu:after { - content: url('../images/popupPointer.png'); - display: block; - position: absolute; - bottom: -8px; - left: 11px; + height: auto; } ul.popupmenu li { @@ -36,11 +19,13 @@ ul.popupmenu li:hover { /*Link Appearance*/ ul.popupmenu li a { + display: block; text-decoration: none; color: #fff; padding: 5px; - display: inline-block; font-size: 9pt; + width: 100%; + cursor: hand; } ul.popupmenu li a i.icon-kick { @@ -54,6 +39,15 @@ ul.popupmenu li a span { text-align: center; } +ul.popupmenu li a div { + display: inline-block; + line-height: 25px; +} + +ul.popupmenu li a i { + line-height: 25px; +} + span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover { display:block !important; } @@ -61,12 +55,4 @@ span.remotevideomenu:hover ul.popupmenu, ul.popupmenu:hover { a.disabled { color: gray !important; pointer-events: none; -} - -.popupmenuPadding { - height: 35px; - width: 100px; - position: absolute; - bottom: -35; - left: 0px; } \ No newline at end of file diff --git a/css/videolayout_default.css b/css/videolayout_default.css index 8e97dcf87d..5588f26a3b 100644 --- a/css/videolayout_default.css +++ b/css/videolayout_default.css @@ -144,8 +144,7 @@ } #remoteVideos .videocontainer>span.focusindicator, -#remoteVideos .videocontainer>span.remotevideomenu { - display: inline-block; +#remoteVideos .videocontainer>div.remotevideomenu { position: absolute; color: #FFFFFF; top: 0; @@ -159,6 +158,14 @@ text-align: center; } +#remoteVideos .videocontainer>span.focusindicator { + display: inline-block; +} + +#remoteVideos .videocontainer>div.remotevideomenu { + display: block; +} + .videocontainer>span.displayname, .videocontainer>input.displayname { display: none; diff --git a/modules/UI/videolayout/RemoteVideo.js b/modules/UI/videolayout/RemoteVideo.js index 1076890e3d..487c90cb02 100644 --- a/modules/UI/videolayout/RemoteVideo.js +++ b/modules/UI/videolayout/RemoteVideo.js @@ -6,6 +6,7 @@ import SmallVideo from "./SmallVideo"; import AudioLevels from "../audio_levels/AudioLevels"; import UIUtils from "../util/UIUtil"; import UIEvents from '../../../service/UI/UIEvents'; +import JitsiPopover from "../util/JitsiPopover"; function RemoteVideo(id, VideoLayout, emitter) { this.id = id; @@ -18,6 +19,7 @@ function RemoteVideo(id, VideoLayout, emitter) { this.bindHoverHandler(); this.flipX = false; this.isLocal = false; + this.isMuted = false; } RemoteVideo.prototype = Object.create(SmallVideo.prototype); @@ -34,6 +36,126 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() { return this.container; }; + +/** + * Initializes the remote participant popup menu, by specifying previously + * constructed popupMenuElement, containing all the menu items. + * + * @param popupMenuElement a pre-constructed element, containing the menu items + * to display in the popup + */ +RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) { + this.popover = new JitsiPopover( + $("#" + this.videoSpanId + " > .remotevideomenu"), + { content: popupMenuElement.outerHTML, + skin: "black"}); + + // override popover show method to make sure we will update the content + // before showing the popover + var origShowFunc = this.popover.show; + this.popover.show = function () { + // update content by forcing it, to finish even if popover + // is not visible + this.updateRemoteVideoMenu(this.isMuted, true); + // call the original show, passing its actual this + origShowFunc.call(this.popover); + }.bind(this); +}; + +/** + * Generates the popup menu content. + * + * @returns {Element|*} the constructed element, containing popup menu items + * @private + */ +RemoteVideo.prototype._generatePopupContent = function () { + var popupmenuElement = document.createElement('ul'); + popupmenuElement.className = 'popupmenu'; + popupmenuElement.id = `remote_popupmenu_${this.id}`; + + var muteMenuItem = document.createElement('li'); + var muteLinkItem = document.createElement('a'); + + var mutedIndicator = ""; + + var doMuteHTML = mutedIndicator + + "
" + + APP.translation.translateString("videothumbnail.domute") + + "
"; + + var mutedHTML = mutedIndicator + + "
" + + APP.translation.translateString("videothumbnail.muted") + + "
"; + + muteLinkItem.id = "muteLinkItem"; + + if (this.isMuted) { + muteLinkItem.innerHTML = mutedHTML; + muteLinkItem.className = 'mutelink disabled'; + } + else { + muteLinkItem.innerHTML = doMuteHTML; + muteLinkItem.className = 'mutelink'; + } + + // Delegate event to the document. + $(document).on("click", ".mutelink", function(){ + + if (this.isMuted) + return; + + this.emitter.emit(UIEvents.REMOTE_AUDIO_MUTED, this.id); + + this.popover.forceHide(); + }.bind(this)); + + muteMenuItem.appendChild(muteLinkItem); + popupmenuElement.appendChild(muteMenuItem); + + var ejectIndicator = ""; + + var ejectMenuItem = document.createElement('li'); + var ejectLinkItem = document.createElement('a'); + + var ejectText = "
" + + APP.translation.translateString("videothumbnail.kick") + + "
"; + + ejectLinkItem.className = 'ejectlink'; + ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText; + + $(document).on("click", ".ejectlink", function(){ + this.emitter.emit(UIEvents.USER_KICKED, this.id); + this.popover.forceHide(); + }.bind(this)); + + ejectMenuItem.appendChild(ejectLinkItem); + popupmenuElement.appendChild(ejectMenuItem); + + return popupmenuElement; +}; + +/** + * Updates the remote video menu. + * + * @param isMuted the new muted state to update to + * @param force to work even if popover is not visible + */ +RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted, force) { + + this.isMuted = isMuted; + + // generate content, translate it and add it to document only if + // popover is visible or we force to do so. + if(this.popover.popoverShown || force) { + this.popover.updateContent(this._generatePopupContent()); + } +}; + /** * Adds the remote video menu element for the given id in the * given parentElement. @@ -43,9 +165,8 @@ RemoteVideo.prototype.addRemoteVideoContainer = function() { */ if (!interfaceConfig.filmStripOnly) { RemoteVideo.prototype.addRemoteVideoMenu = function () { - var spanElement = document.createElement('span'); + var spanElement = document.createElement('div'); spanElement.className = 'remotevideomenu'; - this.container.appendChild(spanElement); var menuElement = document.createElement('i'); @@ -53,77 +174,7 @@ if (!interfaceConfig.filmStripOnly) { menuElement.title = 'Remote user controls'; spanElement.appendChild(menuElement); - - var popupmenuElement = document.createElement('ul'); - popupmenuElement.className = 'popupmenu'; - popupmenuElement.id = `remote_popupmenu_${this.id}`; - spanElement.appendChild(popupmenuElement); - - var muteMenuItem = document.createElement('li'); - var muteLinkItem = document.createElement('a'); - - var mutedIndicator = ""; - - if (!this.isMuted) { - muteLinkItem.innerHTML = mutedIndicator + - "
"; - muteLinkItem.className = 'mutelink'; - } - else { - muteLinkItem.innerHTML = mutedIndicator + - "
"; - muteLinkItem.className = 'mutelink disabled'; - } - - muteLinkItem.onclick = (event) => { - if ($(this).attr('disabled')) { - event.preventDefault(); - } - var isMute = !!this.isMuted; - this.emitter.emit(UIEvents.REMOTE_AUDIO_MUTED, this.id); - - popupmenuElement.setAttribute('style', 'display:none;'); - - if (isMute) { - this.innerHTML = mutedIndicator + - "
"; - this.className = 'mutelink disabled'; - } - else { - this.innerHTML = mutedIndicator + - "
"; - this.className = 'mutelink'; - } - }; - - muteMenuItem.appendChild(muteLinkItem); - popupmenuElement.appendChild(muteMenuItem); - - var ejectIndicator = ""; - - var ejectMenuItem = document.createElement('li'); - var ejectLinkItem = document.createElement('a'); - var ejectText = "
 
"; - ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText; - ejectLinkItem.onclick = (event) => { - this.emitter.emit(UIEvents.USER_KICKED, this.id); - 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")); + this._initPopupMenu(this._generatePopupContent()); }; } else { @@ -313,31 +364,6 @@ RemoteVideo.prototype.hideConnectionIndicator = function () { this.connectionIndicator.hide(); }; -/** - * Updates the remote video menu. - * - * @param id the id 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.id}>li>a.mutelink`); - - var mutedIndicator = ""; - - if (muteMenuItem.length) { - var muteLink = muteMenuItem.get(0); - - if (isMuted) { - 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. */ @@ -388,6 +414,7 @@ RemoteVideo.prototype.setDisplayName = function(displayName, key) { RemoteVideo.prototype.removeRemoteVideoMenu = function() { var menuSpan = $('#' + this.videoSpanId + '>span.remotevideomenu'); if (menuSpan.length) { + this.popover.forceHide(); menuSpan.remove(); } };