diff --git a/api_connector.js b/api_connector.js
index b8cb700d4..a28089f3b 100644
--- a/api_connector.js
+++ b/api_connector.js
@@ -21,7 +21,30 @@ var APIConnector = (function () {
displayName: VideoLayout.inputDisplayNameHandler,
muteAudio: toggleAudio,
muteVideo: toggleVideo,
- filmStrip: BottomToolbar.toggleFilmStrip
+ toggleFilmStrip: BottomToolbar.toggleFilmStrip,
+ toggleChat: BottomToolbar.toggleChat,
+ toggleContactList: BottomToolbar.toggleContactList
+ };
+
+
+ /**
+ * Maps the supported events and their status
+ * (true it the event is enabled and false if it is disabled)
+ * @type {{
+ * incommingMessage: boolean,
+ * outgoingMessage: boolean,
+ * displayNameChange: boolean,
+ * participantJoined: boolean,
+ * participantLeft: boolean
+ * }}
+ */
+ var events =
+ {
+ incommingMessage: false,
+ outgoingMessage:false,
+ displayNameChange: false,
+ participantJoined: false,
+ participantLeft: false
};
/**
@@ -51,7 +74,7 @@ var APIConnector = (function () {
{
window.attachEvent('onmessage', APIConnector.processMessage);
}
- APIConnector.sendMessage({loaded: true});
+ APIConnector.sendMessage({type: "system", loaded: true});
};
/**
@@ -72,12 +95,91 @@ var APIConnector = (function () {
try {
message = JSON.parse(event.data);
} catch (e) {}
+
+ if(!message.type)
+ return;
+ switch (message.type)
+ {
+ case "command":
+ APIConnector.processCommand(message);
+ break;
+ case "event":
+ APIConnector.processEvent(message);
+ break;
+ default:
+ console.error("Unknown type of the message");
+ return;
+ }
+
+ };
+
+ /**
+ * Processes commands from external applicaiton.
+ * @param message the object with the command
+ */
+ APIConnector.processCommand = function (message)
+ {
+ if(message.action != "execute")
+ {
+ console.error("Unknown action of the message");
+ return;
+ }
for(var key in message)
{
if(commands[key])
commands[key].apply(null, message[key]);
}
+ };
+ /**
+ * Processes events objects from external applications
+ * @param event the event
+ */
+ APIConnector.processEvent = function (event) {
+ if(!event.action)
+ {
+ console.error("Event with no action is received.");
+ return;
+ }
+
+ switch(event.action)
+ {
+ case "add":
+ for(var i = 0; i < event.events.length; i++)
+ {
+ events[event.events[i]] = true;
+ }
+ break;
+ case "remove":
+ for(var i = 0; i < event.events.length; i++)
+ {
+ events[event.events[i]] = false;
+ }
+ break;
+ default:
+ console.error("Unknown action for event.");
+ }
+
+ };
+
+ /**
+ * Checks whether the event is enabled ot not.
+ * @param name the name of the event.
+ * @returns {*}
+ */
+ APIConnector.isEventEnabled = function (name) {
+ return events[name];
+ };
+
+ /**
+ * Sends event object to the external application that has been subscribed
+ * for that event.
+ * @param name the name event
+ * @param object data associated with the event
+ */
+ APIConnector.triggerEvent = function (name, object) {
+ APIConnector.sendMessage({
+ type: "event", action: "result", event: name, result: object});
};
/**
diff --git a/app.js b/app.js
index e22eeafc5..168d50778 100644
--- a/app.js
+++ b/app.js
@@ -698,6 +698,11 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
// Add Peer's container
VideoLayout.ensurePeerContainerExists(jid);
+ if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantJoined"))
+ {
+ APIConnector.triggerEvent("participantJoined",{jid: jid});
+ }
+
if (focus !== null) {
// FIXME: this should prepare the video
if (focus.confid === null) {
@@ -734,6 +739,11 @@ $(document).bind('left.muc', function (event, jid) {
}
}, 10);
+ if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantLeft"))
+ {
+ APIConnector.triggerEvent("participantLeft",{jid: jid});
+ }
+
// Unlock large video
if (focusedVideoSrc)
{
diff --git a/doc/api.md b/doc/api.md
index 9b663183f..635be80c3 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -33,6 +33,7 @@ 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)
@@ -56,10 +57,18 @@ api.executeCommand('muteAudio', [])
```
api.executeCommand('muteVideo', [])
```
-* **filmStrip** - hides / shows the film strip. No arguments are required.
+* **toggleFilmStrip** - hides / shows the film strip. No arguments are required.
```
api.executeCommand('filmStrip', [])
```
+* **toggleChat** - hides / shows the chat. No arguments are required.
+```
+api.executeCommand('toggleChat', [])
+```
+* **toggleContactList** - hides / shows the contact list. No arguments are required.
+```
+api.executeCommand('toggleContactList', [])
+```
You can also execute multiple commands using the method ```executeCommands```.
```
@@ -72,7 +81,87 @@ commands.
api.executeCommands({displayName: ['nickname'], muteAudio: []});
```
-You can also remove the embedded Jitsi Meet Conference with the following code:
+You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method.
+```
+api.addEventListener(event, listener)
+```
+The ```event``` parameter is String object with the name of the event.
+The ```listener``` paramenter is Function object with one argument that will be notified when the event occurs
+with data related to the event.
+
+Currently we support the following events:
+
+* **incommingMessage** - event notifications about incomming
+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** - event notifications about outgoing
+messages. The listener will receive object with the following structure:
+```
+{
+"message": txt//the text of the message
+}
+```
+* **displayNameChanged** - 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** - event notifications about new participant.
+The listener will receive object with the following structure:
+```
+{
+jid: jid //the jid of the participant
+}
+```
+* **participantLeft** - event notifications about participant that left room.
+The listener will receive object with the following structure:
+```
+{
+jid: jid //the jid of the participant
+}
+```
+
+You can also add multiple event listeners by using ```addEventListeners```.
+This method requires one argument of type Object. The object argument must
+have keys with the names of the events and values the listeners of the events.
+
+```
+function incommingMessageListener(object)
+{
+...
+}
+
+function outgoingMessageListener(object)
+{
+...
+}
+
+api.addEventListeners({
+ incommingMessage: incommingMessageListener,
+ outgoingMessage: outgoingMessageListener})
+```
+
+If you want to remove a listener you can use ```removeEventListener``` method with argument the name of the event.
+```
+api.removeEventListener("incommingMessage");
+```
+
+If you want to remove more than one event you can use ```removeEventListeners``` method with argument
+ array with the names of the events.
+```
+api.removeEventListeners(["incommingMessage", "outgoingMessageListener"]);
+```
+
+You can remove the embedded Jitsi Meet Conference with the following code:
```
api.dispose()
```
diff --git a/external_api.js b/external_api.js
index 2e2c36a39..9bf7b08fe 100644
--- a/external_api.js
+++ b/external_api.js
@@ -48,7 +48,7 @@ var JitsiMeetExternalAPI = (function()
this.iframeHolder.style.width = width + "px";
this.iframeHolder.style.height = height + "px";
this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id;
- this.url = "https://" + domain + "/";
+ this.url = "http://" + domain + "/";
if(room_name)
this.url += room_name;
this.url += "#external";
@@ -57,12 +57,16 @@ var JitsiMeetExternalAPI = (function()
this.frame = document.createElement("iframe");
this.frame.src = this.url;
this.frame.name = this.frameName;
+ this.frame.id = this.frameName;
this.frame.width = "100%";
this.frame.height = "100%";
+ this.frame.setAttribute("allowFullScreen","true");
this.frame = this.iframeHolder.appendChild(this.frame);
+
this.frameLoaded = false;
this.initialCommands = [];
+ this.eventHandlers = {};
this.initListeners();
}
@@ -108,7 +112,7 @@ var JitsiMeetExternalAPI = (function()
var argumentsArray = argumentsList;
if(!argumentsArray)
argumentsArray = [];
- var object = {};
+ var object = {type: "command", action: "execute"};
object[name] = argumentsArray;
this.sendMessage(object);
};
@@ -125,9 +129,147 @@ var JitsiMeetExternalAPI = (function()
* arguments for the command.
*/
JitsiMeetExternalAPI.prototype.executeCommands = function (object) {
+ object.type = "command";
+ object.action = "execute";
this.sendMessage(object);
};
+ /**
+ * Adds event listeners to Meet Jitsi. The object key should be the name of the
+ * event and value - the listener.
+ * Currently we support the following
+ * events:
+ * incommingMessage - receives event notifications about incomming
+ * 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 participant that left room.
+ * The listener will receive object with the following structure:
+ * {{
+ * jid: jid //the jid of the participant
+ * }}
+ * @param object
+ */
+ JitsiMeetExternalAPI.prototype.addEventListeners
+ = function (object)
+ {
+
+ var message = {type: "event", action: "add", events: []};
+ for(var i in object)
+ {
+ message.events.push(i);
+ this.eventHandlers[i] = object[i];
+ }
+ this.sendMessage(message);
+ };
+
+ /**
+ * Adds event listeners to Meet Jitsi. Currently we support the following
+ * events:
+ * incommingMessage - receives event notifications about incomming
+ * 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 participant that left room.
+ * The listener will receive object with the following structure:
+ * {{
+ * jid: jid //the jid of the participant
+ * }}
+ * @param event the name of the event
+ * @param listener the listener
+ */
+ JitsiMeetExternalAPI.prototype.addEventListener
+ = function (event, listener)
+ {
+
+ var message = {type: "event", action: "add", events: [event]};
+ this.eventHandlers[event] = listener;
+ this.sendMessage(message);
+ };
+
+ /**
+ * Removes event listener.
+ * @param event the name of the event.
+ */
+ JitsiMeetExternalAPI.prototype.removeEventListener
+ = function (event)
+ {
+ if(!this.eventHandlers[event])
+ {
+ console.error("The event " + event + " is not registered.");
+ return;
+ }
+ var message = {type: "event", action: "remove", events: [event]};
+ delete this.eventHandlers[event];
+ this.sendMessage(message);
+ };
+
+ /**
+ * Removes event listeners.
+ * @param events array with the names of the events.
+ */
+ JitsiMeetExternalAPI.prototype.removeEventListeners
+ = function (events)
+ {
+ var eventsArray = [];
+ for(var i = 0; i < events.length; i++)
+ {
+ var event = events[i];
+ if(!this.eventHandlers[event])
+ {
+ console.error("The event " + event + " is not registered.");
+ continue;
+ }
+ delete this.eventHandlers[event];
+ eventsArray.push(event);
+ }
+
+ if(eventsArray.length > 0)
+ {
+ this.sendMessage(
+ {type: "event", action: "remove", events: eventsArray});
+ }
+
+ };
+
/**
* Processes message events sent from Jitsi Meet
* @param event the event
@@ -138,10 +280,33 @@ var JitsiMeetExternalAPI = (function()
try {
message = JSON.parse(event.data);
} catch (e) {}
- if(message.loaded)
- {
- this.onFrameLoaded();
+
+ if(!message.type) {
+ console.error("Message without type is received.");
+ return;
}
+ switch (message.type)
+ {
+ case "system":
+ if(message.loaded)
+ {
+ this.onFrameLoaded();
+ }
+ break;
+ case "event":
+ if(message.action != "result" ||
+ !message.event || !this.eventHandlers[message.event])
+ {
+ console.warn("The received event cannot be parsed.");
+ return;
+ }
+ this.eventHandlers[message.event](message.result);
+ break;
+ default :
+ console.error("Unknown message type.");
+ return;
+ }
+
};
@@ -191,7 +356,14 @@ var JitsiMeetExternalAPI = (function()
window.detachEvent('onmessage',
this.eventListener);
}
- this.iframeHolder.parentNode.removeChild(this.iframeHolder);
+ var frame = document.getElementById(this.frameName);
+ if(frame)
+ frame.src = 'about:blank';
+ var self = this;
+ window.setTimeout(function () {
+ self.iframeHolder.removeChild(self.frame);
+ self.iframeHolder.parentNode.removeChild(self.iframeHolder);
+ }, 10);
};
return JitsiMeetExternalAPI;
diff --git a/index.html b/index.html
index a2b7fa5eb..07d1ccb75 100644
--- a/index.html
+++ b/index.html
@@ -30,11 +30,11 @@
-
+
-
+
@@ -47,7 +47,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/muc.js b/muc.js
index 3580716e4..a71dc2832 100644
--- a/muc.js
+++ b/muc.js
@@ -201,6 +201,10 @@ Strophe.addConnectionPlugin('emuc', {
msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
}
this.connection.send(msg);
+ if(APIConnector.isEnabled() && APIConnector.isEventEnabled("outgoingMessage"))
+ {
+ APIConnector.triggerEvent("outgoingMessage", {"message": body});
+ }
},
setSubject: function (subject){
var msg = $msg({to: this.roomjid, type: 'groupchat'});
@@ -234,8 +238,13 @@ Strophe.addConnectionPlugin('emuc', {
if (txt) {
console.log('chat', nick, txt);
-
Chat.updateChatConversation(from, nick, txt);
+ if(APIConnector.isEnabled() && APIConnector.isEventEnabled("incommingMessage"))
+ {
+ if(from != this.myroomjid)
+ APIConnector.triggerEvent("incommingMessage",
+ {"from": from, "nick": nick, "message": txt});
+ }
}
return true;
},
diff --git a/videolayout.js b/videolayout.js
index 59dc36ebd..968c7d265 100644
--- a/videolayout.js
+++ b/videolayout.js
@@ -1274,18 +1274,28 @@ var VideoLayout = (function (my) {
*/
$(document).bind('displaynamechanged',
function (event, jid, displayName, status) {
+ var name = null;
if (jid === 'localVideoContainer'
|| jid === connection.emuc.myroomjid) {
+ name = nickname;
setDisplayName('localVideoContainer',
displayName);
} else {
VideoLayout.ensurePeerContainerExists(jid);
-
+ name = $('#participant_' + Strophe.getResourceFromJid(jid) + "_name").text();
setDisplayName(
'participant_' + Strophe.getResourceFromJid(jid),
displayName,
status);
}
+
+ if(APIConnector.isEnabled() && APIConnector.isEventEnabled("displayNameChange"))
+ {
+ if(jid === 'localVideoContainer')
+ jid = connection.emuc.myroomjid;
+ if(!name || name != displayName)
+ APIConnector.triggerEvent("displayNameChange",{jid: jid, displayname: displayName});
+ }
});
/**