Implements API that allows external applications to embed Jitsi Meet.

This commit is contained in:
hristoterezov 2014-10-22 12:03:07 +03:00
parent 78d60128a4
commit 667a8c1493
6 changed files with 410 additions and 27 deletions

100
api_connector.js Normal file
View File

@ -0,0 +1,100 @@
/**
* Implements API class that communicates with external api class
* and provides interface to access Jitsi Meet features by external
* applications that embed Jitsi Meet
*/
var APIConnector = (function () {
function APIConnector() { }
/**
* List of the available commands.
* @type {{
* displayName: inputDisplayNameHandler,
* muteAudio: toggleAudio,
* muteVideo: toggleVideo,
* filmStrip: toggleFilmStrip
* }}
*/
var commands =
{
displayName: VideoLayout.inputDisplayNameHandler,
muteAudio: toggleAudio,
muteVideo: toggleVideo,
filmStrip: BottomToolbar.toggleFilmStrip
};
/**
* Check whether the API should be enabled or not.
* @returns {boolean}
*/
APIConnector.isEnabled = function () {
var hash = location.hash;
if(hash && hash.indexOf("external") > -1 && window.postMessage)
return true;
return false;
};
/**
* Initializes the APIConnector. Setups message event listeners that will
* receive information from external applications that embed Jitsi Meet.
* It also sends a message to the external application that APIConnector
* is initialized.
*/
APIConnector.init = function () {
if (window.addEventListener)
{
window.addEventListener('message',
APIConnector.processMessage, false);
}
else
{
window.attachEvent('onmessage', APIConnector.processMessage);
}
APIConnector.sendMessage({loaded: true});
};
/**
* Sends message to the external application.
* @param object
*/
APIConnector.sendMessage = function (object) {
window.parent.postMessage(JSON.stringify(object), "*");
};
/**
* Processes a message event from the external application
* @param event the message event
*/
APIConnector.processMessage = function(event)
{
var message;
try {
message = JSON.parse(event.data);
} catch (e) {}
for(var key in message)
{
if(commands[key])
commands[key].apply(null, message[key]);
}
};
/**
* Removes the listeners.
*/
APIConnector.dispose = function () {
if(window.removeEventListener)
{
window.removeEventListener("message",
APIConnector.processMessage, false);
}
else
{
window.detachEvent('onmessage', APIConnector.processMessage);
}
};
return APIConnector;
})();

5
app.js
View File

