diff --git a/api_connector.js b/api_connector.js
new file mode 100644
index 000000000..b8cb700d4
--- /dev/null
+++ b/api_connector.js
@@ -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;
+})();
\ No newline at end of file
diff --git a/app.js b/app.js
index 4fd665fcb..b7f7bb703 100644
--- a/app.js
+++ b/app.js
@@ -1126,6 +1126,8 @@ function getCameraVideoSize(videoWidth,
$(document).ready(function () {
document.title = brand.appName;
+ if(APIConnector.isEnabled())
+ APIConnector.init();
if(config.enableWelcomePage && window.location.pathname == "/" &&
(!window.localStorage.welcomePageDisabled
@@ -1186,7 +1188,6 @@ $(document).ready(function () {
}
});
-
if (!(interfaceConfig.GENERATE_ROOMNAMES_ON_WELCOME_PAGE === false)){
var updateTimeout;
var animateTimeout;
@@ -1322,6 +1323,8 @@ $(window).bind('beforeunload', function () {
});
}
disposeConference(true);
+ if(APIConnector.isEnabled())
+ APIConnector.dispose();
});
function disposeConference(onUnload) {
diff --git a/doc/api.md b/doc/api.md
new file mode 100644
index 000000000..9b663183f
--- /dev/null
+++ b/doc/api.md
@@ -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
+
+```
+
+The next step for embedding Jitsi Meet is to create the Jitsi Meet API object
+```javascript
+
+```
+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.
\ No newline at end of file
diff --git a/external_api.js b/external_api.js
new file mode 100644
index 000000000..2e2c36a39
--- /dev/null
+++ b/external_api.js
@@ -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;
+
+})();
\ No newline at end of file
diff --git a/index.html b/index.html
index 14185c3a2..557823409 100644
--- a/index.html
+++ b/index.html
@@ -34,7 +34,7 @@
-
+
@@ -47,7 +47,7 @@
-
+
@@ -60,6 +60,7 @@
+
diff --git a/videolayout.js b/videolayout.js
index 1f89ea99c..b3273177b 100644
--- a/videolayout.js
+++ b/videolayout.js
@@ -622,36 +622,14 @@ var VideoLayout = (function (my) {
$('#editDisplayName').focus();
$('#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) {
- inputDisplayNameHandler(this.value);
+ VideoLayout.inputDisplayNameHandler(this.value);
});
$('#editDisplayName').on('keydown', function (e) {
if (e.keyCode === 13) {
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.
* @param videoSpanId the identifier of the video span element