do not use RTC/xmpp in UI module
This commit is contained in:
parent
cb522eadd8
commit
6ded050b51
78
app.js
78
app.js
|
@ -13,6 +13,7 @@ import "jQuery-Impromptu";
|
||||||
import "autosize";
|
import "autosize";
|
||||||
window.toastr = require("toastr");
|
window.toastr = require("toastr");
|
||||||
|
|
||||||
|
import URLProcessor from "./modules/config/URLProcessor";
|
||||||
import RoomnameGenerator from './modules/util/RoomnameGenerator';
|
import RoomnameGenerator from './modules/util/RoomnameGenerator';
|
||||||
import CQEvents from './service/connectionquality/CQEvents';
|
import CQEvents from './service/connectionquality/CQEvents';
|
||||||
import UIEvents from './service/UI/UIEvents';
|
import UIEvents from './service/UI/UIEvents';
|
||||||
|
@ -101,23 +102,23 @@ const APP = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var ConnectionEvents = JitsiMeetJS.events.connection;
|
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||||
var ConnectionErrors = JitsiMeetJS.errors.connection;
|
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||||
function connect() {
|
function connect() {
|
||||||
var connection = new JitsiMeetJS.JitsiConnection(null, null, {
|
let connection = new JitsiMeetJS.JitsiConnection(null, null, {
|
||||||
hosts: config.hosts,
|
hosts: config.hosts,
|
||||||
bosh: config.bosh,
|
bosh: config.bosh,
|
||||||
clientNode: config.clientNode
|
clientNode: config.clientNode
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
var handlers = {};
|
let handlers = {};
|
||||||
|
|
||||||
var unsubscribe = function () {
|
function unsubscribe () {
|
||||||
Object.keys(handlers).forEach(function (event) {
|
Object.keys(handlers).forEach(function (event) {
|
||||||
connection.removeEventListener(event, handlers[event]);
|
connection.removeEventListener(event, handlers[event]);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
|
handlers[ConnectionEvents.CONNECTION_ESTABLISHED] = function () {
|
||||||
console.log('CONNECTED');
|
console.log('CONNECTED');
|
||||||
|
@ -125,14 +126,14 @@ function connect() {
|
||||||
resolve(connection);
|
resolve(connection);
|
||||||
};
|
};
|
||||||
|
|
||||||
var listenForFailure = function (event) {
|
function listenForFailure (event) {
|
||||||
handlers[event] = function (...args) {
|
handlers[event] = function (...args) {
|
||||||
console.error(`CONNECTION FAILED: ${event}`, ...args);
|
console.error(`CONNECTION FAILED: ${event}`, ...args);
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
reject([event, ...args]);
|
reject([event, ...args]);
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
listenForFailure(ConnectionEvents.CONNECTION_FAILED);
|
listenForFailure(ConnectionEvents.CONNECTION_FAILED);
|
||||||
listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
|
listenForFailure(ConnectionErrors.PASSWORD_REQUIRED);
|
||||||
|
@ -172,6 +173,13 @@ function initConference(localTracks, connection) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
APP.conference.listMembers = function () {
|
||||||
|
return room.getParticipants();
|
||||||
|
};
|
||||||
|
APP.conference.listMembersIds = function () {
|
||||||
|
return room.getParticipants().map(p => p.getId());
|
||||||
|
};
|
||||||
|
|
||||||
function getDisplayName(id) {
|
function getDisplayName(id) {
|
||||||
if (APP.conference.isLocalId(id)) {
|
if (APP.conference.isLocalId(id)) {
|
||||||
return APP.settings.getDisplayName();
|
return APP.settings.getDisplayName();
|
||||||
|
@ -187,17 +195,22 @@ function initConference(localTracks, connection) {
|
||||||
room.on(ConferenceEvents.CONFERENCE_JOINED, function () {
|
room.on(ConferenceEvents.CONFERENCE_JOINED, function () {
|
||||||
localTracks.forEach(function (track) {
|
localTracks.forEach(function (track) {
|
||||||
room.addTrack(track);
|
room.addTrack(track);
|
||||||
//APP.UI.addLocalStream(track);
|
APP.UI.addLocalStream(track);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
room.on(ConferenceEvents.USER_JOINED, function (id) {
|
room.on(ConferenceEvents.USER_JOINED, function (id, user) {
|
||||||
|
if (APP.conference.isLocalId(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error('USER %s connnected', id);
|
||||||
// FIXME email???
|
// FIXME email???
|
||||||
//APP.UI.addUser(id);
|
APP.UI.addUser(id, user.getDisplayName());
|
||||||
});
|
});
|
||||||
room.on(ConferenceEvents.USER_LEFT, function (id) {
|
room.on(ConferenceEvents.USER_LEFT, function (id, user) {
|
||||||
APP.UI.removeUser(id);
|
console.error('USER LEFT', id);
|
||||||
|
APP.UI.removeUser(id, user.getDisplayName());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,6 +243,26 @@ function initConference(localTracks, connection) {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
room.on(ConferenceEvents.TRACK_ADDED, function (track) {
|
||||||
|
if (!track.getParticipantId) { // skip local tracks
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error(
|
||||||
|
'REMOTE %s TRACK', track.getType(), track.getParticipantId()
|
||||||
|
);
|
||||||
|
APP.UI.addRemoteStream(track);
|
||||||
|
});
|
||||||
|
room.on(ConferenceEvents.TRACK_REMOVED, function (track) {
|
||||||
|
if (!track.getParticipantId) { // skip local tracks
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
'REMOTE %s TRACK REMOVED', track.getType(), track.getParticipantId()
|
||||||
|
);
|
||||||
|
|
||||||
|
// FIXME handle
|
||||||
|
});
|
||||||
room.on(ConferenceEvents.TRACK_MUTE_CHANGED, function (track) {
|
room.on(ConferenceEvents.TRACK_MUTE_CHANGED, function (track) {
|
||||||
// FIXME handle mute
|
// FIXME handle mute
|
||||||
});
|
});
|
||||||
|
@ -421,10 +454,23 @@ function initConference(localTracks, connection) {
|
||||||
// on SUBJECT_CHANGED UI.setSubject(topic);
|
// on SUBJECT_CHANGED UI.setSubject(topic);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
APP.UI.addListener(UIEvents.USER_KICKED, function (id) {
|
||||||
|
// FIXME handle
|
||||||
|
// APP.xmpp.eject(self.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, function (id) {
|
||||||
|
room.selectParticipant(id);
|
||||||
|
});
|
||||||
|
|
||||||
room.on(ConferenceEvents.DTMF_SUPPORT_CHANGED, function (isDTMFSupported) {
|
room.on(ConferenceEvents.DTMF_SUPPORT_CHANGED, function (isDTMFSupported) {
|
||||||
APP.UI.updateDTMFSupport(isDTMFSupported);
|
APP.UI.updateDTMFSupport(isDTMFSupported);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(window).bind('beforeunload', function () {
|
||||||
|
room.leave();
|
||||||
|
});
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
room.on(ConferenceEvents.CONFERENCE_JOINED, resolve);
|
room.on(ConferenceEvents.CONFERENCE_JOINED, resolve);
|
||||||
|
|
||||||
|
@ -456,6 +502,7 @@ function createLocalTracks () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
APP.UI.start();
|
||||||
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
|
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
|
||||||
JitsiMeetJS.init().then(function () {
|
JitsiMeetJS.init().then(function () {
|
||||||
return Promise.all([createLocalTracks(), connect()]);
|
return Promise.all([createLocalTracks(), connect()]);
|
||||||
|
@ -463,8 +510,6 @@ function init() {
|
||||||
console.log('initialized with %s local tracks', tracks.length);
|
console.log('initialized with %s local tracks', tracks.length);
|
||||||
return initConference(tracks, connection);
|
return initConference(tracks, connection);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
APP.UI.start();
|
|
||||||
|
|
||||||
APP.UI.initConference();
|
APP.UI.initConference();
|
||||||
|
|
||||||
APP.UI.addListener(UIEvents.LANG_CHANGED, function (language) {
|
APP.UI.addListener(UIEvents.LANG_CHANGED, function (language) {
|
||||||
|
@ -518,7 +563,6 @@ function obtainConfigAndInit() {
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
console.log("(TIME) document ready:\t", window.performance.now());
|
console.log("(TIME) document ready:\t", window.performance.now());
|
||||||
|
|
||||||
var URLProcessor = require("./modules/config/URLProcessor");
|
|
||||||
URLProcessor.setConfigParametersFromUrl();
|
URLProcessor.setConfigParametersFromUrl();
|
||||||
APP.init();
|
APP.init();
|
||||||
|
|
||||||
|
@ -537,4 +581,4 @@ $(window).bind('beforeunload', function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default APP;
|
module.exports = APP;
|
||||||
|
|
1529
lib-jitsi-meet.js
1529
lib-jitsi-meet.js
File diff suppressed because it is too large
Load Diff
|
@ -1,31 +1,33 @@
|
||||||
/* global Strophe, APP, $, config, interfaceConfig, toastr */
|
/* global APP, $, config, interfaceConfig, toastr */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var UI = {};
|
var UI = {};
|
||||||
|
|
||||||
var VideoLayout = require("./videolayout/VideoLayout");
|
import AudioLevels from './audio_levels/AudioLevels';
|
||||||
var AudioLevels = require("./audio_levels/AudioLevels");
|
import Chat from "./side_pannels/chat/Chat";
|
||||||
|
import Toolbar from "./toolbars/Toolbar";
|
||||||
|
import ToolbarToggler from "./toolbars/ToolbarToggler";
|
||||||
|
import BottomToolbar from "./toolbars/BottomToolbar";
|
||||||
|
import ContactList from "./side_pannels/contactlist/ContactList";
|
||||||
|
import Avatar from "./avatar/Avatar";
|
||||||
|
import PanelToggler from "./side_pannels/SidePanelToggler";
|
||||||
|
import UIUtil from "./util/UIUtil";
|
||||||
|
import UIEvents from "../../service/UI/UIEvents";
|
||||||
|
|
||||||
|
import VideoLayout from "./videolayout/VideoLayout";
|
||||||
|
|
||||||
var Prezi = require("./prezi/Prezi");
|
var Prezi = require("./prezi/Prezi");
|
||||||
var Etherpad = require("./etherpad/Etherpad");
|
var Etherpad = require("./etherpad/Etherpad");
|
||||||
var Chat = require("./side_pannels/chat/Chat");
|
|
||||||
var Toolbar = require("./toolbars/Toolbar");
|
|
||||||
var ToolbarToggler = require("./toolbars/ToolbarToggler");
|
|
||||||
var BottomToolbar = require("./toolbars/BottomToolbar");
|
|
||||||
var ContactList = require("./side_pannels/contactlist/ContactList");
|
|
||||||
var Avatar = require("./avatar/Avatar");
|
|
||||||
var EventEmitter = require("events");
|
var EventEmitter = require("events");
|
||||||
var SettingsMenu = require("./side_pannels/settings/SettingsMenu");
|
var SettingsMenu = require("./side_pannels/settings/SettingsMenu");
|
||||||
var Settings = require("./../settings/Settings");
|
var Settings = require("./../settings/Settings");
|
||||||
var PanelToggler = require("./side_pannels/SidePanelToggler");
|
|
||||||
UI.messageHandler = require("./util/MessageHandler");
|
UI.messageHandler = require("./util/MessageHandler");
|
||||||
var messageHandler = UI.messageHandler;
|
var messageHandler = UI.messageHandler;
|
||||||
var Authentication = require("./authentication/Authentication");
|
var Authentication = require("./authentication/Authentication");
|
||||||
var UIUtil = require("./util/UIUtil");
|
|
||||||
var JitsiPopover = require("./util/JitsiPopover");
|
var JitsiPopover = require("./util/JitsiPopover");
|
||||||
var CQEvents = require("../../service/connectionquality/CQEvents");
|
var CQEvents = require("../../service/connectionquality/CQEvents");
|
||||||
var DesktopSharingEventTypes
|
var DesktopSharingEventTypes
|
||||||
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
= require("../../service/desktopsharing/DesktopSharingEventTypes");
|
||||||
var StatisticsEvents = require("../../service/statistics/Events");
|
var StatisticsEvents = require("../../service/statistics/Events");
|
||||||
var UIEvents = require("../../service/UI/UIEvents");
|
|
||||||
var Feedback = require("./Feedback");
|
var Feedback = require("./Feedback");
|
||||||
|
|
||||||
var eventEmitter = new EventEmitter();
|
var eventEmitter = new EventEmitter();
|
||||||
|
@ -352,7 +354,7 @@ function initEtherpad(name) {
|
||||||
Etherpad.init(name);
|
Etherpad.init(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.addUser = function (jid, id, displayName) {
|
UI.addUser = function (id, displayName) {
|
||||||
messageHandler.notify(
|
messageHandler.notify(
|
||||||
displayName,'notify.somebody', 'connected', 'notify.connected'
|
displayName,'notify.somebody', 'connected', 'notify.connected'
|
||||||
);
|
);
|
||||||
|
@ -362,16 +364,14 @@ UI.addUser = function (jid, id, displayName) {
|
||||||
UIUtil.playSoundNotification('userJoined');
|
UIUtil.playSoundNotification('userJoined');
|
||||||
|
|
||||||
// Configure avatar
|
// Configure avatar
|
||||||
UI.setUserAvatar(jid, id);
|
UI.setUserAvatar(id, displayName);
|
||||||
|
|
||||||
// Add Peer's container
|
// Add Peer's container
|
||||||
VideoLayout.ensurePeerContainerExists(jid);
|
VideoLayout.ensurePeerContainerExists(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
UI.removeUser = function (jid) {
|
UI.removeUser = function (id, displayName) {
|
||||||
console.log('left.muc', jid);
|
console.log('left.muc', id);
|
||||||
var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) +
|
|
||||||
'>.displayname').html();
|
|
||||||
messageHandler.notify(displayName,'notify.somebody',
|
messageHandler.notify(displayName,'notify.somebody',
|
||||||
'disconnected',
|
'disconnected',
|
||||||
'notify.disconnected');
|
'notify.disconnected');
|
||||||
|
@ -380,9 +380,9 @@ UI.removeUser = function (jid) {
|
||||||
UIUtil.playSoundNotification('userLeft');
|
UIUtil.playSoundNotification('userLeft');
|
||||||
}
|
}
|
||||||
|
|
||||||
ContactList.removeContact(jid);
|
ContactList.removeContact(id);
|
||||||
|
|
||||||
VideoLayout.participantLeft(jid);
|
VideoLayout.participantLeft(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
function onMucPresenceStatus(jid, info) {
|
function onMucPresenceStatus(jid, info) {
|
||||||
|
@ -601,7 +601,7 @@ UI.handleLastNEndpoints = function (ids) {
|
||||||
|
|
||||||
UI.setAudioLevel = function (id, lvl) {
|
UI.setAudioLevel = function (id, lvl) {
|
||||||
AudioLevels.updateAudioLevel(
|
AudioLevels.updateAudioLevel(
|
||||||
id, lvl, VideoLayout.getLargeVideoResource()
|
id, lvl, VideoLayout.getLargeVideoId()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -248,8 +248,7 @@ const AudioLevels = {
|
||||||
|
|
||||||
// Fill the shape.
|
// Fill the shape.
|
||||||
ASDrawContext.fill();
|
ASDrawContext.fill();
|
||||||
},
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AudioLevels;
|
export default AudioLevels;
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
var UIUtil = require("../util/UIUtil");
|
/* global $, APP */
|
||||||
var VideoLayout = require("../videolayout/VideoLayout");
|
/* jshint -W101 */
|
||||||
|
import UIUtil from "../util/UIUtil";
|
||||||
|
import VideoLayout from "../videolayout/VideoLayout";
|
||||||
|
|
||||||
var messageHandler = require("../util/MessageHandler");
|
var messageHandler = require("../util/MessageHandler");
|
||||||
var PreziPlayer = require("./PreziPlayer");
|
var PreziPlayer = require("./PreziPlayer");
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
/* global $, config, interfaceConfig */
|
/* global $, config, interfaceConfig */
|
||||||
|
|
||||||
|
import PanelToggler from "../side_pannels/SidePanelToggler";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by hristo on 12/22/14.
|
* Created by hristo on 12/22/14.
|
||||||
*/
|
*/
|
||||||
var UIUtil = module.exports = {
|
var UIUtil = {
|
||||||
/**
|
/**
|
||||||
* Returns the available video width.
|
* Returns the available video width.
|
||||||
*/
|
*/
|
||||||
getAvailableVideoWidth: function (isVisible) {
|
getAvailableVideoWidth: function (isVisible) {
|
||||||
var PanelToggler = require("../side_pannels/SidePanelToggler");
|
|
||||||
if(typeof isVisible === "undefined" || isVisible === null)
|
if(typeof isVisible === "undefined" || isVisible === null)
|
||||||
isVisible = PanelToggler.isVisible();
|
isVisible = PanelToggler.isVisible();
|
||||||
var rightPanelWidth
|
var rightPanelWidth
|
||||||
|
@ -114,3 +116,5 @@ var UIUtil = module.exports = {
|
||||||
$(selector).hide();
|
$(selector).hide();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default UIUtil;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
/* global APP, $ */
|
/* global APP, $ */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var JitsiPopover = require("../util/JitsiPopover");
|
import JitsiPopover from "../util/JitsiPopover";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs new connection indicator.
|
* Constructs new connection indicator.
|
||||||
* @param videoContainer the video container associated with the indicator.
|
* @param videoContainer the video container associated with the indicator.
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
function ConnectionIndicator(videoContainer, jid) {
|
function ConnectionIndicator(videoContainer, id) {
|
||||||
this.videoContainer = videoContainer;
|
this.videoContainer = videoContainer;
|
||||||
this.bandwidth = null;
|
this.bandwidth = null;
|
||||||
this.packetLoss = null;
|
this.packetLoss = null;
|
||||||
|
@ -16,7 +16,7 @@ function ConnectionIndicator(videoContainer, jid) {
|
||||||
this.resolution = null;
|
this.resolution = null;
|
||||||
this.transport = [];
|
this.transport = [];
|
||||||
this.popover = null;
|
this.popover = null;
|
||||||
this.jid = jid;
|
this.id = id;
|
||||||
this.create();
|
this.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolutionValue = null;
|
var resolutionValue = null;
|
||||||
if(this.resolution && this.jid) {
|
if(this.resolution && this.id) {
|
||||||
var keys = Object.keys(this.resolution);
|
var keys = Object.keys(this.resolution);
|
||||||
for(var ssrc in this.resolution) {
|
for(var ssrc in this.resolution) {
|
||||||
// skip resolutions for ssrc that don't have this info
|
// skip resolutions for ssrc that don't have this info
|
||||||
|
@ -99,7 +99,7 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.jid === null) {
|
if(this.id === null) {
|
||||||
resolution = "";
|
resolution = "";
|
||||||
if(this.resolution === null || !Object.keys(this.resolution) ||
|
if(this.resolution === null || !Object.keys(this.resolution) ||
|
||||||
Object.keys(this.resolution).length === 0) {
|
Object.keys(this.resolution).length === 0) {
|
||||||
|
@ -144,8 +144,8 @@ ConnectionIndicator.prototype.generateText = function () {
|
||||||
if(this.videoContainer.videoSpanId == "localVideoContainer") {
|
if(this.videoContainer.videoSpanId == "localVideoContainer") {
|
||||||
result += "<div class=\"jitsipopover_showmore\" " +
|
result += "<div class=\"jitsipopover_showmore\" " +
|
||||||
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
|
"onclick = \"APP.UI.connectionIndicatorShowMore('" +
|
||||||
// FIXME: we do not know local jid when this text is generated
|
// FIXME: we do not know local id when this text is generated
|
||||||
//this.jid + "')\" data-i18n='connectionindicator." +
|
//this.id + "')\" data-i18n='connectionindicator." +
|
||||||
"local')\" data-i18n='connectionindicator." +
|
"local')\" data-i18n='connectionindicator." +
|
||||||
(this.showMoreValue ? "less" : "more") + "'>" +
|
(this.showMoreValue ? "less" : "more") + "'>" +
|
||||||
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
|
translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
|
||||||
|
@ -385,4 +385,4 @@ ConnectionIndicator.prototype.hideIndicator = function () {
|
||||||
this.popover.forceHide();
|
this.popover.forceHide();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = ConnectionIndicator;
|
export default ConnectionIndicator;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
/* global $, APP, Strophe, interfaceConfig */
|
/* global $, APP, interfaceConfig */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var Avatar = require("../avatar/Avatar");
|
import Avatar from "../avatar/Avatar";
|
||||||
|
import ToolbarToggler from "../toolbars/ToolbarToggler";
|
||||||
|
import UIUtil from "../util/UIUtil";
|
||||||
|
import UIEvents from "../../../service/UI/UIEvents";
|
||||||
|
|
||||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||||
var UIUtil = require("../util/UIUtil");
|
|
||||||
var UIEvents = require("../../../service/UI/UIEvents");
|
|
||||||
var xmpp = require("../../xmpp/xmpp");
|
|
||||||
var ToolbarToggler = require("../toolbars/ToolbarToggler");
|
|
||||||
|
|
||||||
// FIXME: With Temasys we have to re-select everytime
|
// FIXME: With Temasys we have to re-select everytime
|
||||||
//var video = $('#largeVideo');
|
//var video = $('#largeVideo');
|
||||||
|
@ -37,22 +37,22 @@ var state = "video";
|
||||||
* @param state the state.
|
* @param state the state.
|
||||||
* @returns {JQuery|*|jQuery|HTMLElement} the container.
|
* @returns {JQuery|*|jQuery|HTMLElement} the container.
|
||||||
*/
|
*/
|
||||||
function getContainerByState(state)
|
function getContainerByState(state) {
|
||||||
{
|
|
||||||
var selector = null;
|
var selector = null;
|
||||||
switch (state)
|
switch (state) {
|
||||||
{
|
case "video":
|
||||||
case "video":
|
selector = "#largeVideoWrapper";
|
||||||
selector = "#largeVideoWrapper";
|
break;
|
||||||
break;
|
case "etherpad":
|
||||||
case "etherpad":
|
selector = "#etherpad>iframe";
|
||||||
selector = "#etherpad>iframe";
|
break;
|
||||||
break;
|
case "prezi":
|
||||||
case "prezi":
|
selector = "#presentation>iframe";
|
||||||
selector = "#presentation>iframe";
|
break;
|
||||||
break;
|
default:
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return (selector !== null)? $(selector) : null;
|
return $(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,24 +72,25 @@ function positionVideo(video,
|
||||||
animate) {
|
animate) {
|
||||||
if (animate) {
|
if (animate) {
|
||||||
video.animate({
|
video.animate({
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
top: verticalIndent,
|
top: verticalIndent,
|
||||||
bottom: verticalIndent,
|
bottom: verticalIndent,
|
||||||
left: horizontalIndent,
|
left: horizontalIndent,
|
||||||
right: horizontalIndent
|
right: horizontalIndent
|
||||||
},
|
}, {
|
||||||
{
|
queue: false,
|
||||||
queue: false,
|
duration: 500
|
||||||
duration: 500
|
});
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
video.width(width);
|
video.width(width);
|
||||||
video.height(height);
|
video.height(height);
|
||||||
video.css({ top: verticalIndent + 'px',
|
video.css({
|
||||||
bottom: verticalIndent + 'px',
|
top: verticalIndent,
|
||||||
left: horizontalIndent + 'px',
|
bottom: verticalIndent,
|
||||||
right: horizontalIndent + 'px'});
|
left: horizontalIndent,
|
||||||
|
right: horizontalIndent
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -237,16 +238,13 @@ function getCameraVideoSize(videoWidth,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the src of the active speaker avatar
|
* Updates the src of the active speaker avatar
|
||||||
* @param jid of the current active speaker
|
|
||||||
*/
|
*/
|
||||||
function updateActiveSpeakerAvatarSrc() {
|
function updateActiveSpeakerAvatarSrc() {
|
||||||
var avatar = $("#activeSpeakerAvatar")[0];
|
let avatar = $("#activeSpeakerAvatar");
|
||||||
var jid = currentSmallVideo.peerJid;
|
let id = currentSmallVideo.id;
|
||||||
var url = Avatar.getActiveSpeakerUrl(jid);
|
let url = Avatar.getActiveSpeakerUrl(id);
|
||||||
if (avatar.src === url)
|
if (id && avatar.attr('src') !== url) {
|
||||||
return;
|
avatar.attr('src', url);
|
||||||
if (jid) {
|
|
||||||
avatar.src = url;
|
|
||||||
currentSmallVideo.showAvatar();
|
currentSmallVideo.showAvatar();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,13 +261,15 @@ function changeVideo(isVisible) {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateActiveSpeakerAvatarSrc();
|
updateActiveSpeakerAvatarSrc();
|
||||||
var largeVideoElement = $('#largeVideo')[0];
|
let largeVideoElement = $('#largeVideo');
|
||||||
|
|
||||||
APP.RTC.setVideoSrc(largeVideoElement, currentSmallVideo.getSrc());
|
currentSmallVideo.stream.attach(largeVideoElement);
|
||||||
|
|
||||||
var flipX = currentSmallVideo.flipX;
|
let flipX = currentSmallVideo.flipX;
|
||||||
|
|
||||||
largeVideoElement.style.transform = flipX ? "scaleX(-1)" : "none";
|
largeVideoElement.css({
|
||||||
|
transform: flipX ? "scaleX(-1)" : "none"
|
||||||
|
});
|
||||||
|
|
||||||
LargeVideo.updateVideoSizeAndPosition(currentSmallVideo.getVideoType());
|
LargeVideo.updateVideoSizeAndPosition(currentSmallVideo.getVideoType());
|
||||||
|
|
||||||
|
@ -369,40 +369,35 @@ var LargeVideo = {
|
||||||
/**
|
/**
|
||||||
* Returns <tt>true</tt> if the user is currently displayed on large video.
|
* Returns <tt>true</tt> if the user is currently displayed on large video.
|
||||||
*/
|
*/
|
||||||
isCurrentlyOnLarge: function (resourceJid) {
|
isCurrentlyOnLarge: function (id) {
|
||||||
return currentSmallVideo && resourceJid &&
|
return id && id === this.getId();
|
||||||
currentSmallVideo.getResourceJid() === resourceJid;
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Updates the large video with the given new video source.
|
* Updates the large video with the given new video source.
|
||||||
*/
|
*/
|
||||||
updateLargeVideo: function (resourceJid, forceUpdate) {
|
updateLargeVideo: function (id, forceUpdate) {
|
||||||
if(!isEnabled)
|
if(!isEnabled) {
|
||||||
return;
|
return;
|
||||||
var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
|
}
|
||||||
console.info('hover in ' + resourceJid + ', video: ', newSmallVideo);
|
let newSmallVideo = this.VideoLayout.getSmallVideo(id);
|
||||||
|
console.info(`hover in ${id} , video: `, newSmallVideo);
|
||||||
|
|
||||||
if (!newSmallVideo) {
|
if (!newSmallVideo) {
|
||||||
console.error("Small video not found for: " + resourceJid);
|
console.error("Small video not found for: " + id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) {
|
if (!LargeVideo.isCurrentlyOnLarge(id) || forceUpdate) {
|
||||||
$('#activeSpeaker').css('visibility', 'hidden');
|
$('#activeSpeaker').css('visibility', 'hidden');
|
||||||
|
|
||||||
var oldSmallVideo = null;
|
let oldId = this.getId();
|
||||||
if (currentSmallVideo) {
|
|
||||||
oldSmallVideo = currentSmallVideo;
|
|
||||||
}
|
|
||||||
currentSmallVideo = newSmallVideo;
|
currentSmallVideo = newSmallVideo;
|
||||||
|
|
||||||
var oldJid = null;
|
if (oldId !== id) {
|
||||||
if (oldSmallVideo)
|
// we want the notification to trigger even if id is undefined,
|
||||||
oldJid = oldSmallVideo.peerJid;
|
|
||||||
if (oldJid !== resourceJid) {
|
|
||||||
// we want the notification to trigger even if userJid is undefined,
|
|
||||||
// or null.
|
// or null.
|
||||||
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, resourceJid);
|
this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, id);
|
||||||
}
|
}
|
||||||
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
// We are doing fadeOut/fadeIn animations on parent div which wraps
|
||||||
// largeVideo, because when Temasys plugin is in use it replaces
|
// largeVideo, because when Temasys plugin is in use it replaces
|
||||||
|
@ -443,11 +438,10 @@ var LargeVideo = {
|
||||||
currentSmallVideo.enableDominantSpeaker(false);
|
currentSmallVideo.enableDominantSpeaker(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onVideoTypeChanged: function (resourceJid, newVideoType) {
|
onVideoTypeChanged: function (id, newVideoType) {
|
||||||
if (!isEnabled)
|
if (!isEnabled)
|
||||||
return;
|
return;
|
||||||
if (LargeVideo.isCurrentlyOnLarge(resourceJid))
|
if (LargeVideo.isCurrentlyOnLarge(id)) {
|
||||||
{
|
|
||||||
LargeVideo.updateVideoSizeAndPosition(newVideoType);
|
LargeVideo.updateVideoSizeAndPosition(newVideoType);
|
||||||
|
|
||||||
this.position(null, null, null, null, true);
|
this.position(null, null, null, null, true);
|
||||||
|
@ -562,22 +556,23 @@ var LargeVideo = {
|
||||||
getVideoPosition = isDesktop ? getDesktopVideoPosition :
|
getVideoPosition = isDesktop ? getDesktopVideoPosition :
|
||||||
getCameraVideoPosition;
|
getCameraVideoPosition;
|
||||||
},
|
},
|
||||||
getResourceJid: function () {
|
getId: function () {
|
||||||
return currentSmallVideo ? currentSmallVideo.getResourceJid() : null;
|
return currentSmallVideo ? currentSmallVideo.id : null;
|
||||||
},
|
},
|
||||||
updateAvatar: function (resourceJid) {
|
updateAvatar: function (id) {
|
||||||
if(!isEnabled)
|
if (!isEnabled) {
|
||||||
return;
|
return;
|
||||||
if (resourceJid === this.getResourceJid()) {
|
}
|
||||||
|
if (id === this.getId()) {
|
||||||
updateActiveSpeakerAvatarSrc();
|
updateActiveSpeakerAvatarSrc();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showAvatar: function (resourceJid, show) {
|
showAvatar: function (id, show) {
|
||||||
if (!isEnabled)
|
if (!isEnabled) {
|
||||||
return;
|
return;
|
||||||
if (this.getResourceJid() === resourceJid && state === "video") {
|
}
|
||||||
$("#largeVideoWrapper")
|
if (this.getId() === id && state === "video") {
|
||||||
.css("visibility", show ? "hidden" : "visible");
|
$("#largeVideoWrapper").css("visibility", show ? "hidden" : "visible");
|
||||||
$('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
|
$('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -721,4 +716,4 @@ var LargeVideo = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LargeVideo;
|
export default LargeVideo;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* global $, interfaceConfig, APP */
|
/* global $, interfaceConfig, APP */
|
||||||
var SmallVideo = require("./SmallVideo");
|
import ConnectionIndicator from "./ConnectionIndicator";
|
||||||
var ConnectionIndicator = require("./ConnectionIndicator");
|
import UIUtil from "../util/UIUtil";
|
||||||
var UIUtil = require("../util/UIUtil");
|
import UIEvents from "../../../service/UI/UIEvents";
|
||||||
var UIEvents = require("../../../service/UI/UIEvents");
|
import SmallVideo from "./SmallVideo";
|
||||||
|
|
||||||
var LargeVideo = require("./LargeVideo");
|
var LargeVideo = require("./LargeVideo");
|
||||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||||
|
|
||||||
|
@ -13,7 +14,6 @@ function LocalVideo(VideoLayout, emitter) {
|
||||||
this.VideoLayout = VideoLayout;
|
this.VideoLayout = VideoLayout;
|
||||||
this.flipX = true;
|
this.flipX = true;
|
||||||
this.isLocal = true;
|
this.isLocal = true;
|
||||||
this.peerJid = null;
|
|
||||||
this.emitter = emitter;
|
this.emitter = emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,32 +143,25 @@ LocalVideo.prototype.createConnectionIndicator = function() {
|
||||||
this.connectionIndicator = new ConnectionIndicator(this, null);
|
this.connectionIndicator = new ConnectionIndicator(this, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
LocalVideo.prototype.changeVideo = function (stream) {
|
||||||
var self = this;
|
this.stream = stream;
|
||||||
|
|
||||||
function localVideoClick(event) {
|
let localVideoClick = (event) => {
|
||||||
// FIXME: with Temasys plugin event arg is not an event, but
|
// FIXME: with Temasys plugin event arg is not an event, but
|
||||||
// the clicked object itself, so we have to skip this call
|
// the clicked object itself, so we have to skip this call
|
||||||
if (event.stopPropagation) {
|
if (event.stopPropagation) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
self.VideoLayout.handleVideoThumbClicked(
|
this.VideoLayout.handleVideoThumbClicked(true, this.id);
|
||||||
true,
|
};
|
||||||
APP.xmpp.myResource());
|
|
||||||
}
|
|
||||||
|
|
||||||
var localVideoContainerSelector = $('#localVideoContainer');
|
let localVideoContainerSelector = $('#localVideoContainer');
|
||||||
localVideoContainerSelector.off('click');
|
localVideoContainerSelector.off('click');
|
||||||
localVideoContainerSelector.on('click', localVideoClick);
|
localVideoContainerSelector.on('click', localVideoClick);
|
||||||
|
|
||||||
if(isMuted) {
|
|
||||||
APP.UI.setVideoMute(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.flipX = stream.videoType != "screen";
|
this.flipX = stream.videoType != "screen";
|
||||||
var localVideo = document.createElement('video');
|
let localVideo = document.createElement('video');
|
||||||
localVideo.id = 'localVideo_' +
|
localVideo.id = 'localVideo_' + stream.getId();
|
||||||
APP.RTC.getStreamID(stream.getOriginalStream());
|
|
||||||
if (!RTCBrowserType.isIExplorer()) {
|
if (!RTCBrowserType.isIExplorer()) {
|
||||||
localVideo.autoplay = true;
|
localVideo.autoplay = true;
|
||||||
localVideo.volume = 0; // is it required if audio is separated ?
|
localVideo.volume = 0; // is it required if audio is separated ?
|
||||||
|
@ -192,7 +185,10 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach WebRTC stream
|
// Attach WebRTC stream
|
||||||
APP.RTC.attachMediaStream(localVideoSelector, stream.getOriginalStream());
|
stream.attach(localVideoSelector);
|
||||||
|
|
||||||
|
// FIXME handle
|
||||||
|
return;
|
||||||
|
|
||||||
// Add stream ended handler
|
// Add stream ended handler
|
||||||
APP.RTC.addMediaStreamInactiveHandler(
|
APP.RTC.addMediaStreamInactiveHandler(
|
||||||
|
@ -201,20 +197,12 @@ LocalVideo.prototype.changeVideo = function (stream, isMuted) {
|
||||||
// because <video> element is replaced with <object>
|
// because <video> element is replaced with <object>
|
||||||
localVideo = $('#' + localVideo.id)[0];
|
localVideo = $('#' + localVideo.id)[0];
|
||||||
localVideoContainer.removeChild(localVideo);
|
localVideoContainer.removeChild(localVideo);
|
||||||
self.VideoLayout.updateRemovedVideo(APP.xmpp.myResource());
|
self.VideoLayout.updateRemovedVideo(self.id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalVideo.prototype.joined = function (jid) {
|
LocalVideo.prototype.joined = function (id) {
|
||||||
this.peerJid = jid;
|
this.id = id;
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalVideo.prototype.getResourceJid = function () {
|
export default LocalVideo;
|
||||||
var myResource = APP.xmpp.myResource();
|
|
||||||
if (!myResource) {
|
|
||||||
console.error("Requested local resource before we're in the MUC");
|
|
||||||
}
|
|
||||||
return myResource;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = LocalVideo;
|
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
/* global $, APP, require, Strophe, interfaceConfig */
|
/* global $, APP, interfaceConfig */
|
||||||
var ConnectionIndicator = require("./ConnectionIndicator");
|
|
||||||
var SmallVideo = require("./SmallVideo");
|
|
||||||
var AudioLevels = require("../audio_levels/AudioLevels");
|
|
||||||
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
|
||||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
|
||||||
var UIUtils = require("../util/UIUtil");
|
|
||||||
var XMPPEvents = require("../../../service/xmpp/XMPPEvents");
|
|
||||||
|
|
||||||
function RemoteVideo(peerJid, VideoLayout) {
|
import ConnectionIndicator from './ConnectionIndicator';
|
||||||
this.peerJid = peerJid;
|
|
||||||
this.resourceJid = Strophe.getResourceFromJid(peerJid);
|
import SmallVideo from "./SmallVideo";
|
||||||
this.videoSpanId = 'participant_' + this.resourceJid;
|
import AudioLevels from "../audio_levels/AudioLevels";
|
||||||
|
import UIUtils from "../util/UIUtil";
|
||||||
|
import UIEvents from '../../../service/UI/UIEvents';
|
||||||
|
|
||||||
|
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||||
|
|
||||||
|
function RemoteVideo(id, VideoLayout, emitter) {
|
||||||
|
this.id = id;
|
||||||
|
this.emitter = emitter;
|
||||||
|
this.videoSpanId = `participant_${id}`;
|
||||||
this.VideoLayout = VideoLayout;
|
this.VideoLayout = VideoLayout;
|
||||||
this.addRemoteVideoContainer();
|
this.addRemoteVideoContainer();
|
||||||
this.connectionIndicator = new ConnectionIndicator(
|
this.connectionIndicator = new ConnectionIndicator(this, id);
|
||||||
this, this.peerJid);
|
|
||||||
this.setDisplayName();
|
this.setDisplayName();
|
||||||
var nickfield = document.createElement('span');
|
var nickfield = document.createElement('span');
|
||||||
nickfield.className = "nick";
|
nickfield.className = "nick";
|
||||||
nickfield.appendChild(document.createTextNode(this.resourceJid));
|
nickfield.appendChild(document.createTextNode(id));
|
||||||
this.container.appendChild(nickfield);
|
this.container.appendChild(nickfield);
|
||||||
this.bindHoverHandler();
|
this.bindHoverHandler();
|
||||||
this.flipX = false;
|
this.flipX = false;
|
||||||
|
@ -30,18 +31,19 @@ RemoteVideo.prototype.constructor = RemoteVideo;
|
||||||
|
|
||||||
RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
RemoteVideo.prototype.addRemoteVideoContainer = function() {
|
||||||
this.container = RemoteVideo.createContainer(this.videoSpanId);
|
this.container = RemoteVideo.createContainer(this.videoSpanId);
|
||||||
if (APP.xmpp.isModerator())
|
if (APP.conference.isModerator) {
|
||||||
this.addRemoteVideoMenu();
|
this.addRemoteVideoMenu();
|
||||||
AudioLevels.updateAudioLevelCanvas(this.peerJid, this.VideoLayout);
|
}
|
||||||
|
AudioLevels.updateAudioLevelCanvas(this.id, this.VideoLayout);
|
||||||
|
|
||||||
return this.container;
|
return this.container;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the remote video menu element for the given <tt>jid</tt> in the
|
* Adds the remote video menu element for the given <tt>id</tt> in the
|
||||||
* given <tt>parentElement</tt>.
|
* given <tt>parentElement</tt>.
|
||||||
*
|
*
|
||||||
* @param jid the jid indicating the video for which we're adding a menu.
|
* @param id the id indicating the video for which we're adding a menu.
|
||||||
* @param parentElement the parent element where this menu will be added
|
* @param parentElement the parent element where this menu will be added
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ if (!interfaceConfig.filmStripOnly) {
|
||||||
|
|
||||||
var popupmenuElement = document.createElement('ul');
|
var popupmenuElement = document.createElement('ul');
|
||||||
popupmenuElement.className = 'popupmenu';
|
popupmenuElement.className = 'popupmenu';
|
||||||
popupmenuElement.id = 'remote_popupmenu_' + this.getResourceJid();
|
popupmenuElement.id = `remote_popupmenu_${this.id}`;
|
||||||
spanElement.appendChild(popupmenuElement);
|
spanElement.appendChild(popupmenuElement);
|
||||||
|
|
||||||
var muteMenuItem = document.createElement('li');
|
var muteMenuItem = document.createElement('li');
|
||||||
|
@ -88,7 +90,7 @@ if (!interfaceConfig.filmStripOnly) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
var isMute = !!self.isMuted;
|
var isMute = !!self.isMuted;
|
||||||
APP.xmpp.setMute(self.peerJid, !isMute);
|
self.emitter.emit(UIEvents.AUDIO_MUTED, !isMute);
|
||||||
|
|
||||||
popupmenuElement.setAttribute('style', 'display:none;');
|
popupmenuElement.setAttribute('style', 'display:none;');
|
||||||
|
|
||||||
|
@ -117,7 +119,7 @@ if (!interfaceConfig.filmStripOnly) {
|
||||||
"data-i18n='videothumbnail.kick'> </div>";
|
"data-i18n='videothumbnail.kick'> </div>";
|
||||||
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
|
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
|
||||||
ejectLinkItem.onclick = function(){
|
ejectLinkItem.onclick = function(){
|
||||||
APP.xmpp.eject(self.peerJid);
|
self.emitter.emit(UIEvents.USER_KICKED, self.id);
|
||||||
popupmenuElement.setAttribute('style', 'display:none;');
|
popupmenuElement.setAttribute('style', 'display:none;');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,36 +159,36 @@ RemoteVideo.prototype.removeRemoteStreamElement =
|
||||||
select.remove();
|
select.remove();
|
||||||
|
|
||||||
console.info((isVideo ? "Video" : "Audio") +
|
console.info((isVideo ? "Video" : "Audio") +
|
||||||
" removed " + this.getResourceJid(), select);
|
" removed " + this.id, select);
|
||||||
|
|
||||||
if (isVideo)
|
if (isVideo)
|
||||||
this.VideoLayout.updateRemovedVideo(this.getResourceJid());
|
this.VideoLayout.updateRemovedVideo(this.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes RemoteVideo from the page.
|
* Removes RemoteVideo from the page.
|
||||||
*/
|
*/
|
||||||
RemoteVideo.prototype.remove = function () {
|
RemoteVideo.prototype.remove = function () {
|
||||||
console.log("Remove thumbnail", this.peerJid);
|
console.log("Remove thumbnail", this.id);
|
||||||
this.removeConnectionIndicator();
|
this.removeConnectionIndicator();
|
||||||
// Make sure that the large video is updated if are removing its
|
// Make sure that the large video is updated if are removing its
|
||||||
// corresponding small video.
|
// corresponding small video.
|
||||||
this.VideoLayout.updateRemovedVideo(this.getResourceJid());
|
this.VideoLayout.updateRemovedVideo(this.id);
|
||||||
// Remove whole container
|
// Remove whole container
|
||||||
if (this.container.parentNode)
|
if (this.container.parentNode) {
|
||||||
this.container.parentNode.removeChild(this.container);
|
this.container.parentNode.removeChild(this.container);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RemoteVideo.prototype.waitForPlayback = function (sel, stream) {
|
RemoteVideo.prototype.waitForPlayback = function (sel, stream) {
|
||||||
|
|
||||||
var webRtcStream = stream.getOriginalStream();
|
var webRtcStream = stream.getOriginalStream();
|
||||||
var isVideo = stream.isVideoStream();
|
var isVideo = stream.isVideoTrack();
|
||||||
if (!isVideo || webRtcStream.id === 'mixedmslabel') {
|
if (!isVideo || webRtcStream.id === 'mixedmslabel') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var resourceJid = this.getResourceJid();
|
|
||||||
|
|
||||||
// Register 'onplaying' listener to trigger 'videoactive' on VideoLayout
|
// Register 'onplaying' listener to trigger 'videoactive' on VideoLayout
|
||||||
// when video playback starts
|
// when video playback starts
|
||||||
|
@ -198,7 +200,7 @@ RemoteVideo.prototype.waitForPlayback = function (sel, stream) {
|
||||||
if (RTCBrowserType.isTemasysPluginUsed()) {
|
if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||||
sel = self.selectVideoElement();
|
sel = self.selectVideoElement();
|
||||||
}
|
}
|
||||||
self.VideoLayout.videoactive(sel, resourceJid);
|
self.VideoLayout.videoactive(sel, self.id);
|
||||||
sel[0].onplaying = null;
|
sel[0].onplaying = null;
|
||||||
if (RTCBrowserType.isTemasysPluginUsed()) {
|
if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||||
// 'currentTime' is used to check if the video has started
|
// 'currentTime' is used to check if the video has started
|
||||||
|
@ -210,39 +212,33 @@ RemoteVideo.prototype.waitForPlayback = function (sel, stream) {
|
||||||
};
|
};
|
||||||
|
|
||||||
RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
|
RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
|
||||||
if (!this.container)
|
if (!this.container) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var self = this;
|
this.stream = stream;
|
||||||
var webRtcStream = stream.getOriginalStream();
|
|
||||||
var isVideo = stream.isVideoStream();
|
let isVideo = stream.isVideoTrack();
|
||||||
var streamElement = SmallVideo.createStreamElement(stream);
|
let streamElement = SmallVideo.createStreamElement(stream);
|
||||||
var newElementId = streamElement.id;
|
let newElementId = streamElement.id;
|
||||||
|
|
||||||
// Put new stream element always in front
|
// Put new stream element always in front
|
||||||
UIUtils.prependChild(this.container, streamElement);
|
UIUtils.prependChild(this.container, streamElement);
|
||||||
|
|
||||||
var sel = $('#' + newElementId);
|
let sel = $(`#${newElementId}`);
|
||||||
sel.hide();
|
sel.hide();
|
||||||
|
|
||||||
// If the container is currently visible we attach the stream.
|
// If the container is currently visible we attach the stream.
|
||||||
if (!isVideo || (this.container.offsetParent !== null && isVideo)) {
|
if (!isVideo || (this.container.offsetParent !== null && isVideo)) {
|
||||||
this.waitForPlayback(sel, stream);
|
this.waitForPlayback(sel, stream);
|
||||||
|
|
||||||
APP.RTC.attachMediaStream(sel, webRtcStream);
|
stream.attach(sel);
|
||||||
}
|
}
|
||||||
|
|
||||||
APP.RTC.addMediaStreamInactiveHandler(
|
|
||||||
webRtcStream, function () {
|
|
||||||
console.log('stream ended', this);
|
|
||||||
|
|
||||||
self.removeRemoteStreamElement(webRtcStream, isVideo, newElementId);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add click handler.
|
// Add click handler.
|
||||||
var onClickHandler = function (event) {
|
let onClickHandler = (event) => {
|
||||||
|
|
||||||
self.VideoLayout.handleVideoThumbClicked(false, self.getResourceJid());
|
this.VideoLayout.handleVideoThumbClicked(false, this.id);
|
||||||
|
|
||||||
// On IE we need to populate this handler on video <object>
|
// On IE we need to populate this handler on video <object>
|
||||||
// and it does not give event instance as an argument,
|
// and it does not give event instance as an argument,
|
||||||
|
@ -255,13 +251,14 @@ RemoteVideo.prototype.addRemoteStreamElement = function (stream) {
|
||||||
};
|
};
|
||||||
this.container.onclick = onClickHandler;
|
this.container.onclick = onClickHandler;
|
||||||
// reselect
|
// reselect
|
||||||
if (RTCBrowserType.isTemasysPluginUsed())
|
if (RTCBrowserType.isTemasysPluginUsed()) {
|
||||||
sel = $('#' + newElementId);
|
sel = $(`#${newElementId}`);
|
||||||
sel[0].onclick = onClickHandler;
|
}
|
||||||
|
sel.click(onClickHandler);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show/hide peer container for the given resourceJid.
|
* Show/hide peer container for the given id.
|
||||||
*/
|
*/
|
||||||
RemoteVideo.prototype.showPeerContainer = function (state) {
|
RemoteVideo.prototype.showPeerContainer = function (state) {
|
||||||
if (!this.container)
|
if (!this.container)
|
||||||
|
@ -294,7 +291,7 @@ RemoteVideo.prototype.showPeerContainer = function (state) {
|
||||||
|
|
||||||
// We want to be able to pin a participant from the contact list, even
|
// We want to be able to pin a participant from the contact list, even
|
||||||
// if he's not in the lastN set!
|
// if he's not in the lastN set!
|
||||||
// ContactList.setClickable(resourceJid, !isHide);
|
// ContactList.setClickable(id, !isHide);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,12 +308,11 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||||
/**
|
/**
|
||||||
* Updates the remote video menu.
|
* Updates the remote video menu.
|
||||||
*
|
*
|
||||||
* @param jid the jid indicating the video for which we're adding a menu.
|
* @param id the id indicating the video for which we're adding a menu.
|
||||||
* @param isMuted indicates the current mute state
|
* @param isMuted indicates the current mute state
|
||||||
*/
|
*/
|
||||||
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
RemoteVideo.prototype.updateRemoteVideoMenu = function (isMuted) {
|
||||||
var muteMenuItem
|
var muteMenuItem = $(`#remote_popupmenu_${this.id}>li>a.mutelink`);
|
||||||
= $('#remote_popupmenu_' + this.getResourceJid() + '>li>a.mutelink');
|
|
||||||
|
|
||||||
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
|
var mutedIndicator = "<i class='icon-mic-disabled'></i>";
|
||||||
|
|
||||||
|
@ -423,13 +419,6 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
RemoteVideo.prototype.getResourceJid = function () {
|
|
||||||
if (!this.resourceJid) {
|
|
||||||
console.error("Undefined resource jid");
|
|
||||||
}
|
|
||||||
return this.resourceJid;
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoteVideo.createContainer = function (spanId) {
|
RemoteVideo.createContainer = function (spanId) {
|
||||||
var container = document.createElement('span');
|
var container = document.createElement('span');
|
||||||
container.id = spanId;
|
container.id = spanId;
|
||||||
|
@ -439,4 +428,4 @@ RemoteVideo.createContainer = function (spanId) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = RemoteVideo;
|
export default RemoteVideo;
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
/* global $, APP, require */
|
/* global $, APP, require */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var Avatar = require("../avatar/Avatar");
|
import Avatar from "../avatar/Avatar";
|
||||||
var UIUtil = require("../util/UIUtil");
|
import UIUtil from "../util/UIUtil";
|
||||||
var LargeVideo = require("./LargeVideo");
|
import LargeVideo from "./LargeVideo";
|
||||||
|
|
||||||
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
var RTCBrowserType = require("../../RTC/RTCBrowserType");
|
||||||
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
||||||
|
|
||||||
function SmallVideo() {
|
function SmallVideo() {
|
||||||
this.isMuted = false;
|
this.isMuted = false;
|
||||||
this.hasAvatar = false;
|
this.hasAvatar = false;
|
||||||
|
this.stream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setVisibility(selector, show) {
|
function setVisibility(selector, show) {
|
||||||
|
@ -106,9 +108,10 @@ SmallVideo.prototype.setPresenceStatus = function (statusMsg) {
|
||||||
* Creates an audio or video element for a particular MediaStream.
|
* Creates an audio or video element for a particular MediaStream.
|
||||||
*/
|
*/
|
||||||
SmallVideo.createStreamElement = function (stream) {
|
SmallVideo.createStreamElement = function (stream) {
|
||||||
var isVideo = stream.isVideoStream();
|
let isVideo = stream.isVideoTrack();
|
||||||
|
|
||||||
var element = isVideo ? document.createElement('video')
|
let element = isVideo
|
||||||
|
? document.createElement('video')
|
||||||
: document.createElement('audio');
|
: document.createElement('audio');
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
element.setAttribute("muted", "true");
|
element.setAttribute("muted", "true");
|
||||||
|
@ -118,8 +121,7 @@ SmallVideo.createStreamElement = function (stream) {
|
||||||
element.autoplay = true;
|
element.autoplay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
element.id = (isVideo ? 'remoteVideo_' : 'remoteAudio_') +
|
element.id = (isVideo ? 'remoteVideo_' : 'remoteAudio_') + stream.getId();
|
||||||
APP.RTC.getStreamID(stream.getOriginalStream());
|
|
||||||
|
|
||||||
element.onplay = function () {
|
element.onplay = function () {
|
||||||
console.log("(TIME) Render " + (isVideo ? 'video' : 'audio') + ":\t",
|
console.log("(TIME) Render " + (isVideo ? 'video' : 'audio') + ":\t",
|
||||||
|
@ -145,7 +147,7 @@ SmallVideo.prototype.bindHoverHandler = function () {
|
||||||
// If the video has been "pinned" by the user we want to
|
// If the video has been "pinned" by the user we want to
|
||||||
// keep the display name on place.
|
// keep the display name on place.
|
||||||
if (!LargeVideo.isLargeVideoVisible() ||
|
if (!LargeVideo.isLargeVideoVisible() ||
|
||||||
!LargeVideo.isCurrentlyOnLarge(self.getResourceJid()))
|
!LargeVideo.isCurrentlyOnLarge(self.id))
|
||||||
self.showDisplayName(false);
|
self.showDisplayName(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -237,15 +239,14 @@ SmallVideo.prototype.showVideoIndicator = function(isMuted) {
|
||||||
};
|
};
|
||||||
|
|
||||||
SmallVideo.prototype.enableDominantSpeaker = function (isEnable) {
|
SmallVideo.prototype.enableDominantSpeaker = function (isEnable) {
|
||||||
var resourceJid = this.getResourceJid();
|
var displayName = this.id;
|
||||||
var displayName = resourceJid;
|
|
||||||
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
||||||
if (nameSpan.length > 0)
|
if (nameSpan.length > 0)
|
||||||
displayName = nameSpan.html();
|
displayName = nameSpan.html();
|
||||||
|
|
||||||
console.log("UI enable dominant speaker",
|
console.log("UI enable dominant speaker",
|
||||||
displayName,
|
displayName,
|
||||||
resourceJid,
|
this.id,
|
||||||
isEnable);
|
isEnable);
|
||||||
|
|
||||||
|
|
||||||
|
@ -316,6 +317,8 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
SmallVideo.prototype.selectVideoElement = function () {
|
SmallVideo.prototype.selectVideoElement = function () {
|
||||||
|
return $('#' + this.videoSpanId).find(videoElem);
|
||||||
|
// FIXME maybe move this to the library?
|
||||||
var videoElem = APP.RTC.getVideoElementName();
|
var videoElem = APP.RTC.getVideoElementName();
|
||||||
if (!RTCBrowserType.isTemasysPluginUsed()) {
|
if (!RTCBrowserType.isTemasysPluginUsed()) {
|
||||||
return $('#' + this.videoSpanId).find(videoElem);
|
return $('#' + this.videoSpanId).find(videoElem);
|
||||||
|
@ -367,32 +370,31 @@ SmallVideo.prototype.hasVideo = function () {
|
||||||
*/
|
*/
|
||||||
SmallVideo.prototype.showAvatar = function (show) {
|
SmallVideo.prototype.showAvatar = function (show) {
|
||||||
if (!this.hasAvatar) {
|
if (!this.hasAvatar) {
|
||||||
if (this.peerJid) {
|
if (this.id) {
|
||||||
// Init avatar
|
// Init avatar
|
||||||
this.avatarChanged(Avatar.getThumbUrl(this.peerJid));
|
this.avatarChanged(Avatar.getThumbUrl(this.id));
|
||||||
} else {
|
} else {
|
||||||
console.error("Unable to init avatar - no peerjid", this);
|
console.error("Unable to init avatar - no id", this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resourceJid = this.getResourceJid();
|
let video = this.selectVideoElement();
|
||||||
var video = this.selectVideoElement();
|
|
||||||
|
|
||||||
var avatar = $('#avatar_' + resourceJid);
|
let avatar = $(`#avatar_${this.id}`);
|
||||||
|
|
||||||
if (show === undefined || show === null) {
|
if (show === undefined || show === null) {
|
||||||
if (!this.isLocal &&
|
if (!this.isLocal &&
|
||||||
!this.VideoLayout.isInLastN(resourceJid)) {
|
!this.VideoLayout.isInLastN(this.id)) {
|
||||||
show = true;
|
show = true;
|
||||||
} else {
|
} else {
|
||||||
// We want to show the avatar when the video is muted or not exists
|
// We want to show the avatar when the video is muted or not exists
|
||||||
// that is when 'true' or 'null' is returned
|
// that is when 'true' or 'null' is returned
|
||||||
show = APP.RTC.isVideoMuted(this.peerJid) !== false;
|
show = !this.stream || this.stream.isMuted();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LargeVideo.showAvatar(resourceJid, show)) {
|
if (LargeVideo.showAvatar(this.id, show)) {
|
||||||
setVisibility(avatar, false);
|
setVisibility(avatar, false);
|
||||||
setVisibility(video, false);
|
setVisibility(video, false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -405,8 +407,7 @@ SmallVideo.prototype.showAvatar = function (show) {
|
||||||
|
|
||||||
SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
||||||
var thumbnail = $('#' + this.videoSpanId);
|
var thumbnail = $('#' + this.videoSpanId);
|
||||||
var resourceJid = this.getResourceJid();
|
var avatar = $('#avatar_' + this.id);
|
||||||
var avatar = $('#avatar_' + resourceJid);
|
|
||||||
this.hasAvatar = true;
|
this.hasAvatar = true;
|
||||||
|
|
||||||
// set the avatar in the thumbnail
|
// set the avatar in the thumbnail
|
||||||
|
@ -415,7 +416,7 @@ SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
||||||
} else {
|
} else {
|
||||||
if (thumbnail && thumbnail.length > 0) {
|
if (thumbnail && thumbnail.length > 0) {
|
||||||
avatar = document.createElement('img');
|
avatar = document.createElement('img');
|
||||||
avatar.id = 'avatar_' + resourceJid;
|
avatar.id = 'avatar_' + this.id;
|
||||||
avatar.className = 'userAvatar';
|
avatar.className = 'userAvatar';
|
||||||
avatar.src = thumbUrl;
|
avatar.src = thumbUrl;
|
||||||
thumbnail.append(avatar);
|
thumbnail.append(avatar);
|
||||||
|
@ -423,4 +424,4 @@ SmallVideo.prototype.avatarChanged = function (thumbUrl) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = SmallVideo;
|
export default SmallVideo;
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
/* global config, APP, $, Strophe, require, interfaceConfig */
|
/* global config, APP, $, interfaceConfig */
|
||||||
/* jshint -W101 */
|
/* jshint -W101 */
|
||||||
var AudioLevels = require("../audio_levels/AudioLevels");
|
|
||||||
var ContactList = require("../side_pannels/contactlist/ContactList");
|
import AudioLevels from "../audio_levels/AudioLevels";
|
||||||
|
import ContactList from "../side_pannels/contactlist/ContactList";
|
||||||
|
|
||||||
|
import UIEvents from "../../../service/UI/UIEvents";
|
||||||
|
import UIUtil from "../util/UIUtil";
|
||||||
|
|
||||||
|
import RemoteVideo from "./RemoteVideo";
|
||||||
|
import LargeVideo from "./LargeVideo";
|
||||||
|
import LocalVideo from "./LocalVideo";
|
||||||
|
|
||||||
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
var MediaStreamType = require("../../../service/RTC/MediaStreamTypes");
|
||||||
var UIEvents = require("../../../service/UI/UIEvents");
|
|
||||||
var UIUtil = require("../util/UIUtil");
|
|
||||||
|
|
||||||
var RTC = require("../../RTC/RTC");
|
|
||||||
var RTCBrowserType = require('../../RTC/RTCBrowserType');
|
var RTCBrowserType = require('../../RTC/RTCBrowserType');
|
||||||
|
|
||||||
var RemoteVideo = require("./RemoteVideo");
|
|
||||||
var LargeVideo = require("./LargeVideo");
|
|
||||||
var LocalVideo = require("./LocalVideo");
|
|
||||||
|
|
||||||
|
|
||||||
var remoteVideos = {};
|
var remoteVideos = {};
|
||||||
var remoteVideoTypes = {};
|
var remoteVideoTypes = {};
|
||||||
var localVideoThumbnail = null;
|
var localVideoThumbnail = null;
|
||||||
|
@ -23,7 +23,7 @@ var lastNCount = config.channelLastN;
|
||||||
var localLastNCount = config.channelLastN;
|
var localLastNCount = config.channelLastN;
|
||||||
var localLastNSet = [];
|
var localLastNSet = [];
|
||||||
var lastNEndpointsCache = [];
|
var lastNEndpointsCache = [];
|
||||||
var lastNPickupJid = null;
|
var lastNPickupId = null;
|
||||||
|
|
||||||
var eventEmitter = null;
|
var eventEmitter = null;
|
||||||
|
|
||||||
|
@ -33,8 +33,50 @@ var eventEmitter = null;
|
||||||
*/
|
*/
|
||||||
var focusedVideoResourceJid = null;
|
var focusedVideoResourceJid = null;
|
||||||
|
|
||||||
var VideoLayout = (function (my) {
|
/**
|
||||||
my.init = function (emitter) {
|
* On contact list item clicked.
|
||||||
|
*/
|
||||||
|
$(ContactList).bind('contactclicked', function(event, id) {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (APP.conference.isLocalId(id)) {
|
||||||
|
$("#localVideoContainer").click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteVideo = remoteVideos[id];
|
||||||
|
if (remoteVideo && remoteVideo.selectVideoElement().length) {
|
||||||
|
var videoThumb = remoteVideo.selectVideoElement()[0];
|
||||||
|
// It is not always the case that a videoThumb exists (if there is
|
||||||
|
// no actual video).
|
||||||
|
if (RTC.getVideoSrc(videoThumb)) {
|
||||||
|
|
||||||
|
// We have a video src, great! Let's update the large video
|
||||||
|
// now.
|
||||||
|
VideoLayout.handleVideoThumbClicked(false, id);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// If we don't have a video src for jid, there's absolutely
|
||||||
|
// no point in calling handleVideoThumbClicked; Quite
|
||||||
|
// simply, it won't work because it needs an src to attach
|
||||||
|
// to the large video.
|
||||||
|
//
|
||||||
|
// Instead, we trigger the pinned endpoint changed event to
|
||||||
|
// let the bridge adjust its lastN set for myjid and store
|
||||||
|
// the pinned user in the lastNPickupId variable to be
|
||||||
|
// picked up later by the lastN changed event handler.
|
||||||
|
|
||||||
|
lastNPickupId = id;
|
||||||
|
eventEmitter.emit(UIEvents.PINNED_ENDPOINT, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var VideoLayout = {
|
||||||
|
init (emitter) {
|
||||||
eventEmitter = emitter;
|
eventEmitter = emitter;
|
||||||
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
localVideoThumbnail = new LocalVideo(VideoLayout, emitter);
|
||||||
if (interfaceConfig.filmStripOnly) {
|
if (interfaceConfig.filmStripOnly) {
|
||||||
|
@ -45,19 +87,21 @@ var VideoLayout = (function (my) {
|
||||||
|
|
||||||
VideoLayout.resizeLargeVideoContainer();
|
VideoLayout.resizeLargeVideoContainer();
|
||||||
|
|
||||||
};
|
},
|
||||||
|
|
||||||
my.isInLastN = function(resource) {
|
isInLastN (resource) {
|
||||||
return lastNCount < 0 || // lastN is disabled
|
return lastNCount < 0 || // lastN is disabled
|
||||||
// lastNEndpoints cache not built yet
|
// lastNEndpoints cache not built yet
|
||||||
(lastNCount > 0 && !lastNEndpointsCache.length) ||
|
(lastNCount > 0 && !lastNEndpointsCache.length) ||
|
||||||
(lastNEndpointsCache &&
|
(lastNEndpointsCache &&
|
||||||
lastNEndpointsCache.indexOf(resource) !== -1);
|
lastNEndpointsCache.indexOf(resource) !== -1);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.changeLocalAudio = function(stream, isMuted) {
|
changeLocalAudio (stream) {
|
||||||
APP.RTC.attachMediaStream($('#localAudio'), stream.getOriginalStream());
|
let localAudio = document.getElementById('localAudio');
|
||||||
var localAudio = document.getElementById('localAudio');
|
stream.attach($(localAudio));
|
||||||
|
|
||||||
|
return; // FIXME maybe move this into the library?
|
||||||
// Writing volume not allowed in IE
|
// Writing volume not allowed in IE
|
||||||
if (!RTCBrowserType.isIExplorer()) {
|
if (!RTCBrowserType.isIExplorer()) {
|
||||||
localAudio.autoplay = true;
|
localAudio.autoplay = true;
|
||||||
|
@ -73,39 +117,41 @@ var VideoLayout = (function (my) {
|
||||||
// which will result in audio mute issues
|
// which will result in audio mute issues
|
||||||
$('#localAudio').hide();
|
$('#localAudio').hide();
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
my.changeLocalVideo = function(stream, isMuted) {
|
changeLocalVideo (stream) {
|
||||||
// Set default display name.
|
// Set default display name.
|
||||||
localVideoThumbnail.setDisplayName();
|
localVideoThumbnail.setDisplayName();
|
||||||
localVideoThumbnail.createConnectionIndicator();
|
localVideoThumbnail.createConnectionIndicator();
|
||||||
|
|
||||||
this.onVideoTypeChanged(APP.xmpp.myResource(), stream.videoType);
|
let localId = APP.conference.localId;
|
||||||
|
this.onVideoTypeChanged(localId, stream.getType());
|
||||||
|
|
||||||
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
AudioLevels.updateAudioLevelCanvas(null, VideoLayout);
|
||||||
|
|
||||||
localVideoThumbnail.changeVideo(stream, isMuted);
|
localVideoThumbnail.changeVideo(stream);
|
||||||
|
|
||||||
/* force update if we're currently being displayed */
|
/* force update if we're currently being displayed */
|
||||||
if (LargeVideo.isCurrentlyOnLarge(APP.xmpp.myResource())) {
|
if (LargeVideo.isCurrentlyOnLarge(localId)) {
|
||||||
LargeVideo.updateLargeVideo(APP.xmpp.myResource(), true);
|
LargeVideo.updateLargeVideo(localId, true);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
my.mucJoined = function () {
|
mucJoined () {
|
||||||
var myResourceJid = APP.xmpp.myResource();
|
let id = APP.conference.localId;
|
||||||
localVideoThumbnail.joined(APP.xmpp.myJid());
|
localVideoThumbnail.joined(id);
|
||||||
|
|
||||||
if (!LargeVideo.getResourceJid())
|
if (!LargeVideo.id) {
|
||||||
LargeVideo.updateLargeVideo(myResourceJid, true);
|
LargeVideo.updateLargeVideo(id, true);
|
||||||
};
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds or removes icons for not available camera and microphone.
|
* Adds or removes icons for not available camera and microphone.
|
||||||
* @param resourceJid the jid of user
|
* @param resourceJid the jid of user
|
||||||
* @param devices available devices
|
* @param devices available devices
|
||||||
*/
|
*/
|
||||||
my.setDeviceAvailabilityIcons = function (resourceJid, devices) {
|
setDeviceAvailabilityIcons (resourceJid, devices) {
|
||||||
if(!devices)
|
if(!devices)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -115,31 +161,31 @@ var VideoLayout = (function (my) {
|
||||||
if(remoteVideos[resourceJid])
|
if(remoteVideos[resourceJid])
|
||||||
remoteVideos[resourceJid].setDeviceAvailabilityIcons(devices);
|
remoteVideos[resourceJid].setDeviceAvailabilityIcons(devices);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if removed video is currently displayed and tries to display
|
* Checks if removed video is currently displayed and tries to display
|
||||||
* another one instead.
|
* another one instead.
|
||||||
*/
|
*/
|
||||||
my.updateRemovedVideo = function(resourceJid) {
|
updateRemovedVideo (id) {
|
||||||
var newResourceJid;
|
let newId;
|
||||||
|
|
||||||
if (resourceJid === LargeVideo.getResourceJid()) {
|
if (id === LargeVideo.getId()) {
|
||||||
// We'll show user's avatar if he is the dominant speaker or if
|
// We'll show user's avatar if he is the dominant speaker or if
|
||||||
// his video thumbnail is pinned
|
// his video thumbnail is pinned
|
||||||
if (remoteVideos[resourceJid] &&
|
if (remoteVideos[id] &&
|
||||||
resourceJid === focusedVideoResourceJid ||
|
id === focusedVideoResourceJid ||
|
||||||
resourceJid === currentDominantSpeaker) {
|
id === currentDominantSpeaker) {
|
||||||
newResourceJid = resourceJid;
|
newId = id;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise select last visible video
|
// Otherwise select last visible video
|
||||||
newResourceJid = this.electLastVisibleVideo();
|
newId = this.electLastVisibleVideo();
|
||||||
}
|
}
|
||||||
LargeVideo.updateLargeVideo(newResourceJid);
|
LargeVideo.updateLargeVideo(id);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
my.electLastVisibleVideo = function () {
|
electLastVisibleVideo () {
|
||||||
// pick the last visible video in the row
|
// pick the last visible video in the row
|
||||||
// if nobody else is left, this picks the local video
|
// if nobody else is left, this picks the local video
|
||||||
var jid;
|
var jid;
|
||||||
|
@ -174,39 +220,36 @@ var VideoLayout = (function (my) {
|
||||||
|
|
||||||
console.info("electLastVisibleVideo: " + jid);
|
console.info("electLastVisibleVideo: " + jid);
|
||||||
return jid;
|
return jid;
|
||||||
};
|
},
|
||||||
|
|
||||||
my.onRemoteStreamAdded = function (stream) {
|
onRemoteStreamAdded (stream) {
|
||||||
if (stream.peerjid) {
|
let id = stream.getParticipantId();
|
||||||
VideoLayout.ensurePeerContainerExists(stream.peerjid);
|
VideoLayout.ensurePeerContainerExists(id);
|
||||||
|
|
||||||
var resourceJid = Strophe.getResourceFromJid(stream.peerjid);
|
remoteVideos[id].addRemoteStreamElement(stream);
|
||||||
remoteVideos[resourceJid].addRemoteStreamElement(stream);
|
},
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
my.getLargeVideoResource = function () {
|
getLargeVideoId () {
|
||||||
return LargeVideo.getResourceJid();
|
return LargeVideo.getId();
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the type of the remote video.
|
* Return the type of the remote video.
|
||||||
* @param jid the jid for the remote video
|
* @param id the id for the remote video
|
||||||
* @returns the video type video or screen.
|
* @returns the video type video or screen.
|
||||||
*/
|
*/
|
||||||
my.getRemoteVideoType = function (jid) {
|
getRemoteVideoType (id) {
|
||||||
return remoteVideoTypes[jid];
|
return remoteVideoTypes[id];
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when large video update is finished
|
* Called when large video update is finished
|
||||||
* @param currentSmallVideo small video currently displayed on large video
|
* @param currentSmallVideo small video currently displayed on large video
|
||||||
*/
|
*/
|
||||||
my.largeVideoUpdated = function (currentSmallVideo) {
|
largeVideoUpdated (currentSmallVideo) {
|
||||||
// Makes sure that dominant speaker UI
|
// Makes sure that dominant speaker UI
|
||||||
// is enabled only on current small video
|
// is enabled only on current small video
|
||||||
localVideoThumbnail.enableDominantSpeaker(
|
localVideoThumbnail.enableDominantSpeaker(localVideoThumbnail === currentSmallVideo);
|
||||||
localVideoThumbnail === currentSmallVideo);
|
|
||||||
Object.keys(remoteVideos).forEach(
|
Object.keys(remoteVideos).forEach(
|
||||||
function (resourceJid) {
|
function (resourceJid) {
|
||||||
var remoteVideo = remoteVideos[resourceJid];
|
var remoteVideo = remoteVideos[resourceJid];
|
||||||
|
@ -216,9 +259,9 @@ var VideoLayout = (function (my) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.handleVideoThumbClicked = function(noPinnedEndpointChangedEvent,
|
handleVideoThumbClicked (noPinnedEndpointChangedEvent,
|
||||||
resourceJid) {
|
resourceJid) {
|
||||||
if(focusedVideoResourceJid) {
|
if(focusedVideoResourceJid) {
|
||||||
var oldSmallVideo
|
var oldSmallVideo
|
||||||
|
@ -269,50 +312,46 @@ var VideoLayout = (function (my) {
|
||||||
el.volume = 1;
|
el.volume = 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if container for participant identified by given peerJid exists
|
* Checks if container for participant identified by given id exists
|
||||||
* in the document and creates it eventually.
|
* in the document and creates it eventually.
|
||||||
*
|
*
|
||||||
* @param peerJid peer Jid to check.
|
|
||||||
*
|
|
||||||
* @return Returns <tt>true</tt> if the peer container exists,
|
* @return Returns <tt>true</tt> if the peer container exists,
|
||||||
* <tt>false</tt> - otherwise
|
* <tt>false</tt> - otherwise
|
||||||
*/
|
*/
|
||||||
my.ensurePeerContainerExists = function(peerJid) {
|
ensurePeerContainerExists (id) {
|
||||||
ContactList.ensureAddContact(peerJid);
|
ContactList.ensureAddContact(id);
|
||||||
|
|
||||||
var resourceJid = Strophe.getResourceFromJid(peerJid);
|
if (remoteVideos[id]) {
|
||||||
|
return;
|
||||||
if (!remoteVideos[resourceJid]) {
|
|
||||||
|
|
||||||
var remoteVideo = new RemoteVideo(peerJid, VideoLayout);
|
|
||||||
remoteVideos[resourceJid] = remoteVideo;
|
|
||||||
|
|
||||||
var videoType = remoteVideoTypes[resourceJid];
|
|
||||||
if (videoType) {
|
|
||||||
remoteVideo.setVideoType(videoType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case this is not currently in the last n we don't show it.
|
|
||||||
if (localLastNCount &&
|
|
||||||
localLastNCount > 0 &&
|
|
||||||
$('#remoteVideos>span').length >= localLastNCount + 2) {
|
|
||||||
remoteVideo.showPeerContainer('hide');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
VideoLayout.resizeThumbnails();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
let remoteVideo = new RemoteVideo(id, VideoLayout, eventEmitter);
|
||||||
|
remoteVideos[id] = remoteVideo;
|
||||||
|
|
||||||
|
let videoType = remoteVideoTypes[id];
|
||||||
|
if (videoType) {
|
||||||
|
remoteVideo.setVideoType(videoType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case this is not currently in the last n we don't show it.
|
||||||
|
if (localLastNCount && localLastNCount > 0 &&
|
||||||
|
$('#remoteVideos>span').length >= localLastNCount + 2) {
|
||||||
|
remoteVideo.showPeerContainer('hide');
|
||||||
|
} else {
|
||||||
|
VideoLayout.resizeThumbnails();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
my.inputDisplayNameHandler = function (name) {
|
inputDisplayNameHandler (name) {
|
||||||
localVideoThumbnail.inputDisplayNameHandler(name);
|
localVideoThumbnail.inputDisplayNameHandler(name);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.videoactive = function (videoelem, resourceJid) {
|
videoactive (videoelem, resourceJid) {
|
||||||
|
|
||||||
console.info(resourceJid + " video is now active");
|
console.info(resourceJid + " video is now active");
|
||||||
|
|
||||||
|
@ -330,61 +369,50 @@ var VideoLayout = (function (my) {
|
||||||
currentDominantSpeaker === resourceJid)) {
|
currentDominantSpeaker === resourceJid)) {
|
||||||
LargeVideo.updateLargeVideo(resourceJid, true);
|
LargeVideo.updateLargeVideo(resourceJid, true);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the presence status message for the given video.
|
* Shows the presence status message for the given video.
|
||||||
*/
|
*/
|
||||||
my.setPresenceStatus = function (resourceJid, statusMsg) {
|
setPresenceStatus (resourceJid, statusMsg) {
|
||||||
remoteVideos[resourceJid].setPresenceStatus(statusMsg);
|
remoteVideos[resourceJid].setPresenceStatus(statusMsg);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows a visual indicator for the moderator of the conference.
|
* Shows a visual indicator for the moderator of the conference.
|
||||||
*/
|
*/
|
||||||
my.showModeratorIndicator = function () {
|
showModeratorIndicator () {
|
||||||
|
let isModerator = APP.conference.isModerator;
|
||||||
var isModerator = APP.xmpp.isModerator();
|
|
||||||
if (isModerator) {
|
if (isModerator) {
|
||||||
localVideoThumbnail.createModeratorIndicatorElement();
|
localVideoThumbnail.createModeratorIndicatorElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
var members = APP.xmpp.getMembers();
|
APP.conference.listMembers().forEach(function (member) {
|
||||||
|
let id = member.getId();
|
||||||
Object.keys(members).forEach(function (jid) {
|
if (member.isModerator()) {
|
||||||
|
remoteVideos[id].removeRemoteVideoMenu();
|
||||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
remoteVideos[id].createModeratorIndicatorElement();
|
||||||
var member = members[jid];
|
|
||||||
|
|
||||||
if (member.isFocus) {
|
|
||||||
// Skip server side focus
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member.role === 'moderator') {
|
|
||||||
remoteVideos[resourceJid].removeRemoteVideoMenu();
|
|
||||||
remoteVideos[resourceJid].createModeratorIndicatorElement();
|
|
||||||
} else if (isModerator) {
|
} else if (isModerator) {
|
||||||
// We are moderator, but user is not - add menu
|
// We are moderator, but user is not - add menu
|
||||||
if ($('#remote_popupmenu_' + resourceJid).length <= 0) {
|
if ($(`#remote_popupmenu_${id}`).length <= 0) {
|
||||||
remoteVideos[resourceJid].addRemoteVideoMenu();
|
remoteVideos[id].addRemoteVideoMenu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shows or hides the audio muted indicator over the local thumbnail video.
|
* Shows or hides the audio muted indicator over the local thumbnail video.
|
||||||
* @param {boolean} isMuted
|
* @param {boolean} isMuted
|
||||||
*/
|
*/
|
||||||
my.showLocalAudioIndicator = function(isMuted) {
|
showLocalAudioIndicator (isMuted) {
|
||||||
localVideoThumbnail.showAudioIndicator(isMuted);
|
localVideoThumbnail.showAudioIndicator(isMuted);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the large video container.
|
* Resizes the large video container.
|
||||||
*/
|
*/
|
||||||
my.resizeLargeVideoContainer = function () {
|
resizeLargeVideoContainer () {
|
||||||
if(LargeVideo.isEnabled()) {
|
if(LargeVideo.isEnabled()) {
|
||||||
LargeVideo.resize();
|
LargeVideo.resize();
|
||||||
} else {
|
} else {
|
||||||
|
@ -392,12 +420,12 @@ var VideoLayout = (function (my) {
|
||||||
}
|
}
|
||||||
VideoLayout.resizeThumbnails();
|
VideoLayout.resizeThumbnails();
|
||||||
LargeVideo.position();
|
LargeVideo.position();
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes thumbnails.
|
* Resizes thumbnails.
|
||||||
*/
|
*/
|
||||||
my.resizeThumbnails = function(animate) {
|
resizeThumbnails (animate) {
|
||||||
var videoSpaceWidth = $('#remoteVideos').width();
|
var videoSpaceWidth = $('#remoteVideos').width();
|
||||||
|
|
||||||
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
|
var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
|
||||||
|
@ -441,14 +469,14 @@ var VideoLayout = (function (my) {
|
||||||
|
|
||||||
$(document).trigger("remotevideo.resized", [width, height]);
|
$(document).trigger("remotevideo.resized", [width, height]);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the thumbnail size.
|
* Calculates the thumbnail size.
|
||||||
*
|
*
|
||||||
* @param videoSpaceWidth the width of the video space
|
* @param videoSpaceWidth the width of the video space
|
||||||
*/
|
*/
|
||||||
my.calculateThumbnailSize = function (videoSpaceWidth) {
|
calculateThumbnailSize (videoSpaceWidth) {
|
||||||
// Calculate the available height, which is the inner window height
|
// Calculate the available height, which is the inner window height
|
||||||
// minus 39px for the header minus 2px for the delimiter lines on the
|
// minus 39px for the header minus 2px for the delimiter lines on the
|
||||||
// top and bottom of the large video, minus the 36px space inside the
|
// top and bottom of the large video, minus the 36px space inside the
|
||||||
|
@ -478,7 +506,7 @@ var VideoLayout = (function (my) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return [availableWidth, availableHeight];
|
return [availableWidth, availableHeight];
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the corresponding resource jid to the given peer container
|
* Returns the corresponding resource jid to the given peer container
|
||||||
|
@ -487,66 +515,21 @@ var VideoLayout = (function (my) {
|
||||||
* @return the corresponding resource jid to the given peer container
|
* @return the corresponding resource jid to the given peer container
|
||||||
* DOM element
|
* DOM element
|
||||||
*/
|
*/
|
||||||
my.getPeerContainerResourceJid = function (containerElement) {
|
getPeerContainerResourceJid (containerElement) {
|
||||||
if (localVideoThumbnail.container === containerElement) {
|
if (localVideoThumbnail.container === containerElement) {
|
||||||
return localVideoThumbnail.getResourceJid();
|
return localVideoThumbnail.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = containerElement.id.indexOf('participant_');
|
var i = containerElement.id.indexOf('participant_');
|
||||||
|
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
return containerElement.id.substring(i + 12);
|
return containerElement.id.substring(i + 12);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* On contact list item clicked.
|
|
||||||
*/
|
|
||||||
$(ContactList).bind('contactclicked', function(event, jid) {
|
|
||||||
if (!jid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jid === APP.xmpp.myJid()) {
|
|
||||||
$("#localVideoContainer").click();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resource = Strophe.getResourceFromJid(jid);
|
|
||||||
var remoteVideo = remoteVideos[resource];
|
|
||||||
if (remoteVideo && remoteVideo.selectVideoElement().length) {
|
|
||||||
var videoThumb = remoteVideo.selectVideoElement()[0];
|
|
||||||
// It is not always the case that a videoThumb exists (if there is
|
|
||||||
// no actual video).
|
|
||||||
if (RTC.getVideoSrc(videoThumb)) {
|
|
||||||
|
|
||||||
// We have a video src, great! Let's update the large video
|
|
||||||
// now.
|
|
||||||
VideoLayout.handleVideoThumbClicked(
|
|
||||||
false,
|
|
||||||
Strophe.getResourceFromJid(jid));
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// If we don't have a video src for jid, there's absolutely
|
|
||||||
// no point in calling handleVideoThumbClicked; Quite
|
|
||||||
// simply, it won't work because it needs an src to attach
|
|
||||||
// to the large video.
|
|
||||||
//
|
|
||||||
// Instead, we trigger the pinned endpoint changed event to
|
|
||||||
// let the bridge adjust its lastN set for myjid and store
|
|
||||||
// the pinned user in the lastNPickupJid variable to be
|
|
||||||
// picked up later by the lastN changed event handler.
|
|
||||||
|
|
||||||
lastNPickupJid = jid;
|
|
||||||
eventEmitter.emit(UIEvents.PINNED_ENDPOINT,
|
|
||||||
Strophe.getResourceFromJid(jid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On audio muted event.
|
* On audio muted event.
|
||||||
*/
|
*/
|
||||||
my.onAudioMute = function (jid, isMuted) {
|
onAudioMute (jid, isMuted) {
|
||||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||||
if (resourceJid === APP.xmpp.myResource()) {
|
if (resourceJid === APP.xmpp.myResource()) {
|
||||||
localVideoThumbnail.showAudioIndicator(isMuted);
|
localVideoThumbnail.showAudioIndicator(isMuted);
|
||||||
|
@ -557,12 +540,12 @@ var VideoLayout = (function (my) {
|
||||||
remoteVideos[resourceJid].updateRemoteVideoMenu(isMuted);
|
remoteVideos[resourceJid].updateRemoteVideoMenu(isMuted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On video muted event.
|
* On video muted event.
|
||||||
*/
|
*/
|
||||||
my.onVideoMute = function (jid, value) {
|
onVideoMute (jid, value) {
|
||||||
if (jid !== APP.xmpp.myJid() &&
|
if (jid !== APP.xmpp.myJid() &&
|
||||||
!APP.RTC.muteRemoteVideoStream(jid, value))
|
!APP.RTC.muteRemoteVideoStream(jid, value))
|
||||||
return;
|
return;
|
||||||
|
@ -582,12 +565,12 @@ var VideoLayout = (function (my) {
|
||||||
else
|
else
|
||||||
el.hide();
|
el.hide();
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display name changed.
|
* Display name changed.
|
||||||
*/
|
*/
|
||||||
my.onDisplayNameChanged = function (id, displayName, status) {
|
onDisplayNameChanged (id, displayName, status) {
|
||||||
if (id === 'localVideoContainer' ||
|
if (id === 'localVideoContainer' ||
|
||||||
APP.conference.isLocalId(id)) {
|
APP.conference.isLocalId(id)) {
|
||||||
localVideoThumbnail.setDisplayName(displayName);
|
localVideoThumbnail.setDisplayName(displayName);
|
||||||
|
@ -595,39 +578,39 @@ var VideoLayout = (function (my) {
|
||||||
VideoLayout.ensurePeerContainerExists(id);
|
VideoLayout.ensurePeerContainerExists(id);
|
||||||
remoteVideos[id].setDisplayName(displayName, status);
|
remoteVideos[id].setDisplayName(displayName, status);
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On dominant speaker changed event.
|
* On dominant speaker changed event.
|
||||||
*/
|
*/
|
||||||
my.onDominantSpeakerChanged = function (resourceJid) {
|
onDominantSpeakerChanged (id) {
|
||||||
// We ignore local user events.
|
// We ignore local user events.
|
||||||
if (resourceJid === APP.xmpp.myResource())
|
if (APP.conference.isLocalId(id)) {
|
||||||
return;
|
|
||||||
|
|
||||||
var remoteVideo = remoteVideos[resourceJid];
|
|
||||||
var members = APP.xmpp.getMembers();
|
|
||||||
// Update the current dominant speaker.
|
|
||||||
if (resourceJid !== currentDominantSpeaker) {
|
|
||||||
if (remoteVideo) {
|
|
||||||
remoteVideo.updateDominantSpeakerIndicator(true);
|
|
||||||
// let's remove the indications from the remote video if any
|
|
||||||
var oldSpeakerRemoteVideo
|
|
||||||
= remoteVideos[currentDominantSpeaker];
|
|
||||||
if (oldSpeakerRemoteVideo) {
|
|
||||||
oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
currentDominantSpeaker = resourceJid;
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!remoteVideo)
|
if (id === currentDominantSpeaker) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let remoteVideo = remoteVideos[id];
|
||||||
|
|
||||||
|
if (!remoteVideo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the current dominant speaker.
|
||||||
|
remoteVideo.updateDominantSpeakerIndicator(true);
|
||||||
|
|
||||||
|
// let's remove the indications from the remote video if any
|
||||||
|
let oldSpeakerRemoteVideo = remoteVideos[currentDominantSpeaker];
|
||||||
|
if (oldSpeakerRemoteVideo) {
|
||||||
|
oldSpeakerRemoteVideo.updateDominantSpeakerIndicator(false);
|
||||||
|
}
|
||||||
|
currentDominantSpeaker = id;
|
||||||
|
|
||||||
// Obtain container for new dominant speaker.
|
// Obtain container for new dominant speaker.
|
||||||
var videoSel = remoteVideo.selectVideoElement();
|
let videoSel = remoteVideo.selectVideoElement();
|
||||||
|
|
||||||
// Local video will not have container found, but that's ok
|
// Local video will not have container found, but that's ok
|
||||||
// since we don't want to switch to local video.
|
// since we don't want to switch to local video.
|
||||||
|
@ -635,10 +618,10 @@ var VideoLayout = (function (my) {
|
||||||
// Update the large video if the video source is already available,
|
// Update the large video if the video source is already available,
|
||||||
// otherwise wait for the "videoactive.jingle" event.
|
// otherwise wait for the "videoactive.jingle" event.
|
||||||
if (videoSel[0].currentTime > 0) {
|
if (videoSel[0].currentTime > 0) {
|
||||||
LargeVideo.updateLargeVideo(resourceJid);
|
LargeVideo.updateLargeVideo(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On last N change event.
|
* On last N change event.
|
||||||
|
@ -647,7 +630,7 @@ var VideoLayout = (function (my) {
|
||||||
* @param endpointsEnteringLastN the list currently entering last N
|
* @param endpointsEnteringLastN the list currently entering last N
|
||||||
* endpoints
|
* endpoints
|
||||||
*/
|
*/
|
||||||
my.onLastNEndpointsChanged = function (lastNEndpoints, endpointsEnteringLastN) {
|
onLastNEndpointsChanged (lastNEndpoints, endpointsEnteringLastN) {
|
||||||
if (lastNCount !== lastNEndpoints.length)
|
if (lastNCount !== lastNEndpoints.length)
|
||||||
lastNCount = lastNEndpoints.length;
|
lastNCount = lastNEndpoints.length;
|
||||||
|
|
||||||
|
@ -727,7 +710,7 @@ var VideoLayout = (function (my) {
|
||||||
// displayed in the large video we have to switch to another
|
// displayed in the large video we have to switch to another
|
||||||
// user.
|
// user.
|
||||||
if (!updateLargeVideo &&
|
if (!updateLargeVideo &&
|
||||||
resourceJid === LargeVideo.getResourceJid()) {
|
resourceJid === LargeVideo.getId()) {
|
||||||
updateLargeVideo = true;
|
updateLargeVideo = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,9 +734,9 @@ var VideoLayout = (function (my) {
|
||||||
var sel = remoteVideo.selectVideoElement();
|
var sel = remoteVideo.selectVideoElement();
|
||||||
|
|
||||||
APP.RTC.attachMediaStream(sel, mediaStream.stream);
|
APP.RTC.attachMediaStream(sel, mediaStream.stream);
|
||||||
if (lastNPickupJid == mediaStream.peerjid) {
|
if (lastNPickupId == mediaStream.peerjid) {
|
||||||
// Clean up the lastN pickup jid.
|
// Clean up the lastN pickup id.
|
||||||
lastNPickupJid = null;
|
lastNPickupId = null;
|
||||||
|
|
||||||
// Don't fire the events again, they've already
|
// Don't fire the events again, they've already
|
||||||
// been fired in the contact list click handler.
|
// been fired in the contact list click handler.
|
||||||
|
@ -789,14 +772,14 @@ var VideoLayout = (function (my) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates local stats
|
* Updates local stats
|
||||||
* @param percent
|
* @param percent
|
||||||
* @param object
|
* @param object
|
||||||
*/
|
*/
|
||||||
my.updateLocalConnectionStats = function (percent, object) {
|
updateLocalConnectionStats (percent, object) {
|
||||||
var resolution = null;
|
var resolution = null;
|
||||||
if (object.resolution !== null) {
|
if (object.resolution !== null) {
|
||||||
resolution = object.resolution;
|
resolution = object.resolution;
|
||||||
|
@ -814,7 +797,7 @@ var VideoLayout = (function (my) {
|
||||||
updateResolution(resolution[jid]);
|
updateResolution(resolution[jid]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates remote stats.
|
* Updates remote stats.
|
||||||
|
@ -822,92 +805,90 @@ var VideoLayout = (function (my) {
|
||||||
* @param percent the connection quality percent
|
* @param percent the connection quality percent
|
||||||
* @param object the stats data
|
* @param object the stats data
|
||||||
*/
|
*/
|
||||||
my.updateConnectionStats = function (jid, percent, object) {
|
updateConnectionStats (jid, percent, object) {
|
||||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
var resourceJid = Strophe.getResourceFromJid(jid);
|
||||||
|
|
||||||
if (remoteVideos[resourceJid])
|
if (remoteVideos[resourceJid])
|
||||||
remoteVideos[resourceJid].updateStatsIndicator(percent, object);
|
remoteVideos[resourceJid].updateStatsIndicator(percent, object);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides the connection indicator
|
* Hides the connection indicator
|
||||||
* @param jid
|
* @param jid
|
||||||
*/
|
*/
|
||||||
my.hideConnectionIndicator = function (jid) {
|
hideConnectionIndicator (jid) {
|
||||||
remoteVideos[Strophe.getResourceFromJid(jid)].hideConnectionIndicator();
|
remoteVideos[Strophe.getResourceFromJid(jid)].hideConnectionIndicator();
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hides all the indicators
|
* Hides all the indicators
|
||||||
*/
|
*/
|
||||||
my.hideStats = function () {
|
hideStats () {
|
||||||
for(var video in remoteVideos) {
|
for(var video in remoteVideos) {
|
||||||
remoteVideos[video].hideIndicator();
|
remoteVideos[video].hideIndicator();
|
||||||
}
|
}
|
||||||
localVideoThumbnail.hideIndicator();
|
localVideoThumbnail.hideIndicator();
|
||||||
};
|
},
|
||||||
|
|
||||||
my.participantLeft = function (jid) {
|
participantLeft (id) {
|
||||||
// Unlock large video
|
// Unlock large video
|
||||||
var resourceJid = Strophe.getResourceFromJid(jid);
|
if (focusedVideoResourceJid === id) {
|
||||||
if (focusedVideoResourceJid === resourceJid) {
|
|
||||||
console.info("Focused video owner has left the conference");
|
console.info("Focused video owner has left the conference");
|
||||||
focusedVideoResourceJid = null;
|
focusedVideoResourceJid = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentDominantSpeaker === resourceJid) {
|
if (currentDominantSpeaker === id) {
|
||||||
console.info("Dominant speaker has left the conference");
|
console.info("Dominant speaker has left the conference");
|
||||||
currentDominantSpeaker = null;
|
currentDominantSpeaker = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteVideo = remoteVideos[resourceJid];
|
var remoteVideo = remoteVideos[id];
|
||||||
if (remoteVideo) {
|
if (remoteVideo) {
|
||||||
// Remove remote video
|
// Remove remote video
|
||||||
console.info("Removing remote video: " + resourceJid);
|
console.info("Removing remote video: " + id);
|
||||||
delete remoteVideos[resourceJid];
|
delete remoteVideos[id];
|
||||||
remoteVideo.remove();
|
remoteVideo.remove();
|
||||||
} else {
|
} else {
|
||||||
console.warn("No remote video for " + resourceJid);
|
console.warn("No remote video for " + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoLayout.resizeThumbnails();
|
VideoLayout.resizeThumbnails();
|
||||||
};
|
},
|
||||||
|
|
||||||
my.onVideoTypeChanged = function (resourceJid, newVideoType) {
|
onVideoTypeChanged (id, newVideoType) {
|
||||||
if (remoteVideoTypes[resourceJid] === newVideoType) {
|
if (remoteVideoTypes[id] === newVideoType) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info("Peer video type changed: ", resourceJid, newVideoType);
|
console.info("Peer video type changed: ", id, newVideoType);
|
||||||
remoteVideoTypes[resourceJid] = newVideoType;
|
remoteVideoTypes[id] = newVideoType;
|
||||||
|
|
||||||
var smallVideo;
|
var smallVideo;
|
||||||
if (resourceJid === APP.xmpp.myResource()) {
|
if (APP.conference.isLocalId(id)) {
|
||||||
if (!localVideoThumbnail) {
|
if (!localVideoThumbnail) {
|
||||||
console.warn("Local video not ready yet");
|
console.warn("Local video not ready yet");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
smallVideo = localVideoThumbnail;
|
smallVideo = localVideoThumbnail;
|
||||||
} else if (remoteVideos[resourceJid]) {
|
} else if (remoteVideos[id]) {
|
||||||
smallVideo = remoteVideos[resourceJid];
|
smallVideo = remoteVideos[id];
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
smallVideo.setVideoType(newVideoType);
|
smallVideo.setVideoType(newVideoType);
|
||||||
LargeVideo.onVideoTypeChanged(resourceJid, newVideoType);
|
LargeVideo.onVideoTypeChanged(id, newVideoType);
|
||||||
|
},
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the video size and position.
|
* Updates the video size and position.
|
||||||
*/
|
*/
|
||||||
my.updateLargeVideoSize = function () {
|
updateLargeVideoSize () {
|
||||||
LargeVideo.updateVideoSizeAndPosition();
|
LargeVideo.updateVideoSizeAndPosition();
|
||||||
LargeVideo.position(null, null, null, null, true);
|
LargeVideo.position(null, null, null, null, true);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.showMore = function (jid) {
|
showMore (jid) {
|
||||||
if (jid === 'local') {
|
if (jid === 'local') {
|
||||||
localVideoThumbnail.connectionIndicator.showMore();
|
localVideoThumbnail.connectionIndicator.showMore();
|
||||||
} else {
|
} else {
|
||||||
|
@ -918,15 +899,15 @@ var VideoLayout = (function (my) {
|
||||||
console.info("Error - no remote video for jid: " + jid);
|
console.info("Error - no remote video for jid: " + jid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
my.addPreziContainer = function (id) {
|
addPreziContainer (id) {
|
||||||
var container = RemoteVideo.createContainer(id);
|
var container = RemoteVideo.createContainer(id);
|
||||||
VideoLayout.resizeThumbnails();
|
VideoLayout.resizeThumbnails();
|
||||||
return container;
|
return container;
|
||||||
};
|
},
|
||||||
|
|
||||||
my.setLargeVideoVisible = function (isVisible) {
|
setLargeVideoVisible (isVisible) {
|
||||||
LargeVideo.setLargeVideoVisible(isVisible);
|
LargeVideo.setLargeVideoVisible(isVisible);
|
||||||
if(!isVisible && focusedVideoResourceJid) {
|
if(!isVisible && focusedVideoResourceJid) {
|
||||||
var smallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
|
var smallVideo = VideoLayout.getSmallVideo(focusedVideoResourceJid);
|
||||||
|
@ -936,7 +917,7 @@ var VideoLayout = (function (my) {
|
||||||
}
|
}
|
||||||
focusedVideoResourceJid = null;
|
focusedVideoResourceJid = null;
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the video area.
|
* Resizes the video area.
|
||||||
|
@ -945,10 +926,10 @@ var VideoLayout = (function (my) {
|
||||||
* @param callback a function to be called when the video space is
|
* @param callback a function to be called when the video space is
|
||||||
* resized.
|
* resized.
|
||||||
*/
|
*/
|
||||||
my.resizeVideoArea = function(isSideBarVisible, callback) {
|
resizeVideoArea (isSideBarVisible, callback) {
|
||||||
LargeVideo.resizeVideoAreaAnimated(isSideBarVisible, callback);
|
LargeVideo.resizeVideoAreaAnimated(isSideBarVisible, callback);
|
||||||
VideoLayout.resizeThumbnails(true);
|
VideoLayout.resizeThumbnails(true);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the #videospace html element
|
* Resizes the #videospace html element
|
||||||
|
@ -961,7 +942,7 @@ var VideoLayout = (function (my) {
|
||||||
* @param completeFunction a function to be called when the video space
|
* @param completeFunction a function to be called when the video space
|
||||||
* is resized.
|
* is resized.
|
||||||
*/
|
*/
|
||||||
my.resizeVideoSpace = function (animate, isChatVisible, completeFunction) {
|
resizeVideoSpace (animate, isChatVisible, completeFunction) {
|
||||||
var availableHeight = window.innerHeight;
|
var availableHeight = window.innerHeight;
|
||||||
var availableWidth = UIUtil.getAvailableVideoWidth(isChatVisible);
|
var availableWidth = UIUtil.getAvailableVideoWidth(isChatVisible);
|
||||||
|
|
||||||
|
@ -983,19 +964,17 @@ var VideoLayout = (function (my) {
|
||||||
$('#videospace').height(availableHeight);
|
$('#videospace').height(availableHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
},
|
||||||
|
|
||||||
my.getSmallVideo = function (resourceJid) {
|
getSmallVideo (id) {
|
||||||
if(resourceJid == APP.xmpp.myResource()) {
|
if (APP.conference.isLocalId(id)) {
|
||||||
return localVideoThumbnail;
|
return localVideoThumbnail;
|
||||||
} else {
|
} else {
|
||||||
if(!remoteVideos[resourceJid])
|
return remoteVideos[id];
|
||||||
return null;
|
|
||||||
return remoteVideos[resourceJid];
|
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
my.changeUserAvatar = function(id, thumbUrl) {
|
changeUserAvatar (id, thumbUrl) {
|
||||||
var smallVideo = VideoLayout.getSmallVideo(id);
|
var smallVideo = VideoLayout.getSmallVideo(id);
|
||||||
if (smallVideo) {
|
if (smallVideo) {
|
||||||
smallVideo.avatarChanged(thumbUrl);
|
smallVideo.avatarChanged(thumbUrl);
|
||||||
|
@ -1005,46 +984,43 @@ var VideoLayout = (function (my) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
LargeVideo.updateAvatar(id, thumbUrl);
|
LargeVideo.updateAvatar(id, thumbUrl);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.createEtherpadIframe = function(src, onloadHandler)
|
createEtherpadIframe (src, onloadHandler) {
|
||||||
{
|
|
||||||
return LargeVideo.createEtherpadIframe(src, onloadHandler);
|
return LargeVideo.createEtherpadIframe(src, onloadHandler);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.setLargeVideoState = function (state) {
|
setLargeVideoState (state) {
|
||||||
LargeVideo.setState(state);
|
LargeVideo.setState(state);
|
||||||
};
|
},
|
||||||
|
|
||||||
my.getLargeVideoState = function () {
|
getLargeVideoState () {
|
||||||
return LargeVideo.getState();
|
return LargeVideo.getState();
|
||||||
};
|
},
|
||||||
|
|
||||||
my.setLargeVideoHover = function (inHandler, outHandler) {
|
setLargeVideoHover (inHandler, outHandler) {
|
||||||
LargeVideo.setHover(inHandler, outHandler);
|
LargeVideo.setHover(inHandler, outHandler);
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the video has been interrupted.
|
* Indicates that the video has been interrupted.
|
||||||
*/
|
*/
|
||||||
my.onVideoInterrupted = function () {
|
onVideoInterrupted () {
|
||||||
LargeVideo.enableVideoProblemFilter(true);
|
LargeVideo.enableVideoProblemFilter(true);
|
||||||
var reconnectingKey = "connection.RECONNECTING";
|
var reconnectingKey = "connection.RECONNECTING";
|
||||||
$('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
|
$('#videoConnectionMessage').attr("data-i18n", reconnectingKey);
|
||||||
$('#videoConnectionMessage')
|
$('#videoConnectionMessage')
|
||||||
.text(APP.translation.translateString(reconnectingKey));
|
.text(APP.translation.translateString(reconnectingKey));
|
||||||
$('#videoConnectionMessage').css({display: "block"});
|
$('#videoConnectionMessage').css({display: "block"});
|
||||||
};
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that the video has been restored.
|
* Indicates that the video has been restored.
|
||||||
*/
|
*/
|
||||||
my.onVideoRestored = function () {
|
onVideoRestored () {
|
||||||
LargeVideo.enableVideoProblemFilter(false);
|
LargeVideo.enableVideoProblemFilter(false);
|
||||||
$('#videoConnectionMessage').css({display: "none"});
|
$('#videoConnectionMessage').css({display: "none"});
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return my;
|
export default VideoLayout;
|
||||||
}(VideoLayout || {}));
|
|
||||||
|
|
||||||
module.exports = VideoLayout;
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default {
|
||||||
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
ETHERPAD_CLICKED: "UI.etherpad_clicked",
|
||||||
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
ROOM_LOCK_CLICKED: "UI.room_lock_clicked",
|
||||||
USER_INVITED: "UI.user_invited",
|
USER_INVITED: "UI.user_invited",
|
||||||
|
USER_KICKED: "UI.user_kicked",
|
||||||
FULLSCREEN_TOGGLE: "UI.fullscreen_toggle",
|
FULLSCREEN_TOGGLE: "UI.fullscreen_toggle",
|
||||||
AUTH_CLICKED: "UI.auth_clicked",
|
AUTH_CLICKED: "UI.auth_clicked",
|
||||||
TOGGLE_CHAT: "UI.toggle_chat",
|
TOGGLE_CHAT: "UI.toggle_chat",
|
||||||
|
|
Loading…
Reference in New Issue