@ -1126,6 +1126,8 @@ function getCameraVideoSize(videoWidth,
$(document).ready(function () { $(document).ready(function () {
document.title = brand.appName; document.title = brand.appName;
if(APIConnector.isEnabled())
APIConnector.init();
if(config.enableWelcomePage && window.location.pathname == "/" && if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled (!window.localStorage.welcomePageDisabled
@ -1186,7 +1188,6 @@ $(document).ready(function () {
} }
}); });
if (!(interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE === false)){ if (!(interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE === false)){
var updateTimeout; var updateTimeout;
var animateTimeout; var animateTimeout;
@ -1322,6 +1323,8 @@ $(window).bind('beforeunload', function () {
}); });
} }
disposeConference(true); disposeConference(true);
if(APIConnector.isEnabled())
APIConnector.dispose();
}); });
function disposeConference(onUnload) { function disposeConference(onUnload) {

80
doc/api.md Normal file
View File

@ -0,0 +1,80 @@
Jitsi Meet API
============
You can use Jitsi Meet API to embed Jitsi Meet in to your application.
Installation
==========
To embed Jitsi Meet in your application you need to add Jitsi Meet API library
```javascript
<script src="https://meet.jit.si/external_api.js"></script>
```
The next step for embedding Jitsi Meet is to create the Jitsi Meet API object
```javascript
<script>
var domain = "meet.jit.si";
var room = "JitsiMeetAPIExample";
var width = 700;
var height = 700;
var api = new JitsiMeetExternalAPI(domain, room, width, height);
</script>
```
You can paste that lines in your html code where you want to be placed the Jitsi Meet conference
or you can specify the parent HTML element for the Jitsi Meet conference in the JitsiMeetExternalAPI
constructor.
```javascript
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement);
```
If you don't specify room the user will enter in new conference with random room name.
Controlling embedded Jitsi Meet Conference
=========
You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object.
You can send command to Jitsi Meet conference using ```executeCommand```.
```
api.executeCommand(command, arguments)
```
The ```command``` parameter is String object with the name of the command.
The ```arguments``` parameter is array with the arguments required by the command.
If no arguments are required by the command this parameter can be omitted or you can pass empty array.
Currently we support the following commands:
* **displayName** - sets the display name of the local participant. This command requires one argument -
the new display name to be set
```
api.executeCommand('displayName', ['New Nickname']);
```
* **muteAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
```
api.executeCommand('muteAudio', [])
```
* **muteVideo** - mutes / unmutes the video for the local participant. No arguments are required.
```
api.executeCommand('muteVideo', [])
```
* **filmStrip** - hides / shows the film strip. No arguments are required.
```
api.executeCommand('filmStrip', [])
```
You can also execute multiple commands using the method ```executeCommands```.
```
api.executeCommands(commands)
```
The ```commands``` parameter is object with keys the names of the commands and values the arguments for the
commands.
```
api.executeCommands({displayName: ['nickname'], muteAudio: []});
```
You can also remove the embedded Jitsi Meet Conference with the following code:
```
api.dispose()
```
It is a good practice to remove the conference before the page is unloaded.

199
external_api.js Normal file
View File

@ -0,0 +1,199 @@
/**
* Implements API class that embeds Jitsi Meet in external applications.
*/
var JitsiMeetExternalAPI = (function()
{
/**
* The minimum width for the Jitsi Meet frame
* @type {number}
*/
var MIN_WIDTH = 790;
/**
* The minimum height for the Jitsi Meet frame
* @type {number}
*/
var MIN_HEIGHT = 300;
/**
* Constructs new API instance. Creates iframe element that loads
* Jitsi Meet.
* @param domain the domain name of the server that hosts the conference
* @param room_name the name of the room to join
* @param width width of the iframe
* @param height height of the iframe
* @param parent_node the node that will contain the iframe
* @constructor
*/
function JitsiMeetExternalAPI(domain, room_name, width, height, parent_node)
{
this.parentNode = null;
if(parent_node)
{
this.parentNode = parent_node;
}
else
{
var scriptTag = document.scripts[document.scripts.length - 1];
this.parentNode = scriptTag.parentNode;
}
this.iframeHolder =
this.parentNode.appendChild(document.createElement("div"));
this.iframeHolder.id = "jitsiConference" + JitsiMeetExternalAPI.id;
if(width < MIN_WIDTH)
width = MIN_WIDTH;
if(height < MIN_HEIGHT)
height = MIN_HEIGHT;
this.iframeHolder.style.width = width + "px";
this.iframeHolder.style.height = height + "px";
this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id;
this.url = "https://" + domain + "/";
if(room_name)
this.url += room_name;
this.url += "#external";
JitsiMeetExternalAPI.id++;
this.frame = document.createElement("iframe");
this.frame.src = this.url;
this.frame.name = this.frameName;
this.frame.width = "100%";
this.frame.height = "100%";
this.frame = this.iframeHolder.appendChild(this.frame);
this.frameLoaded = false;
this.initialCommands = [];
this.initListeners();
}
/**
* Last id of api object
* @type {number}
*/
JitsiMeetExternalAPI.id = 0;
/**
* Sends the passed object to Jitsi Meet
* @param object the object to be sent
*/
JitsiMeetExternalAPI.prototype.sendMessage = function(object)
{
if(this.frameLoaded)
{
this.frame.contentWindow.postMessage(
JSON.stringify(object), this.frame.src);
}
else
{
this.initialCommands.push(object);
}
};
/**
* Executes command. The available commands are:
* displayName - sets the display name of the local participant to the value
* passed in the arguments array.
* muteAudio - mutes / unmutes audio with no arguments
* muteVideo - mutes / unmutes video with no arguments
* filmStrip - hides / shows the film strip 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 name the name of the command
* @param arguments array of arguments
*/
JitsiMeetExternalAPI.prototype.executeCommand = function(name,
argumentsList)
{
var argumentsArray = argumentsList;
if(!argumentsArray)
argumentsArray = [];
var object = {};
object[name] = argumentsArray;
this.sendMessage(object);
};
/**
* Executes commands. The available commands are:
* displayName - sets the display name of the local participant to the value
* passed in the arguments array.
* muteAudio - mutes / unmutes audio with no arguments
* muteVideo - mutes / unmutes video with no arguments
* filmStrip - hides / shows the film strip with no arguments
* @param object 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.
*/
JitsiMeetExternalAPI.prototype.executeCommands = function (object) {
this.sendMessage(object);
};
/**
* Processes message events sent from Jitsi Meet
* @param event the event
*/
JitsiMeetExternalAPI.prototype.processMessage = function(event)
{
var message;
try {
message = JSON.parse(event.data);
} catch (e) {}
if(message.loaded)
{
this.onFrameLoaded();
}
};
/**
* That method is called when the Jitsi Meet is loaded. Executes saved
* commands that are send before the frame was loaded.
*/
JitsiMeetExternalAPI.prototype.onFrameLoaded = function () {
this.frameLoaded = true;
for (var i = 0; i < this.initialCommands.length; i++)
{
this.sendMessage(this.initialCommands[i]);
}
this.initialCommands = null;
};
/**
* Setups the listener for message events from Jitsi Meet.
*/
JitsiMeetExternalAPI.prototype.initListeners = function () {
var self = this;
this.eventListener = function (event) {
self.processMessage(event);
};
if (window.addEventListener)
{
window.addEventListener('message',
this.eventListener, false);
}
else
{
window.attachEvent('onmessage', this.eventListener);
}
};
/**
* Removes the listeners and removes the Jitsi Meet frame.
*/
JitsiMeetExternalAPI.prototype.dispose = function () {
if (window.removeEventListener)
{
window.removeEventListener('message',
this.eventListener, false);
}
else
{
window.detachEvent('onmessage',
this.eventListener);
}
this.iframeHolder.parentNode.removeChild(this.iframeHolder);
};
return JitsiMeetExternalAPI;
})();

View File

@ -34,7 +34,7 @@
<script src="estos_log.js?v=2"></script><!-- simple stanza logger --> <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
<script src="desktopsharing.js?v=3"></script><!-- desktop sharing --> <script src="desktopsharing.js?v=3"></script><!-- desktop sharing -->
<script src="data_channels.js?v=3"></script><!-- data channels --> <script src="data_channels.js?v=3"></script><!-- data channels -->
<script src="app.js?v=17"></script><!-- application logic --> <script src="app.js?v=18"></script><!-- application logic -->
<script src="commands.js?v=1"></script><!-- application logic --> <script src="commands.js?v=1"></script><!-- application logic -->
<script src="chat.js?v=11"></script><!-- chat logic --> <script src="chat.js?v=11"></script><!-- chat logic -->
<script src="contact_list.js?v=4"></script><!-- contact list logic --> <script src="contact_list.js?v=4"></script><!-- contact list logic -->
@ -47,7 +47,7 @@
<script src="analytics.js?v=1"></script><!-- google analytics plugin --> <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
<script src="rtp_sts.js?v=4"></script><!-- RTP stats processing --> <script src="rtp_sts.js?v=4"></script><!-- RTP stats processing -->
<script src="local_sts.js?v=2"></script><!-- Local stats processing --> <script src="local_sts.js?v=2"></script><!-- Local stats processing -->
<script src="videolayout.js?v=21"></script><!-- video ui --> <script src="videolayout.js?v=22"></script><!-- video ui -->
<script src="connectionquality.js?v=1"></script> <script src="connectionquality.js?v=1"></script>
<script src="toolbar.js?v=6"></script><!-- toolbar ui --> <script src="toolbar.js?v=6"></script><!-- toolbar ui -->
<script src="toolbar_toggler.js?v=2"></script> <script src="toolbar_toggler.js?v=2"></script>
@ -60,6 +60,7 @@
<script src="tracking.js?v=1"></script><!-- tracking --> <script src="tracking.js?v=1"></script><!-- tracking -->
<script src="jitsipopover.js?v=3"></script> <script src="jitsipopover.js?v=3"></script>
<script src="message_handler.js?v=1"></script> <script src="message_handler.js?v=1"></script>
<script src="api_connector.js?v=1"></script>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet"> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="css/font.css?v=4"/> <link rel="stylesheet" href="css/font.css?v=4"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=26"/> <link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=26"/>

View File

@ -622,36 +622,14 @@ var VideoLayout = (function (my) {
$('#editDisplayName').focus(); $('#editDisplayName').focus();
$('#editDisplayName').select(); $('#editDisplayName').select();
var inputDisplayNameHandler = function (name) {
if (nickname !== name) {
nickname = name;
window.localStorage.displayname = nickname;
connection.emuc.addDisplayNameToPresence(nickname);
connection.emuc.sendPresence();
Chat.setChatConversationMode(true);
}
if (!$('#localDisplayName').is(":visible")) {
if (nickname)
$('#localDisplayName').text(nickname + " (me)");
else
$('#localDisplayName')
.text(defaultLocalDisplayName);
$('#localDisplayName').show();
}
$('#editDisplayName').hide();
};
$('#editDisplayName').one("focusout", function (e) { $('#editDisplayName').one("focusout", function (e) {
inputDisplayNameHandler(this.value); VideoLayout.inputDisplayNameHandler(this.value);
}); });
$('#editDisplayName').on('keydown', function (e) { $('#editDisplayName').on('keydown', function (e) {
if (e.keyCode === 13) { if (e.keyCode === 13) {
e.preventDefault(); e.preventDefault();
inputDisplayNameHandler(this.value); VideoLayout.inputDisplayNameHandler(this.value);
} }
}); });
}); });
@ -659,6 +637,28 @@ var VideoLayout = (function (my) {
} }
}; };
my.inputDisplayNameHandler = function (name) {
if (nickname !== name) {
nickname = name;
window.localStorage.displayname = nickname;
connection.emuc.addDisplayNameToPresence(nickname);
connection.emuc.sendPresence();
Chat.setChatConversationMode(true);
}
if (!$('#localDisplayName').is(":visible")) {
if (nickname)
$('#localDisplayName').text(nickname + " (me)");
else
$('#localDisplayName')
.text(defaultLocalDisplayName);
$('#localDisplayName').show();
}
$('#editDisplayName').hide();
};
/** /**
* Shows/hides the display name on the remote video. * Shows/hides the display name on the remote video.
* @param videoSpanId the identifier of the video span element * @param videoSpanId the identifier of the video span element