mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-03-06 19:50:22 +00:00
In Chrome M63 a warning is shown, permissions won't be automatically allowed afterwards. Reference: https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes Setting this early makes us future proof, while working with current versions too: Chrome < 63 gives an error because it doesn't understand the option and Firefox seems to ignore it, so both continue to work as before.
547 lines
17 KiB
JavaScript
547 lines
17 KiB
JavaScript
import EventEmitter from 'events';
|
|
|
|
import { urlObjectToString } from '../../../react/features/base/util/uri';
|
|
import {
|
|
PostMessageTransportBackend,
|
|
Transport
|
|
} from '../../transport';
|
|
|
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
|
|
|
const ALWAYS_ON_TOP_FILENAMES = [
|
|
'css/all.css', 'libs/alwaysontop.min.js'
|
|
];
|
|
|
|
/**
|
|
* Maps the names of the commands expected by the API with the name of the
|
|
* commands expected by jitsi-meet
|
|
*/
|
|
const commands = {
|
|
avatarUrl: 'avatar-url',
|
|
displayName: 'display-name',
|
|
email: 'email',
|
|
hangup: 'video-hangup',
|
|
toggleAudio: 'toggle-audio',
|
|
toggleChat: 'toggle-chat',
|
|
toggleContactList: 'toggle-contact-list',
|
|
toggleFilmStrip: 'toggle-film-strip',
|
|
toggleShareScreen: 'toggle-share-screen',
|
|
toggleVideo: 'toggle-video'
|
|
};
|
|
|
|
/**
|
|
* Maps the names of the events expected by the API with the name of the
|
|
* events expected by jitsi-meet
|
|
*/
|
|
const events = {
|
|
'audio-availability-changed': 'audioAvailabilityChanged',
|
|
'audio-mute-status-changed': 'audioMuteStatusChanged',
|
|
'display-name-change': 'displayNameChange',
|
|
'incoming-message': 'incomingMessage',
|
|
'outgoing-message': 'outgoingMessage',
|
|
'participant-joined': 'participantJoined',
|
|
'participant-left': 'participantLeft',
|
|
'video-ready-to-close': 'readyToClose',
|
|
'video-conference-joined': 'videoConferenceJoined',
|
|
'video-conference-left': 'videoConferenceLeft',
|
|
'video-availability-changed': 'videoAvailabilityChanged',
|
|
'video-mute-status-changed': 'videoMuteStatusChanged'
|
|
};
|
|
|
|
/**
|
|
* Last id of api object
|
|
* @type {number}
|
|
*/
|
|
let id = 0;
|
|
|
|
/**
|
|
* Adds given number to the numberOfParticipants property of given APIInstance.
|
|
*
|
|
* @param {JitsiMeetExternalAPI} APIInstance - The instance of the API.
|
|
* @param {int} number - The number of participants to be added to
|
|
* numberOfParticipants property (this parameter can be negative number if the
|
|
* numberOfParticipants should be decreased).
|
|
* @returns {void}
|
|
*/
|
|
function changeParticipantNumber(APIInstance, number) {
|
|
APIInstance._numberOfParticipants += number;
|
|
}
|
|
|
|
/**
|
|
* Generates the URL for the iframe.
|
|
*
|
|
* @param {string} domain - The domain name of the server that hosts the
|
|
* conference.
|
|
* @param {string} [options] - Another optional parameters.
|
|
* @param {Object} [options.configOverwrite] - Object containing configuration
|
|
* options defined in config.js to be overridden.
|
|
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
|
|
* configuration options defined in interface_config.js to be overridden.
|
|
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
|
|
* authentication.
|
|
* @param {boolean} [options.noSSL] - If the value is true https won't be used.
|
|
* @param {string} [options.roomName] - The name of the room to join.
|
|
* @returns {string} The URL.
|
|
*/
|
|
function generateURL(domain, options = {}) {
|
|
return urlObjectToString({
|
|
...options,
|
|
url:
|
|
`${options.noSSL ? 'http' : 'https'}://${domain
|
|
}/#jitsi_meet_external_api_id=${id}`
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Parses the arguments passed to the constructor. If the old format is used
|
|
* the function translates the arguments to the new format.
|
|
*
|
|
* @param {Array} args - The arguments to be parsed.
|
|
* @returns {Object} JS object with properties.
|
|
*/
|
|
function parseArguments(args) {
|
|
if (!args.length) {
|
|
return {};
|
|
}
|
|
|
|
const firstArg = args[0];
|
|
|
|
switch (typeof firstArg) {
|
|
case 'string': // old arguments format
|
|
case undefined: // eslint-disable-line no-case-declarations
|
|
// not sure which format but we are trying to parse the old
|
|
// format because if the new format is used everything will be undefined
|
|
// anyway.
|
|
const [
|
|
roomName,
|
|
width,
|
|
height,
|
|
parentNode,
|
|
configOverwrite,
|
|
interfaceConfigOverwrite,
|
|
noSSL,
|
|
jwt
|
|
] = args;
|
|
|
|
return {
|
|
roomName,
|
|
width,
|
|
height,
|
|
parentNode,
|
|
configOverwrite,
|
|
interfaceConfigOverwrite,
|
|
noSSL,
|
|
jwt
|
|
};
|
|
case 'object': // new arguments format
|
|
return args[0];
|
|
default:
|
|
throw new Error('Can\'t parse the arguments!');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compute valid values for height and width. If a number is specified it's
|
|
* treated as pixel units. If the value is expressed in px, em, pt or
|
|
* percentage, it's used as is.
|
|
*
|
|
* @param {any} value - The value to be parsed.
|
|
* @returns {string|undefined} The parsed value that can be used for setting
|
|
* sizes through the style property. If invalid value is passed the method
|
|
* retuns undefined.
|
|
*/
|
|
function parseSizeParam(value) {
|
|
let parsedValue;
|
|
|
|
// This regex parses values of the form 100px, 100em, 100pt or 100%.
|
|
// Values like 100 or 100px are handled outside of the regex, and
|
|
// invalid values will be ignored and the minimum will be used.
|
|
const re = /([0-9]*\.?[0-9]+)(em|pt|px|%)$/;
|
|
|
|
if (typeof value === 'string' && String(value).match(re) !== null) {
|
|
parsedValue = value;
|
|
} else if (typeof value === 'number') {
|
|
parsedValue = `${value}px`;
|
|
}
|
|
|
|
return parsedValue;
|
|
}
|
|
|
|
|
|
/**
|
|
* The IFrame API interface class.
|
|
*/
|
|
export default class JitsiMeetExternalAPI extends EventEmitter {
|
|
/**
|
|
* Constructs new API instance. Creates iframe and loads Jitsi Meet in it.
|
|
*
|
|
* @param {string} domain - The domain name of the server that hosts the
|
|
* conference.
|
|
* @param {Object} [options] - Optional arguments.
|
|
* @param {string} [options.roomName] - The name of the room to join.
|
|
* @param {number|string} [options.width] - Width of the iframe. Check
|
|
* parseSizeParam for format details.
|
|
* @param {number|string} [options.height] - Height of the iframe. Check
|
|
* parseSizeParam for format details.
|
|
* @param {DOMElement} [options.parentNode] - The node that will contain the
|
|
* iframe.
|
|
* @param {Object} [options.configOverwrite] - Object containing
|
|
* configuration options defined in config.js to be overridden.
|
|
* @param {Object} [options.interfaceConfigOverwrite] - Object containing
|
|
* configuration options defined in interface_config.js to be overridden.
|
|
* @param {boolean} [options.noSSL] - If the value is true https won't be
|
|
* used.
|
|
* @param {string} [options.jwt] - The JWT token if needed by jitsi-meet for
|
|
* authentication.
|
|
*/
|
|
constructor(domain, ...args) {
|
|
super();
|
|
const {
|
|
roomName = '',
|
|
width = '100%',
|
|
height = '100%',
|
|
parentNode = document.body,
|
|
configOverwrite = {},
|
|
interfaceConfigOverwrite = {},
|
|
noSSL = false,
|
|
jwt = undefined
|
|
} = parseArguments(args);
|
|
|
|
this._parentNode = parentNode;
|
|
this._url = generateURL(domain, {
|
|
configOverwrite,
|
|
interfaceConfigOverwrite,
|
|
jwt,
|
|
noSSL,
|
|
roomName
|
|
});
|
|
this._baseUrl = `${noSSL ? 'http' : 'https'}://${domain}/`;
|
|
this._createIFrame(height, width);
|
|
this._transport = new Transport({
|
|
backend: new PostMessageTransportBackend({
|
|
postisOptions: {
|
|
scope: `jitsi_meet_external_api_${id}`,
|
|
window: this._frame.contentWindow
|
|
}
|
|
})
|
|
});
|
|
this._numberOfParticipants = 1;
|
|
this._setupListeners();
|
|
id++;
|
|
}
|
|
|
|
/**
|
|
* Creates the iframe element.
|
|
*
|
|
* @param {number|string} height - The height of the iframe. Check
|
|
* parseSizeParam for format details.
|
|
* @param {number|string} width - The with of the iframe. Check
|
|
* parseSizeParam for format details.
|
|
* @returns {void}
|
|
*
|
|
* @private
|
|
*/
|
|
_createIFrame(height, width) {
|
|
const frameName = `jitsiConferenceFrame${id}`;
|
|
|
|
this._frame = document.createElement('iframe');
|
|
this._frame.allow = 'camera; microphone';
|
|
this._frame.src = this._url;
|
|
this._frame.name = frameName;
|
|
this._frame.id = frameName;
|
|
this._setSize(height, width);
|
|
this._frame.setAttribute('allowFullScreen', 'true');
|
|
this._frame.style.border = 0;
|
|
this._frame = this._parentNode.appendChild(this._frame);
|
|
}
|
|
|
|
/**
|
|
* Returns arrays with the all resources for the always on top feature.
|
|
*
|
|
* @returns {Array<string>}
|
|
*/
|
|
_getAlwaysOnTopResources() {
|
|
return ALWAYS_ON_TOP_FILENAMES.map(
|
|
filename => this._baseUrl + filename
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sets the size of the iframe element.
|
|
*
|
|
* @param {number|string} height - The height of the iframe.
|
|
* @param {number|string} width - The with of the iframe.
|
|
* @returns {void}
|
|
*
|
|
* @private
|
|
*/
|
|
_setSize(height, width) {
|
|
const parsedHeight = parseSizeParam(height);
|
|
const parsedWidth = parseSizeParam(width);
|
|
|
|
if (parsedHeight !== undefined) {
|
|
this._frame.style.height = parsedHeight;
|
|
}
|
|
|
|
if (parsedWidth !== undefined) {
|
|
this._frame.style.width = parsedWidth;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setups listeners that are used internally for JitsiMeetExternalAPI.
|
|
*
|
|
* @returns {void}
|
|
*
|
|
* @private
|
|
*/
|
|
_setupListeners() {
|
|
|
|
this._transport.on('event', ({ name, ...data }) => {
|
|
if (name === 'participant-joined') {
|
|
changeParticipantNumber(this, 1);
|
|
} else if (name === 'participant-left') {
|
|
changeParticipantNumber(this, -1);
|
|
}
|
|
|
|
const eventName = events[name];
|
|
|
|
if (eventName) {
|
|
this.emit(eventName, data);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Adds event listener to Meet Jitsi.
|
|
*
|
|
* @param {string} event - The name of the event.
|
|
* @param {Function} listener - The listener.
|
|
* @returns {void}
|
|
*
|
|
* @deprecated
|
|
* NOTE: This method is not removed for backward comatability purposes.
|
|
*/
|
|
addEventListener(event, listener) {
|
|
this.on(event, listener);
|
|
}
|
|
|
|
/**
|
|
* Adds event listeners to Meet Jitsi.
|
|
*
|
|
* @param {Object} listeners - The object key should be the name of
|
|
* the event and value - the listener.
|
|
* Currently we support the following
|
|
* events:
|
|
* incomingMessage - receives event notifications about incoming
|
|
* messages. The listener will receive object with the following structure:
|
|
* {{
|
|
* 'from': from,//JID of the user that sent the message
|
|
* 'nick': nick,//the nickname of the user that sent the message
|
|
* 'message': txt//the text of the message
|
|
* }}
|
|
* outgoingMessage - receives event notifications about outgoing
|
|
* messages. The listener will receive object with the following structure:
|
|
* {{
|
|
* 'message': txt//the text of the message
|
|
* }}
|
|
* displayNameChanged - receives event notifications about display name
|
|
* change. The listener will receive object with the following structure:
|
|
* {{
|
|
* jid: jid,//the JID of the participant that changed his display name
|
|
* displayname: displayName //the new display name
|
|
* }}
|
|
* participantJoined - receives event notifications about new participant.
|
|
* The listener will receive object with the following structure:
|
|
* {{
|
|
* jid: jid //the jid of the participant
|
|
* }}
|
|
* participantLeft - receives event notifications about the participant that
|
|
* left the room.
|
|
* The listener will receive object with the following structure:
|
|
* {{
|
|
* jid: jid //the jid of the participant
|
|
* }}
|
|
* video-conference-joined - receives event notifications about the local
|
|
* user has successfully joined the video conference.
|
|
* The listener will receive object with the following structure:
|
|
* {{
|
|
* roomName: room //the room name of the conference
|
|
* }}
|
|
* video-conference-left - receives event notifications about the local user
|
|
* has left the video conference.
|
|
* The listener will receive object with the following structure:
|
|
* {{
|
|
* roomName: room //the room name of the conference
|
|
* }}
|
|
* readyToClose - all hangup operations are completed and Jitsi Meet is
|
|
* ready to be disposed.
|
|
* @returns {void}
|
|
*
|
|
* @deprecated
|
|
* NOTE: This method is not removed for backward comatability purposes.
|
|
*/
|
|
addEventListeners(listeners) {
|
|
for (const event in listeners) { // eslint-disable-line guard-for-in
|
|
this.addEventListener(event, listeners[event]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the listeners and removes the Jitsi Meet frame.
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
dispose() {
|
|
this._transport.dispose();
|
|
this.removeAllListeners();
|
|
if (this._frame) {
|
|
this._frame.parentNode.removeChild(this._frame);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes command. The available commands are:
|
|
* displayName - sets the display name of the local participant to the value
|
|
* passed in the arguments array.
|
|
* toggleAudio - mutes / unmutes audio with no arguments.
|
|
* toggleVideo - mutes / unmutes video with no arguments.
|
|
* toggleFilmStrip - hides / shows the filmstrip with no arguments.
|
|
* If the command doesn't require any arguments the parameter should be set
|
|
* to empty array or it may be omitted.
|
|
*
|
|
* @param {string} name - The name of the command.
|
|
* @returns {void}
|
|
*/
|
|
executeCommand(name, ...args) {
|
|
if (!(name in commands)) {
|
|
logger.error('Not supported command name.');
|
|
|
|
return;
|
|
}
|
|
this._transport.sendEvent({
|
|
data: args,
|
|
name: commands[name]
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Executes commands. The available commands are:
|
|
* displayName - sets the display name of the local participant to the value
|
|
* passed in the arguments array.
|
|
* toggleAudio - mutes / unmutes audio. no arguments
|
|
* toggleVideo - mutes / unmutes video. no arguments
|
|
* toggleFilmStrip - hides / shows the filmstrip. no arguments
|
|
* toggleChat - hides / shows chat. no arguments.
|
|
* toggleContactList - hides / shows contact list. no arguments.
|
|
* toggleShareScreen - starts / stops screen sharing. no arguments.
|
|
*
|
|
* @param {Object} commandList - The object with commands to be executed.
|
|
* The keys of the object are the commands that will be executed and the
|
|
* values are the arguments for the command.
|
|
* @returns {void}
|
|
*/
|
|
executeCommands(commandList) {
|
|
for (const key in commandList) { // eslint-disable-line guard-for-in
|
|
this.executeCommand(key, commandList[key]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the audio is available.
|
|
*
|
|
* @returns {Promise} - Resolves with true if the audio available, with
|
|
* false if not and rejects on failure.
|
|
*/
|
|
isAudioAvailable() {
|
|
return this._transport.sendRequest({
|
|
name: 'is-audio-available'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the audio mute status.
|
|
*
|
|
* @returns {Promise} - Resolves with the audio mute status and rejects on
|
|
* failure.
|
|
*/
|
|
isAudioMuted() {
|
|
return this._transport.sendRequest({
|
|
name: 'is-audio-muted'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the iframe that loads Jitsi Meet.
|
|
*
|
|
* @returns {HTMLElement} The iframe.
|
|
*/
|
|
getIFrame() {
|
|
return this._frame;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of participants in the conference. The local
|
|
* participant is included.
|
|
*
|
|
* @returns {int} The number of participants in the conference.
|
|
*/
|
|
getNumberOfParticipants() {
|
|
return this._numberOfParticipants;
|
|
}
|
|
|
|
/**
|
|
* Check if the video is available.
|
|
*
|
|
* @returns {Promise} - Resolves with true if the video available, with
|
|
* false if not and rejects on failure.
|
|
*/
|
|
isVideoAvailable() {
|
|
return this._transport.sendRequest({
|
|
name: 'is-video-available'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the audio mute status.
|
|
*
|
|
* @returns {Promise} - Resolves with the audio mute status and rejects on
|
|
* failure.
|
|
*/
|
|
isVideoMuted() {
|
|
return this._transport.sendRequest({
|
|
name: 'is-video-muted'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Removes event listener.
|
|
*
|
|
* @param {string} event - The name of the event.
|
|
* @returns {void}
|
|
*
|
|
* @deprecated
|
|
* NOTE: This method is not removed for backward comatability purposes.
|
|
*/
|
|
removeEventListener(event) {
|
|
this.removeAllListeners(event);
|
|
}
|
|
|
|
/**
|
|
* Removes event listeners.
|
|
*
|
|
* @param {Array<string>} eventList - Array with the names of the events.
|
|
* @returns {void}
|
|
*
|
|
* @deprecated
|
|
* NOTE: This method is not removed for backward comatability purposes.
|
|
*/
|
|
removeEventListeners(eventList) {
|
|
eventList.forEach(event => this.removeEventListener(event));
|
|
}
|
|
}
|