diff --git a/app.js b/app.js index 4da17677b..705dfcaa7 100644 --- a/app.js +++ b/app.js @@ -10,7 +10,7 @@ var focusJid = null; var roomUrl = null; var roomName = null; var ssrc2jid = {}; -var mediaStreams = []; +var mediaStreams = {}; var bridgeIsDown = false; /** @@ -108,9 +108,16 @@ function connect(jid, password) { localVideo = connection.jingle.localVideo; } connection = new Strophe.Connection(document.getElementById('boshURL').value || config.bosh || '/http-bind'); - - if (nickname) { - connection.emuc.addDisplayNameToPresence(nickname); + + var email = SettingsMenu.getEmail(); + var displayName = SettingsMenu.getDisplayName(); + if(email) { + connection.emuc.addEmailToPresence(email); + } else { + connection.emuc.addUserIdToPresence(SettingsMenu.getUID()); + } + if(displayName) { + connection.emuc.addDisplayNameToPresence(displayName); } if (connection.disco) { @@ -355,7 +362,12 @@ function waitForPresence(data, sid) { // NOTE(gp) now that we have simulcast, a media stream can have more than 1 // ssrc. We should probably take that into account in our MediaStream // wrapper. - mediaStreams.push(new MediaStream(data, sid, thessrc)); + var mediaStream = new MediaStream(data, sid, thessrc); + var jid = data.peerjid || connection.emuc.myroomjid; + if(!mediaStreams[jid]) { + mediaStreams[jid] = {}; + } + mediaStreams[jid][mediaStream.type] = mediaStream; var container; var remotes = document.getElementById('remoteVideos'); @@ -392,6 +404,8 @@ function waitForPresence(data, sid) { data.stream, data.peerjid, thessrc); + if(isVideo && container.id !== 'mixedstream') + videoSrcToSsrc[$(container).find('>video')[0].src] = thessrc; } // an attempt to work around https://github.com/jitsi/jitmeet/issues/32 @@ -691,7 +705,7 @@ $(document).bind('joined.muc', function (event, jid, info) { ); // Add myself to the contact list. - ContactList.addContact(jid); + ContactList.addContact(jid, SettingsMenu.getEmail() || SettingsMenu.getUID()); // Once we've joined the muc show the toolbar ToolbarToggler.showToolbar(); @@ -718,7 +732,12 @@ $(document).bind('entered.muc', function (event, jid, info, pres) { } // Add Peer's container - VideoLayout.ensurePeerContainerExists(jid); + var id = $(pres).find('>userID').text(); + var email = $(pres).find('>email'); + if(email.length > 0) { + id = email.text(); + } + VideoLayout.ensurePeerContainerExists(jid,id); if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantJoined")) { @@ -845,6 +864,13 @@ $(document).bind('presence.muc', function (event, jid, info, pres) { "Jitsi Videobridge is currently unavailable. Please try again later!"); } + var id = $(pres).find('>userID').text(); + var email = $(pres).find('>email'); + if(email.length > 0) { + id = email.text(); + } + Avatar.setUserAvatar(jid, id); + }); $(document).bind('presence.status.muc', function (event, jid, info, pres) { @@ -1310,15 +1336,20 @@ $(document).ready(function () { "showMethod": "fadeIn", "hideMethod": "fadeOut", "reposition": function() { - if(Chat.isVisible() || ContactList.isVisible()) { - $("#toast-container").addClass("toast-bottom-right-center"); + if(PanelToggler.isVisible()) { + $("#toast-container").addClass("notification-bottom-right-center"); } else { - $("#toast-container").removeClass("toast-bottom-right-center"); + $("#toast-container").removeClass("notification-bottom-right-center"); } }, "newestOnTop": false - } + }; + $('#settingsmenu>input').keyup(function(event){ + if(event.keyCode === 13) {//enter + SettingsMenu.update(); + } + }) }); @@ -1617,3 +1648,14 @@ function hangup() { ); } + +$(document).on('videomuted.muc', function(event, jid, value) { + if(mediaStreams[jid] && mediaStreams[jid][MediaStream.VIDEO_TYPE]) { + var stream = mediaStreams[jid][MediaStream.VIDEO_TYPE]; + var isMuted = (value === "true"); + if (isMuted != stream.muted) { + stream.muted = isMuted; + Avatar.showUserAvatar(jid, isMuted); + } + } +}); diff --git a/avatar.js b/avatar.js new file mode 100644 index 000000000..2888f44ae --- /dev/null +++ b/avatar.js @@ -0,0 +1,150 @@ +var Avatar = (function(my) { + var users = {}; + var activeSpeakerJid; + /** + * Sets the user's avatar in the settings menu(if local user), contact list + * and thumbnail + * @param jid jid of the user + * @param id email or userID to be used as a hash + */ + my.setUserAvatar = function(jid, id) { + if(id) { + if(users[jid] === id) { + return; + } + users[jid] = id; + } + var url = getGravatarUrl(users[jid] || jid); + var resourceJid = Strophe.getResourceFromJid(jid); + var thumbnail = $('#participant_' + resourceJid); + var avatar = $('#avatar_' + resourceJid); + + // set the avatar in the settings menu if it is local user and get the + // local video container + if(jid === connection.emuc.myroomjid) { + $('#avatar').get(0).src = url; + thumbnail = $('#localVideoContainer'); + } + + // set the avatar in the contact list + var contact = $('#' + resourceJid + '>img'); + if(contact && contact.length > 0) { + contact.get(0).src = url; + } + + // set the avatar in the thumbnail + if(avatar && avatar.length > 0) { + avatar[0].src = url; + } else { + if (thumbnail && thumbnail.length > 0) { + avatar = document.createElement('img'); + avatar.id = 'avatar_' + resourceJid; + avatar.className = 'userAvatar'; + avatar.src = url; + thumbnail.append(avatar); + } + } + + //if the user is the current active speaker - update the active speaker + // avatar + if(jid === activeSpeakerJid) { + Avatar.updateActiveSpeakerAvatarSrc(jid); + } + }; + + /** + * Hides or shows the user's avatar + * @param jid jid of the user + * @param show whether we should show the avatar or not + * video because there is no dominant speaker and no focused speaker + */ + my.showUserAvatar = function(jid, show) { + if(users[jid]) { + var resourceJid = Strophe.getResourceFromJid(jid); + var video = $('#participant_' + resourceJid + '>video'); + var avatar = $('#avatar_' + resourceJid); + + if(jid === connection.emuc.myroomjid) { + video = $('#localVideoWrapper>video'); + } + if(show === undefined || show === null) { + show = isUserMuted(jid); + } + + //if the user is the currently focused, the dominant speaker or if + //there is no focused and no dominant speaker + if (activeSpeakerJid === jid) { + setVisibility($("#largeVideo"), !show); + setVisibility($('#activeSpeakerAvatar'), show); + setVisibility(avatar, false); + setVisibility(video, false); + } else { + if (video && video.length > 0) { + setVisibility(video, !show); + setVisibility(avatar, show); + } + } + } + }; + + /** + * Updates the src of the active speaker avatar + * @param jid of the current active speaker + */ + my.updateActiveSpeakerAvatarSrc = function(jid) { + if(!jid) { + if (focusedVideoSrc) { + jid = getJidFromVideoSrc(focusedVideoSrc); + } else { + jid = connection.emuc.findJidFromResource( + VideoLayout.getDominantSpeakerResourceJid()); + } + } + var avatar = $("#activeSpeakerAvatar")[0]; + var url = getGravatarUrl(users[jid], + interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE); + if(jid === activeSpeakerJid && avatar.src === url) { + return; + } + activeSpeakerJid = jid; + var isMuted = isUserMuted(jid); + if(jid && isMuted !== null) { + avatar.src = url; + setVisibility($("#largeVideo"), !isMuted); + Avatar.showUserAvatar(jid, isMuted); + } + }; + + function setVisibility(selector, show) { + if (selector && selector.length > 0) { + selector.css("visibility", show ? "visible" : "hidden"); + } + } + + function isUserMuted(jid) { + // XXX(gp) we may want to rename this method to something like + // isUserStreaming, for example. + if (jid && jid != connection.emuc.myroomjid) { + var resource = Strophe.getResourceFromJid(jid); + if (!VideoLayout.isInLastN(resource)) { + return true; + } + } + + if (!mediaStreams[jid] || !mediaStreams[jid][MediaStream.VIDEO_TYPE]) { + return null; + } + return mediaStreams[jid][MediaStream.VIDEO_TYPE].muted; + } + + function getGravatarUrl(id, size) { + if(id === connection.emuc.myroomjid || !id) { + id = SettingsMenu.getUID(); + } + return 'https://www.gravatar.com/avatar/' + + MD5.hexdigest(id.trim().toLowerCase()) + + "?d=retro&size=" + (size || "30"); + } + + return my; +}(Avatar || {})); diff --git a/bottom_toolbar.js b/bottom_toolbar.js index c504c944d..13073632e 100644 --- a/bottom_toolbar.js +++ b/bottom_toolbar.js @@ -1,30 +1,10 @@ var BottomToolbar = (function (my) { my.toggleChat = function() { - if (ContactList.isVisible()) { - buttonClick("#contactListButton", "active"); - $('#contactlist').css('z-index', 4); - setTimeout(function() { - $('#contactlist').css('display', 'none'); - $('#contactlist').css('z-index', 5); - }, 500); - } - - Chat.toggleChat(); - - buttonClick("#chatBottomButton", "active"); + PanelToggler.toggleChat(); }; my.toggleContactList = function() { - if (Chat.isVisible()) { - buttonClick("#chatBottomButton", "active"); - setTimeout(function() { - $('#chatspace').css('display', 'none'); - }, 500); - } - - buttonClick("#contactListButton", "active"); - - ContactList.toggleContactList(); + PanelToggler.toggleContactList(); }; my.toggleFilmStrip = function() { diff --git a/chat.js b/chat.js index e90635042..2dee6c514 100644 --- a/chat.js +++ b/chat.js @@ -57,7 +57,7 @@ var Chat = (function (my) { var onTextAreaResize = function () { resizeChatConversation(); - scrollChatToBottom(); + Chat.scrollChatToBottom(); }; $('#usermsg').autosize({callback: onTextAreaResize}); @@ -144,112 +144,7 @@ var Chat = (function (my) { } }; - /** - * Opens / closes the chat area. - */ - my.toggleChat = function () { - var chatspace = $('#chatspace'); - var videospace = $('#videospace'); - var chatSize = (Chat.isVisible()) ? [0, 0] : Chat.getChatSize(); - var videospaceWidth = window.innerWidth - chatSize[0]; - var videospaceHeight = window.innerHeight; - var videoSize - = getVideoSize(null, null, videospaceWidth, videospaceHeight); - var videoWidth = videoSize[0]; - var videoHeight = videoSize[1]; - var videoPosition = 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]; - var completeFunction = Chat.isVisible() ? - function() {} : function () { - scrollChatToBottom(); - chatspace.trigger('shown'); - }; - - videospace.animate({right: chatSize[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 - } - ); - - if (Chat.isVisible()) { - $("#toast-container").animate({right: '5px'}, - {queue: false, - duration: 500}); - chatspace.hide("slide", { direction: "right", - queue: false, - duration: 500}); - - } - else { - // Undock the toolbar when the chat is shown and if we're in a - // video mode. - if (VideoLayout.isLargeVideoVisible()) { - ToolbarToggler.dockToolbar(false); - } - - - $("#toast-container").animate({right: (chatSize[0] + 5) + 'px'}, - {queue: false, - duration: 500}); - chatspace.show("slide", { direction: "right", - queue: false, - duration: 500, - complete: function () { - // Request the focus in the nickname field or the chat input field. - if ($('#nickname').css('visibility') === 'visible') { - $('#nickinput').focus(); - } else { - $('#usermsg').focus(); - } - } - }); - - Chat.resizeChat(); - } - - }; /** * Sets the chat conversation mode. @@ -268,7 +163,7 @@ var Chat = (function (my) { * Resizes the chat area. */ my.resizeChat = function () { - var chatSize = Chat.getChatSize(); + var chatSize = PanelToggler.getPanelSize(); $('#chatspace').width(chatSize[0]); $('#chatspace').height(chatSize[1]); @@ -276,20 +171,6 @@ var Chat = (function (my) { resizeChatConversation(); }; - /** - * Returns the size of the chat. - */ - my.getChatSize = function () { - var availableHeight = window.innerHeight; - var availableWidth = window.innerWidth; - - var chatWidth = 200; - if (availableWidth * 0.2 < 200) - chatWidth = availableWidth * 0.2; - - return [chatWidth, availableHeight]; - }; - /** * Indicates if the chat is currently visible. */ @@ -309,6 +190,16 @@ var Chat = (function (my) { $('#usermsg').focus(); }; + /** + * Scrolls chat to the bottom. + */ + my.scrollChatToBottom = function() { + setTimeout(function () { + $('#chatconversation').scrollTop( + $('#chatconversation')[0].scrollHeight); + }, 5); + }; + /** * Adds the smileys container to the chat */ @@ -426,15 +317,6 @@ var Chat = (function (my) { } } - /** - * Scrolls chat to the bottom. - */ - function scrollChatToBottom() { - setTimeout(function () { - $('#chatconversation').scrollTop( - $('#chatconversation')[0].scrollHeight); - }, 5); - } /** * Returns the current time in the format it is shown to the user diff --git a/contact_list.js b/contact_list.js index 4e31d1be7..68536ff72 100644 --- a/contact_list.js +++ b/contact_list.js @@ -20,22 +20,24 @@ var ContactList = (function (my) { * Adds a contact for the given peerJid if such doesn't yet exist. * * @param peerJid the peerJid corresponding to the contact + * @param id the user's email or userId used to get the user's avatar */ - my.ensureAddContact = function(peerJid) { + my.ensureAddContact = function(peerJid, id) { var resourceJid = Strophe.getResourceFromJid(peerJid); var contact = $('#contactlist>ul>li[id="' + resourceJid + '"]'); if (!contact || contact.length <= 0) - ContactList.addContact(peerJid); + ContactList.addContact(peerJid,id); }; /** * Adds a contact for the given peer jid. * * @param peerJid the jid of the contact to add + * @param id the email or userId of the user */ - my.addContact = function(peerJid) { + my.addContact = function(peerJid, id) { var resourceJid = Strophe.getResourceFromJid(peerJid); var contactlist = $('#contactlist>ul'); @@ -51,7 +53,7 @@ var ContactList = (function (my) { } }; - newContact.appendChild(createAvatar()); + newContact.appendChild(createAvatar(id)); newContact.appendChild(createDisplayNameParagraph("Participant")); var clElement = contactlist.get(0); @@ -87,145 +89,7 @@ var ContactList = (function (my) { } }; - /** - * Opens / closes the contact list area. - */ - my.toggleContactList = function () { - var contactlist = $('#contactlist'); - var videospace = $('#videospace'); - - var chatSize = (ContactList.isVisible()) ? [0, 0] : Chat.getChatSize(); - var videospaceWidth = window.innerWidth - chatSize[0]; - var videospaceHeight = window.innerHeight; - var videoSize - = getVideoSize(null, null, videospaceWidth, videospaceHeight); - var videoWidth = videoSize[0]; - var videoHeight = videoSize[1]; - var videoPosition = 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]; - var completeFunction = ContactList.isVisible() ? - function() {} : function () { contactlist.trigger('shown');}; - - videospace.animate({right: chatSize[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 - }); - - if (ContactList.isVisible()) { - $("#toast-container").animate({right: '12px'}, - {queue: false, - duration: 500}); - $('#contactlist').hide("slide", { direction: "right", - queue: false, - duration: 500}); - } else { - // Undock the toolbar when the chat is shown and if we're in a - // video mode. - if (VideoLayout.isLargeVideoVisible()) - ToolbarToggler.dockToolbar(false); - - - $("#toast-container").animate({right: '212px'}, - {queue: false, - duration: 500}); - $('#contactlist').show("slide", { direction: "right", - queue: false, - duration: 500}); - - //stop the glowing of the contact list icon - setVisualNotification(false); - } - }; - - /** - * Updates the number of participants in the contact list button and sets - * the glow - * @param delta indicates whether a new user has joined (1) or someone has - * left(-1) - */ - function updateNumberOfParticipants(delta) { - //when the user is alone we don't show the number of participants - if(numberOfContacts === 0) { - $("#numberOfParticipants").text(''); - numberOfContacts += delta; - } else if(numberOfContacts !== 0 && !ContactList.isVisible()) { - setVisualNotification(true); - numberOfContacts += delta; - $("#numberOfParticipants").text(numberOfContacts); - } - }; - - /** - * Creates the avatar element. - * - * @return the newly created avatar element - */ - function createAvatar() { - var avatar = document.createElement('i'); - avatar.className = "icon-avatar avatar"; - - return avatar; - } - - /** - * Creates the display name paragraph. - * - * @param displayName the display name to set - */ - function createDisplayNameParagraph(displayName) { - var p = document.createElement('p'); - p.innerText = displayName; - - return p; - } - - /** - * Shows/hides a visual notification, indicating that a new user has joined - * the conference. - */ - function setVisualNotification(show, stopGlowingIn) { + my.setVisualNotification = function(show, stopGlowingIn) { var glower = $('#contactListButton'); function stopGlowing() { window.clearInterval(notificationInterval); @@ -247,8 +111,52 @@ var ContactList = (function (my) { if(stopGlowingIn) { setTimeout(stopGlowing, stopGlowingIn); } + }; + + /** + * Updates the number of participants in the contact list button and sets + * the glow + * @param delta indicates whether a new user has joined (1) or someone has + * left(-1) + */ + function updateNumberOfParticipants(delta) { + //when the user is alone we don't show the number of participants + if(numberOfContacts === 0) { + $("#numberOfParticipants").text(''); + numberOfContacts += delta; + } else if(numberOfContacts !== 0 && !ContactList.isVisible()) { + ContactList.setVisualNotification(true); + numberOfContacts += delta; + $("#numberOfParticipants").text(numberOfContacts); + } } + /** + * Creates the avatar element. + * + * @return the newly created avatar element + */ + function createAvatar(id) { + var avatar = document.createElement('img'); + avatar.className = "icon-avatar avatar"; + avatar.src = "https://www.gravatar.com/avatar/" + id + "?d=retro&size=30"; + + return avatar; + } + + /** + * Creates the display name paragraph. + * + * @param displayName the display name to set + */ + function createDisplayNameParagraph(displayName) { + var p = document.createElement('p'); + p.innerText = displayName; + + return p; + } + + /** * Indicates that the display name has changed. */ diff --git a/css/contact_list.css b/css/contact_list.css index 335f26e8a..07a682162 100644 --- a/css/contact_list.css +++ b/css/contact_list.css @@ -34,6 +34,7 @@ margin-right: 10px; vertical-align: middle; font-size: 22pt; + border-radius: 20px; } #contactlist .clickable { diff --git a/css/font.css b/css/font.css index e08b8705d..dbd44a2f7 100644 --- a/css/font.css +++ b/css/font.css @@ -112,4 +112,8 @@ .icon-connection:before { line-height: normal; content: "\e61a"; +} + +.icon-settings:before { + content: "\e61b"; } \ No newline at end of file diff --git a/css/main.css b/css/main.css index 9a0a6e54e..4f55b58e8 100644 --- a/css/main.css +++ b/css/main.css @@ -13,8 +13,7 @@ html, body{ overflow-x: hidden; } -#chatspace, -#contactlist { +.right-panel { display:none; position:absolute; float: right; @@ -38,10 +37,6 @@ html, body{ display:none; } -#settingsButton { - visibility: hidden; -} - .toolbar_span { display: inline-block; position: relative; diff --git a/css/settingsmenu.css b/css/settingsmenu.css new file mode 100644 index 000000000..46d68f69e --- /dev/null +++ b/css/settingsmenu.css @@ -0,0 +1,45 @@ +#settingsmenu { + background: black; + color: #00ccff; +} + +#settingsmenu input { + margin-top: 10px; + margin-left: 10%; + width: 80%; + font-size: 14px; + background: #3a3a3a; + border: none; + box-shadow: none; + color: #a7a7a7; +} + +#settingsmenu .arrow-up { + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #3a3a3a; + position: relative; + top: 10px; + margin-left: auto; + margin-right: auto; +} + +#settingsmenu button { + width: 36%; + left: 32%; + padding: 0; + margin-top: 10px; +} + +#settingsmenu #avatar { + width: 24%; + left: 38%; + border-radius: 25px; + position: relative; +} + +#settingsmenu .icon-settings { + padding: 34px; +} diff --git a/css/videolayout_default.css b/css/videolayout_default.css index 1bfddccf2..401c6f69d 100644 --- a/css/videolayout_default.css +++ b/css/videolayout_default.css @@ -35,7 +35,9 @@ #remoteVideos .videocontainer { display: inline-block; - background-image:url(../images/avatar1.png); + background-color: black; + background-repeat: no-repeat; + background-position: 45; background-size: contain; border-radius:8px; border: 2px solid #212425; @@ -115,10 +117,6 @@ height: 100%; } -.dominantspeaker { - background: #000 !important; -} - #etherpad, #presentation { text-align: center; @@ -378,3 +376,19 @@ #mixedstream { display:none !important; } + +#activeSpeakerAvatar { + visibility: hidden; + width: 100px; + height: 100px; + margin: auto; + position: relative; + border-radius: 50px; +} + +.userAvatar { + height: 100%; + position: absolute; + left: 35px; + border-radius: 200px; +} diff --git a/fonts/jitsi.eot b/fonts/jitsi.eot index eaed7ee86..d85113241 100644 Binary files a/fonts/jitsi.eot and b/fonts/jitsi.eot differ diff --git a/fonts/jitsi.svg b/fonts/jitsi.svg index 007716b1b..19ef9bd0a 100644 --- a/fonts/jitsi.svg +++ b/fonts/jitsi.svg @@ -34,4 +34,5 @@ + \ No newline at end of file diff --git a/fonts/jitsi.ttf b/fonts/jitsi.ttf index 90d00844c..e3d1af5c6 100644 Binary files a/fonts/jitsi.ttf and b/fonts/jitsi.ttf differ diff --git a/fonts/jitsi.woff b/fonts/jitsi.woff index 7983dc6c2..a6df0b031 100644 Binary files a/fonts/jitsi.woff and b/fonts/jitsi.woff differ diff --git a/fonts/selection.json b/fonts/selection.json index 2939893ed..9d8efb6e7 100644 --- a/fonts/selection.json +++ b/fonts/selection.json @@ -1,12 +1,46 @@ { "IcoMoonType": "selection", "icons": [ + { + "icon": { + "paths": [ + "M526.071 725.251c-28.637 30.869-56.465 60.861-84.282 90.859-51.578 55.636-103.047 111.376-154.842 166.832-7.606 8.135-15.958 16.1-25.317 22.012-28.075 17.708-58.31 18.090-88.472 6.492-59.84-23.028-80.004-90.727-59.734-139.234 5.413-12.95 13.721-23.601 23.709-33.173 70.256-67.351 140.506-134.717 210.76-202.077 15.638-14.993 31.264-29.995 47.364-45.45-9.302-9.529-18.386-18.833-27.451-28.137-12.122-12.442-13.234-20.28-5.067-35.498 4.735-8.816 4.789-8.878-2.627-16.198-20.012-19.72-40.168-39.198-63.498-55.188-27.167-18.624-57.161-24.233-89.083-19.849-53.402 7.328-91.609 38.372-121.413 81.046-12.774 18.299-15.365 40.313-17.517 61.875-3.23 32.245-2.415 64.479 2.209 96.597 1.654 11.515-3.863 16.539-13.835 11.175-8.306-4.448-16.095-11.048-22.115-18.353-15.574-18.89-22.223-42.042-27.474-65.395-12.955-57.652-8.86-114.49 12.191-169.495 32.345-84.537 79.743-159.571 145.953-221.932 13.659-12.857 176.841-180.564 202.944-207.021 7.493-7.599 14.895-7.635 22.393-0.028 43.009 43.641 85.985 87.316 128.927 131.029 8.117 8.267 8.019 15.097-0.222 23.49-26.339 26.834-52.726 53.627-79.106 80.419-6.244 6.334-97.34 82.437-73.027 128.816 22.693 25.090 46.196 49.449 69.575 73.904 1.189 1.238 4.686 1.386 6.523 0.632 3.63-1.499 6.848-3.997 10.248-6.066 9.745-5.94 19.545-4.918 27.812 3.083 11.755 11.381 23.405 22.858 35.392 34.59 4.807-4.575 9.939-9.41 15.027-14.294 27.128-26.039 54.272-52.071 81.351-78.146 16.413-15.778 18.652-28.418 11.038-49.658-10.473-29.221-14.356-59.677-13.85-90.624 1.017-61.045 20.438-115.334 61.003-161.416 32.825-37.286 72.054-64.311 121.643-74.325 35.227-7.101 69.139-4.513 100.663 14.026 6.365 3.752 11.908 9.007 17.455 14.005 3.491 3.125 3.153 6.236-0.565 9.98-42.503 42.885-84.772 86.013-127.154 129.035-12.442 12.638-12.356 23.167 0.196 35.914 40.344 40.978 80.597 82.050 120.936 123.052 10.076 10.233 19.537 10.021 29.504-0.134 43.195-44.077 86.449-88.090 129.706-132.118 1.21-1.233 2.572-2.322 5.135-4.624 5.491 5.893 11.895 10.924 15.961 17.406 19.452 30.944 22.608 64.83 17.073 100.25-14.253 91.080-97.188 175.638-197.712 190.123-39.977 5.764-79.372 2.562-118.067-9.031-5.898-1.775-11.541-4.629-17.538-5.829-12.47-2.474-23.872 0.366-32.74 9.877-30.921 33.168-61.674 66.484-92.474 99.758-0.73 0.805-1.349 1.718-0.181 1.099 8.992 10.006 17.354 20.662 27.061 29.94 81.064 77.54 164.91 151.986 250.882 224.063 9.936 8.347 10.274 15.695 1.040 25.1-42.338 43.068-84.689 86.111-127.059 129.154-9.413 9.575-16.846 9.152-25.291-1.295-76.686-94.78-156.8-186.609-239.707-276.002-1.334-1.453-2.562-3.029-4.257-5.042z" + ], + "attrs": [ + { + "opacity": 1, + "visibility": false + } + ], + "width": 1105, + "grid": 0, + "tags": [ + "settings" + ] + }, + "attrs": [ + { + "opacity": 1, + "visibility": false + } + ], + "properties": { + "order": 1, + "id": 33, + "prevSize": 32, + "code": 58907, + "name": "settings" + }, + "setIdx": 0, + "iconIdx": 0 + }, { "icon": { "paths": [ "M1223.129 242.783l-180.128 175.796v-217.716c0-74.673-59.512-135.496-132.599-135.496h-634.716c-73.084 0-132.596 60.823-132.596 135.496v609.237c0 74.673 59.512 135.496 132.596 135.496h634.716c73.084 0 132.599-60.82 132.599-135.496v-172.679l193.45 153.712c48.784 35.558 96.695-5.178 96.695-40.424v-483.533c-0.003-35.248-55.897-71.306-110.017-24.393zM601.169 760.065c-141.111 0-255.524-114.411-255.524-255.521s114.411-255.521 255.524-255.521c141.108 0 255.519 114.411 255.519 255.521-0 141.113-114.408 255.521-255.519 255.521z", "M599.045 359.751c-80.474 0-145.727 65.253-145.727 145.729 0 80.471 65.25 145.727 145.727 145.727s145.729-65.256 145.729-145.727c0-80.474-65.253-145.729-145.729-145.729z" ], + "width": 1334, "attrs": [ { "opacity": 1, @@ -17,11 +51,10 @@ "visibility": false } ], - "width": 1334, - "grid": 0, "tags": [ "webCam" - ] + ], + "grid": 0 }, "attrs": [ { @@ -714,7 +747,7 @@ "ligatures": "" }, "setIdx": 0, - "iconIdx": 26 + "iconIdx": 25 }, { "icon": { @@ -740,7 +773,7 @@ "ligatures": "" }, "setIdx": 0, - "iconIdx": 27 + "iconIdx": 26 }, { "icon": { @@ -765,7 +798,7 @@ "ligatures": "" }, "setIdx": 0, - "iconIdx": 28 + "iconIdx": 27 } ], "height": 1024, @@ -775,6 +808,7 @@ "preferences": { "showGlyphs": true, "showQuickUse": true, + "showQuickUse2": true, "showSVGs": true, "fontPref": { "prefix": "icon-", @@ -791,7 +825,8 @@ }, "imagePref": { "prefix": "icon-", - "png": true + "png": true, + "useClassSelector": true }, "historySize": 100, "showCodes": true, diff --git a/index.html b/index.html index 3f54be9f4..312ba8bb4 100644 --- a/index.html +++ b/index.html @@ -29,17 +29,18 @@ - - + + - + - - - + + + + @@ -48,14 +49,14 @@ - + - - + + @@ -64,19 +65,22 @@ - - + + + - - + + + - + + @@ -230,6 +234,10 @@
+ + + +
@@ -256,6 +264,7 @@
powered by jitsi.org +
@@ -299,7 +308,7 @@
-
+
Enter a nickname in the box below
@@ -317,11 +326,19 @@
-
+
  • CONTACT LIST
+
+
SETTINGS
+ +
+ + + +
diff --git a/interface_config.js b/interface_config.js index b8e836033..a9c20b65a 100644 --- a/interface_config.js +++ b/interface_config.js @@ -6,11 +6,13 @@ var interfaceConfig = { TOOLBAR_TIMEOUT: 4000, DEFAULT_REMOTE_DISPLAY_NAME: "Fellow Jitster", DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME: "Speaker", + DEFAULT_LOCAL_DISPLAY_NAME: "me", SHOW_JITSI_WATERMARK: true, JITSI_WATERMARK_LINK: "http://jitsi.org", SHOW_BRAND_WATERMARK: false, BRAND_WATERMARK_LINK: "", SHOW_POWERED_BY: false, GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true, - APP_NAME: "Jitsi Meet" + APP_NAME: "Jitsi Meet", + ACTIVE_SPEAKER_AVATAR_SIZE: 100 }; diff --git a/media_stream.js b/media_stream.js index 7a71f6aba..348a258e5 100644 --- a/media_stream.js +++ b/media_stream.js @@ -16,15 +16,17 @@ var MediaStream = (function() { * @constructor */ function MediaStreamProto(data, sid, ssrc) { - this.VIDEO_TYPE = "Video"; - this.AUDIO_TYPE = "Audio"; this.stream = data.stream; this.peerjid = data.peerjid; this.ssrc = ssrc; this.session = connection.jingle.sessions[sid]; this.type = (this.stream.getVideoTracks().length > 0) - ? this.VIDEO_TYPE : this.AUDIO_TYPE; + ? MediaStream.VIDEO_TYPE : MediaStream.AUDIO_TYPE; + this.muted = false; } return MediaStreamProto; -})(); \ No newline at end of file +})(); + +MediaStream.VIDEO_TYPE = 'Video'; +MediaStream.AUDIO_TYPE = 'Audio'; \ No newline at end of file diff --git a/muc.js b/muc.js index 2e31bc9ac..d131e3da8 100644 --- a/muc.js +++ b/muc.js @@ -357,6 +357,14 @@ Strophe.addConnectionPlugin('emuc', { pres.c('bridgeIsDown').up(); } + if(this.presMap['email']) { + pres.c('email').t(this.presMap['email']).up(); + } + + if(this.presMap['userId']) { + pres.c('userId').t(this.presMap['userId']).up(); + } + if (this.presMap['displayName']) { // XEP-0172 pres.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}) @@ -479,6 +487,12 @@ Strophe.addConnectionPlugin('emuc', { addBridgeIsDownToPresence: function() { this.presMap['bridgeIsDown'] = true; }, + addEmailToPresence: function(email) { + this.presMap['email'] = email; + }, + addUserIdToPresence: function(userId) { + this.presMap['userId'] = userId; + }, isModerator: function() { return this.role === 'moderator'; } diff --git a/settings_menu.js b/settings_menu.js new file mode 100644 index 000000000..978836640 --- /dev/null +++ b/settings_menu.js @@ -0,0 +1,83 @@ +var SettingsMenu = (function(my) { + + var email = ''; + var displayName = ''; + var userId; + + if(supportsLocalStorage()) { + if(!window.localStorage.jitsiMeetId) { + window.localStorage.jitsiMeetId = generateUniqueId(); + console.log("generated id", window.localStorage.jitsiMeetId); + } + userId = window.localStorage.jitsiMeetId || ''; + email = window.localStorage.email || ''; + displayName = window.localStorage.displayname || ''; + } else { + console.log("local storage is not supported"); + userId = generateUniqueId(); + } + + my.update = function() { + var newDisplayName = Util.escapeHtml($('#setDisplayName').get(0).value); + if(newDisplayName) { + displayName = newDisplayName; + connection.emuc.addDisplayNameToPresence(displayName); + window.localStorage.displayname = displayName; + } + + var newEmail = Util.escapeHtml($('#setEmail').get(0).value); + connection.emuc.addEmailToPresence(newEmail); + email = newEmail; + window.localStorage.email = newEmail; + + connection.emuc.sendPresence(); + Avatar.setUserAvatar(connection.emuc.myroomjid, email); + }; + + my.isVisible = function() { + return $('#settingsmenu').is(':visible'); + }; + + my.getUID = function() { + return userId; + }; + + my.getEmail = function() { + return email; + }; + + my.getDisplayName = function() { + return displayName; + }; + + my.setDisplayName = function(newDisplayName) { + displayName = newDisplayName; + window.localStorage.displayname = displayName; + $('#setDisplayName').get(0).value = displayName; + }; + + function supportsLocalStorage() { + try { + return 'localStorage' in window && window.localStorage !== null; + } catch (e) { + console.log("localstorage is not supported"); + return false; + } + } + + function generateUniqueId() { + function _p8() { + return (Math.random().toString(16)+"000000000").substr(2,8); + } + return _p8() + _p8() + _p8() + _p8(); + } + + $(document).bind('displaynamechanged', function(event, peerJid, newDisplayName) { + if(peerJid === 'localVideoContainer' || + peerJid === connection.emuc.myroomjid) { + SettingsMenu.setDisplayName(newDisplayName); + } + }); + + return my; +}(SettingsMenu || {})); diff --git a/side_panel_toggler.js b/side_panel_toggler.js new file mode 100644 index 000000000..5716c9aa3 --- /dev/null +++ b/side_panel_toggler.js @@ -0,0 +1,245 @@ +/** + * Toggler for the chat, contact list, settings menu, etc.. + */ +var PanelToggler = (function(my) { + + var currentlyOpen = null; + var buttons = { + '#chatspace': '#chatBottomButton', + '#contactlist': '#contactListButton', + '#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 + = getVideoSize(null, null, videospaceWidth, videospaceHeight); + var videoWidth = videoSize[0]; + var videoHeight = videoSize[1]; + var videoPosition = 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 + * @param selector the selector for the element containing the panel + * @param onOpenComplete function to be called when the panel is opened + * @param onOpen function to be called if the window is going to be opened + * @param onClose function to be called if the window is going to be closed + */ + var toggle = function(object, selector, onOpenComplete, onOpen, onClose) { + buttonClick(buttons[selector], "active"); + + if (object.isVisible()) { + $("#toast-container").animate({ + right: '5px' + }, + { + queue: false, + duration: 500 + }); + $(selector).hide("slide", { + direction: "right", + queue: false, + duration: 500 + }); + if(typeof onClose === "function") { + onClose(); + } + + currentlyOpen = null; + } + else { + // Undock the toolbar when the chat is shown and if we're in a + // video mode. + if (VideoLayout.isLargeVideoVisible()) { + ToolbarToggler.dockToolbar(false); + } + + if(currentlyOpen) { + var current = $(currentlyOpen); + buttonClick(buttons[currentlyOpen], "active"); + current.css('z-index', 4); + setTimeout(function () { + current.css('display', 'none'); + current.css('z-index', 5); + }, 500); + } + + $("#toast-container").animate({ + right: (PanelToggler.getPanelSize()[0] + 5) + 'px' + }, + { + queue: false, + duration: 500 + }); + $(selector).show("slide", { + direction: "right", + queue: false, + duration: 500, + complete: onOpenComplete + }); + if(typeof onOpen === "function") { + onOpen(); + } + + currentlyOpen = selector; + } + }; + + /** + * Opens / closes the chat area. + */ + my.toggleChat = function() { + var chatCompleteFunction = Chat.isVisible() ? + function() {} : function () { + Chat.scrollChatToBottom(); + $('#chatspace').trigger('shown'); + }; + + resizeVideoArea(Chat.isVisible(), chatCompleteFunction); + + toggle(Chat, + '#chatspace', + function () { + // Request the focus in the nickname field or the chat input field. + if ($('#nickname').css('visibility') === 'visible') { + $('#nickinput').focus(); + } else { + $('#usermsg').focus(); + } + }, + null, + Chat.resizeChat, + null); + }; + + /** + * Opens / closes the contact list area. + */ + my.toggleContactList = function () { + var completeFunction = ContactList.isVisible() ? + function() {} : function () { $('#contactlist').trigger('shown');}; + resizeVideoArea(ContactList.isVisible(), completeFunction); + + toggle(ContactList, + '#contactlist', + null, + function() { + ContactList.setVisualNotification(false); + }, + null); + }; + + /** + * Opens / closes the settings menu + */ + my.toggleSettingsMenu = function() { + resizeVideoArea(SettingsMenu.isVisible(), function (){}); + toggle(SettingsMenu, + '#settingsmenu', + null, + function() { + $('#setDisplayName').get(0).value = SettingsMenu.getDisplayName(); + $('#setEmail').get(0).value = SettingsMenu.getEmail(); + }, + null); + }; + + /** + * Returns the size of the side panel. + */ + my.getPanelSize = function () { + var availableHeight = window.innerHeight; + var availableWidth = window.innerWidth; + + var panelWidth = 200; + if (availableWidth * 0.2 < 200) { + panelWidth = availableWidth * 0.2; + } + + return [panelWidth, availableHeight]; + }; + + my.isVisible = function() { + return (Chat.isVisible() || ContactList.isVisible() || SettingsMenu.isVisible()); + }; + + return my; + +}(PanelToggler || {})); diff --git a/util.js b/util.js index e9e7f1f60..2e10a8a8b 100644 --- a/util.js +++ b/util.js @@ -51,12 +51,10 @@ var Util = (function (my) { * Returns the available video width. */ my.getAvailableVideoWidth = function () { - var chatspaceWidth - = (Chat.isVisible() || ContactList.isVisible()) - ? $('#chatspace').width() - : 0; + var rightPanelWidth + = PanelToggler.isVisible() ? PanelToggler.getPanelSize()[0] : 0; - return window.innerWidth - chatspaceWidth; + return window.innerWidth - rightPanelWidth; }; my.imageToGrayScale = function (canvas) { diff --git a/videolayout.js b/videolayout.js index d5d831433..0076f2ea8 100644 --- a/videolayout.js +++ b/videolayout.js @@ -11,6 +11,12 @@ var VideoLayout = (function (my) { }; my.connectionIndicators = {}; + my.isInLastN = function(resource) { + return lastNCount < 0 // lastN is disabled, return true + || (lastNCount > 0 && lastNEndpointsCache.length == 0) // lastNEndpoints cache not built yet, return true + || (lastNEndpointsCache && lastNEndpointsCache.indexOf(resource) !== -1); + }; + my.changeLocalAudio = function(stream) { connection.jingle.localAudio = stream; @@ -127,6 +133,7 @@ var VideoLayout = (function (my) { if ($('#largeVideo').attr('src') != newSrc) { + $('#activeSpeakerAvatar').css('visibility', 'hidden'); // Due to the simulcast the localVideoSrc may have changed when the // fadeOut event triggers. In that case the getJidFromVideoSrc and // isVideoSrcDesktop methods will not function correctly. @@ -159,6 +166,8 @@ var VideoLayout = (function (my) { var doUpdate = function () { + Avatar.updateActiveSpeakerAvatarSrc(largeVideoState.userJid); + if (!userChanged && largeVideoState.preload && largeVideoState.preload != null && $(largeVideoState.preload).attr('src') == newSrc) { @@ -231,6 +240,10 @@ var VideoLayout = (function (my) { $(this).fadeIn(300); } + if(userChanged) { + Avatar.showUserAvatar(largeVideoState.oldJid); + } + largeVideoState.updateInProgress = false; }; @@ -372,29 +385,30 @@ var VideoLayout = (function (my) { * in the document and creates it eventually. * * @param peerJid peer Jid to check. + * @param userId user email or id for setting the avatar * * @return Returns true if the peer container exists, * false - otherwise */ - my.ensurePeerContainerExists = function(peerJid) { - ContactList.ensureAddContact(peerJid); + my.ensurePeerContainerExists = function(peerJid, userId) { + ContactList.ensureAddContact(peerJid, userId); var resourceJid = Strophe.getResourceFromJid(peerJid); var videoSpanId = 'participant_' + resourceJid; if ($('#' + videoSpanId).length > 0) { - // If there's been a moderator change, make sure we add moderator - // related interface!! - if (Moderator.isModerator() && - $('#remote_popupmenu_' + resourceJid).length <= 0) + // If there's been a focus change, make sure we add focus related + // interface!! + if (Moderator.isModerator() && $('#remote_popupmenu_' + resourceJid).length <= 0) { addRemoteVideoMenu(peerJid, - document.getElementById(videoSpanId)); + document.getElementById(videoSpanId)); + } } else { - var container - = VideoLayout.addRemoteVideoContainer(peerJid, videoSpanId); - + var container = + VideoLayout.addRemoteVideoContainer(peerJid, videoSpanId, userId); + Avatar.setUserAvatar(peerJid, userId); // Set default display name. setDisplayName(videoSpanId); @@ -473,8 +487,10 @@ var VideoLayout = (function (my) { var videoStream = simulcast.getReceivingVideoStream(stream); RTC.attachMediaStream(sel, videoStream); - if (isVideo) + if (isVideo) { waitForRemoteVideo(sel, thessrc, stream); + } + } stream.onended = function () { @@ -591,14 +607,17 @@ var VideoLayout = (function (my) { peerContainer.show(); } - // TODO(gp) add proper avatars handling. if (state == 'show') { - peerContainer.css('-webkit-filter', ''); + // peerContainer.css('-webkit-filter', ''); + var jid = connection.emuc.findJidFromResource(resourceJid); + Avatar.showUserAvatar(jid, false); } else // if (state == 'avatar') { - peerContainer.css('-webkit-filter', 'grayscale(100%)'); + // peerContainer.css('-webkit-filter', 'grayscale(100%)'); + var jid = connection.emuc.findJidFromResource(resourceJid); + Avatar.showUserAvatar(jid, true); } } else if (peerContainer.is(':visible') && isHide) @@ -624,7 +643,7 @@ var VideoLayout = (function (my) { */ function setDisplayName(videoSpanId, displayName) { var nameSpan = $('#' + videoSpanId + '>span.displayname'); - var defaultLocalDisplayName = "Me"; + var defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME; // If we already have a display name for this video. if (nameSpan.length > 0) { @@ -685,6 +704,7 @@ var VideoLayout = (function (my) { .bind("click", function (e) { e.preventDefault(); + e.stopPropagation(); $('#localDisplayName').hide(); $('#editDisplayName').show(); $('#editDisplayName').focus(); @@ -703,7 +723,7 @@ var VideoLayout = (function (my) { }); } } - }; + } my.inputDisplayNameHandler = function (name) { if (nickname !== name) { @@ -720,7 +740,7 @@ var VideoLayout = (function (my) { $('#localDisplayName').text(nickname + " (me)"); else $('#localDisplayName') - .text(defaultLocalDisplayName); + .text(interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME); $('#localDisplayName').show(); } @@ -921,6 +941,10 @@ var VideoLayout = (function (my) { $('#largeVideoContainer').width(availableWidth); $('#largeVideoContainer').height(availableHeight); + + $('#activeSpeakerAvatar').css('top', + (availableHeight - interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE) / 2); + VideoLayout.resizeThumbnails(); }; @@ -940,6 +964,8 @@ var VideoLayout = (function (my) { $('#remoteVideos>span').width(width); $('#remoteVideos>span').height(height); + $('.userAvatar').css('left', (width - height) / 2); + $(document).trigger("remotevideo.resized", [width, height]); }; @@ -1545,36 +1571,27 @@ var VideoLayout = (function (my) { if (!isVisible) { console.log("Add to last N", resourceJid); - mediaStreams.some(function (mediaStream) { - if (mediaStream.peerjid - && Strophe.getResourceFromJid(mediaStream.peerjid) - === resourceJid - && mediaStream.type === mediaStream.VIDEO_TYPE) { - var sel = $('#participant_' + resourceJid + '>video'); + var jid = connection.emuc.findJidFromResource(resourceJid); + var mediaStream = mediaStreams[jid][MediaStream.VIDEO_TYPE]; + var sel = $('#participant_' + resourceJid + '>video'); - var videoStream = simulcast.getReceivingVideoStream(mediaStream.stream); - RTC.attachMediaStream(sel, videoStream); - videoSrcToSsrc[sel.attr('src')] = mediaStream.ssrc; - if (lastNPickupJid == mediaStream.peerjid) { - // Clean up the lastN pickup jid. - lastNPickupJid = null; + var videoStream = simulcast.getReceivingVideoStream( + mediaStream.stream); + RTC.attachMediaStream(sel, videoStream); + videoSrcToSsrc[sel.attr('src')] = mediaStream.ssrc; + if (lastNPickupJid == mediaStream.peerjid) { + // Clean up the lastN pickup jid. + lastNPickupJid = null; - // Don't fire the events again, they've already - // been fired in the contact list click handler. - VideoLayout.handleVideoThumbClicked($(sel).attr('src'), false); + // Don't fire the events again, they've already + // been fired in the contact list click handler. + VideoLayout.handleVideoThumbClicked($(sel).attr('src'), false); - updateLargeVideo = false; - } - - waitForRemoteVideo( - sel, - mediaStream.ssrc, - mediaStream.stream); - return true; - } - }); + updateLargeVideo = false; + } + waitForRemoteVideo(sel, mediaStream.ssrc, mediaStream.stream); } - }); + }) } // The endpoint that was being shown in the large video has dropped out