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,
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});
};
/**

10
app.js
View File

@ -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)
{

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 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()
```

View File

@ -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;

View File

@ -30,11 +30,11 @@
<script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
<script src="interface_config.js?v=2"></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="desktopsharing.js?v=3"></script><!-- desktop sharing -->
<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="chat.js?v=13"></script><!-- chat 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="rtp_sts.js?v=4"></script><!-- RTP 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="toolbar.js?v=6"></script><!-- toolbar ui -->
<script src="toolbar_toggler.js?v=2"></script>
@ -60,7 +60,7 @@
<script src="tracking.js?v=1"></script><!-- tracking -->
<script src="jitsipopover.js?v=3"></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 rel="stylesheet" href="css/font.css?v=5"/>
<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();
}
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;
},

View File

@ -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});
}
});
/**