Implements support for events for the API. Adds toggleChat and toggleContactList commands. Renames filmStrip to toggleFilmStrip command. Fixes issues with removing the embedded Jitsi Meet.

This commit is contained in:
hristoterezov 2014-10-27 15:24:09 +02:00
parent 007564a1e5
commit a3d0050328
7 changed files with 408 additions and 16 deletions

View File

@ -21,7 +21,30 @@ var APIConnector = (function () {
displayName: VideoLayout.inputDisplayNameHandler, displayName: VideoLayout.inputDisplayNameHandler,
muteAudio: toggleAudio, muteAudio: toggleAudio,
muteVideo: toggleVideo, 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); window.attachEvent('onmessage', APIConnector.processMessage);
} }
APIConnector.sendMessage({loaded: true}); APIConnector.sendMessage({type: "system", loaded: true});
}; };
/** /**
@ -72,12 +95,91 @@ var APIConnector = (function () {
try { try {
message = JSON.parse(event.data); message = JSON.parse(event.data);
} catch (e) {} } 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) for(var key in message)
{ {
if(commands[key]) if(commands[key])
commands[key].apply(null, message[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});
}; };
/** /**

10
app.js
View File

@ -698,6 +698,11 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
// Add Peer's container // Add Peer's container
VideoLayout.ensurePeerContainerExists(jid); VideoLayout.ensurePeerContainerExists(jid);
if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantJoined"))
{
APIConnector.triggerEvent("participantJoined",{jid: jid});
}
if (focus !== null) { if (focus !== null) {
// FIXME: this should prepare the video // FIXME: this should prepare the video
if (focus.confid === null) { if (focus.confid === null) {
@ -734,6 +739,11 @@ $(document).bind('left.muc', function (event, jid) {
} }
}, 10); }, 10);
if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantLeft"))
{
APIConnector.triggerEvent("participantLeft",{jid: jid});
}
// Unlock large video // Unlock large video
if (focusedVideoSrc) if (focusedVideoSrc)
{ {

View File

@ -33,6 +33,7 @@ Controlling embedded Jitsi Meet Conference
========= =========
You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object. You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object.
You can send command to Jitsi Meet conference using ```executeCommand```. You can send command to Jitsi Meet conference using ```executeCommand```.
``` ```
api.executeCommand(command, arguments) api.executeCommand(command, arguments)
@ -56,10 +57,18 @@ api.executeCommand('muteAudio', [])
``` ```
api.executeCommand('muteVideo', []) 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', []) 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```. You can also execute multiple commands using the method ```executeCommands```.
``` ```
@ -72,7 +81,87 @@ commands.
api.executeCommands({displayName: ['nickname'], muteAudio: []}); 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() api.dispose()
``` ```

View File

@ -48,7 +48,7 @@ var JitsiMeetExternalAPI = (function()
this.iframeHolder.style.width = width + "px"; this.iframeHolder.style.width = width + "px";
this.iframeHolder.style.height = height + "px"; this.iframeHolder.style.height = height + "px";
this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id; this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id;
this.url = "https://" + domain + "/"; this.url = "http://" + domain + "/";
if(room_name) if(room_name)
this.url += room_name; this.url += room_name;
this.url += "#external"; this.url += "#external";
@ -57,12 +57,16 @@ var JitsiMeetExternalAPI = (function()
this.frame = document.createElement("iframe"); this.frame = document.createElement("iframe");
this.frame.src = this.url; this.frame.src = this.url;
this.frame.name = this.frameName; this.frame.name = this.frameName;
this.frame.id = this.frameName;
this.frame.width = "100%"; this.frame.width = "100%";
this.frame.height = "100%"; this.frame.height = "100%";
this.frame.setAttribute("allowFullScreen","true");
this.frame = this.iframeHolder.appendChild(this.frame); this.frame = this.iframeHolder.appendChild(this.frame);
this.frameLoaded = false; this.frameLoaded = false;
this.initialCommands = []; this.initialCommands = [];
this.eventHandlers = {};
this.initListeners(); this.initListeners();
} }
@ -108,7 +112,7 @@ var JitsiMeetExternalAPI = (function()
var argumentsArray = argumentsList; var argumentsArray = argumentsList;
if(!argumentsArray) if(!argumentsArray)
argumentsArray = []; argumentsArray = [];
var object = {}; var object = {type: "command", action: "execute"};
object[name] = argumentsArray; object[name] = argumentsArray;
this.sendMessage(object); this.sendMessage(object);
}; };
@ -125,9 +129,147 @@ var JitsiMeetExternalAPI = (function()
* arguments for the command. * arguments for the command.
*/ */
JitsiMeetExternalAPI.prototype.executeCommands = function (object) { JitsiMeetExternalAPI.prototype.executeCommands = function (object) {
object.type = "command";
object.action = "execute";
this.sendMessage(object); 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 * Processes message events sent from Jitsi Meet
* @param event the event * @param event the event
@ -138,10 +280,33 @@ var JitsiMeetExternalAPI = (function()
try { try {
message = JSON.parse(event.data); message = JSON.parse(event.data);
} catch (e) {} } catch (e) {}
if(message.loaded)
{ if(!message.type) {
this.onFrameLoaded(); 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', window.detachEvent('onmessage',
this.eventListener); 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; return JitsiMeetExternalAPI;

View File

@ -30,11 +30,11 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib --> <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="interface_config.js?v=2"></script> <script src="interface_config.js?v=2"></script>
<script src="brand.js?v=1"></script> <script src="brand.js?v=1"></script>
<script src="muc.js?v=15"></script><!-- simple MUC library --> <script src="muc.js?v=16"></script><!-- simple MUC library -->
<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=18"></script><!-- application logic --> <script src="app.js?v=19"></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=13"></script><!-- chat logic --> <script src="chat.js?v=13"></script><!-- chat logic -->
<script src="contact_list.js?v=5"></script><!-- contact list logic --> <script src="contact_list.js?v=5"></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=22"></script><!-- video ui --> <script src="videolayout.js?v=23"></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,7 +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> <script src="api_connector.js?v=2"></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=5"/> <link rel="stylesheet" href="css/font.css?v=5"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=28"/> <link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=28"/>

11
muc.js
View File

@ -201,6 +201,10 @@ Strophe.addConnectionPlugin('emuc', {
msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up(); msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
} }
this.connection.send(msg); this.connection.send(msg);
if(APIConnector.isEnabled() && APIConnector.isEventEnabled("outgoingMessage"))
{
APIConnector.triggerEvent("outgoingMessage", {"message": body});
}
}, },
setSubject: function (subject){ setSubject: function (subject){
var msg = $msg({to: this.roomjid, type: 'groupchat'}); var msg = $msg({to: this.roomjid, type: 'groupchat'});
@ -234,8 +238,13 @@ Strophe.addConnectionPlugin('emuc', {
if (txt) { if (txt) {
console.log('chat', nick, txt); console.log('chat', nick, txt);
Chat.updateChatConversation(from, 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; return true;
}, },

View File

@ -1274,18 +1274,28 @@ var VideoLayout = (function (my) {
*/ */
$(document).bind('displaynamechanged', $(document).bind('displaynamechanged',
function (event, jid, displayName, status) { function (event, jid, displayName, status) {
var name = null;
if (jid === 'localVideoContainer' if (jid === 'localVideoContainer'
|| jid === connection.emuc.myroomjid) { || jid === connection.emuc.myroomjid) {
name = nickname;
setDisplayName('localVideoContainer', setDisplayName('localVideoContainer',
displayName); displayName);
} else { } else {
VideoLayout.ensurePeerContainerExists(jid); VideoLayout.ensurePeerContainerExists(jid);
name = $('#participant_' + Strophe.getResourceFromJid(jid) + "_name").text();
setDisplayName( setDisplayName(
'participant_' + Strophe.getResourceFromJid(jid), 'participant_' + Strophe.getResourceFromJid(jid),
displayName, displayName,
status); 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});
}
}); });
/** /**