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;
/**
* Known custom conference commands.
*/
const Commands = {
CONNECTION_QUALITY: "stats",
EMAIL: "email",
@ -26,6 +29,10 @@ const Commands = {
STOP_PREZI: "stop-prezi"
};
/**
* Open Connection. When authentication failed it shows auth dialog.
* @returns Promise<JitsiConnection>
*/
function connect() {
return openConnection({retry: true}).catch(function (err) {
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);
if (track.isAudioTrack()) {
return;
}
@ -50,25 +63,35 @@ const addTrack = (track) => {
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, {
value: email,
attributes: {
id: room.myUserId()
}
});
};
}
const unload = () => {
/**
* Leave the conference and close connection.
*/
function unload () {
room.leave();
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)) {
return APP.settings.getDisplayName();
}
@ -77,7 +100,7 @@ const getDisplayName = (id) => {
if (participant && participant.getDisplayName()) {
return participant.getDisplayName();
}
};
}
class ConferenceConnector {
constructor(resolve, reject) {
@ -151,6 +174,12 @@ export default {
isModerator: false,
audioMuted: 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) {
this.roomName = options.roomName;
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) {
return JitsiMeetJS.createLocalTracks({
// copy array to avoid mutations inside library
@ -184,6 +219,11 @@ export default {
return Promise.reject(err);
});
},
/**
* Check if id is id of the local user.
* @param {string} id id to check
* @returns {boolean}
*/
isLocalId (id) {
return this.localId === id;
},
@ -217,12 +257,24 @@ export default {
toggleVideoMuted () {
this.muteVideo(!this.videoMuted);
},
/**
* Retrieve list of conference participants (without local user).
* @returns {JitsiParticipant[]}
*/
listMembers () {
return room.getParticipants();
},
/**
* Retrieve list of ids of conference participants (without local user).
* @returns {string[]}
*/
listMembersIds () {
return room.getParticipants().map(p => p.getId());
},
/**
* Check if SIP is supported.
* @returns {boolean}
*/
sipGatewayEnabled () {
return room.isSIPCallingSupported();
},
@ -279,7 +331,7 @@ export default {
/**
* Will check for number of remote particiapnts that have at least one
* remote track.
* @return boolean whether we have enough participants with remote streams
* @return {boolean} whether we have enough participants with remote streams
*/
checkEnoughParticipants (number) {
var participants = this._room.getParticipants();
@ -336,6 +388,9 @@ export default {
}
return options;
},
/**
* Setup interaction between conference and UI.
*/
_setupListeners () {
// add local streams when joined to the conference
room.on(ConferenceEvents.CONFERENCE_JOINED, () => {

View File

@ -5,6 +5,13 @@ import LoginDialog from './modules/UI/authentication/LoginDialog';
const ConnectionEvents = JitsiMeetJS.events.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) {
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() {
return new Promise(function (resolve, reject) {
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}) {
return connect(id, password).catch(function (err) {
if (!retry) {

View File

@ -31,6 +31,9 @@ UI.eventEmitter = eventEmitter;
let preziManager;
let etherpadManager;
/**
* Prompt user for nickname.
*/
function promptDisplayName() {
let nickRequiredMsg = APP.translation.translateString("dialog.displayNameRequired");
let defaultNickMsg = APP.translation.translateString(
@ -76,6 +79,9 @@ function promptDisplayName() {
);
}
/**
* Initialize chat.
*/
function setupChat() {
Chat.init(eventEmitter);
$("#toggle_smileys").click(function() {
@ -83,6 +89,9 @@ function setupChat() {
});
}
/**
* Initialize toolbars.
*/
function setupToolbars() {
Toolbar.init(eventEmitter);
Toolbar.setupButtonsFromConfig();
@ -113,6 +122,9 @@ function toggleFullScreen () {
}
}
/**
* Notify user that server has shut down.
*/
UI.notifyGracefulShudown = function () {
messageHandler.openMessageDialog(
'dialog.serviceUnavailable',
@ -120,6 +132,9 @@ UI.notifyGracefulShudown = function () {
);
};
/**
* Notify user that reservation error happened.
*/
UI.notifyReservationError = function (code, msg) {
var title = APP.translation.generateTranslationHTML(
"dialog.reservationError");
@ -135,14 +150,25 @@ UI.notifyReservationError = function (code, msg) {
);
};
/**
* Notify user that he has been kicked from the server.
*/
UI.notifyKicked = function () {
messageHandler.openMessageDialog("dialog.sessTerminated", "dialog.kickMessage");
};
/**
* Notify user that Jitsi Videobridge is not accessible.
*/
UI.notifyBridgeDown = function () {
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) {
ContactList.onDisplayNameChange(id, displayName);
SettingsMenu.onDisplayNameChange(id, displayName);
@ -153,6 +179,9 @@ UI.changeDisplayName = function (id, displayName) {
}
};
/**
* Intitialize conference UI.
*/
UI.initConference = function () {
var id = APP.conference.localId;
Toolbar.updateRoomUrl(window.location.href);
@ -186,6 +215,9 @@ UI.mucJoined = function () {
VideoLayout.mucJoined();
};
/**
* Setup some UI event listeners.
*/
function registerListeners() {
UI.addListener(UIEvents.EMAIL_CHANGED, function (email) {
UI.setUserAvatar(APP.conference.localId, email);
@ -214,6 +246,9 @@ function registerListeners() {
UI.addListener(UIEvents.TOGGLE_FILM_STRIP, UI.toggleFilmStrip);
}
/**
* Setup some DOM event listeners.
*/
function bindEvents() {
function onResize() {
PanelToggler.resizeChat();
@ -341,6 +376,10 @@ UI.start = function () {
};
/**
* Show local stream on UI.
* @param {JitsiTrack} track stream to show
*/
UI.addLocalStream = function (track) {
switch (track.getType()) {
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) {
return Chat.chatAddError(errorMessage, originalText);
}
/**
* Update chat subject.
* @param {string} subject new chat subject
*/
UI.setSubject = function (subject) {
Chat.setSubject(subject);
};
/**
* Setup and show Etherpad.
* @param {string} name etherpad id
*/
UI.initEtherpad = function (name) {
if (etherpadManager || !config.etherpad_base || !name) {
return;
@ -377,6 +428,11 @@ UI.initEtherpad = function (name) {
Toolbar.showEtherpadButton();
};
/**
* Show user on UI.
* @param {string} id user id
* @param {string} displayName user nickname
*/
UI.addUser = function (id, displayName) {
ContactList.addContact(id);
@ -395,6 +451,11 @@ UI.addUser = function (id, displayName) {
VideoLayout.addParticipantContainer(id);
};
/**
* Remove user from UI.
* @param {string} id user id
* @param {string} displayName user nickname
*/
UI.removeUser = function (id, displayName) {
ContactList.removeContact(id);
@ -415,10 +476,19 @@ UI.removeUser = function (id, displayName) {
// 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) => {
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) {
VideoLayout.showModeratorIndicator();
@ -462,22 +532,38 @@ UI.updateUserRole = function (user) {
};
/**
* Toggles smileys in the chat.
*/
UI.toggleSmileys = function () {
Chat.toggleSmileys();
};
/**
* Get current settings.
* @returns {object} settings
*/
UI.getSettings = function () {
return Settings.getSettings();
};
/**
* Toggles film strip.
*/
UI.toggleFilmStrip = function () {
BottomToolbar.toggleFilmStrip();
};
/**
* Toggles chat panel.
*/
UI.toggleChat = function () {
PanelToggler.toggleChat();
};
/**
* Toggles contact list panel.
*/
UI.toggleContactList = function () {
PanelToggler.toggleContactList();
};
@ -499,6 +585,7 @@ UI.connectionIndicatorShowMore = function(jid) {
return VideoLayout.showMore(jid);
};
// FIXME check if someone user this
UI.showLoginPopup = function(callback) {
console.log('password is required');
var message = '<h2 data-i18n="dialog.passwordRequired">';
@ -569,6 +656,11 @@ UI.dockToolbar = function (isDock) {
ToolbarToggler.dockToolbar(isDock);
};
/**
* Update user avatar.
* @param {string} id user id
* @param {stirng} email user email
*/
UI.setUserAvatar = function (id, email) {
// update avatar
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) {
var title = APP.translation.generateTranslationHTML(
"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) {
messageHandler.openMessageDialog(
"dialog.extensionRequired",
@ -611,12 +711,19 @@ UI.notifyFirefoxExtensionRequired = function (url) {
);
};
/**
* Notify user that he was automatically muted when joned the conference.
*/
UI.notifyInitiallyMuted = function () {
messageHandler.notify(
null, "notify.mutedTitle", "connected", "notify.muted", null, {timeOut: 120000}
);
};
/**
* Mark user as dominant speaker.
* @param {string} id user id
*/
UI.markDominantSpeaker = function (id) {
VideoLayout.onDominantSpeakerChanged(id);
};
@ -625,26 +732,53 @@ UI.handleLastNEndpoints = function (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) {
VideoLayout.setAudioLevel(id, lvl);
};
/**
* Update state of desktop sharing buttons.
* @param {boolean} isSharingScreen if user is currently sharing his screen
*/
UI.updateDesktopSharingButtons = function (isSharingScreen) {
Toolbar.changeDesktopSharingButtonState(isSharingScreen);
};
/**
* Hide connection quality statistics from UI.
*/
UI.hideStats = function () {
VideoLayout.hideStats();
};
/**
* Update local connection quality statistics.
* @param {number} percent
* @param {object} stats
*/
UI.updateLocalStats = function (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) {
VideoLayout.updateConnectionStats(id, percent, stats);
};
/**
* Mark video as interrupted or not.
* @param {boolean} interrupted if video is interrupted
*/
UI.markVideoInterrupted = function (interrupted) {
if (interrupted) {
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) {
if (locked) {
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) {
Chat.updateChatConversation(from, displayName, message, stamp);
};
@ -672,6 +817,10 @@ UI.updateDTMFSupport = function (isDTMFSupported) {
/**
* Invite participants to conference.
* @param {string} roomUrl
* @param {string} conferenceName
* @param {string} key
* @param {string} nick
*/
UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
let keyText = "";
@ -710,6 +859,10 @@ UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
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 () {
return new Promise(function (resolve, reject) {
if (Feedback.isEnabled()) {
@ -736,6 +889,10 @@ UI.requestFeedback = function () {
});
};
/**
* Request recording token from the user.
* @returns {Promise}
*/
UI.requestRecordingToken = function () {
let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
let token = APP.translation.translateString("dialog.token");
@ -769,6 +926,11 @@ UI.notifyTokenAuthFailed = function () {
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) {
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) {
preziManager.showPrezi(userId, url, slide);
};
/**
* Stop showing Prezi from the user.
* @param {string} userId user id
*/
UI.stopPrezi = function (userId) {
if (preziManager.isSharing(userId)) {
preziManager.removePrezi(userId);
@ -798,7 +970,7 @@ UI.onStartMutedChanged = function () {
/**
* Returns the id of the current video shown on large.
* Currently used by tests (troture).
* Currently used by tests (torture).
*/
UI.getLargeVideoID = function () {
return VideoLayout.getLargeVideoID();

View File

@ -10,6 +10,13 @@ const ConferenceEvents = JitsiMeetJS.events.conference;
let externalAuthWindow;
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) {
if (externalAuthWindow) {
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) {
let loginDialog = LoginDialog.showAuthDialog(function (id, password) {
// 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) {
if (room.isExternalAuthEnabled()) {
doExternalAuth(room, lockPassword);
@ -91,6 +109,9 @@ function authenticate (room, lockPassword) {
}
}
/**
* Notify user that authentication is required to create the conference.
*/
function requireAuth(roomName) {
if (authRequiredDialog) {
return;
@ -101,6 +122,9 @@ function requireAuth(roomName) {
);
}
/**
* Close auth-related dialogs if there are any.
*/
function closeAuth() {
if (externalAuthWindow) {
externalAuthWindow.close();

View File

@ -2,6 +2,10 @@
var messageHandler = require('../util/MessageHandler');
/**
* Build html for "password required" dialog.
* @returns {string} html string
*/
function getPasswordInputHtml() {
let placeholder = config.hosts.authdomain
? "user identity"
@ -13,11 +17,16 @@ function getPasswordInputHtml() {
<h2 data-i18n="dialog.passwordRequired">${passRequiredMsg}</h2>
<input name="username" type="text" placeholder=${placeholder} autofocus>
<input name="password" type="password"
data-i18n="[placeholder]dialog.userPassword"
placeholder="user password">
data-i18n="[placeholder]dialog.userPassword"
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) {
if (id.indexOf("@") >= 0) {
return id;
@ -33,6 +42,10 @@ function toJid(id) {
return jid;
}
/**
* Generate cancel button config for the dialog.
* @returns {Object}
*/
function cancelButton() {
return {
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 = [{
title: APP.translation.generateTranslationHTML("dialog.Ok"),
value: true
@ -118,6 +142,10 @@ function Dialog(successCallback, cancelCallback) {
connDialog.goToState('finished');
};
/**
* Show message as connection status.
* @param {string} message
*/
this.displayConnectionStatus = function (message) {
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) {
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) {
var dialog = messageHandler.openCenteredPopup(
url, 910, 660,
@ -153,6 +195,14 @@ const LoginDialog = {
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) {
var title = APP.translation.generateTranslationHTML(
"dialog.WaitingForHost"
@ -184,5 +234,3 @@ const LoginDialog = {
);
}
};
export default LoginDialog;

View File

@ -4,6 +4,10 @@ import UIUtil from '../util/UIUtil';
//FIXME:
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 () {
let passMsg = APP.translation.generateTranslationHTML("dialog.passwordMsg");
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 () {
let passRequiredMsg = APP.translation.translateString(
"dialog.passwordRequired"
@ -58,6 +66,10 @@ function askForPassword () {
});
}
/**
* Show dialog which asks if user want remove password from the conference.
* @returns {Promise}
*/
function askToUnlock () {
return new Promise(function (resolve, reject) {
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");
}
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");
}
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) {
let password;
@ -103,6 +129,9 @@ export default function createRoomLocker (room) {
});
}
/**
* @class RoomLocker
*/
return {
get isLocked () {
return !!password;
@ -112,6 +141,10 @@ export default function createRoomLocker (room) {
return password;
},
/**
* Allows to remove password from the conference (asks user first).
* @returns {Promise}
*/
askToUnlock () {
return askToUnlock().then(function () {
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 () {
return askForNewPassword().then(function (newPass) {
return lock(newPass);
@ -128,12 +166,18 @@ export default function createRoomLocker (room) {
});
},
/**
* Asks user for required conference password.
*/
requirePassword () {
return askForPassword().then(function (newPass) {
password = newPass;
});
},
/**
* Show notification that to set/remove password user must be moderator.
*/
notifyModeratorRequired () {
if (password) {
messageHandler.openMessageDialog(null, "dialog.passwordError");

View File

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

View File

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

View File

@ -11,6 +11,10 @@ const RTCBrowserType = require("../../RTC/RTCBrowserType");
const avatarSize = interfaceConfig.DOMINANT_SPEAKER_AVATAR_SIZE;
/**
* Get stream id.
* @param {JitsiTrack?} stream
*/
function getStreamId(stream) {
if(!stream)
return;
@ -147,6 +151,9 @@ function getDesktopVideoPosition(videoWidth,
export const VideoContainerType = "video";
/**
* Container for user video.
*/
class VideoContainer extends LargeContainer {
// FIXME: With Temasys we have to re-select everytime
get $video () {
@ -174,6 +181,10 @@ class VideoContainer extends LargeContainer {
this.$video.on('play', onPlay);
}
/**
* Get size of video element.
* @returns {{width, height}}
*/
getStreamSize () {
let video = this.$video[0];
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) {
let { width, height } = this.getStreamSize();
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) {
if (this.stream && this.isScreenSharing()) {
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) {
this.stream = stream;
this.videoType = videoType;
@ -250,10 +281,18 @@ class VideoContainer extends LargeContainer {
});
}
/**
* Check if current video stream is screen sharing.
* @returns {boolean}
*/
isScreenSharing () {
return this.videoType === 'desktop';
}
/**
* Show or hide user avatar.
* @param {boolean} show
*/
showAvatar (show) {
this.$avatar.css("visibility", show ? "visible" : "hidden");
}
@ -289,7 +328,9 @@ class VideoContainer extends LargeContainer {
}
}
/**
* Manager for all Large containers.
*/
export default class LargeVideoManager {
constructor () {
this.containers = {};
@ -356,6 +397,13 @@ export default class LargeVideoManager {
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) {
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) {
this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
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) {
let container = this.getContainer(type);
container.resize(this.width, this.height, animate);
}
/**
* Resize all Large containers.
* @param {boolean} animate if resize process should be animated.
*/
resize (animate) {
// resize all containers
Object.keys(this.containers)
@ -420,11 +481,20 @@ export default class LargeVideoManager {
$("#dominantSpeakerAvatar").attr('src', avatarUrl);
}
/**
* Show avatar on Large video container or not.
* @param {boolean} show
*/
showAvatar (show) {
show ? this.videoContainer.hide() : this.videoContainer.show();
this.videoContainer.showAvatar(show);
}
/**
* Add container of specified type.
* @param {string} type container type
* @param {LargeContainer} container container to add.
*/
addContainer (type, container) {
if (this.containers[type]) {
throw new Error(`container of type ${type} already exist`);
@ -434,6 +504,11 @@ export default class LargeVideoManager {
this.resizeContainer(type);
}
/**
* Get Large container of specified type.
* @param {string} type container type.
* @returns {LargeContainer}
*/
getContainer (type) {
let container = this.containers[type];
@ -444,6 +519,10 @@ export default class LargeVideoManager {
return container;
}
/**
* Remove Large container of specified type.
* @param {string} type container type.
*/
removeContainer (type) {
if (!this.containers[type]) {
throw new Error(`container of type ${type} doesn't exist`);
@ -452,6 +531,12 @@ export default class LargeVideoManager {
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) {
if (this.state === type) {
return Promise.resolve();

View File

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