added some documentation (jsdocs)

This commit is contained in:
isymchych 2016-01-15 16:59:35 +02:00
parent a0355ea080
commit 6b98fd52ea
11 changed files with 590 additions and 27 deletions

View File

@ -17,6 +17,9 @@ const ConferenceErrors = JitsiMeetJS.errors.conference;
let room, connection, localTracks, localAudio, localVideo, roomLocker; let room, connection, localTracks, localAudio, localVideo, roomLocker;
/**
* Known custom conference commands.
*/
const Commands = { const Commands = {
CONNECTION_QUALITY: "stats", CONNECTION_QUALITY: "stats",
EMAIL: "email", EMAIL: "email",
@ -26,6 +29,10 @@ const Commands = {
STOP_PREZI: "stop-prezi" STOP_PREZI: "stop-prezi"
}; };
/**
* Open Connection. When authentication failed it shows auth dialog.
* @returns Promise<JitsiConnection>
*/
function connect() { function connect() {
return openConnection({retry: true}).catch(function (err) { return openConnection({retry: true}).catch(function (err) {
if (err === ConnectionErrors.PASSWORD_REQUIRED) { if (err === ConnectionErrors.PASSWORD_REQUIRED) {
@ -37,8 +44,14 @@ function connect() {
}); });
} }
const addTrack = (track) => { /**
* Add local track to the conference and shares
* video type with other users if its video track.
* @param {JitsiLocalTrack} track local track
*/
function addTrack (track) {
room.addTrack(track); room.addTrack(track);
if (track.isAudioTrack()) { if (track.isAudioTrack()) {
return; return;
} }
@ -50,25 +63,35 @@ const addTrack = (track) => {
xmlns: 'http://jitsi.org/jitmeet/video' xmlns: 'http://jitsi.org/jitmeet/video'
} }
}); });
}; }
// share email with other users /**
const sendEmail = (email) => { * Share email with other users.
* @param {string} email new email
*/
function sendEmail (email) {
room.sendCommand(Commands.EMAIL, { room.sendCommand(Commands.EMAIL, {
value: email, value: email,
attributes: { attributes: {
id: room.myUserId() id: room.myUserId()
} }
}); });
}; }
/**
const unload = () => { * Leave the conference and close connection.
*/
function unload () {
room.leave(); room.leave();
connection.disconnect(); connection.disconnect();
}; }
const getDisplayName = (id) => { /**
* Get user nickname by user id.
* @param {string} id user id
* @returns {string?} user nickname or undefined if user is unknown.
*/
function getDisplayName (id) {
if (APP.conference.isLocalId(id)) { if (APP.conference.isLocalId(id)) {
return APP.settings.getDisplayName(); return APP.settings.getDisplayName();
} }
@ -77,7 +100,7 @@ const getDisplayName = (id) => {
if (participant && participant.getDisplayName()) { if (participant && participant.getDisplayName()) {
return participant.getDisplayName(); return participant.getDisplayName();
} }
}; }
class ConferenceConnector { class ConferenceConnector {
constructor(resolve, reject) { constructor(resolve, reject) {
@ -151,6 +174,12 @@ export default {
isModerator: false, isModerator: false,
audioMuted: false, audioMuted: false,
videoMuted: false, videoMuted: false,
/**
* Open new connection and join to the conference.
* @param {object} options
* @param {string} roomName name of the conference
* @returns {Promise}
*/
init(options) { init(options) {
this.roomName = options.roomName; this.roomName = options.roomName;
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE); JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
@ -173,6 +202,12 @@ export default {
}); });
}); });
}, },
/**
* Create local tracks of specified types.
* If we cannot obtain required tracks it will return empty array.
* @param {string[]} devices required track types ('audio', 'video' etc.)
* @returns {Promise<JitsiLocalTrack[]>}
*/
createLocalTracks (...devices) { createLocalTracks (...devices) {
return JitsiMeetJS.createLocalTracks({ return JitsiMeetJS.createLocalTracks({
// copy array to avoid mutations inside library // copy array to avoid mutations inside library
@ -184,6 +219,11 @@ export default {
return Promise.reject(err); return Promise.reject(err);
}); });
}, },
/**
* Check if id is id of the local user.
* @param {string} id id to check
* @returns {boolean}
*/
isLocalId (id) { isLocalId (id) {
return this.localId === id; return this.localId === id;
}, },
@ -217,12 +257,24 @@ export default {
toggleVideoMuted () { toggleVideoMuted () {
this.muteVideo(!this.videoMuted); this.muteVideo(!this.videoMuted);
}, },
/**
* Retrieve list of conference participants (without local user).
* @returns {JitsiParticipant[]}
*/
listMembers () { listMembers () {
return room.getParticipants(); return room.getParticipants();
}, },
/**
* Retrieve list of ids of conference participants (without local user).
* @returns {string[]}
*/
listMembersIds () { listMembersIds () {
return room.getParticipants().map(p => p.getId()); return room.getParticipants().map(p => p.getId());
}, },
/**
* Check if SIP is supported.
* @returns {boolean}
*/
sipGatewayEnabled () { sipGatewayEnabled () {
return room.isSIPCallingSupported(); return room.isSIPCallingSupported();
}, },
@ -279,7 +331,7 @@ export default {
/** /**
* Will check for number of remote particiapnts that have at least one * Will check for number of remote particiapnts that have at least one
* remote track. * remote track.
* @return boolean whether we have enough participants with remote streams * @return {boolean} whether we have enough participants with remote streams
*/ */
checkEnoughParticipants (number) { checkEnoughParticipants (number) {
var participants = this._room.getParticipants(); var participants = this._room.getParticipants();
@ -336,6 +388,9 @@ export default {
} }
return options; return options;
}, },
/**
* Setup interaction between conference and UI.
*/
_setupListeners () { _setupListeners () {
// add local streams when joined to the conference // add local streams when joined to the conference
room.on(ConferenceEvents.CONFERENCE_JOINED, () => { room.on(ConferenceEvents.CONFERENCE_JOINED, () => {

View File

@ -5,6 +5,13 @@ import LoginDialog from './modules/UI/authentication/LoginDialog';
const ConnectionEvents = JitsiMeetJS.events.connection; const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection; const ConnectionErrors = JitsiMeetJS.errors.connection;
/**
* Try to open connection using provided credentials.
* @param {string} [id]
* @param {string} [password]
* @returns {Promise<JitsiConnection>} connection if
* everything is ok, else error.
*/
function connect(id, password) { function connect(id, password) {
let connection = new JitsiMeetJS.JitsiConnection(null, null, config); let connection = new JitsiMeetJS.JitsiConnection(null, null, config);
@ -42,6 +49,12 @@ function connect(id, password) {
}); });
} }
/**
* Show Authentication Dialog and try to connect with new credentials.
* If failed to connect because of PASSWORD_REQUIRED error
* then ask for password again.
* @returns {Promise<JitsiConnection>}
*/
function requestAuth() { function requestAuth() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
let authDialog = LoginDialog.showAuthDialog( let authDialog = LoginDialog.showAuthDialog(
@ -62,6 +75,18 @@ function requestAuth() {
}); });
} }
/**
* Open JitsiConnection using provided credentials.
* If retry option is true it will show auth dialog on PASSWORD_REQUIRED error.
*
* @param {object} options
* @param {string} [options.id]
* @param {string} [options.password]
* @param {boolean} [retry] if we should show auth dialog
* on PASSWORD_REQUIRED error.
*
* @returns {Promise<JitsiConnection>}
*/
export function openConnection({id, password, retry}) { export function openConnection({id, password, retry}) {
return connect(id, password).catch(function (err) { return connect(id, password).catch(function (err) {
if (!retry) { if (!retry) {

View File

@ -31,6 +31,9 @@ UI.eventEmitter = eventEmitter;
let preziManager; let preziManager;
let etherpadManager; let etherpadManager;
/**
* Prompt user for nickname.
*/
function promptDisplayName() { function promptDisplayName() {
let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired"); let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
let defaultNickMsg = APP.translation.translateString( let defaultNickMsg = APP.translation.translateString(
@ -76,6 +79,9 @@ function promptDisplayName() {
); );
} }
/**
* Initialize chat.
*/
function setupChat() { function setupChat() {
Chat.init(eventEmitter); Chat.init(eventEmitter);
$("#toggle_smileys").click(function() { $("#toggle_smileys").click(function() {
@ -83,6 +89,9 @@ function setupChat() {
}); });
} }
/**
* Initialize toolbars.
*/
function setupToolbars() { function setupToolbars() {
Toolbar.init(eventEmitter); Toolbar.init(eventEmitter);
Toolbar.setupButtonsFromConfig(); Toolbar.setupButtonsFromConfig();
@ -113,6 +122,9 @@ function toggleFullScreen () {
} }
} }
/**
* Notify user that server has shut down.
*/
UI.notifyGracefulShudown = function () { UI.notifyGracefulShudown = function () {
messageHandler.openMessageDialog( messageHandler.openMessageDialog(
'dialog.serviceUnavailable', 'dialog.serviceUnavailable',
@ -120,6 +132,9 @@ UI.notifyGracefulShudown = function () {
); );
}; };
/**
* Notify user that reservation error happened.
*/
UI.notifyReservationError = function (code, msg) { UI.notifyReservationError = function (code, msg) {
var title = APP.translation.generateTranslationHTML( var title = APP.translation.generateTranslationHTML(
"dialog.reservationError"); "dialog.reservationError");
@ -135,14 +150,25 @@ UI.notifyReservationError = function (code, msg) {
); );
}; };
/**
* Notify user that he has been kicked from the server.
*/
UI.notifyKicked = function () { UI.notifyKicked = function () {
messageHandler.openMessageDialog("dialog.sessTerminated", "dialog.kickMessage"); messageHandler.openMessageDialog("dialog.sessTerminated", "dialog.kickMessage");
}; };
/**
* Notify user that Jitsi Videobridge is not accessible.
*/
UI.notifyBridgeDown = function () { UI.notifyBridgeDown = function () {
messageHandler.showError("dialog.error", "dialog.bridgeUnavailable"); messageHandler.showError("dialog.error", "dialog.bridgeUnavailable");
}; };
/**
* Change nickname for the user.
* @param {string} id user id
* @param {string} displayName new nickname
*/
UI.changeDisplayName = function (id, displayName) { UI.changeDisplayName = function (id, displayName) {
ContactList.onDisplayNameChange(id, displayName); ContactList.onDisplayNameChange(id, displayName);
SettingsMenu.onDisplayNameChange(id, displayName); SettingsMenu.onDisplayNameChange(id, displayName);
@ -153,6 +179,9 @@ UI.changeDisplayName = function (id, displayName) {
} }
}; };
/**
* Intitialize conference UI.
*/
UI.initConference = function () { UI.initConference = function () {
var id = APP.conference.localId; var id = APP.conference.localId;
Toolbar.updateRoomUrl(window.location.href); Toolbar.updateRoomUrl(window.location.href);
@ -186,6 +215,9 @@ UI.mucJoined = function () {
VideoLayout.mucJoined(); VideoLayout.mucJoined();
}; };
/**
* Setup some UI event listeners.
*/
function registerListeners() { function registerListeners() {
UI.addListener(UIEvents.EMAIL_CHANGED, function (email) { UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
UI.setUserAvatar(APP.conference.localId, email); UI.setUserAvatar(APP.conference.localId, email);
@ -214,6 +246,9 @@ function registerListeners() {
UI.addListener(UIEvents.TOGGLE_FILM_STRIP, UI.toggleFilmStrip); UI.addListener(UIEvents.TOGGLE_FILM_STRIP, UI.toggleFilmStrip);
} }
/**
* Setup some DOM event listeners.
*/
function bindEvents() { function bindEvents() {
function onResize() { function onResize() {
PanelToggler.resizeChat(); PanelToggler.resizeChat();
@ -341,6 +376,10 @@ UI.start = function () {
}; };
/**
* Show local stream on UI.
* @param {JitsiTrack} track stream to show
*/
UI.addLocalStream = function (track) { UI.addLocalStream = function (track) {
switch (track.getType()) { switch (track.getType()) {
case 'audio': case 'audio':
@ -356,18 +395,30 @@ UI.addLocalStream = function (track) {
}; };
UI.addRemoteStream = function (stream) { /**
VideoLayout.onRemoteStreamAdded(stream); * Show remote stream on UI.
* @param {JitsiTrack} track stream to show
*/
UI.addRemoteStream = function (track) {
VideoLayout.onRemoteStreamAdded(track);
}; };
function chatAddError(errorMessage, originalText) { function chatAddError(errorMessage, originalText) {
return Chat.chatAddError(errorMessage, originalText); return Chat.chatAddError(errorMessage, originalText);
} }
/**
* Update chat subject.
* @param {string} subject new chat subject
*/
UI.setSubject = function (subject) { UI.setSubject = function (subject) {
Chat.setSubject(subject); Chat.setSubject(subject);
}; };
/**
* Setup and show Etherpad.
* @param {string} name etherpad id
*/
UI.initEtherpad = function (name) { UI.initEtherpad = function (name) {
if (etherpadManager || !config.etherpad_base || !name) { if (etherpadManager || !config.etherpad_base || !name) {
return; return;
@ -377,6 +428,11 @@ UI.initEtherpad = function (name) {
Toolbar.showEtherpadButton(); Toolbar.showEtherpadButton();
}; };
/**
* Show user on UI.
* @param {string} id user id
* @param {string} displayName user nickname
*/
UI.addUser = function (id, displayName) { UI.addUser = function (id, displayName) {
ContactList.addContact(id); ContactList.addContact(id);
@ -395,6 +451,11 @@ UI.addUser = function (id, displayName) {
VideoLayout.addParticipantContainer(id); VideoLayout.addParticipantContainer(id);
}; };
/**
* Remove user from UI.
* @param {string} id user id
* @param {string} displayName user nickname
*/
UI.removeUser = function (id, displayName) { UI.removeUser = function (id, displayName) {
ContactList.removeContact(id); ContactList.removeContact(id);
@ -415,10 +476,19 @@ UI.removeUser = function (id, displayName) {
// VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status); // VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status);
// } // }
/**
* Update videotype for specified user.
* @param {string} id user id
* @param {string} newVideoType new videotype
*/
UI.onPeerVideoTypeChanged = (id, newVideoType) => { UI.onPeerVideoTypeChanged = (id, newVideoType) => {
VideoLayout.onVideoTypeChanged(id, newVideoType); VideoLayout.onVideoTypeChanged(id, newVideoType);
}; };
/**
* Update local user role and show notification if user is moderator.
* @param {boolean} isModerator if local user is moderator or not
*/
UI.updateLocalRole = function (isModerator) { UI.updateLocalRole = function (isModerator) {
VideoLayout.showModeratorIndicator(); VideoLayout.showModeratorIndicator();
@ -462,22 +532,38 @@ UI.updateUserRole = function (user) {
}; };
/**
* Toggles smileys in the chat.
*/
UI.toggleSmileys = function () { UI.toggleSmileys = function () {
Chat.toggleSmileys(); Chat.toggleSmileys();
}; };
/**
* Get current settings.
* @returns {object} settings
*/
UI.getSettings = function () { UI.getSettings = function () {
return Settings.getSettings(); return Settings.getSettings();
}; };
/**
* Toggles film strip.
*/
UI.toggleFilmStrip = function () { UI.toggleFilmStrip = function () {
BottomToolbar.toggleFilmStrip(); BottomToolbar.toggleFilmStrip();
}; };
/**
* Toggles chat panel.
*/
UI.toggleChat = function () { UI.toggleChat = function () {
PanelToggler.toggleChat(); PanelToggler.toggleChat();
}; };
/**
* Toggles contact list panel.
*/
UI.toggleContactList = function () { UI.toggleContactList = function () {
PanelToggler.toggleContactList(); PanelToggler.toggleContactList();
}; };
@ -499,6 +585,7 @@ UI.connectionIndicatorShowMore = function(jid) {
return VideoLayout.showMore(jid); return VideoLayout.showMore(jid);
}; };
// FIXME check if someone user this
UI.showLoginPopup = function(callback) { UI.showLoginPopup = function(callback) {
console.log('password is required'); console.log('password is required');
var message = '<h2 data-i18n="dialog.passwordRequired">'; var message = '<h2 data-i18n="dialog.passwordRequired">';
@ -569,6 +656,11 @@ UI.dockToolbar = function (isDock) {
ToolbarToggler.dockToolbar(isDock); ToolbarToggler.dockToolbar(isDock);
}; };
/**
* Update user avatar.
* @param {string} id user id
* @param {stirng} email user email
*/
UI.setUserAvatar = function (id, email) { UI.setUserAvatar = function (id, email) {
// update avatar // update avatar
Avatar.setUserAvatar(id, email); Avatar.setUserAvatar(id, email);
@ -582,6 +674,10 @@ UI.setUserAvatar = function (id, email) {
} }
}; };
/**
* Notify user that connection failed.
* @param {string} stropheErrorMsg raw Strophe error message
*/
UI.notifyConnectionFailed = function (stropheErrorMsg) { UI.notifyConnectionFailed = function (stropheErrorMsg) {
var title = APP.translation.generateTranslationHTML( var title = APP.translation.generateTranslationHTML(
"dialog.error"); "dialog.error");
@ -600,6 +696,10 @@ UI.notifyConnectionFailed = function (stropheErrorMsg) {
); );
}; };
/**
* Notify user that he need to install Firefox extension to share screen.
* @param {stirng} url extension url
*/
UI.notifyFirefoxExtensionRequired = function (url) { UI.notifyFirefoxExtensionRequired = function (url) {
messageHandler.openMessageDialog( messageHandler.openMessageDialog(
"dialog.extensionRequired", "dialog.extensionRequired",
@ -611,12 +711,19 @@ UI.notifyFirefoxExtensionRequired = function (url) {
); );
}; };
/**
* Notify user that he was automatically muted when joned the conference.
*/
UI.notifyInitiallyMuted = function () { UI.notifyInitiallyMuted = function () {
messageHandler.notify( messageHandler.notify(
null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000} null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000}
); );
}; };
/**
* Mark user as dominant speaker.
* @param {string} id user id
*/
UI.markDominantSpeaker = function (id) { UI.markDominantSpeaker = function (id) {
VideoLayout.onDominantSpeakerChanged(id); VideoLayout.onDominantSpeakerChanged(id);
}; };
@ -625,26 +732,53 @@ UI.handleLastNEndpoints = function (ids) {
VideoLayout.onLastNEndpointsChanged(ids, []); VideoLayout.onLastNEndpointsChanged(ids, []);
}; };
/**
* Update audio level visualization for specified user.
* @param {string} id user id
* @param {number} lvl audio level
*/
UI.setAudioLevel = function (id, lvl) { UI.setAudioLevel = function (id, lvl) {
VideoLayout.setAudioLevel(id, lvl); VideoLayout.setAudioLevel(id, lvl);
}; };
/**
* Update state of desktop sharing buttons.
* @param {boolean} isSharingScreen if user is currently sharing his screen
*/
UI.updateDesktopSharingButtons = function (isSharingScreen) { UI.updateDesktopSharingButtons = function (isSharingScreen) {
Toolbar.changeDesktopSharingButtonState(isSharingScreen); Toolbar.changeDesktopSharingButtonState(isSharingScreen);
}; };
/**
* Hide connection quality statistics from UI.
*/
UI.hideStats = function () { UI.hideStats = function () {
VideoLayout.hideStats(); VideoLayout.hideStats();
}; };
/**
* Update local connection quality statistics.
* @param {number} percent
* @param {object} stats
*/
UI.updateLocalStats = function (percent, stats) { UI.updateLocalStats = function (percent, stats) {
VideoLayout.updateLocalConnectionStats(percent, stats); VideoLayout.updateLocalConnectionStats(percent, stats);
}; };
/**
* Update connection quality statistics for remote user.
* @param {string} id user id
* @param {number} percent
* @param {object} stats
*/
UI.updateRemoteStats = function (id, percent, stats) { UI.updateRemoteStats = function (id, percent, stats) {
VideoLayout.updateConnectionStats(id, percent, stats); VideoLayout.updateConnectionStats(id, percent, stats);
}; };
/**
* Mark video as interrupted or not.
* @param {boolean} interrupted if video is interrupted
*/
UI.markVideoInterrupted = function (interrupted) { UI.markVideoInterrupted = function (interrupted) {
if (interrupted) { if (interrupted) {
VideoLayout.onVideoInterrupted(); VideoLayout.onVideoInterrupted();
@ -653,6 +787,10 @@ UI.markVideoInterrupted = function (interrupted) {
} }
}; };
/**
* Mark room as locked or not.
* @param {boolean} locked if room is locked.
*/
UI.markRoomLocked = function (locked) { UI.markRoomLocked = function (locked) {
if (locked) { if (locked) {
Toolbar.lockLockButton(); Toolbar.lockLockButton();
@ -661,6 +799,13 @@ UI.markRoomLocked = function (locked) {
} }
}; };
/**
* Add chat message.
* @param {string} from user id
* @param {string} displayName user nickname
* @param {string} message message text
* @param {number} stamp timestamp when message was created
*/
UI.addMessage = function (from, displayName, message, stamp) { UI.addMessage = function (from, displayName, message, stamp) {
Chat.updateChatConversation(from, displayName, message, stamp); Chat.updateChatConversation(from, displayName, message, stamp);
}; };
@ -672,6 +817,10 @@ UI.updateDTMFSupport = function (isDTMFSupported) {
/** /**
* Invite participants to conference. * Invite participants to conference.
* @param {string} roomUrl
* @param {string} conferenceName
* @param {string} key
* @param {string} nick
*/ */
UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) { UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
let keyText = ""; let keyText = "";
@ -710,6 +859,10 @@ UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
window.open(`mailto:?subject=${subject}&body=${body}`, '_blank'); window.open(`mailto:?subject=${subject}&body=${body}`, '_blank');
}; };
/**
* Show user feedback dialog if its required or just show "thank you" dialog.
* @returns {Promise} when dialog is closed.
*/
UI.requestFeedback = function () { UI.requestFeedback = function () {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
if (Feedback.isEnabled()) { if (Feedback.isEnabled()) {
@ -736,6 +889,10 @@ UI.requestFeedback = function () {
}); });
}; };
/**
* Request recording token from the user.
* @returns {Promise}
*/
UI.requestRecordingToken = function () { UI.requestRecordingToken = function () {
let msg = APP.translation.generateTranslationHTML("dialog.recordingToken"); let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
let token = APP.translation.translateString("dialog.token"); let token = APP.translation.translateString("dialog.token");
@ -769,6 +926,11 @@ UI.notifyTokenAuthFailed = function () {
messageHandler.showError("dialog.error", "dialog.tokenAuthFailed"); messageHandler.showError("dialog.error", "dialog.tokenAuthFailed");
}; };
/**
* Updates auth info on the UI.
* @param {boolean} isAuthEnabled if authentication is enabled
* @param {string} [login] current login
*/
UI.updateAuthInfo = function (isAuthEnabled, login) { UI.updateAuthInfo = function (isAuthEnabled, login) {
let loggedIn = !!login; let loggedIn = !!login;
@ -782,10 +944,20 @@ UI.updateAuthInfo = function (isAuthEnabled, login) {
} }
}; };
/**
* Show Prezi from the user.
* @param {string} userId user id
* @param {string} url Prezi url
* @param {number} slide slide to show
*/
UI.showPrezi = function (userId, url, slide) { UI.showPrezi = function (userId, url, slide) {
preziManager.showPrezi(userId, url, slide); preziManager.showPrezi(userId, url, slide);
}; };
/**
* Stop showing Prezi from the user.
* @param {string} userId user id
*/
UI.stopPrezi = function (userId) { UI.stopPrezi = function (userId) {
if (preziManager.isSharing(userId)) { if (preziManager.isSharing(userId)) {
preziManager.removePrezi(userId); preziManager.removePrezi(userId);
@ -798,7 +970,7 @@ UI.onStartMutedChanged = function () {
/** /**
* Returns the id of the current video shown on large. * Returns the id of the current video shown on large.
* Currently used by tests (troture). * Currently used by tests (torture).
*/ */
UI.getLargeVideoID = function () { UI.getLargeVideoID = function () {
return VideoLayout.getLargeVideoID(); return VideoLayout.getLargeVideoID();

View File

@ -10,6 +10,13 @@ const ConferenceEvents = JitsiMeetJS.events.conference;
let externalAuthWindow; let externalAuthWindow;
let authRequiredDialog; let authRequiredDialog;
/**
* Authenticate using external service or just focus
* external auth window if there is one already.
*
* @param {JitsiConference} room
* @param {string} [lockPassword] password to use if the conference is locked
*/
function doExternalAuth (room, lockPassword) { function doExternalAuth (room, lockPassword) {
if (externalAuthWindow) { if (externalAuthWindow) {
externalAuthWindow.focus(); externalAuthWindow.focus();
@ -32,6 +39,11 @@ function doExternalAuth (room, lockPassword) {
} }
} }
/**
* Authenticate on the server.
* @param {JitsiConference} room
* @param {string} [lockPassword] password to use if the conference is locked
*/
function doXmppAuth (room, lockPassword) { function doXmppAuth (room, lockPassword) {
let loginDialog = LoginDialog.showAuthDialog(function (id, password) { let loginDialog = LoginDialog.showAuthDialog(function (id, password) {
// auth "on the fly": // auth "on the fly":
@ -83,6 +95,12 @@ function doXmppAuth (room, lockPassword) {
}); });
} }
/**
* Authenticate for the conference.
* Uses external service for auth if conference supports that.
* @param {JitsiConference} room
* @param {string} [lockPassword] password to use if the conference is locked
*/
function authenticate (room, lockPassword) { function authenticate (room, lockPassword) {
if (room.isExternalAuthEnabled()) { if (room.isExternalAuthEnabled()) {
doExternalAuth(room, lockPassword); doExternalAuth(room, lockPassword);
@ -91,6 +109,9 @@ function authenticate (room, lockPassword) {
} }
} }
/**
* Notify user that authentication is required to create the conference.
*/
function requireAuth(roomName) { function requireAuth(roomName) {
if (authRequiredDialog) { if (authRequiredDialog) {
return; return;
@ -101,6 +122,9 @@ function requireAuth(roomName) {
); );
} }
/**
* Close auth-related dialogs if there are any.
*/
function closeAuth() { function closeAuth() {
if (externalAuthWindow) { if (externalAuthWindow) {
externalAuthWindow.close(); externalAuthWindow.close();

View File

@ -2,6 +2,10 @@
var messageHandler = require('../util/MessageHandler'); var messageHandler = require('../util/MessageHandler');
/**
* Build html for "password required" dialog.
* @returns {string} html string
*/
function getPasswordInputHtml() { function getPasswordInputHtml() {
let placeholder = config.hosts.authdomain let placeholder = config.hosts.authdomain
? "user identity" ? "user identity"
@ -13,11 +17,16 @@ function getPasswordInputHtml() {
<h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2> <h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
<input name="username" type="text" placeholder=${placeholder} autofocus> <input name="username" type="text" placeholder=${placeholder} autofocus>
<input name="password" type="password" <input name="password" type="password"
data-i18n="[placeholder]dialog.userPassword" data-i18n="[placeholder]dialog.userPassword"
placeholder="user password"> placeholder="user password">
`; `;
} }
/**
* Convert provided id to jid if it's not jid yet.
* @param {string} id user id or jid
* @returns {string} jid
*/
function toJid(id) { function toJid(id) {
if (id.indexOf("@") >= 0) { if (id.indexOf("@") >= 0) {
return id; return id;
@ -33,6 +42,10 @@ function toJid(id) {
return jid; return jid;
} }
/**
* Generate cancel button config for the dialog.
* @returns {Object}
*/
function cancelButton() { function cancelButton() {
return { return {
title: APP.translation.generateTranslationHTML("dialog.Cancel"), title: APP.translation.generateTranslationHTML("dialog.Cancel"),
@ -40,7 +53,18 @@ function cancelButton() {
}; };
} }
function Dialog(successCallback, cancelCallback) { /**
* Auth dialog for JitsiConnection which supports retries.
* If no cancelCallback provided then there will be
* no cancel button on the dialog.
*
* @class LoginDialog
* @constructor
*
* @param {function(jid, password)} successCallback
* @param {function} [cancelCallback] callback to invoke if user canceled.
*/
function LoginDialog(successCallback, cancelCallback) {
let loginButtons = [{ let loginButtons = [{
title: APP.translation.generateTranslationHTML("dialog.Ok"), title: APP.translation.generateTranslationHTML("dialog.Ok"),
value: true value: true
@ -118,6 +142,10 @@ function Dialog(successCallback, cancelCallback) {
connDialog.goToState('finished'); connDialog.goToState('finished');
}; };
/**
* Show message as connection status.
* @param {string} message
*/
this.displayConnectionStatus = function (message) { this.displayConnectionStatus = function (message) {
let connectingState = connDialog.getState('connecting'); let connectingState = connDialog.getState('connecting');
@ -133,12 +161,26 @@ function Dialog(successCallback, cancelCallback) {
}; };
} }
const LoginDialog = { export default {
/**
* Show new auth dialog for JitsiConnection.
*
* @param {function(jid, password)} successCallback
* @param {function} [cancelCallback] callback to invoke if user canceled.
*
* @returns {LoginDialog}
*/
showAuthDialog: function (successCallback, cancelCallback) { showAuthDialog: function (successCallback, cancelCallback) {
return new Dialog(successCallback, cancelCallback); return new LoginDialog(successCallback, cancelCallback);
}, },
/**
* Show notification that external auth is required (using provided url).
* @param {string} url URL to use for external auth.
* @param {function} callback callback to invoke when auth popup is closed.
* @returns auth dialog
*/
showExternalAuthDialog: function (url, callback) { showExternalAuthDialog: function (url, callback) {
var dialog = messageHandler.openCenteredPopup( var dialog = messageHandler.openCenteredPopup(
url, 910, 660, url, 910, 660,
@ -153,6 +195,14 @@ const LoginDialog = {
return dialog; return dialog;
}, },
/**
* Show notification that authentication is required
* to create the conference, so he should authenticate or wait for a host.
* @param {string} roomName name of the conference
* @param {function} onAuthNow callback to invoke if
* user want to authenticate.
* @returns dialog
*/
showAuthRequiredDialog: function (roomName, onAuthNow) { showAuthRequiredDialog: function (roomName, onAuthNow) {
var title = APP.translation.generateTranslationHTML( var title = APP.translation.generateTranslationHTML(
"dialog.WaitingForHost" "dialog.WaitingForHost"
@ -184,5 +234,3 @@ const LoginDialog = {
); );
} }
}; };
export default LoginDialog;

View File

@ -4,6 +4,10 @@ import UIUtil from '../util/UIUtil';
//FIXME: //FIXME:
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter'; import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
/**
* Show dialog which asks user for new password for the conference.
* @returns {Promise<string>} password or nothing if user canceled
*/
function askForNewPassword () { function askForNewPassword () {
let passMsg = APP.translation.generateTranslationHTML("dialog.passwordMsg"); let passMsg = APP.translation.generateTranslationHTML("dialog.passwordMsg");
let yourPassMsg = APP.translation.translateString("dialog.yourPassword"); let yourPassMsg = APP.translation.translateString("dialog.yourPassword");
@ -30,6 +34,10 @@ function askForNewPassword () {
}); });
} }
/**
* Show dialog which asks for required conference password.
* @returns {Promise<string>} password or nothing if user canceled
*/
function askForPassword () { function askForPassword () {
let passRequiredMsg = APP.translation.translateString( let passRequiredMsg = APP.translation.translateString(
"dialog.passwordRequired" "dialog.passwordRequired"
@ -58,6 +66,10 @@ function askForPassword () {
}); });
} }
/**
* Show dialog which asks if user want remove password from the conference.
* @returns {Promise}
*/
function askToUnlock () { function askToUnlock () {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
messageHandler.openTwoButtonDialog( messageHandler.openTwoButtonDialog(
@ -74,18 +86,32 @@ function askToUnlock () {
}); });
} }
function notifyPasswordNotSupported (err) { /**
console.warn('setting password failed', err); * Show notification that user cannot set password for the conference
* because server doesn't support that.
*/
function notifyPasswordNotSupported () {
console.warn('room passwords not supported');
messageHandler.showError("dialog.warning", "dialog.passwordNotSupported"); messageHandler.showError("dialog.warning", "dialog.passwordNotSupported");
} }
function notifyPasswordFailed() { /**
console.warn('room passwords not supported'); * Show notification that setting password for the conference failed.
* @param {Error} err error
*/
function notifyPasswordFailed(err) {
console.warn('setting password failed', err);
messageHandler.showError("dialog.lockTitle", "dialog.lockMessage"); messageHandler.showError("dialog.lockTitle", "dialog.lockMessage");
} }
const ConferenceErrors = JitsiMeetJS.errors.conference; const ConferenceErrors = JitsiMeetJS.errors.conference;
/**
* Create new RoomLocker for the conference.
* It allows to set or remove password for the conference,
* or ask for required password.
* @returns {RoomLocker}
*/
export default function createRoomLocker (room) { export default function createRoomLocker (room) {
let password; let password;
@ -103,6 +129,9 @@ export default function createRoomLocker (room) {
}); });
} }
/**
* @class RoomLocker
*/
return { return {
get isLocked () { get isLocked () {
return !!password; return !!password;
@ -112,6 +141,10 @@ export default function createRoomLocker (room) {
return password; return password;
}, },
/**
* Allows to remove password from the conference (asks user first).
* @returns {Promise}
*/
askToUnlock () { askToUnlock () {
return askToUnlock().then(function () { return askToUnlock().then(function () {
return lock(); return lock();
@ -120,6 +153,11 @@ export default function createRoomLocker (room) {
}); });
}, },
/**
* Allows to set password for the conference.
* It asks user for new password and locks the room.
* @returns {Promise}
*/
askToLock () { askToLock () {
return askForNewPassword().then(function (newPass) { return askForNewPassword().then(function (newPass) {
return lock(newPass); return lock(newPass);
@ -128,12 +166,18 @@ export default function createRoomLocker (room) {
}); });
}, },
/**
* Asks user for required conference password.
*/
requirePassword () { requirePassword () {
return askForPassword().then(function (newPass) { return askForPassword().then(function (newPass) {
password = newPass; password = newPass;
}); });
}, },
/**
* Show notification that to set/remove password user must be moderator.
*/
notifyModeratorRequired () { notifyModeratorRequired () {
if (password) { if (password) {
messageHandler.openMessageDialog(null, "dialog.passwordError"); messageHandler.openMessageDialog(null, "dialog.passwordError");

View File

@ -6,6 +6,9 @@ import UIUtil from "../util/UIUtil";
import SidePanelToggler from "../side_pannels/SidePanelToggler"; import SidePanelToggler from "../side_pannels/SidePanelToggler";
import BottomToolbar from '../toolbars/BottomToolbar'; import BottomToolbar from '../toolbars/BottomToolbar';
/**
* Etherpad options.
*/
const options = $.param({ const options = $.param({
showControns: true, showControns: true,
showChat: false, showChat: false,
@ -40,11 +43,20 @@ function bubbleIframeMouseMove(iframe){
}; };
} }
/**
* Default Etherpad frame width.
*/
const DEFAULT_WIDTH = 640; const DEFAULT_WIDTH = 640;
/**
* Default Etherpad frame height.
*/
const DEFAULT_HEIGHT = 480; const DEFAULT_HEIGHT = 480;
const EtherpadContainerType = "etherpad"; const EtherpadContainerType = "etherpad";
/**
* Container for Etherpad iframe.
*/
class Etherpad extends LargeContainer { class Etherpad extends LargeContainer {
constructor (domain, name) { constructor (domain, name) {
super(); super();
@ -123,6 +135,9 @@ class Etherpad extends LargeContainer {
} }
} }
/**
* Manager of the Etherpad frame.
*/
export default class EtherpadManager { export default class EtherpadManager {
constructor (domain, name) { constructor (domain, name) {
if (!domain || !name) { if (!domain || !name) {
@ -138,6 +153,9 @@ export default class EtherpadManager {
return !!this.etherpad; return !!this.etherpad;
} }
/**
* Create new Etherpad frame.
*/
openEtherpad () { openEtherpad () {
this.etherpad = new Etherpad(this.domain, this.name); this.etherpad = new Etherpad(this.domain, this.name);
VideoLayout.addLargeVideoContainer( VideoLayout.addLargeVideoContainer(
@ -146,6 +164,10 @@ export default class EtherpadManager {
); );
} }
/**
* Toggle Etherpad frame visibility.
* Open new Etherpad frame if there is no Etherpad frame yet.
*/
toggleEtherpad () { toggleEtherpad () {
if (!this.isOpen) { if (!this.isOpen) {
this.openEtherpad(); this.openEtherpad();

View File

@ -11,17 +11,31 @@ import ToolbarToggler from "../toolbars/ToolbarToggler";
import SidePanelToggler from "../side_pannels/SidePanelToggler"; import SidePanelToggler from "../side_pannels/SidePanelToggler";
import BottomToolbar from '../toolbars/BottomToolbar'; import BottomToolbar from '../toolbars/BottomToolbar';
/**
* Example of Prezi link.
*/
const defaultPreziLink = "http://prezi.com/wz7vhjycl7e6/my-prezi"; const defaultPreziLink = "http://prezi.com/wz7vhjycl7e6/my-prezi";
const alphanumRegex = /^[a-z0-9-_\/&\?=;]+$/i; const alphanumRegex = /^[a-z0-9-_\/&\?=;]+$/i;
/**
* Default aspect ratio for Prezi frame.
*/
const aspectRatio = 16.0 / 9.0; const aspectRatio = 16.0 / 9.0;
/**
* Default Prezi frame width.
*/
const DEFAULT_WIDTH = 640; const DEFAULT_WIDTH = 640;
/**
* Default Prezi frame height.
*/
const DEFAULT_HEIGHT = 480; const DEFAULT_HEIGHT = 480;
/** /**
* Indicates if the given string is an alphanumeric string. * Indicates if the given string is an alphanumeric string.
* Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the * Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
* purpose of checking URIs. * purpose of checking URIs.
* @param {string} unsafeText string to check
* @returns {boolean}
*/ */
function isAlphanumeric(unsafeText) { function isAlphanumeric(unsafeText) {
return alphanumRegex.test(unsafeText); return alphanumRegex.test(unsafeText);
@ -29,12 +43,19 @@ function isAlphanumeric(unsafeText) {
/** /**
* Returns the presentation id from the given url. * Returns the presentation id from the given url.
* @param {string} url Prezi link
* @returns {string} presentation id
*/ */
function getPresentationId (url) { function getPresentationId (url) {
let presId = url.substring(url.indexOf("prezi.com/") + 10); let presId = url.substring(url.indexOf("prezi.com/") + 10);
return presId.substring(0, presId.indexOf('/')); return presId.substring(0, presId.indexOf('/'));
} }
/**
* Checks if given string is Prezi url.
* @param {string} url string to check.
* @returns {boolean}
*/
function isPreziLink(url) { function isPreziLink(url) {
if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) { if (url.indexOf('http://prezi.com/') !== 0 && url.indexOf('https://prezi.com/') !== 0) {
return false; return false;
@ -48,6 +69,9 @@ function isPreziLink(url) {
return true; return true;
} }
/**
* Notify user that other user if already sharing Prezi.
*/
function notifyOtherIsSharingPrezi() { function notifyOtherIsSharingPrezi() {
messageHandler.openMessageDialog( messageHandler.openMessageDialog(
"dialog.sharePreziTitle", "dialog.sharePreziTitle",
@ -55,6 +79,9 @@ function notifyOtherIsSharingPrezi() {
); );
} }
/**
* Ask user if he want to close Prezi he's sharing.
*/
function proposeToClosePrezi() { function proposeToClosePrezi() {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
messageHandler.openTwoButtonDialog( messageHandler.openTwoButtonDialog(
@ -76,6 +103,10 @@ function proposeToClosePrezi() {
}); });
} }
/**
* Ask user for Prezi url to share with others.
* Dialog validates client input to allow only Prezi urls.
*/
function requestPreziLink() { function requestPreziLink() {
const title = APP.translation.generateTranslationHTML("dialog.sharePreziTitle"); const title = APP.translation.generateTranslationHTML("dialog.sharePreziTitle");
const cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel"); const cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
@ -154,6 +185,9 @@ function requestPreziLink() {
export const PreziContainerType = "prezi"; export const PreziContainerType = "prezi";
/**
* Container for Prezi iframe.
*/
class PreziContainer extends LargeContainer { class PreziContainer extends LargeContainer {
constructor ({preziId, isMy, slide, onSlideChanged}) { constructor ({preziId, isMy, slide, onSlideChanged}) {
@ -187,6 +221,10 @@ class PreziContainer extends LargeContainer {
}); });
} }
/**
* Change Prezi slide.
* @param {number} slide slide to show
*/
goToSlide (slide) { goToSlide (slide) {
if (this.preziPlayer.getCurrentStep() === slide) { if (this.preziPlayer.getCurrentStep() === slide) {
return; return;
@ -204,6 +242,10 @@ class PreziContainer extends LargeContainer {
} }
} }
/**
* Show or hide "reload presentation" button.
* @param {boolean} show
*/
showReloadBtn (show) { showReloadBtn (show) {
this.reloadBtn.css('display', show ? 'inline-block' : 'none'); this.reloadBtn.css('display', show ? 'inline-block' : 'none');
} }
@ -256,6 +298,9 @@ class PreziContainer extends LargeContainer {
this.$iframe.width(width).height(height); this.$iframe.width(width).height(height);
} }
/**
* Close Prezi frame.
*/
close () { close () {
this.showReloadBtn(false); this.showReloadBtn(false);
this.preziPlayer.destroy(); this.preziPlayer.destroy();
@ -263,6 +308,9 @@ class PreziContainer extends LargeContainer {
} }
} }
/**
* Manager of Prezi frames.
*/
export default class PreziManager { export default class PreziManager {
constructor (emitter) { constructor (emitter) {
this.emitter = emitter; this.emitter = emitter;
@ -282,6 +330,10 @@ export default class PreziManager {
return this.userId === APP.conference.localId; return this.userId === APP.conference.localId;
} }
/**
* Check if user is currently sharing.
* @param {string} id user id to check for
*/
isSharing (id) { isSharing (id) {
return this.userId === id; return this.userId === id;
} }
@ -302,6 +354,9 @@ export default class PreziManager {
} }
} }
/**
* Reload current Prezi frame.
*/
reloadPresentation () { reloadPresentation () {
if (!this.prezi) { if (!this.prezi) {
return; return;
@ -310,6 +365,12 @@ export default class PreziManager {
iframe.src = iframe.src; iframe.src = iframe.src;
} }
/**
* Show Prezi. Create new Prezi if there is no Prezi yet.
* @param {string} id owner id
* @param {string} url Prezi url
* @param {number} slide slide to show
*/
showPrezi (id, url, slide) { showPrezi (id, url, slide) {
if (!this.isPresenting) { if (!this.isPresenting) {
this.createPrezi(id, url, slide); this.createPrezi(id, url, slide);
@ -324,6 +385,12 @@ export default class PreziManager {
} }
} }
/**
* Create new Prezi frame..
* @param {string} id owner id
* @param {string} url Prezi url
* @param {number} slide slide to show
*/
createPrezi (id, url, slide) { createPrezi (id, url, slide) {
console.log("presentation added", url); console.log("presentation added", url);
@ -354,6 +421,10 @@ export default class PreziManager {
VideoLayout.showLargeVideoContainer(PreziContainerType, true); VideoLayout.showLargeVideoContainer(PreziContainerType, true);
} }
/**
* Close Prezi.
* @param {string} id owner id
*/
removePrezi (id) { removePrezi (id) {
if (this.userId !== id) { if (this.userId !== id) {
throw new Error(`cannot close presentation from ${this.userId} instead of ${id}`); throw new Error(`cannot close presentation from ${this.userId} instead of ${id}`);

View File

@ -1,24 +1,41 @@
/**
* Base class for all Large containers which we can show.
*/
export default class LargeContainer { export default class LargeContainer {
/** /**
* Show this container.
* @returns Promise * @returns Promise
*/ */
show () { show () {
} }
/** /**
* Hide this container.
* @returns Promise * @returns Promise
*/ */
hide () { hide () {
} }
/**
* Resize this container.
* @param {number} containerWidth available width
* @param {number} containerHeight available height
* @param {boolean} animate if container should animate it's resize process
*/
resize (containerWidth, containerHeight, animate) { resize (containerWidth, containerHeight, animate) {
} }
/**
* Handler for "hover in" events.
*/
onHoverIn (e) { onHoverIn (e) {
} }
/**
* Handler for "hover out" events.
*/
onHoverOut (e) { onHoverOut (e) {
} }
} }

View File

@ -11,6 +11,10 @@ const RTCBrowserType = require("../../RTC/RTCBrowserType");
const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE; const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
/**
* Get stream id.
* @param {JitsiTrack?} stream
*/
function getStreamId(stream) { function getStreamId(stream) {
if(!stream) if(!stream)
return; return;
@ -147,6 +151,9 @@ function getDesktopVideoPosition(videoWidth,
export const VideoContainerType = "video"; export const VideoContainerType = "video";
/**
* Container for user video.
*/
class VideoContainer extends LargeContainer { class VideoContainer extends LargeContainer {
// FIXME: With Temasys we have to re-select everytime // FIXME: With Temasys we have to re-select everytime
get $video () { get $video () {
@ -174,6 +181,10 @@ class VideoContainer extends LargeContainer {
this.$video.on('play', onPlay); this.$video.on('play', onPlay);
} }
/**
* Get size of video element.
* @returns {{width, height}}
*/
getStreamSize () { getStreamSize () {
let video = this.$video[0]; let video = this.$video[0];
return { return {
@ -182,6 +193,12 @@ class VideoContainer extends LargeContainer {
}; };
} }
/**
* Calculate optimal video size for specified container size.
* @param {number} containerWidth container width
* @param {number} containerHeight container height
* @returns {{availableWidth, availableHeight}}
*/
getVideoSize (containerWidth, containerHeight) { getVideoSize (containerWidth, containerHeight) {
let { width, height } = this.getStreamSize(); let { width, height } = this.getStreamSize();
if (this.stream && this.isScreenSharing()) { if (this.stream && this.isScreenSharing()) {
@ -197,6 +214,15 @@ class VideoContainer extends LargeContainer {
} }
} }
/**
* Calculate optimal video position (offset for top left corner)
* for specified video size and container size.
* @param {number} width video width
* @param {number} height video height
* @param {number} containerWidth container width
* @param {number} containerHeight container height
* @returns {{horizontalIndent, verticalIndent}}
*/
getVideoPosition (width, height, containerWidth, containerHeight) { getVideoPosition (width, height, containerWidth, containerHeight) {
if (this.stream && this.isScreenSharing()) { if (this.stream && this.isScreenSharing()) {
return getDesktopVideoPosition( width, return getDesktopVideoPosition( width,
@ -238,6 +264,11 @@ class VideoContainer extends LargeContainer {
}); });
} }
/**
* Update video stream.
* @param {JitsiTrack?} stream new stream
* @param {string} videoType video type
*/
setStream (stream, videoType) { setStream (stream, videoType) {
this.stream = stream; this.stream = stream;
this.videoType = videoType; this.videoType = videoType;
@ -250,10 +281,18 @@ class VideoContainer extends LargeContainer {
}); });
} }
/**
* Check if current video stream is screen sharing.
* @returns {boolean}
*/
isScreenSharing () { isScreenSharing () {
return this.videoType === 'desktop'; return this.videoType === 'desktop';
} }
/**
* Show or hide user avatar.
* @param {boolean} show
*/
showAvatar (show) { showAvatar (show) {
this.$avatar.css("visibility", show ? "visible" : "hidden"); this.$avatar.css("visibility", show ? "visible" : "hidden");
} }
@ -289,7 +328,9 @@ class VideoContainer extends LargeContainer {
} }
} }
/**
* Manager for all Large containers.
*/
export default class LargeVideoManager { export default class LargeVideoManager {
constructor () { constructor () {
this.containers = {}; this.containers = {};
@ -356,6 +397,13 @@ export default class LargeVideoManager {
return this.videoContainer.id; return this.videoContainer.id;
} }
/**
* Update large video.
* Switches to large video even if previously other container was visible.
* @param {JitsiTrack?} stream new stream
* @param {string?} videoType new video type
* @returns {Promise}
*/
updateLargeVideo (smallVideo, videoType, largeVideoUpdatedCallBack) { updateLargeVideo (smallVideo, videoType, largeVideoUpdatedCallBack) {
let id = getStreamId(smallVideo.stream); let id = getStreamId(smallVideo.stream);
@ -380,16 +428,29 @@ export default class LargeVideoManager {
}); });
} }
/**
* Update container size optionally taking side bar size into account.
* @param {boolean} isSideBarVisible if side bar is visible.
*/
updateContainerSize (isSideBarVisible) { updateContainerSize (isSideBarVisible) {
this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible); this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
this.height = window.innerHeight; this.height = window.innerHeight;
} }
/**
* Resize Large container of specified type.
* @param {string} type type of container which should be resized.
* @param {boolean} [animate=false] if resize process should be animated.
*/
resizeContainer (type, animate = false) { resizeContainer (type, animate = false) {
let container = this.getContainer(type); let container = this.getContainer(type);
container.resize(this.width, this.height, animate); container.resize(this.width, this.height, animate);
} }
/**
* Resize all Large containers.
* @param {boolean} animate if resize process should be animated.
*/
resize (animate) { resize (animate) {
// resize all containers // resize all containers
Object.keys(this.containers) Object.keys(this.containers)
@ -420,11 +481,20 @@ export default class LargeVideoManager {
$("#dominantSpeakerAvatar").attr('src', avatarUrl); $("#dominantSpeakerAvatar").attr('src', avatarUrl);
} }
/**
* Show avatar on Large video container or not.
* @param {boolean} show
*/
showAvatar (show) { showAvatar (show) {
show ? this.videoContainer.hide() : this.videoContainer.show(); show ? this.videoContainer.hide() : this.videoContainer.show();
this.videoContainer.showAvatar(show); this.videoContainer.showAvatar(show);
} }
/**
* Add container of specified type.
* @param {string} type container type
* @param {LargeContainer} container container to add.
*/
addContainer (type, container) { addContainer (type, container) {
if (this.containers[type]) { if (this.containers[type]) {
throw new Error(`container of type ${type} already exist`); throw new Error(`container of type ${type} already exist`);
@ -434,6 +504,11 @@ export default class LargeVideoManager {
this.resizeContainer(type); this.resizeContainer(type);
} }
/**
* Get Large container of specified type.
* @param {string} type container type.
* @returns {LargeContainer}
*/
getContainer (type) { getContainer (type) {
let container = this.containers[type]; let container = this.containers[type];
@ -444,6 +519,10 @@ export default class LargeVideoManager {
return container; return container;
} }
/**
* Remove Large container of specified type.
* @param {string} type container type.
*/
removeContainer (type) { removeContainer (type) {
if (!this.containers[type]) { if (!this.containers[type]) {
throw new Error(`container of type ${type} doesn't exist`); throw new Error(`container of type ${type} doesn't exist`);
@ -452,6 +531,12 @@ export default class LargeVideoManager {
delete this.containers[type]; delete this.containers[type];
} }
/**
* Show Large container of specified type.
* Does nothing if such container is already visible.
* @param {string} type container type.
* @returns {Promise}
*/
showContainer (type) { showContainer (type) {
if (this.state === type) { if (this.state === type) {
return Promise.resolve(); return Promise.resolve();

View File

@ -997,7 +997,7 @@ var VideoLayout = {
// update current small video and the old one // update current small video and the old one
smallVideo.updateView(); smallVideo.updateView();
oldSmallVideo && oldSmallVideo.updateView(); oldSmallVideo && oldSmallVideo.updateView();
}); });
} else if (currentId) { } else if (currentId) {
let currentSmallVideo = this.getSmallVideo(currentId); let currentSmallVideo = this.getSmallVideo(currentId);