Kaynağa Gözat

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.

j8
hristoterezov 10 yıl önce
ebeveyn
işleme
a3d0050328
7 değiştirilmiş dosya ile 407 ekleme ve 15 silme
  1. 104
    2
      api_connector.js
  2. 10
    0
      app.js
  3. 91
    2
      doc/api.md
  4. 177
    5
      external_api.js
  5. 4
    4
      index.html
  6. 10
    1
      muc.js
  7. 11
    1
      videolayout.js

+ 104
- 2
api_connector.js Dosyayı Görüntüle

@@ -21,7 +21,30 @@ var APIConnector = (function () {
21 21
         displayName: VideoLayout.inputDisplayNameHandler,
22 22
         muteAudio: toggleAudio,
23 23
         muteVideo: toggleVideo,
24
-        filmStrip: BottomToolbar.toggleFilmStrip
24
+        toggleFilmStrip: BottomToolbar.toggleFilmStrip,
25
+        toggleChat: BottomToolbar.toggleChat,
26
+        toggleContactList: BottomToolbar.toggleContactList
27
+    };
28
+
29
+
30
+    /**
31
+     * Maps the supported events and their status
32
+     * (true it the event is enabled and false if it is disabled)
33
+     * @type {{
34
+     *              incommingMessage: boolean,
35
+     *              outgoingMessage: boolean,
36
+     *              displayNameChange: boolean,
37
+     *              participantJoined: boolean,
38
+     *              participantLeft: boolean
39
+     *      }}
40
+     */
41
+    var events =
42
+    {
43
+        incommingMessage: false,
44
+        outgoingMessage:false,
45
+        displayNameChange: false,
46
+        participantJoined: false,
47
+        participantLeft: false
25 48
     };
26 49
 
27 50
     /**
@@ -51,7 +74,7 @@ var APIConnector = (function () {
51 74
         {
52 75
             window.attachEvent('onmessage', APIConnector.processMessage);
53 76
         }
54
-        APIConnector.sendMessage({loaded: true});
77
+        APIConnector.sendMessage({type: "system", loaded: true});
55 78
     };
56 79
 
57 80
     /**
@@ -72,14 +95,93 @@ var APIConnector = (function () {
72 95
         try {
73 96
             message = JSON.parse(event.data);
74 97
         } catch (e) {}
98
+
99
+        if(!message.type)
100
+            return;
101
+        switch (message.type)
102
+        {
103
+            case "command":
104
+                APIConnector.processCommand(message);
105
+                break;
106
+            case "event":
107
+                APIConnector.processEvent(message);
108
+                break;
109
+            default:
110
+                console.error("Unknown type of the message");
111
+                return;
112
+        }
113
+
114
+    };
115
+
116
+    /**
117
+     * Processes commands from external applicaiton.
118
+     * @param message the object with the command
119
+     */
120
+    APIConnector.processCommand = function (message)
121
+    {
122
+        if(message.action != "execute")
123
+        {
124
+            console.error("Unknown action of the message");
125
+            return;
126
+        }
75 127
         for(var key in message)
76 128
         {
77 129
             if(commands[key])
78 130
                 commands[key].apply(null, message[key]);
79 131
         }
132
+    };
133
+
134
+    /**
135
+     * Processes events objects from external applications
136
+     * @param event the event
137
+     */
138
+    APIConnector.processEvent = function (event) {
139
+        if(!event.action)
140
+        {
141
+            console.error("Event with no action is received.");
142
+            return;
143
+        }
144
+
145
+        switch(event.action)
146
+        {
147
+            case "add":
148
+                for(var i = 0; i < event.events.length; i++)
149
+                {
150
+                    events[event.events[i]] = true;
151
+                }
152
+                break;
153
+            case "remove":
154
+                for(var i = 0; i < event.events.length; i++)
155
+                {
156
+                    events[event.events[i]] = false;
157
+                }
158
+                break;
159
+            default:
160
+                console.error("Unknown action for event.");
161
+        }
80 162
 
81 163
     };
82 164
 
165
+    /**
166
+     * Checks whether the event is enabled ot not.
167
+     * @param name the name of the event.
168
+     * @returns {*}
169
+     */
170
+    APIConnector.isEventEnabled = function (name) {
171
+        return events[name];
172
+    };
173
+
174
+    /**
175
+     * Sends event object to the external application that has been subscribed
176
+     * for that event.
177
+     * @param name the name event
178
+     * @param object data associated with the event
179
+     */
180
+    APIConnector.triggerEvent = function (name, object) {
181
+        APIConnector.sendMessage({
182
+            type: "event", action: "result", event: name, result: object});
183
+    };
184
+
83 185
     /**
84 186
      * Removes the listeners.
85 187
      */

+ 10
- 0
app.js Dosyayı Görüntüle

@@ -698,6 +698,11 @@ $(document).bind('entered.muc', function (event, jid, info, pres) {
698 698
     // Add Peer's container
699 699
     VideoLayout.ensurePeerContainerExists(jid);
700 700
 
701
+    if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantJoined"))
702
+    {
703
+        APIConnector.triggerEvent("participantJoined",{jid: jid});
704
+    }
705
+
701 706
     if (focus !== null) {
702 707
         // FIXME: this should prepare the video
703 708
         if (focus.confid === null) {
@@ -734,6 +739,11 @@ $(document).bind('left.muc', function (event, jid) {
734 739
         }
735 740
     }, 10);
736 741
 
742
+    if(APIConnector.isEnabled() && APIConnector.isEventEnabled("participantLeft"))
743
+    {
744
+        APIConnector.triggerEvent("participantLeft",{jid: jid});
745
+    }
746
+
737 747
     // Unlock large video
738 748
     if (focusedVideoSrc)
739 749
     {

+ 91
- 2
doc/api.md Dosyayı Görüntüle

@@ -33,6 +33,7 @@ Controlling embedded Jitsi Meet Conference
33 33
 =========
34 34
 
35 35
 You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object.
36
+
36 37
 You can send command to Jitsi Meet conference using ```executeCommand```. 
37 38
 ```
38 39
 api.executeCommand(command, arguments)
@@ -56,10 +57,18 @@ api.executeCommand('muteAudio', [])
56 57
 ```
57 58
 api.executeCommand('muteVideo', [])
58 59
 ```
59
-* **filmStrip** - hides / shows the film strip. No arguments are required.
60
+* **toggleFilmStrip** - hides / shows the film strip. No arguments are required.
60 61
 ```
61 62
 api.executeCommand('filmStrip', [])
62 63
 ```
64
+* **toggleChat** - hides / shows the chat. No arguments are required.
65
+```
66
+api.executeCommand('toggleChat', [])
67
+```
68
+* **toggleContactList** - hides / shows the contact list. No arguments are required.
69
+```
70
+api.executeCommand('toggleContactList', [])
71
+```
63 72
 
64 73
 You can also execute multiple commands using the method ```executeCommands```. 
65 74
 ```
@@ -72,7 +81,87 @@ commands.
72 81
 api.executeCommands({displayName: ['nickname'], muteAudio: []});
73 82
 ```
74 83
 
75
-You can also remove the embedded Jitsi Meet Conference with the following code:
84
+You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method.
85
+```
86
+api.addEventListener(event, listener)
87
+```
88
+The ```event``` parameter is String object with the name of the event.
89
+The ```listener``` paramenter is Function object with one argument that will be notified when the event occurs
90
+with data related to the event.
91
+
92
+Currently we support the following events:
93
+
94
+* **incommingMessage** - event notifications about incomming
95
+messages. The listener will receive object with the following structure:
96
+```
97
+{
98
+"from": from,//JID of the user that sent the message
99
+"nick": nick,//the nickname of the user that sent the message
100
+"message": txt//the text of the message
101
+}
102
+```
103
+* **outgoingMessage** - event notifications about outgoing
104
+messages. The listener will receive object with the following structure:
105
+```
106
+{
107
+"message": txt//the text of the message
108
+}
109
+```
110
+* **displayNameChanged** - event notifications about display name
111
+change. The listener will receive object with the following structure:
112
+```
113
+{
114
+jid: jid,//the JID of the participant that changed his display name
115
+displayname: displayName //the new display name
116
+}
117
+```
118
+* **participantJoined** - event notifications about new participant.
119
+The listener will receive object with the following structure:
120
+```
121
+{
122
+jid: jid //the jid of the participant
123
+}
124
+```
125
+* **participantLeft** - event notifications about participant that left room.
126
+The listener will receive object with the following structure:
127
+```
128
+{
129
+jid: jid //the jid of the participant
130
+}
131
+```
132
+
133
+You can also add multiple event listeners by using ```addEventListeners```.
134
+This method requires one argument of type Object. The object argument must 
135
+have keys with the names of the events and values the listeners of the events.
136
+
137
+```
138
+function incommingMessageListener(object)
139
+{
140
+...
141
+}
142
+
143
+function outgoingMessageListener(object)
144
+{
145
+...
146
+}
147
+
148
+api.addEventListeners({
149
+    incommingMessage: incommingMessageListener,
150
+    outgoingMessage: outgoingMessageListener})
151
+```
152
+
153
+If you want to remove a listener you can use ```removeEventListener``` method with argument the name of the event.
154
+```
155
+api.removeEventListener("incommingMessage");
156
+```
157
+
158
+If you want to remove more than one event you can use ```removeEventListeners``` method with argument
159
+ array with the names of the events.
160
+```
161
+api.removeEventListeners(["incommingMessage", "outgoingMessageListener"]);
162
+```
163
+
164
+You can remove the embedded Jitsi Meet Conference with the following code:
76 165
 ```
77 166
 api.dispose()
78 167
 ```

+ 177
- 5
external_api.js Dosyayı Görüntüle

@@ -48,7 +48,7 @@ var JitsiMeetExternalAPI = (function()
48 48
         this.iframeHolder.style.width = width + "px";
49 49
         this.iframeHolder.style.height = height + "px";
50 50
         this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id;
51
-        this.url = "https://" + domain + "/";
51
+        this.url = "http://" + domain + "/";
52 52
         if(room_name)
53 53
             this.url += room_name;
54 54
         this.url += "#external";
@@ -57,12 +57,16 @@ var JitsiMeetExternalAPI = (function()
57 57
         this.frame = document.createElement("iframe");
58 58
         this.frame.src = this.url;
59 59
         this.frame.name = this.frameName;
60
+        this.frame.id = this.frameName;
60 61
         this.frame.width = "100%";
61 62
         this.frame.height = "100%";
63
+        this.frame.setAttribute("allowFullScreen","true");
62 64
         this.frame = this.iframeHolder.appendChild(this.frame);
63 65
 
66
+
64 67
         this.frameLoaded = false;
65 68
         this.initialCommands = [];
69
+        this.eventHandlers = {};
66 70
         this.initListeners();
67 71
     }
68 72
 
@@ -108,7 +112,7 @@ var JitsiMeetExternalAPI = (function()
108 112
         var argumentsArray = argumentsList;
109 113
         if(!argumentsArray)
110 114
             argumentsArray = [];
111
-        var object = {};
115
+        var object = {type: "command", action: "execute"};
112 116
         object[name] = argumentsArray;
113 117
         this.sendMessage(object);
114 118
     };
@@ -125,9 +129,147 @@ var JitsiMeetExternalAPI = (function()
125 129
      * arguments for the command.
126 130
      */
127 131
     JitsiMeetExternalAPI.prototype.executeCommands = function (object) {
132
+        object.type = "command";
133
+        object.action = "execute";
128 134
         this.sendMessage(object);
129 135
     };
130 136
 
137
+    /**
138
+     * Adds event listeners to Meet Jitsi. The object key should be the name of the
139
+     * event and value - the listener.
140
+     * Currently we support the following
141
+     * events:
142
+     * incommingMessage - receives event notifications about incomming
143
+     * messages. The listener will receive object with the following structure:
144
+     * {{
145
+     *  "from": from,//JID of the user that sent the message
146
+     *  "nick": nick,//the nickname of the user that sent the message
147
+     *  "message": txt//the text of the message
148
+     * }}
149
+     * outgoingMessage - receives event notifications about outgoing
150
+     * messages. The listener will receive object with the following structure:
151
+     * {{
152
+     *  "message": txt//the text of the message
153
+     * }}
154
+     * displayNameChanged - receives event notifications about display name
155
+     * change. The listener will receive object with the following structure:
156
+     * {{
157
+     * jid: jid,//the JID of the participant that changed his display name
158
+     * displayname: displayName //the new display name
159
+     * }}
160
+     * participantJoined - receives event notifications about new participant.
161
+     * The listener will receive object with the following structure:
162
+     * {{
163
+     * jid: jid //the jid of the participant
164
+     * }}
165
+     * participantLeft - receives event notifications about participant that left room.
166
+     * The listener will receive object with the following structure:
167
+     * {{
168
+     * jid: jid //the jid of the participant
169
+     * }}
170
+     * @param object
171
+     */
172
+    JitsiMeetExternalAPI.prototype.addEventListeners
173
+        = function (object)
174
+    {
175
+
176
+        var message = {type: "event", action: "add", events: []};
177
+        for(var i in object)
178
+        {
179
+            message.events.push(i);
180
+            this.eventHandlers[i] = object[i];
181
+        }
182
+        this.sendMessage(message);
183
+    };
184
+
185
+    /**
186
+     * Adds event listeners to Meet Jitsi. Currently we support the following
187
+     * events:
188
+     * incommingMessage - receives event notifications about incomming
189
+     * messages. The listener will receive object with the following structure:
190
+     * {{
191
+     *  "from": from,//JID of the user that sent the message
192
+     *  "nick": nick,//the nickname of the user that sent the message
193
+     *  "message": txt//the text of the message
194
+     * }}
195
+     * outgoingMessage - receives event notifications about outgoing
196
+     * messages. The listener will receive object with the following structure:
197
+     * {{
198
+     *  "message": txt//the text of the message
199
+     * }}
200
+     * displayNameChanged - receives event notifications about display name
201
+     * change. The listener will receive object with the following structure:
202
+     * {{
203
+     * jid: jid,//the JID of the participant that changed his display name
204
+     * displayname: displayName //the new display name
205
+     * }}
206
+     * participantJoined - receives event notifications about new participant.
207
+     * The listener will receive object with the following structure:
208
+     * {{
209
+     * jid: jid //the jid of the participant
210
+     * }}
211
+     * participantLeft - receives event notifications about participant that left room.
212
+     * The listener will receive object with the following structure:
213
+     * {{
214
+     * jid: jid //the jid of the participant
215
+     * }}
216
+     * @param event the name of the event
217
+     * @param listener the listener
218
+     */
219
+    JitsiMeetExternalAPI.prototype.addEventListener
220
+        = function (event, listener)
221
+    {
222
+
223
+        var message = {type: "event", action: "add", events: [event]};
224
+        this.eventHandlers[event] = listener;
225
+        this.sendMessage(message);
226
+    };
227
+
228
+    /**
229
+     * Removes event listener.
230
+     * @param event the name of the event.
231
+     */
232
+    JitsiMeetExternalAPI.prototype.removeEventListener
233
+        = function (event)
234
+    {
235
+        if(!this.eventHandlers[event])
236
+        {
237
+            console.error("The event " + event + " is not registered.");
238
+            return;
239
+        }
240
+        var message = {type: "event", action: "remove", events: [event]};
241
+        delete this.eventHandlers[event];
242
+        this.sendMessage(message);
243
+    };
244
+
245
+    /**
246
+     * Removes event listeners.
247
+     * @param events array with the names of the events.
248
+     */
249
+    JitsiMeetExternalAPI.prototype.removeEventListeners
250
+        = function (events)
251
+    {
252
+        var eventsArray = [];
253
+        for(var i = 0; i < events.length; i++)
254
+        {
255
+            var event = events[i];
256
+            if(!this.eventHandlers[event])
257
+            {
258
+                console.error("The event " + event + " is not registered.");
259
+                continue;
260
+            }
261
+            delete this.eventHandlers[event];
262
+            eventsArray.push(event);
263
+        }
264
+
265
+        if(eventsArray.length > 0)
266
+        {
267
+            this.sendMessage(
268
+                {type: "event", action: "remove", events: eventsArray});
269
+        }
270
+
271
+    };
272
+
131 273
     /**
132 274
      * Processes message events sent from Jitsi Meet
133 275
      * @param event the event
@@ -138,11 +280,34 @@ var JitsiMeetExternalAPI = (function()
138 280
         try {
139 281
             message = JSON.parse(event.data);
140 282
         } catch (e) {}
141
-        if(message.loaded)
283
+
284
+        if(!message.type) {
285
+            console.error("Message without type is received.");
286
+            return;
287
+        }
288
+        switch (message.type)
142 289
         {
143
-            this.onFrameLoaded();
290
+            case "system":
291
+                if(message.loaded)
292
+                {
293
+                    this.onFrameLoaded();
294
+                }
295
+                break;
296
+            case "event":
297
+                if(message.action != "result" ||
298
+                    !message.event || !this.eventHandlers[message.event])
299
+                {
300
+                    console.warn("The received event cannot be parsed.");
301
+                    return;
302
+                }
303
+                this.eventHandlers[message.event](message.result);
304
+                break;
305
+            default :
306
+                console.error("Unknown message type.");
307
+                return;
144 308
         }
145 309
 
310
+
146 311
     };
147 312
 
148 313
     /**
@@ -191,7 +356,14 @@ var JitsiMeetExternalAPI = (function()
191 356
             window.detachEvent('onmessage',
192 357
                 this.eventListener);
193 358
         }
194
-        this.iframeHolder.parentNode.removeChild(this.iframeHolder);
359
+        var frame = document.getElementById(this.frameName);
360
+        if(frame)
361
+            frame.src = 'about:blank';
362
+        var self = this;
363
+        window.setTimeout(function () {
364
+                self.iframeHolder.removeChild(self.frame);
365
+                self.iframeHolder.parentNode.removeChild(self.iframeHolder);
366
+        }, 10);
195 367
     };
196 368
 
197 369
     return JitsiMeetExternalAPI;

+ 4
- 4
index.html Dosyayı Görüntüle

@@ -30,11 +30,11 @@
30 30
     <script src="libs/popover.js?v=1"></script><!-- bootstrap tooltip lib -->
31 31
     <script src="interface_config.js?v=2"></script>
32 32
     <script src="brand.js?v=1"></script>
33
-    <script src="muc.js?v=15"></script><!-- simple MUC library -->
33
+    <script src="muc.js?v=16"></script><!-- simple MUC library -->
34 34
     <script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
35 35
     <script src="desktopsharing.js?v=3"></script><!-- desktop sharing -->
36 36
     <script src="data_channels.js?v=3"></script><!-- data channels -->
37
-    <script src="app.js?v=18"></script><!-- application logic -->
37
+    <script src="app.js?v=19"></script><!-- application logic -->
38 38
     <script src="commands.js?v=1"></script><!-- application logic -->
39 39
     <script src="chat.js?v=13"></script><!-- chat logic -->
40 40
     <script src="contact_list.js?v=5"></script><!-- contact list logic -->
@@ -47,7 +47,7 @@
47 47
     <script src="analytics.js?v=1"></script><!-- google analytics plugin -->
48 48
     <script src="rtp_sts.js?v=4"></script><!-- RTP stats processing -->
49 49
     <script src="local_sts.js?v=2"></script><!-- Local stats processing -->
50
-    <script src="videolayout.js?v=22"></script><!-- video ui -->
50
+    <script src="videolayout.js?v=23"></script><!-- video ui -->
51 51
     <script src="connectionquality.js?v=1"></script>
52 52
     <script src="toolbar.js?v=6"></script><!-- toolbar ui -->
53 53
     <script src="toolbar_toggler.js?v=2"></script>
@@ -60,7 +60,7 @@
60 60
     <script src="tracking.js?v=1"></script><!-- tracking -->
61 61
     <script src="jitsipopover.js?v=3"></script>
62 62
     <script src="message_handler.js?v=1"></script>
63
-    <script src="api_connector.js?v=1"></script>
63
+    <script src="api_connector.js?v=2"></script>
64 64
     <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
65 65
     <link rel="stylesheet" href="css/font.css?v=5"/>
66 66
     <link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=28"/>

+ 10
- 1
muc.js Dosyayı Görüntüle

@@ -201,6 +201,10 @@ Strophe.addConnectionPlugin('emuc', {
201 201
             msg.c('nick', {xmlns: 'http://jabber.org/protocol/nick'}).t(nickname).up().up();
202 202
         }
203 203
         this.connection.send(msg);
204
+        if(APIConnector.isEnabled() && APIConnector.isEventEnabled("outgoingMessage"))
205
+        {
206
+            APIConnector.triggerEvent("outgoingMessage", {"message": body});
207
+        }
204 208
     },
205 209
     setSubject: function (subject){
206 210
         var msg = $msg({to: this.roomjid, type: 'groupchat'});
@@ -234,8 +238,13 @@ Strophe.addConnectionPlugin('emuc', {
234 238
 
235 239
         if (txt) {
236 240
             console.log('chat', nick, txt);
237
-
238 241
             Chat.updateChatConversation(from, nick, txt);
242
+            if(APIConnector.isEnabled() && APIConnector.isEventEnabled("incommingMessage"))
243
+            {
244
+                if(from != this.myroomjid)
245
+                    APIConnector.triggerEvent("incommingMessage",
246
+                        {"from": from, "nick": nick, "message": txt});
247
+            }
239 248
         }
240 249
         return true;
241 250
     },

+ 11
- 1
videolayout.js Dosyayı Görüntüle

@@ -1274,18 +1274,28 @@ var VideoLayout = (function (my) {
1274 1274
      */
1275 1275
     $(document).bind('displaynamechanged',
1276 1276
                     function (event, jid, displayName, status) {
1277
+        var name = null;
1277 1278
         if (jid === 'localVideoContainer'
1278 1279
             || jid === connection.emuc.myroomjid) {
1280
+            name = nickname;
1279 1281
             setDisplayName('localVideoContainer',
1280 1282
                            displayName);
1281 1283
         } else {
1282 1284
             VideoLayout.ensurePeerContainerExists(jid);
1283
-
1285
+            name = $('#participant_' + Strophe.getResourceFromJid(jid) + "_name").text();
1284 1286
             setDisplayName(
1285 1287
                 'participant_' + Strophe.getResourceFromJid(jid),
1286 1288
                 displayName,
1287 1289
                 status);
1288 1290
         }
1291
+
1292
+        if(APIConnector.isEnabled() && APIConnector.isEventEnabled("displayNameChange"))
1293
+        {
1294
+            if(jid === 'localVideoContainer')
1295
+                jid = connection.emuc.myroomjid;
1296
+            if(!name || name != displayName)
1297
+                APIConnector.triggerEvent("displayNameChange",{jid: jid, displayname: displayName});
1298
+        }
1289 1299
     });
1290 1300
 
1291 1301
     /**

Loading…
İptal
Kaydet