Browse Source

Fixes join and leave methods. Adds communication with jicofo. Implements custom commands methods.

dev1
hristoterezov 10 years ago
parent
commit
e9cf303fa6

+ 44
- 18
JitsiConference.js View File

1
-var room = null;
2
-
3
 
1
 
4
 /**
2
 /**
5
  * Creates a JitsiConference object with the given name and properties.
3
  * Creates a JitsiConference object with the given name and properties.
15
     this.options = options;
13
     this.options = options;
16
     this.connection = this.options.connection;
14
     this.connection = this.options.connection;
17
     this.xmpp = this.connection.xmpp;
15
     this.xmpp = this.connection.xmpp;
16
+    this.room = this.xmpp.createRoom(this.options.name, null, null);
18
 }
17
 }
19
 
18
 
20
 /**
19
 /**
21
  * Joins the conference.
20
  * Joins the conference.
21
+ * @param password {string} the password
22
  */
22
  */
23
-JitsiConference.prototype.join = function () {
24
-    room = this.xmpp.joinRoom(this.options.name, null, null);
23
+JitsiConference.prototype.join = function (password) {
24
+
25
+    this.room.joinRoom(password);
25
 }
26
 }
26
 
27
 
27
 /**
28
 /**
29
  */
30
  */
30
 JitsiConference.prototype.leave = function () {
31
 JitsiConference.prototype.leave = function () {
31
     this.xmpp.leaveRoom(room.roomjid);
32
     this.xmpp.leaveRoom(room.roomjid);
32
-    room = null;
33
+    this.room = null;
33
 }
34
 }
34
 
35
 
35
 /**
36
 /**
60
  * Note: consider adding eventing functionality by extending an EventEmitter impl, instead of rolling ourselves
61
  * Note: consider adding eventing functionality by extending an EventEmitter impl, instead of rolling ourselves
61
  */
62
  */
62
 JitsiConference.prototype.on = function (eventId, handler) {
63
 JitsiConference.prototype.on = function (eventId, handler) {
63
-    this.xmpp.addListener(eventId, handler);
64
+    this.add.addListener(eventId, handler);
64
 }
65
 }
65
 
66
 
66
 /**
67
 /**
71
  * Note: consider adding eventing functionality by extending an EventEmitter impl, instead of rolling ourselves
72
  * Note: consider adding eventing functionality by extending an EventEmitter impl, instead of rolling ourselves
72
  */
73
  */
73
 JitsiConference.prototype.off = function (eventId, handler) {
74
 JitsiConference.prototype.off = function (eventId, handler) {
74
-    this.xmpp.removeListener(event, listener);
75
+    this.room.removeListener(eventId, listener);
75
 }
76
 }
76
 
77
 
77
 // Common aliases for event emitter
78
 // Common aliases for event emitter
84
  * @param handler {Function} handler for the command
85
  * @param handler {Function} handler for the command
85
  */
86
  */
86
  JitsiConference.prototype.addCommandListener = function (command, handler) {
87
  JitsiConference.prototype.addCommandListener = function (command, handler) {
87
-
88
+     this.room.addPresenceListener(command, handler);
88
  }
89
  }
89
 
90
 
90
 /**
91
 /**
92
   * @param command {String}  the name of the command
93
   * @param command {String}  the name of the command
93
   */
94
   */
94
  JitsiConference.prototype.removeCommandListener = function (command) {
95
  JitsiConference.prototype.removeCommandListener = function (command) {
95
-
96
+    this.room.removePresenceListener(command);
96
  }
97
  }
97
 
98
 
98
 /**
99
 /**
100
  * @param message the text message.
101
  * @param message the text message.
101
  */
102
  */
102
 JitsiConference.prototype.sendTextMessage = function (message) {
103
 JitsiConference.prototype.sendTextMessage = function (message) {
103
-    room.send
104
+    this.room.sendMessage(message);
104
 }
105
 }
105
 
106
 
106
 /**
107
 /**
107
  * Send presence command.
108
  * Send presence command.
108
  * @param name the name of the command.
109
  * @param name the name of the command.
109
  * @param values Object with keys and values that will be send.
110
  * @param values Object with keys and values that will be send.
110
- * @param persistent if false the command will be sent only one time
111
- * @param successCallback will be called when the command is successfully send.
112
- * @param errorCallback will be called when the command is not sent successfully.
113
- * @returns {Promise.<{void}, JitsiConferenceError>} A promise that returns an array of created streams if resolved,
114
- *     or an JitsiConferenceError if rejected.
115
- */
116
-JitsiConference.prototype.sendCommand = function (name, values, persistent) {
111
+ **/
112
+JitsiConference.prototype.sendCommand = function (name, values) {
113
+    this.room.addToPresence(name, values);
114
+    this.room.sendPresence();
115
+}
116
+
117
+/**
118
+ * Send presence command one time.
119
+ * @param name the name of the command.
120
+ * @param values Object with keys and values that will be send.
121
+ **/
122
+JitsiConference.prototype.sendCommandOnce = function (name, values) {
123
+    this.sendCommand(name, values);
124
+    this.removeCommand(name);
125
+}
117
 
126
 
127
+/**
128
+ * Send presence command.
129
+ * @param name the name of the command.
130
+ * @param values Object with keys and values that will be send.
131
+ * @param persistent if false the command will be sent only one time
132
+ **/
133
+JitsiConference.prototype.removeCommand = function (name) {
134
+    this.room.removeFromPresence(name);
118
 }
135
 }
119
 
136
 
120
 /**
137
 /**
122
  * @param name the display name to set
139
  * @param name the display name to set
123
  */
140
  */
124
 JitsiConference.prototype.setDisplayName = function(name) {
141
 JitsiConference.prototype.setDisplayName = function(name) {
125
-    room.addToPresence("nick", {attributes: {xmlns: 'http://jabber.org/protocol/nick'}, value: name});
142
+    this.room.addToPresence("nick", {attributes: {xmlns: 'http://jabber.org/protocol/nick'}, value: name});
126
 }
143
 }
127
 
144
 
128
 /**
145
 /**
141
 
158
 
142
 }
159
 }
143
 
160
 
161
+/**
162
+ * @returns {JitsiParticipant} the participant in this conference with the specified id (or
163
+ * null if there isn't one).
164
+ * @param id the id of the participant.
165
+ */
166
+JitsiConference.prototype.getParticipantById = function(id) {
167
+
168
+}
169
+
144
 
170
 
145
 module.exports = JitsiConference;
171
 module.exports = JitsiConference;

+ 9
- 1
JitsiConferenceEvents.js View File

62
     /**
62
     /**
63
      * Indicates that the connection to the conference has been restored.
63
      * Indicates that the connection to the conference has been restored.
64
      */
64
      */
65
-    CONNECTION_ESTABLISHED: "conference.connecionEstablished"
65
+    CONNECTION_RESTORED: "conference.connecionRestored",
66
+    /**
67
+     * Indicates that conference has been joined.
68
+     */
69
+    CONFERENCE_JOINED: "conference.joined",
70
+    /**
71
+     * Indicates that conference has been left.
72
+     */
73
+    CONFERENCE_LEFT: "conference.left"
66
 };
74
 };
67
 
75
 
68
 module.exports = JitsiConferenceEvents;
76
 module.exports = JitsiConferenceEvents;

+ 3
- 6
JitsiConnection.js View File

1
 var JitsiConference = require("./JitsiConference");
1
 var JitsiConference = require("./JitsiConference");
2
 var XMPP = require("./modules/xmpp/xmpp");
2
 var XMPP = require("./modules/xmpp/xmpp");
3
 
3
 
4
-function wrapper()
5
-{
6
-    var jitsiconnectioninstance = new JitsiConnection();
7
-    this.a = jitsiconnectioninstance.a();
8
-}
9
 /**
4
 /**
10
  * Creates new connection object for the Jitsi Meet server side video conferencing service. Provides access to the
5
  * Creates new connection object for the Jitsi Meet server side video conferencing service. Provides access to the
11
  * JitsiConference interface.
6
  * JitsiConference interface.
20
     this.token = token;
15
     this.token = token;
21
     this.options = options;
16
     this.options = options;
22
     this.xmpp = new XMPP(options);
17
     this.xmpp = new XMPP(options);
18
+    this.conferences = {};
23
 }
19
 }
24
 
20
 
25
 /**
21
 /**
54
  * @returns {JitsiConference} returns the new conference object.
50
  * @returns {JitsiConference} returns the new conference object.
55
  */
51
  */
56
 JitsiConnection.prototype.initJitsiConference = function (name, options) {
52
 JitsiConnection.prototype.initJitsiConference = function (name, options) {
57
-    return new JitsiConference({name: name, config: options, connection: this});
53
+    this.conferences[name] = new JitsiConference({name: name, config: options, connection: this});
54
+    return this.conferences[name];
58
 }
55
 }
59
 
56
 
60
 /**
57
 /**

+ 135
- 0
JitsiParticipant.js View File

1
+/**
2
+ * Represents a participant in (a member of) a conference.
3
+ */
4
+function JitsiParticipant(){
5
+
6
+}
7
+
8
+/**
9
+ * @returns {JitsiConference} The conference that this participant belongs to.
10
+ */
11
+JitsiParticipant.prototype.getConference = function() {
12
+
13
+}
14
+
15
+/**
16
+ * @returns {Array.<JitsiTrack>} The list of media tracks for this participant.
17
+ */
18
+JitsiParticipant.prototype.getTracks = function() {
19
+
20
+}
21
+
22
+/**
23
+ * @returns {String} The ID (i.e. JID) of this participant.
24
+ */
25
+JitsiParticipant.prototype.getId = function() {
26
+
27
+}
28
+
29
+/**
30
+ * @returns {String} The human-readable display name of this participant.
31
+ */
32
+JitsiParticipant.prototype.getDisplayName = function() {
33
+
34
+}
35
+
36
+/**
37
+ * @returns {Boolean} Whether this participant is a moderator or not.
38
+ */
39
+JitsiParticipant.prototype.isModerator = function() {
40
+
41
+}
42
+
43
+// Gets a link to an etherpad instance advertised by the participant?
44
+//JitsiParticipant.prototype.getEtherpad = function() {
45
+//
46
+//}
47
+
48
+
49
+/*
50
+ * @returns {Boolean} Whether this participant has muted their audio.
51
+ */
52
+JitsiParticipant.prototype.isAudioMuted = function() {
53
+
54
+}
55
+
56
+/*
57
+ * @returns {Boolean} Whether this participant has muted their video.
58
+ */
59
+JitsiParticipant.prototype.isVideoMuted = function() {
60
+
61
+}
62
+
63
+/*
64
+ * @returns {???} The latest statistics reported by this participant (i.e. info used to populate the GSM bars)
65
+ * TODO: do we expose this or handle it internally?
66
+ */
67
+JitsiParticipant.prototype.getLatestStats = function() {
68
+
69
+}
70
+
71
+/**
72
+ * @returns {String} The role of this participant.
73
+ */
74
+JitsiParticipant.prototype.getRole = function() {
75
+
76
+}
77
+
78
+/*
79
+ * @returns {Boolean} Whether this participant is the conference focus (i.e. jicofo).
80
+ */
81
+JitsiParticipant.prototype.isFocus = function() {
82
+
83
+}
84
+
85
+/*
86
+ * @returns {Boolean} Whether this participant is a conference recorder (i.e. jirecon).
87
+ */
88
+JitsiParticipant.prototype.isRecorder = function() {
89
+
90
+}
91
+
92
+/*
93
+ * @returns {Boolean} Whether this participant is a SIP gateway (i.e. jigasi).
94
+ */
95
+JitsiParticipant.prototype.isSipGateway = function() {
96
+
97
+}
98
+
99
+/**
100
+ * @returns {String} The ID for this participant's avatar.
101
+ */
102
+JitsiParticipant.prototype.getAvatarId = function() {
103
+
104
+}
105
+
106
+/**
107
+ * @returns {Boolean} Whether this participant is currently sharing their screen.
108
+ */
109
+JitsiParticipant.prototype.isScreenSharing = function() {
110
+
111
+}
112
+
113
+/**
114
+ * @returns {String} The user agent of this participant (i.e. browser userAgent string).
115
+ */
116
+JitsiParticipant.prototype.getUserAgent = function() {
117
+
118
+}
119
+
120
+/**
121
+ * Kicks the participant from the conference (requires certain privileges).
122
+ */
123
+JitsiParticipant.prototype.kick = function() {
124
+
125
+}
126
+
127
+/**
128
+ * Asks this participant to mute themselves.
129
+ */
130
+JitsiParticipant.prototype.askToMute = function() {
131
+
132
+}
133
+
134
+
135
+module.exports = JitsiParticipant();

+ 1
- 1
JitsiTrack.js View File

26
 };
26
 };
27
 
27
 
28
 /**
28
 /**
29
- * Returns the JitsiParticipant to which this track belongs, or null if it is a local track.
29
+ * @returns {JitsiParticipant} to which this track belongs, or null if it is a local track.
30
  */
30
  */
31
 JitsiTrack.prototype.getParitcipant() = function() {
31
 JitsiTrack.prototype.getParitcipant() = function() {
32
 
32
 

+ 14152
- 8072
lib-jitsi-meet.js
File diff suppressed because it is too large
View File


+ 5492
- 0
libs/strophe/strophe.js
File diff suppressed because it is too large
View File


+ 14
- 6
modules/settings/Settings.js View File

21
     this.userId;
21
     this.userId;
22
     this.language = null;
22
     this.language = null;
23
     this.confSettings = null;
23
     this.confSettings = null;
24
+    this.conferenceID = conferenceID;
24
     if (supportsLocalStorage()) {
25
     if (supportsLocalStorage()) {
25
-        if(!window.localStorage.jitsiConferences)
26
-            window.localStorage.jitsiConferences = {}
27
-        if (!window.localStorage.jitsiConferences[conferenceID]) {
28
-            window.localStorage.jitsiConferences[conferenceID] = {}
29
-        }
30
-        this.confSettings = window.localStorage.jitsiConferences[conferenceID];
26
+        if(!window.localStorage.getItem(conferenceID))
27
+            this.confSettings = {};
28
+        else
29
+            this.confSettings = JSON.parse(window.localStorage.getItem(conferenceID));
31
         if(!this.confSettings.jitsiMeetId) {
30
         if(!this.confSettings.jitsiMeetId) {
32
             this.confSettings.jitsiMeetId = generateUniqueId();
31
             this.confSettings.jitsiMeetId = generateUniqueId();
33
             console.log("generated id",
32
             console.log("generated id",
34
                 this.confSettings.jitsiMeetId);
33
                 this.confSettings.jitsiMeetId);
34
+            this.save();
35
         }
35
         }
36
         this.userId = this.confSettings.jitsiMeetId || '';
36
         this.userId = this.confSettings.jitsiMeetId || '';
37
         this.email = this.confSettings.email || '';
37
         this.email = this.confSettings.email || '';
43
     }
43
     }
44
 }
44
 }
45
 
45
 
46
+Settings.prototype.save = function () {
47
+    if(!supportsLocalStorage())
48
+        window.localStorage.setItem(this.conferenceID, JSON.stringify(this.confSettings));
49
+}
50
+
46
 Settings.prototype.setDisplayName = function (newDisplayName) {
51
 Settings.prototype.setDisplayName = function (newDisplayName) {
47
     this.displayName = newDisplayName;
52
     this.displayName = newDisplayName;
48
     if(this.confSettings != null)
53
     if(this.confSettings != null)
49
         this.confSettings.displayname = displayName;
54
         this.confSettings.displayname = displayName;
55
+    this.save();
50
     return this.displayName;
56
     return this.displayName;
51
 },
57
 },
52
 Settings.prototype.setEmail = function (newEmail) {
58
 Settings.prototype.setEmail = function (newEmail) {
53
     this.email = newEmail;
59
     this.email = newEmail;
54
     if(this.confSettings != null)
60
     if(this.confSettings != null)
55
         this.confSettings.email = newEmail;
61
         this.confSettings.email = newEmail;
62
+    this.save();
56
     return this.email;
63
     return this.email;
57
 },
64
 },
58
 Settings.prototype.getSettings = function () {
65
 Settings.prototype.getSettings = function () {
67
     this.language = lang;
74
     this.language = lang;
68
     if(this.confSettings != null)
75
     if(this.confSettings != null)
69
         this.confSettings.language = lang;
76
         this.confSettings.language = lang;
77
+    this.save();
70
 }
78
 }
71
 
79
 
72
 module.exports = Settings;
80
 module.exports = Settings;

+ 14
- 10
modules/xmpp/JingleSessionPC.js View File

9
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
9
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
10
 var RTCBrowserType = require("../RTC/RTCBrowserType");
10
 var RTCBrowserType = require("../RTC/RTCBrowserType");
11
 var SSRCReplacement = require("./LocalSSRCReplacement");
11
 var SSRCReplacement = require("./LocalSSRCReplacement");
12
+var RTC = require("../RTC/RTC");
12
 
13
 
13
 // Jingle stuff
14
 // Jingle stuff
14
 function JingleSessionPC(me, sid, connection, service, eventEmitter) {
15
 function JingleSessionPC(me, sid, connection, service, eventEmitter) {
86
 
87
 
87
     this.peerconnection = new TraceablePeerConnection(
88
     this.peerconnection = new TraceablePeerConnection(
88
             this.connection.jingle.ice_config,
89
             this.connection.jingle.ice_config,
89
-            APP.RTC.getPCConstraints(),
90
+            RTC.getPCConstraints(),
90
             this);
91
             this);
91
 
92
 
92
     this.peerconnection.onicecandidate = function (event) {
93
     this.peerconnection.onicecandidate = function (event) {
147
     this.peerconnection.onnegotiationneeded = function (event) {
148
     this.peerconnection.onnegotiationneeded = function (event) {
148
         self.eventEmitter.emit(XMPPEvents.PEERCONNECTION_READY, self);
149
         self.eventEmitter.emit(XMPPEvents.PEERCONNECTION_READY, self);
149
     };
150
     };
150
-    // add any local and relayed stream
151
-    APP.RTC.localStreams.forEach(function(stream) {
152
-        self.peerconnection.addStream(stream.getOriginalStream());
153
-    });
151
+
154
     this.relayedStreams.forEach(function(stream) {
152
     this.relayedStreams.forEach(function(stream) {
155
         self.peerconnection.addStream(stream);
153
         self.peerconnection.addStream(stream);
156
     });
154
     });
157
 };
155
 };
158
 
156
 
157
+JingleSessionPC.prototype.addLocalStreams = function (localStreams) {
158
+    var self = this;
159
+// add any local and relayed stream
160
+    localStreams.forEach(function(stream) {
161
+        self.peerconnection.addStream(stream.getOriginalStream());
162
+    });
163
+}
164
+
159
 function onIceConnectionStateChange(sid, session) {
165
 function onIceConnectionStateChange(sid, session) {
160
     switch (session.peerconnection.iceConnectionState) {
166
     switch (session.peerconnection.iceConnectionState) {
161
         case 'checking':
167
         case 'checking':
1307
 
1313
 
1308
 JingleSessionPC.onJingleFatalError = function (session, error)
1314
 JingleSessionPC.onJingleFatalError = function (session, error)
1309
 {
1315
 {
1310
-    this.service.sessionTerminated = true;
1311
-    this.connection.emuc.doLeave();
1312
     this.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
1316
     this.eventEmitter.emit(XMPPEvents.CONFERENCE_SETUP_FAILED);
1313
     this.eventEmitter.emit(XMPPEvents.JINGLE_FATAL_ERROR, session, error);
1317
     this.eventEmitter.emit(XMPPEvents.JINGLE_FATAL_ERROR, session, error);
1314
 }
1318
 }
1391
 JingleSessionPC.prototype.remoteStreamAdded = function (data, times) {
1395
 JingleSessionPC.prototype.remoteStreamAdded = function (data, times) {
1392
     var self = this;
1396
     var self = this;
1393
     var thessrc;
1397
     var thessrc;
1394
-    var streamId = APP.RTC.getStreamID(data.stream);
1398
+    var streamId = RTC.getStreamID(data.stream);
1395
 
1399
 
1396
     // look up an associated JID for a stream id
1400
     // look up an associated JID for a stream id
1397
     if (!streamId) {
1401
     if (!streamId) {
1426
         }
1430
         }
1427
     }
1431
     }
1428
 
1432
 
1429
-    APP.RTC.createRemoteStream(data, this.sid, thessrc);
1433
+    RTC.createRemoteStream(data, this.sid, thessrc);
1430
 
1434
 
1431
     var isVideo = data.stream.getVideoTracks().length > 0;
1435
     var isVideo = data.stream.getVideoTracks().length > 0;
1432
     // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
1436
     // an attempt to work around https://github.com/jitsi/jitmeet/issues/32
1433
     if (isVideo &&
1437
     if (isVideo &&
1434
         data.peerjid && this.peerjid === data.peerjid &&
1438
         data.peerjid && this.peerjid === data.peerjid &&
1435
         data.stream.getVideoTracks().length === 0 &&
1439
         data.stream.getVideoTracks().length === 0 &&
1436
-        APP.RTC.localVideo.getTracks().length > 0) {
1440
+        RTC.localVideo.getTracks().length > 0) {
1437
         window.setTimeout(function () {
1441
         window.setTimeout(function () {
1438
             sendKeyframe(self.peerconnection);
1442
             sendKeyframe(self.peerconnection);
1439
         }, 3000);
1443
         }, 3000);

+ 354
- 367
modules/xmpp/moderator.js View File

6
 var AuthenticationEvents
6
 var AuthenticationEvents
7
     = require("../../service/authentication/AuthenticationEvents");
7
     = require("../../service/authentication/AuthenticationEvents");
8
 
8
 
9
-/**
10
- * Contains logic responsible for enabling/disabling functionality available
11
- * only to moderator users.
12
- */
13
-var connection = null;
14
-var focusUserJid;
15
-
16
 function createExpBackoffTimer(step) {
9
 function createExpBackoffTimer(step) {
17
     var count = 1;
10
     var count = 1;
18
     return function (reset) {
11
     return function (reset) {
28
     };
21
     };
29
 }
22
 }
30
 
23
 
31
-var getNextTimeout = createExpBackoffTimer(1000);
32
-var getNextErrorTimeout = createExpBackoffTimer(1000);
24
+
25
+
26
+
27
+
28
+function Moderator(roomName, xmpp, emitter) {
29
+    this.roomName = roomName;
30
+    this.xmppService = xmpp;
31
+    this.getNextTimeout = createExpBackoffTimer(1000);
32
+    this.getNextErrorTimeout = createExpBackoffTimer(1000);
33
 // External authentication stuff
33
 // External authentication stuff
34
-var externalAuthEnabled = false;
34
+    this.externalAuthEnabled = false;
35
+    this.settings = new Settings(roomName);
35
 // Sip gateway can be enabled by configuring Jigasi host in config.js or
36
 // Sip gateway can be enabled by configuring Jigasi host in config.js or
36
 // it will be enabled automatically if focus detects the component through
37
 // it will be enabled automatically if focus detects the component through
37
 // service discovery.
38
 // service discovery.
38
-var sipGatewayEnabled = null;
39
-
40
-var eventEmitter = null;
41
-
42
-var Moderator = {
43
-    isModerator: function () {
44
-        return connection && connection.emuc.isModerator();
45
-    },
46
-
47
-    isPeerModerator: function (peerJid) {
48
-        return connection &&
49
-            connection.emuc.getMemberRole(peerJid) === 'moderator';
50
-    },
51
-
52
-    isExternalAuthEnabled: function () {
53
-        return externalAuthEnabled;
54
-    },
55
-
56
-    isSipGatewayEnabled: function () {
57
-        return sipGatewayEnabled;
58
-    },
59
-
60
-    setConnection: function (con) {
61
-        connection = con;
62
-    },
63
-
64
-    init: function (xmpp, emitter) {
65
-        this.xmppService = xmpp;
66
-        sipGatewayEnabled = this.xmppService.options.hosts.call_control !== undefined;
67
-        eventEmitter = emitter;
68
-
69
-        // Message listener that talks to POPUP window
70
-        function listener(event) {
71
-            if (event.data && event.data.sessionId) {
72
-                if (event.origin !== window.location.origin) {
73
-                    console.warn("Ignoring sessionId from different origin: " +
74
-                        event.origin);
75
-                    return;
76
-                }
77
-                localStorage.setItem('sessionId', event.data.sessionId);
78
-                // After popup is closed we will authenticate
39
+    this.sipGatewayEnabled = this.xmppService.options.hosts.call_control !== undefined;
40
+
41
+    this.eventEmitter = emitter;
42
+
43
+    this.connection = this.xmppService.connection;
44
+    this.focusUserJid;
45
+    //FIXME:
46
+    // Message listener that talks to POPUP window
47
+    function listener(event) {
48
+        if (event.data && event.data.sessionId) {
49
+            if (event.origin !== window.location.origin) {
50
+                console.warn("Ignoring sessionId from different origin: " +
51
+                    event.origin);
52
+                return;
79
             }
53
             }
54
+            localStorage.setItem('sessionId', event.data.sessionId);
55
+            // After popup is closed we will authenticate
80
         }
56
         }
81
-        // Register
82
-        if (window.addEventListener) {
83
-            window.addEventListener("message", listener, false);
84
-        } else {
85
-            window.attachEvent("onmessage", listener);
86
-        }
87
-    },
88
-
89
-    onMucMemberLeft: function (jid) {
90
-        console.info("Someone left is it focus ? " + jid);
91
-        var resource = Strophe.getResourceFromJid(jid);
92
-        if (resource === 'focus' && !this.xmppService.sessionTerminated) {
93
-            console.info(
94
-                "Focus has left the room - leaving conference");
95
-            //hangUp();
96
-            // We'd rather reload to have everything re-initialized
97
-            // FIXME: show some message before reload
98
-            location.reload();
99
-        }
100
-    },
101
-    
102
-    setFocusUserJid: function (focusJid) {
103
-        if (!focusUserJid) {
104
-            focusUserJid = focusJid;
105
-            console.info("Focus jid set to: " + focusUserJid);
106
-        }
107
-    },
108
-
109
-    getFocusUserJid: function () {
110
-        return focusUserJid;
111
-    },
112
-
113
-    getFocusComponent: function () {
114
-        // Get focus component address
115
-        var focusComponent = this.xmppService.options.hosts.focus;
116
-        // If not specified use default: 'focus.domain'
117
-        if (!focusComponent) {
118
-            focusComponent = 'focus.' + this.xmppService.options.hosts.domain;
119
-        }
120
-        return focusComponent;
121
-    },
57
+    }
58
+    // Register
59
+    if (window.addEventListener) {
60
+        window.addEventListener("message", listener, false);
61
+    } else {
62
+        window.attachEvent("onmessage", listener);
63
+    }
64
+}
65
+
66
+Moderator.prototype.isExternalAuthEnabled =  function () {
67
+    return this.externalAuthEnabled;
68
+};
122
 
69
 
123
-    createConferenceIq: function (roomName) {
124
-        // Generate create conference IQ
125
-        var elem = $iq({to: Moderator.getFocusComponent(), type: 'set'});
70
+Moderator.prototype.isSipGatewayEnabled =  function () {
71
+    return this.sipGatewayEnabled;
72
+};
126
 
73
 
127
-        // Session Id used for authentication
128
-        var sessionId = localStorage.getItem('sessionId');
129
-        var machineUID = Settings.getSettings().uid;
130
 
74
 
75
+Moderator.prototype.onMucMemberLeft =  function (jid) {
76
+    console.info("Someone left is it focus ? " + jid);
77
+    var resource = Strophe.getResourceFromJid(jid);
78
+    if (resource === 'focus' && !this.xmppService.sessionTerminated) {
131
         console.info(
79
         console.info(
132
-            "Session ID: " + sessionId + " machine UID: " + machineUID);
80
+            "Focus has left the room - leaving conference");
81
+        //hangUp();
82
+        // We'd rather reload to have everything re-initialized
83
+        //FIXME: show some message before reload
84
+        this.eventEmitter.emit(XMPPEvents.FOCUS_LEFT);
85
+    }
86
+};
133
 
87
 
134
-        elem.c('conference', {
135
-            xmlns: 'http://jitsi.org/protocol/focus',
136
-            room: roomName,
137
-            'machine-uid': machineUID
138
-        });
139
 
88
 
140
-        if (sessionId) {
141
-            elem.attrs({ 'session-id': sessionId});
142
-        }
89
+Moderator.prototype.setFocusUserJid =  function (focusJid) {
90
+    if (!this.focusUserJid) {
91
+        this.focusUserJid = focusJid;
92
+        console.info("Focus jid set to:  " + this.focusUserJid);
93
+    }
94
+};
143
 
95
 
144
-        if (this.xmppService.options.hosts.bridge !== undefined) {
145
-            elem.c(
146
-                'property',
147
-                { name: 'bridge', value: this.xmppService.options.hosts.bridge})
148
-                .up();
149
-        }
150
-        // Tell the focus we have Jigasi configured
151
-        if (this.xmppService.options.hosts.call_control !== undefined) {
152
-            elem.c(
153
-                'property',
154
-                { name: 'call_control', value: this.xmppService.options.hosts.call_control})
155
-                .up();
156
-        }
157
-        if (this.xmppService.options.channelLastN !== undefined) {
158
-            elem.c(
159
-                'property',
160
-                { name: 'channelLastN', value: this.xmppService.options.channelLastN})
161
-                .up();
162
-        }
163
-        if (this.xmppService.options.adaptiveLastN !== undefined) {
164
-            elem.c(
165
-                'property',
166
-                { name: 'adaptiveLastN', value: this.xmppService.options.adaptiveLastN})
167
-                .up();
168
-        }
169
-        if (this.xmppService.options.adaptiveSimulcast !== undefined) {
170
-            elem.c(
171
-                'property',
172
-                { name: 'adaptiveSimulcast', value: this.xmppService.options.adaptiveSimulcast})
173
-                .up();
174
-        }
175
-        if (this.xmppService.options.openSctp !== undefined) {
176
-            elem.c(
177
-                'property',
178
-                { name: 'openSctp', value: this.xmppService.options.openSctp})
179
-                .up();
180
-        }
181
-        if(this.xmppService.options.startAudioMuted !== undefined)
182
-        {
183
-            elem.c(
184
-                'property',
185
-                { name: 'startAudioMuted', value: this.xmppService.options.startAudioMuted})
186
-                .up();
187
-        }
188
-        if(this.xmppService.options.startVideoMuted !== undefined)
189
-        {
190
-            elem.c(
191
-                'property',
192
-                { name: 'startVideoMuted', value: this.xmppService.options.startVideoMuted})
193
-                .up();
194
-        }
96
+
97
+Moderator.prototype.getFocusUserJid =  function () {
98
+    return this.focusUserJid;
99
+};
100
+
101
+Moderator.prototype.getFocusComponent =  function () {
102
+    // Get focus component address
103
+    var focusComponent = this.xmppService.options.hosts.focus;
104
+    // If not specified use default:  'focus.domain'
105
+    if (!focusComponent) {
106
+        focusComponent = 'focus.' + this.xmppService.options.hosts.domain;
107
+    }
108
+    return focusComponent;
109
+};
110
+
111
+Moderator.prototype.createConferenceIq =  function () {
112
+    // Generate create conference IQ
113
+    var elem = $iq({to: this.getFocusComponent(), type: 'set'});
114
+
115
+    // Session Id used for authentication
116
+    var sessionId = localStorage.getItem('sessionId');
117
+    var machineUID = this.settings.getSettings().uid;
118
+
119
+    console.info(
120
+            "Session ID: " + sessionId + " machine UID: " + machineUID);
121
+
122
+    elem.c('conference', {
123
+        xmlns: 'http://jitsi.org/protocol/focus',
124
+        room: this.roomName,
125
+        'machine-uid': machineUID
126
+    });
127
+
128
+    if (sessionId) {
129
+        elem.attrs({ 'session-id': sessionId});
130
+    }
131
+    if (this.xmppService.options.hosts.bridge !== undefined) {
195
         elem.c(
132
         elem.c(
196
             'property',
133
             'property',
197
-            { name: 'simulcastMode', value: 'rewriting'})
134
+            {name: 'bridge',value: this.xmppService.options.hosts.bridge})
198
             .up();
135
             .up();
199
-        elem.up();
200
-        return elem;
201
-    },
202
-
203
-    parseSessionId: function (resultIq) {
204
-        var sessionId = $(resultIq).find('conference').attr('session-id');
205
-        if (sessionId) {
206
-            console.info('Received sessionId: ' + sessionId);
207
-            localStorage.setItem('sessionId', sessionId);
208
-        }
209
-    },
136
+    }
137
+    // Tell the focus we have Jigasi configured
138
+    if (this.xmppService.options.hosts.call_control !== undefined) {
139
+        elem.c(
140
+            'property',
141
+            {name: 'call_control',value:  this.xmppService.options.hosts.call_control})
142
+            .up();
143
+    }
144
+    if (this.xmppService.options.channelLastN !== undefined) {
145
+        elem.c(
146
+            'property',
147
+            {name: 'channelLastN',value: this.xmppService.options.channelLastN})
148
+            .up();
149
+    }
150
+    if (this.xmppService.options.adaptiveLastN !== undefined) {
151
+        elem.c(
152
+            'property',
153
+            {name: 'adaptiveLastN',value: this.xmppService.options.adaptiveLastN})
154
+            .up();
155
+    }
156
+    if (this.xmppService.options.adaptiveSimulcast !== undefined) {
157
+        elem.c(
158
+            'property',
159
+            {name: 'adaptiveSimulcast',value: this.xmppService.options.adaptiveSimulcast})
160
+            .up();
161
+    }
162
+    if (this.xmppService.options.openSctp !== undefined) {
163
+        elem.c(
164
+            'property',
165
+            {name: 'openSctp',value: this.xmppService.options.openSctp})
166
+            .up();
167
+    }
168
+    if(this.xmppService.options.startAudioMuted !== undefined)
169
+    {
170
+        elem.c(
171
+            'property',
172
+            {name: 'startAudioMuted',value: this.xmppService.options.startAudioMuted})
173
+            .up();
174
+    }
175
+    if(this.xmppService.options.startVideoMuted !== undefined)
176
+    {
177
+        elem.c(
178
+            'property',
179
+            {name: 'startVideoMuted',value: this.xmppService.options.startVideoMuted})
180
+            .up();
181
+    }
182
+    elem.c(
183
+        'property',
184
+        {name: 'simulcastMode',value: 'rewriting'})
185
+        .up();
186
+    elem.up();
187
+    return elem;
188
+};
210
 
189
 
211
-    parseConfigOptions: function (resultIq) {
212
 
190
 
213
-        Moderator.setFocusUserJid(
214
-            $(resultIq).find('conference').attr('focusjid'));
191
+Moderator.prototype.parseSessionId =  function (resultIq) {
192
+    var sessionId = $(resultIq).find('conference').attr('session-id');
193
+    if (sessionId) {
194
+        console.info('Received sessionId:  ' + sessionId);
195
+        localStorage.setItem('sessionId', sessionId);
196
+    }
197
+};
215
 
198
 
216
-        var authenticationEnabled
217
-            = $(resultIq).find(
218
-                '>conference>property' +
219
-                '[name=\'authentication\'][value=\'true\']').length > 0;
199
+Moderator.prototype.parseConfigOptions =  function (resultIq) {
220
 
200
 
221
-        console.info("Authentication enabled: " + authenticationEnabled);
201
+    this.setFocusUserJid(
202
+        $(resultIq).find('conference').attr('focusjid'));
222
 
203
 
223
-        externalAuthEnabled = $(resultIq).find(
224
-                '>conference>property' +
225
-                '[name=\'externalAuth\'][value=\'true\']').length > 0;
204
+    var authenticationEnabled
205
+        = $(resultIq).find(
206
+            '>conference>property' +
207
+            '[name=\'authentication\'][value=\'true\']').length > 0;
226
 
208
 
227
-        console.info('External authentication enabled: ' + externalAuthEnabled);
209
+    console.info("Authentication enabled: " + authenticationEnabled);
228
 
210
 
229
-        if (!externalAuthEnabled) {
230
-            // We expect to receive sessionId in 'internal' authentication mode
231
-            Moderator.parseSessionId(resultIq);
232
-        }
211
+    this.externalAuthEnabled = $(resultIq).find(
212
+            '>conference>property' +
213
+            '[name=\'externalAuth\'][value=\'true\']').length > 0;
233
 
214
 
234
-        var authIdentity = $(resultIq).find('>conference').attr('identity');
215
+    console.info('External authentication enabled: ' + this.externalAuthEnabled);
235
 
216
 
236
-        eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,
237
-            authenticationEnabled, authIdentity);
238
-    
239
-        // Check if focus has auto-detected Jigasi component(this will be also
240
-        // included if we have passed our host from the config)
241
-        if ($(resultIq).find(
242
-            '>conference>property' +
243
-            '[name=\'sipGatewayEnabled\'][value=\'true\']').length) {
244
-            sipGatewayEnabled = true;
245
-        }
246
-    
247
-        console.info("Sip gateway enabled: " + sipGatewayEnabled);
248
-    },
249
-
250
-    // FIXME: we need to show the fact that we're waiting for the focus
251
-    // to the user(or that focus is not available)
252
-    allocateConferenceFocus: function (roomName, callback) {
253
-        // Try to use focus user JID from the config
254
-        Moderator.setFocusUserJid(this.xmppService.options.focusUserJid);
255
-        // Send create conference IQ
256
-        var iq = Moderator.createConferenceIq(roomName);
257
-        var self = this;
258
-        connection.sendIQ(
259
-            iq,
260
-            function (result) {
261
-
262
-                // Setup config options
263
-                Moderator.parseConfigOptions(result);
264
-
265
-                if ('true' === $(result).find('conference').attr('ready')) {
266
-                    // Reset both timers
267
-                    getNextTimeout(true);
268
-                    getNextErrorTimeout(true);
269
-                    // Exec callback
270
-                    callback();
271
-                } else {
272
-                    var waitMs = getNextTimeout();
273
-                    console.info("Waiting for the focus... " + waitMs);
274
-                    // Reset error timeout
275
-                    getNextErrorTimeout(true);
276
-                    window.setTimeout(
277
-                        function () {
278
-                            Moderator.allocateConferenceFocus(
279
-                                roomName, callback);
280
-                        }, waitMs);
281
-                }
282
-            },
283
-            function (error) {
284
-                // Invalid session ? remove and try again
285
-                // without session ID to get a new one
286
-                var invalidSession
287
-                    = $(error).find('>error>session-invalid').length;
288
-                if (invalidSession) {
289
-                    console.info("Session expired! - removing");
290
-                    localStorage.removeItem("sessionId");
291
-                }
292
-                if ($(error).find('>error>graceful-shutdown').length) {
293
-                    eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
294
-                    return;
295
-                }
296
-                // Check for error returned by the reservation system
297
-                var reservationErr = $(error).find('>error>reservation-error');
298
-                if (reservationErr.length) {
299
-                    // Trigger error event
300
-                    var errorCode = reservationErr.attr('error-code');
301
-                    var errorMsg;
302
-                    if ($(error).find('>error>text')) {
303
-                        errorMsg = $(error).find('>error>text').text();
304
-                    }
305
-                    eventEmitter.emit(
306
-                        XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
307
-                    return;
308
-                }
309
-                // Not authorized to create new room
310
-                if ($(error).find('>error>not-authorized').length) {
311
-                    console.warn("Unauthorized to start the conference", error);
312
-                    var toDomain
313
-                        = Strophe.getDomainFromJid(error.getAttribute('to'));
314
-                    if (toDomain !== this.xmppService.options.hosts.anonymousdomain) {
315
-                        // FIXME: "is external" should come either from
316
-                        // the focus or config.js
317
-                        externalAuthEnabled = true;
318
-                    }
319
-                    eventEmitter.emit(
320
-                        XMPPEvents.AUTHENTICATION_REQUIRED,
321
-                        function () {
322
-                            Moderator.allocateConferenceFocus(
323
-                                roomName, callback);
324
-                        });
325
-                    return;
326
-                }
327
-                var waitMs = getNextErrorTimeout();
328
-                console.error("Focus error, retry after " + waitMs, error);
329
-                // Show message
330
-                var focusComponent = Moderator.getFocusComponent();
331
-                var retrySec = waitMs / 1000;
332
-                // FIXME: message is duplicated ?
333
-                // Do not show in case of session invalid
334
-                // which means just a retry
335
-                if (!invalidSession) {
336
-                    eventEmitter.emit(XMPPEvents.FOCUS_DISCONNECTED,
337
-                        focusComponent, retrySec);
338
-                }
339
-                // Reset response timeout
340
-                getNextTimeout(true);
217
+    if (!this.externalAuthEnabled) {
218
+        // We expect to receive sessionId in 'internal' authentication mode
219
+        this.parseSessionId(resultIq);
220
+    }
221
+
222
+    var authIdentity = $(resultIq).find('>conference').attr('identity');
223
+
224
+    this.eventEmitter.emit(AuthenticationEvents.IDENTITY_UPDATED,
225
+        authenticationEnabled, authIdentity);
226
+
227
+    // Check if focus has auto-detected Jigasi component(this will be also
228
+    // included if we have passed our host from the config)
229
+    if ($(resultIq).find(
230
+        '>conference>property' +
231
+        '[name=\'sipGatewayEnabled\'][value=\'true\']').length) {
232
+        this.sipGatewayEnabled = true;
233
+    }
234
+
235
+    console.info("Sip gateway enabled:  " + this.sipGatewayEnabled);
236
+};
237
+
238
+// FIXME =  we need to show the fact that we're waiting for the focus
239
+// to the user(or that focus is not available)
240
+Moderator.prototype.allocateConferenceFocus =  function ( callback) {
241
+    // Try to use focus user JID from the config
242
+    this.setFocusUserJid(this.xmppService.options.focusUserJid);
243
+    // Send create conference IQ
244
+    var iq = this.createConferenceIq();
245
+    var self = this;
246
+    this.connection.sendIQ(
247
+        iq,
248
+        function (result) {
249
+
250
+            // Setup config options
251
+            self.parseConfigOptions(result);
252
+
253
+            if ('true' === $(result).find('conference').attr('ready')) {
254
+                // Reset both timers
255
+                self.getNextTimeout(true);
256
+                self.getNextErrorTimeout(true);
257
+                // Exec callback
258
+                callback();
259
+            } else {
260
+                var waitMs = self.getNextTimeout();
261
+                console.info("Waiting for the focus... " + waitMs);
262
+                // Reset error timeout
263
+                self.getNextErrorTimeout(true);
341
                 window.setTimeout(
264
                 window.setTimeout(
342
                     function () {
265
                     function () {
343
-                        Moderator.allocateConferenceFocus(roomName, callback);
266
+                        self.allocateConferenceFocus(callback);
344
                     }, waitMs);
267
                     }, waitMs);
345
             }
268
             }
346
-        );
347
-    },
348
-
349
-    getLoginUrl: function (roomName, urlCallback) {
350
-        var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
351
-        iq.c('login-url', {
352
-            xmlns: 'http://jitsi.org/protocol/focus',
353
-            room: roomName,
354
-            'machine-uid': Settings.getSettings().uid
355
-        });
356
-        connection.sendIQ(
357
-            iq,
358
-            function (result) {
359
-                var url = $(result).find('login-url').attr('url');
360
-                url = url = decodeURIComponent(url);
361
-                if (url) {
362
-                    console.info("Got auth url: " + url);
363
-                    urlCallback(url);
364
-                } else {
365
-                    console.error(
366
-                        "Failed to get auth url from the focus", result);
269
+        },
270
+        function (error) {
271
+            // Invalid session ? remove and try again
272
+            // without session ID to get a new one
273
+            var invalidSession
274
+                = $(error).find('>error>session-invalid').length;
275
+            if (invalidSession) {
276
+                console.info("Session expired! - removing");
277
+                localStorage.removeItem("sessionId");
278
+            }
279
+            if ($(error).find('>error>graceful-shutdown').length) {
280
+                self.eventEmitter.emit(XMPPEvents.GRACEFUL_SHUTDOWN);
281
+                return;
282
+            }
283
+            // Check for error returned by the reservation system
284
+            var reservationErr = $(error).find('>error>reservation-error');
285
+            if (reservationErr.length) {
286
+                // Trigger error event
287
+                var errorCode = reservationErr.attr('error-code');
288
+                var errorMsg;
289
+                if ($(error).find('>error>text')) {
290
+                    errorMsg = $(error).find('>error>text').text();
367
                 }
291
                 }
368
-            },
369
-            function (error) {
370
-                console.error("Get auth url error", error);
292
+                self.eventEmitter.emit(
293
+                    XMPPEvents.RESERVATION_ERROR, errorCode, errorMsg);
294
+                return;
371
             }
295
             }
372
-        );
373
-    },
374
-    getPopupLoginUrl: function (roomName, urlCallback) {
375
-        var iq = $iq({to: Moderator.getFocusComponent(), type: 'get'});
376
-        iq.c('login-url', {
377
-            xmlns: 'http://jitsi.org/protocol/focus',
378
-            room: roomName,
379
-            'machine-uid': Settings.getSettings().uid,
380
-            popup: true
381
-        });
382
-        connection.sendIQ(
383
-            iq,
384
-            function (result) {
385
-                var url = $(result).find('login-url').attr('url');
386
-                url = url = decodeURIComponent(url);
387
-                if (url) {
388
-                    console.info("Got POPUP auth url: " + url);
389
-                    urlCallback(url);
390
-                } else {
391
-                    console.error(
392
-                        "Failed to get POPUP auth url from the focus", result);
296
+            // Not authorized to create new room
297
+            if ($(error).find('>error>not-authorized').length) {
298
+                console.warn("Unauthorized to start the conference", error);
299
+                var toDomain
300
+                    = Strophe.getDomainFromJid(error.getAttribute('to'));
301
+                if (toDomain !== this.xmppService.options.hosts.anonymousdomain) {
302
+                    //FIXME:  "is external" should come either from
303
+                    // the focus or config.js
304
+                    self.externalAuthEnabled = true;
393
                 }
305
                 }
394
-            },
395
-            function (error) {
396
-                console.error('Get POPUP auth url error', error);
306
+                self.eventEmitter.emit(
307
+                    XMPPEvents.AUTHENTICATION_REQUIRED,
308
+                    function () {
309
+                        self.allocateConferenceFocus(
310
+                            callback);
311
+                    });
312
+                return;
397
             }
313
             }
398
-        );
399
-    },
400
-    logout: function (callback) {
401
-        var iq = $iq({to: Moderator.getFocusComponent(), type: 'set'});
402
-        var sessionId = localStorage.getItem('sessionId');
403
-        if (!sessionId) {
404
-            callback();
405
-            return;
314
+            var waitMs = self.getNextErrorTimeout();
315
+            console.error("Focus error, retry after " + waitMs, error);
316
+            // Show message
317
+            var focusComponent = self.getFocusComponent();
318
+            var retrySec = waitMs / 1000;
319
+            //FIXME:  message is duplicated ?
320
+            // Do not show in case of session invalid
321
+            // which means just a retry
322
+            if (!invalidSession) {
323
+                self.eventEmitter.emit(XMPPEvents.FOCUS_DISCONNECTED,
324
+                    focusComponent, retrySec);
325
+            }
326
+            // Reset response timeout
327
+            self.getNextTimeout(true);
328
+            window.setTimeout(
329
+                function () {
330
+                    self.allocateConferenceFocus(callback);
331
+                }, waitMs);
406
         }
332
         }
407
-        iq.c('logout', {
408
-            xmlns: 'http://jitsi.org/protocol/focus',
409
-            'session-id': sessionId
410
-        });
411
-        connection.sendIQ(
412
-            iq,
413
-            function (result) {
414
-                var logoutUrl = $(result).find('logout').attr('logout-url');
415
-                if (logoutUrl) {
416
-                    logoutUrl = decodeURIComponent(logoutUrl);
417
-                }
418
-                console.info("Log out OK, url: " + logoutUrl, result);
419
-                localStorage.removeItem('sessionId');
420
-                callback(logoutUrl);
421
-            },
422
-            function (error) {
423
-                console.error("Logout error", error);
333
+    );
334
+};
335
+
336
+Moderator.prototype.getLoginUrl =  function (urlCallback) {
337
+    var iq = $iq({to: this.getFocusComponent(), type: 'get'});
338
+    iq.c('login-url', {
339
+        xmlns: 'http://jitsi.org/protocol/focus',
340
+        room: this.roomName,
341
+        'machine-uid': this.settings.getSettings().uid
342
+    });
343
+    this.connection.sendIQ(
344
+        iq,
345
+        function (result) {
346
+            var url = $(result).find('login-url').attr('url');
347
+            url = url = decodeURIComponent(url);
348
+            if (url) {
349
+                console.info("Got auth url: " + url);
350
+                urlCallback(url);
351
+            } else {
352
+                console.error(
353
+                    "Failed to get auth url from the focus", result);
424
             }
354
             }
425
-        );
355
+        },
356
+        function (error) {
357
+            console.error("Get auth url error", error);
358
+        }
359
+    );
360
+};
361
+Moderator.prototype.getPopupLoginUrl =  function (urlCallback) {
362
+    var iq = $iq({to: this.getFocusComponent(), type: 'get'});
363
+    iq.c('login-url', {
364
+        xmlns: 'http://jitsi.org/protocol/focus',
365
+        room: this.roomName,
366
+        'machine-uid': this.settings.getSettings().uid,
367
+        popup: true
368
+    });
369
+    this.connection.sendIQ(
370
+        iq,
371
+        function (result) {
372
+            var url = $(result).find('login-url').attr('url');
373
+            url = url = decodeURIComponent(url);
374
+            if (url) {
375
+                console.info("Got POPUP auth url:  " + url);
376
+                urlCallback(url);
377
+            } else {
378
+                console.error(
379
+                    "Failed to get POPUP auth url from the focus", result);
380
+            }
381
+        },
382
+        function (error) {
383
+            console.error('Get POPUP auth url error', error);
384
+        }
385
+    );
386
+};
387
+
388
+Moderator.prototype.logout =  function (callback) {
389
+    var iq = $iq({to: this.getFocusComponent(), type: 'set'});
390
+    var sessionId = localStorage.getItem('sessionId');
391
+    if (!sessionId) {
392
+        callback();
393
+        return;
426
     }
394
     }
395
+    iq.c('logout', {
396
+        xmlns: 'http://jitsi.org/protocol/focus',
397
+        'session-id': sessionId
398
+    });
399
+    this.connection.sendIQ(
400
+        iq,
401
+        function (result) {
402
+            var logoutUrl = $(result).find('logout').attr('logout-url');
403
+            if (logoutUrl) {
404
+                logoutUrl = decodeURIComponent(logoutUrl);
405
+            }
406
+            console.info("Log out OK, url: " + logoutUrl, result);
407
+            localStorage.removeItem('sessionId');
408
+            callback(logoutUrl);
409
+        },
410
+        function (error) {
411
+            console.error("Logout error", error);
412
+        }
413
+    );
427
 };
414
 };
428
 
415
 
429
 module.exports = Moderator;
416
 module.exports = Moderator;

+ 46
- 15
modules/xmpp/strophe.emuc.js View File

5
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
5
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
6
 var Moderator = require("./moderator");
6
 var Moderator = require("./moderator");
7
 var RTC = require("../RTC/RTC");
7
 var RTC = require("../RTC/RTC");
8
+var EventEmitter = require("events");
8
 
9
 
9
 var parser = {
10
 var parser = {
10
     packet2JSON: function (packet, nodes) {
11
     packet2JSON: function (packet, nodes) {
46
 function ChatRoom(connection, jid, password, XMPP, eventEmitter)
47
 function ChatRoom(connection, jid, password, XMPP, eventEmitter)
47
 {
48
 {
48
     this.eventEmitter = eventEmitter;
49
     this.eventEmitter = eventEmitter;
50
+    this.roomEmitter = new EventEmitter();
49
     this.xmpp = XMPP;
51
     this.xmpp = XMPP;
50
     this.connection = connection;
52
     this.connection = connection;
51
     this.roomjid = Strophe.getBareJidFromJid(jid);
53
     this.roomjid = Strophe.getBareJidFromJid(jid);
54
     console.info("Joined MUC as " + this.myroomjid);
56
     console.info("Joined MUC as " + this.myroomjid);
55
     this.members = {};
57
     this.members = {};
56
     this.presMap = {};
58
     this.presMap = {};
59
+    this.presHandlers = {};
57
     this.joined = false;
60
     this.joined = false;
58
     this.role = null;
61
     this.role = null;
59
     this.focusMucJid = null;
62
     this.focusMucJid = null;
60
     this.bridgeIsDown = false;
63
     this.bridgeIsDown = false;
64
+    this.moderator = new Moderator(this.roomjid, this.xmpp, eventEmitter);
61
     this.initPresenceMap();
65
     this.initPresenceMap();
66
+    this.readyToJoin = false;
67
+    this.joinRequested = false;
68
+    var self = this;
69
+    this.moderator.allocateConferenceFocus(function()
70
+    {
71
+        self.readyToJoin = true;
72
+        if(self.joinRequested)
73
+        {
74
+            self.join();
75
+        }
76
+    });
62
 }
77
 }
63
 
78
 
64
 ChatRoom.prototype.initPresenceMap = function () {
79
 ChatRoom.prototype.initPresenceMap = function () {
84
     });
99
     });
85
 };
100
 };
86
 
101
 
102
+ChatRoom.prototype.join = function (password) {
103
+    if(password)
104
+        this.password = password;
105
+    if(!this.readyToJoin)
106
+    {
107
+        this.joinRequested = true;
108
+        return;
109
+    }
110
+    this.joinRequested = false;
111
+    this.sendPresence();
112
+}
113
+
87
 ChatRoom.prototype.sendPresence = function () {
114
 ChatRoom.prototype.sendPresence = function () {
88
     if (!this.presMap['to']) {
115
     if (!this.presMap['to']) {
89
         // Too early to send presence - not initialized
116
         // Too early to send presence - not initialized
169
     member.jid = tmp.attr('jid');
196
     member.jid = tmp.attr('jid');
170
     member.isFocus = false;
197
     member.isFocus = false;
171
     if (member.jid
198
     if (member.jid
172
-        && member.jid.indexOf(Moderator.getFocusUserJid() + "/") == 0) {
199
+        && member.jid.indexOf(this.moderator.getFocusUserJid() + "/") == 0) {
173
         member.isFocus = true;
200
         member.isFocus = true;
174
     }
201
     }
175
 
202
 
176
-    pres.find(">x").remove();
203
+    $(pres).find(">x").remove();
177
     var nodes = [];
204
     var nodes = [];
178
     parser.packet2JSON(pres, nodes);
205
     parser.packet2JSON(pres, nodes);
179
     for(var i = 0; i < nodes.length; i++)
206
     for(var i = 0; i < nodes.length; i++)
206
                 }
233
                 }
207
                 break;
234
                 break;
208
             default :
235
             default :
209
-                this.processNode(node);
236
+                this.processNode(node, from);
210
         }
237
         }
211
 
238
 
212
     }
239
     }
218
             this.role = member.role;
245
             this.role = member.role;
219
 
246
 
220
             this.eventEmitter.emit(XMPPEvents.LOCAL_ROLE_CHANGED,
247
             this.eventEmitter.emit(XMPPEvents.LOCAL_ROLE_CHANGED,
221
-                member, Moderator.isModerator());
248
+                member, this.isModerator());
222
         }
249
         }
223
         if (!this.joined) {
250
         if (!this.joined) {
224
             this.joined = true;
251
             this.joined = true;
257
 
284
 
258
 };
285
 };
259
 
286
 
260
-ChatRoom.prototype.processNode = function (node) {
261
-    this.eventEmitter.emit(XMPPEvents.PRESENCE_SETTING, node);
287
+ChatRoom.prototype.processNode = function (node, from) {
288
+    if(this.presHandlers[node.tagName])
289
+        this.presHandlers[node.tagName](node, from);
262
 };
290
 };
263
 
291
 
264
 ChatRoom.prototype.sendMessage = function (body, nickname) {
292
 ChatRoom.prototype.sendMessage = function (body, nickname) {
283
 
311
 
284
     this.eventEmitter.emit(XMPPEvents.MUC_MEMBER_LEFT, jid);
312
     this.eventEmitter.emit(XMPPEvents.MUC_MEMBER_LEFT, jid);
285
 
313
 
286
-    this.connection.jingle.terminateByJid(jid);
287
-
288
-    Moderator.onMucMemberLeft(jid);
314
+    this.moderator.onMucMemberLeft(jid);
289
 };
315
 };
290
 
316
 
291
 ChatRoom.prototype.onPresenceUnavailable = function (pres, from) {
317
 ChatRoom.prototype.onPresenceUnavailable = function (pres, from) {
447
     }
473
     }
448
 };
474
 };
449
 
475
 
476
+ChatRoom.prototype.addPresenceListener = function (name, handler) {
477
+    this.presHandlers[name] = handler;
478
+}
479
+
480
+ChatRoom.prototype.removePresenceListener = function (name) {
481
+    delete this.presHandlers[name];
482
+}
483
+
450
 ChatRoom.prototype.isModerator = function (jid) {
484
 ChatRoom.prototype.isModerator = function (jid) {
451
     return this.role === 'moderator';
485
     return this.role === 'moderator';
452
 };
486
 };
466
         init: function (conn) {
500
         init: function (conn) {
467
             this.connection = conn;
501
             this.connection = conn;
468
             // add handlers (just once)
502
             // add handlers (just once)
469
-            this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null);
503
+            this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, null, null);
470
             this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null);
504
             this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null);
471
             this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null);
505
             this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null);
472
             this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null);
506
             this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null);
473
         },
507
         },
474
-        doJoin: function (jid, password, eventEmitter) {
508
+        createRoom: function (jid, password, eventEmitter) {
475
             var roomJid = Strophe.getBareJidFromJid(jid);
509
             var roomJid = Strophe.getBareJidFromJid(jid);
476
-            if(this.rooms[roomJid])
477
-            {
510
+            if (this.rooms[roomJid]) {
478
                 console.error("You are already in the room!");
511
                 console.error("You are already in the room!");
479
                 return;
512
                 return;
480
             }
513
             }
481
             this.rooms[roomJid] = new ChatRoom(this.connection, jid, password, XMPP, eventEmitter);
514
             this.rooms[roomJid] = new ChatRoom(this.connection, jid, password, XMPP, eventEmitter);
482
-
483
-            this.rooms[roomJid].sendPresence();
484
             return this.rooms[roomJid];
515
             return this.rooms[roomJid];
485
         },
516
         },
486
         doLeave: function (jid) {
517
         doLeave: function (jid) {

+ 1
- 52
modules/xmpp/strophe.jingle.js View File

11
         sessions: {},
11
         sessions: {},
12
         jid2session: {},
12
         jid2session: {},
13
         ice_config: {iceServers: []},
13
         ice_config: {iceServers: []},
14
-        activecall: null,
15
         media_constraints: {
14
         media_constraints: {
16
             mandatory: {
15
             mandatory: {
17
                 'OfferToReceiveAudio': true,
16
                 'OfferToReceiveAudio': true,
108
                     sess.media_constraints = this.media_constraints;
107
                     sess.media_constraints = this.media_constraints;
109
                     sess.ice_config = this.ice_config;
108
                     sess.ice_config = this.ice_config;
110
 
109
 
111
-                    sess.initialize(fromJid, false);
110
+                    sess.initialize(Strophe.getBareJidFromJid(fromJid), false);
112
                     // FIXME: setRemoteDescription should only be done when this call is to be accepted
111
                     // FIXME: setRemoteDescription should only be done when this call is to be accepted
113
                     sess.setOffer($(iq).find('>jingle'));
112
                     sess.setOffer($(iq).find('>jingle'));
114
 
113
 
119
                     // .sendAnswer and .accept
118
                     // .sendAnswer and .accept
120
                     // or .sendTerminate -- not necessarily synchronous
119
                     // or .sendTerminate -- not necessarily synchronous
121
 
120
 
122
-                    // TODO: do we check activecall == null?
123
-                    this.connection.jingle.activecall = sess;
124
-
125
                     eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
121
                     eventEmitter.emit(XMPPEvents.CALL_INCOMING, sess);
126
 
122
 
127
-                    // TODO: check affiliation and/or role
128
-                    console.log('emuc data for', sess.peerjid,
129
-                        this.connection.emuc.members[sess.peerjid]);
130
                     sess.sendAnswer();
123
                     sess.sendAnswer();
131
                     sess.accept();
124
                     sess.accept();
132
                     break;
125
                     break;
133
                 case 'session-accept':
126
                 case 'session-accept':
134
                     sess.setAnswer($(iq).find('>jingle'));
127
                     sess.setAnswer($(iq).find('>jingle'));
135
                     sess.accept();
128
                     sess.accept();
136
-                    $(document).trigger('callaccepted.jingle', [sess.sid]);
137
                     break;
129
                     break;
138
                 case 'session-terminate':
130
                 case 'session-terminate':
139
                     // If this is not the focus sending the terminate, we have
131
                     // If this is not the focus sending the terminate, we have
188
             }
180
             }
189
             return true;
181
             return true;
190
         },
182
         },
191
-        initiate: function (peerjid, myjid) { // initiate a new jinglesession to peerjid
192
-            var sess = new JingleSession(myjid || this.connection.jid,
193
-                Math.random().toString(36).substr(2, 12), // random string
194
-                this.connection, XMPP, eventEmitter);
195
-            // configure session
196
-
197
-            sess.media_constraints = this.media_constraints;
198
-            sess.ice_config = this.ice_config;
199
-
200
-            sess.initialize(peerjid, true);
201
-            this.sessions[sess.sid] = sess;
202
-            this.jid2session[sess.peerjid] = sess;
203
-            sess.sendOffer();
204
-            return sess;
205
-        },
206
         terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
183
         terminate: function (sid, reason, text) { // terminate by sessionid (or all sessions)
207
             if (sid === null || sid === undefined) {
184
             if (sid === null || sid === undefined) {
208
                 for (sid in this.sessions) {
185
                 for (sid in this.sessions) {
222
                 delete this.sessions[sid];
199
                 delete this.sessions[sid];
223
             }
200
             }
224
         },
201
         },
225
-        // Used to terminate a session when an unavailable presence is received.
226
-        terminateByJid: function (jid) {
227
-            if (this.jid2session.hasOwnProperty(jid)) {
228
-                var sess = this.jid2session[jid];
229
-                if (sess) {
230
-                    sess.terminate();
231
-                    console.log('peer went away silently', jid);
232
-                    delete this.sessions[sess.sid];
233
-                    delete this.jid2session[jid];
234
-                    $(document).trigger('callterminated.jingle',
235
-                        [sess.sid, jid], 'gone');
236
-                }
237
-            }
238
-        },
239
-        terminateRemoteByJid: function (jid, reason) {
240
-            if (this.jid2session.hasOwnProperty(jid)) {
241
-                var sess = this.jid2session[jid];
242
-                if (sess) {
243
-                    sess.sendTerminate(reason || (!sess.active()) ? 'kick' : null);
244
-                    sess.terminate();
245
-                    console.log('terminate peer with jid', sess.sid, jid);
246
-                    delete this.sessions[sess.sid];
247
-                    delete this.jid2session[jid];
248
-                    $(document).trigger('callterminated.jingle',
249
-                        [sess.sid, jid, 'kicked']);
250
-                }
251
-            }
252
-        },
253
         getStunAndTurnCredentials: function () {
202
         getStunAndTurnCredentials: function () {
254
             // get stun and turn configuration from server via xep-0215
203
             // get stun and turn configuration from server via xep-0215
255
             // uses time-limited credentials as described in
204
             // uses time-limited credentials as described in

+ 20
- 59
modules/xmpp/xmpp.js View File

23
 function initStrophePlugins(XMPP)
23
 function initStrophePlugins(XMPP)
24
 {
24
 {
25
     require("./strophe.emuc")(XMPP);
25
     require("./strophe.emuc")(XMPP);
26
-//    require("./strophe.jingle")(XMPP, eventEmitter);
26
+    require("./strophe.jingle")(XMPP, XMPP.eventEmitter);
27
 //    require("./strophe.moderate")(XMPP, eventEmitter);
27
 //    require("./strophe.moderate")(XMPP, eventEmitter);
28
     require("./strophe.util")();
28
     require("./strophe.util")();
29
     require("./strophe.rayo")();
29
     require("./strophe.rayo")();
52
 //}
52
 //}
53
 
53
 
54
 function XMPP(options) {
54
 function XMPP(options) {
55
-    this.sessionTerminated = false;
56
     this.eventEmitter = new EventEmitter();
55
     this.eventEmitter = new EventEmitter();
57
     this.connection = null;
56
     this.connection = null;
58
     this.disconnectInProgress = false;
57
     this.disconnectInProgress = false;
61
     this.options = options;
60
     this.options = options;
62
     initStrophePlugins(this);
61
     initStrophePlugins(this);
63
 //    registerListeners();
62
 //    registerListeners();
64
-    Moderator.init(this, this.eventEmitter);
63
+
65
     this.connection = createConnection(options.bosh);
64
     this.connection = createConnection(options.bosh);
66
-    Moderator.setConnection(this.connection);
67
 }
65
 }
68
 
66
 
69
 
67
 
118
             if (self.connection && self.connection.connected &&
116
             if (self.connection && self.connection.connected &&
119
                 Strophe.getResourceFromJid(self.connection.jid)) {
117
                 Strophe.getResourceFromJid(self.connection.jid)) {
120
                 // .connected is true while connecting?
118
                 // .connected is true while connecting?
121
-                self.connection.send($pres());
119
+//                self.connection.send($pres());
122
                 self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_ESTABLISHED,
120
                 self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_ESTABLISHED,
123
                     Strophe.getResourceFromJid(self.connection.jid));
121
                     Strophe.getResourceFromJid(self.connection.jid));
124
             }
122
             }
167
     return this._connect(jid, password);
165
     return this._connect(jid, password);
168
 };
166
 };
169
 
167
 
170
-XMPP.prototype.joinRoom = function(roomName, useNicks, nick) {
171
-    var roomjid = roomName  + '@' + Strophe.getDomainFromJid(this.connection.jid);
168
+XMPP.prototype.createRoom = function (roomName, useNicks, nick) {
169
+    var roomjid = roomName  + '@' + this.options.hosts.muc;
172
 
170
 
173
     if (useNicks) {
171
     if (useNicks) {
174
         if (nick) {
172
         if (nick) {
184
 
182
 
185
         roomjid += '/' + tmpJid;
183
         roomjid += '/' + tmpJid;
186
     }
184
     }
187
-    return this.connection.emuc.doJoin(roomjid, null, this.eventEmitter);
185
+
186
+    return this.connection.emuc.createRoom(roomjid, null, this.eventEmitter);
187
+}
188
+
189
+XMPP.prototype.addListener = function(type, listener) {
190
+    this.eventEmitter.on(type, listener);
188
 };
191
 };
189
 
192
 
193
+XMPP.prototype.removeListener = function (type, listener) {
194
+    this.eventEmitter.removeListener(type, listener);
195
+};
190
 
196
 
191
-XMPP.prototype.disposeConference = function (onUnload) {
192
-    var handler = this.connection.jingle.activecall;
197
+XMPP.prototype.leaveRoom = function (jid) {
198
+    var handler = this.connection.jingle.jid2session[jid];
193
     if (handler && handler.peerconnection) {
199
     if (handler && handler.peerconnection) {
194
         // FIXME: probably removing streams is not required and close() should
200
         // FIXME: probably removing streams is not required and close() should
195
         // be enough
201
         // be enough
196
         if (RTC.localAudio) {
202
         if (RTC.localAudio) {
197
             handler.peerconnection.removeStream(
203
             handler.peerconnection.removeStream(
198
-                RTC.localAudio.getOriginalStream(), onUnload);
204
+                RTC.localAudio.getOriginalStream(), true);
199
         }
205
         }
200
         if (RTC.localVideo) {
206
         if (RTC.localVideo) {
201
             handler.peerconnection.removeStream(
207
             handler.peerconnection.removeStream(
202
-                RTC.localVideo.getOriginalStream(), onUnload);
208
+                RTC.localVideo.getOriginalStream(), true);
203
         }
209
         }
204
         handler.peerconnection.close();
210
         handler.peerconnection.close();
205
     }
211
     }
206
-    this.eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE, onUnload);
207
-    this.connection.jingle.activecall = null;
208
-    if (!onUnload) {
209
-        this.sessionTerminated = true;
210
-        this.connection.emuc.doLeave();
211
-    }
212
-};
213
-
214
-XMPP.prototype.addListener = function(type, listener) {
215
-    this.eventEmitter.on(type, listener);
216
-};
217
-
218
-XMPP.prototype.removeListener = function (type, listener) {
219
-    this.eventEmitter.removeListener(type, listener);
220
-};
221
-
222
-XMPP.prototype.leaveRoom = function (jid) {
212
+    this.eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE);
223
     this.connection.emuc.doLeave(jid);
213
     this.connection.emuc.doLeave(jid);
224
 };
214
 };
225
 
215
 
226
-
227
-XMPP.prototype.allocateConferenceFocus = function(roomName, callback) {
228
-    Moderator.allocateConferenceFocus(roomName, callback);
229
-};
230
-
231
-XMPP.prototype.getLoginUrl = function (roomName, callback) {
232
-    Moderator.getLoginUrl(roomName, callback);
233
-}
234
-
235
-XMPP.prototype.getPopupLoginUrl = function (roomName, callback) {
236
-    Moderator.getPopupLoginUrl(roomName, callback);
237
-};
238
-
239
-XMPP.prototype.isModerator = function () {
240
-    return Moderator.isModerator();
241
-};
242
-
243
-XMPP.prototype.isSipGatewayEnabled = function () {
244
-    return Moderator.isSipGatewayEnabled();
245
-}
246
-
247
-XMPP.prototype.isExternalAuthEnabled = function () {
248
-    return Moderator.isExternalAuthEnabled();
249
-};
250
-
251
 XMPP.prototype.isConferenceInProgress = function () {
216
 XMPP.prototype.isConferenceInProgress = function () {
252
     return this.connection && this.connection.jingle.activecall &&
217
     return this.connection && this.connection.jingle.activecall &&
253
         this.connection.jingle.activecall.peerconnection;
218
         this.connection.jingle.activecall.peerconnection;
376
     this.connection.moderate.eject(jid);
341
     this.connection.moderate.eject(jid);
377
 };
342
 };
378
 
343
 
379
-XMPP.prototype.logout = function (callback) {
380
-    Moderator.logout(callback);
381
-};
382
-
383
 XMPP.prototype.getJidFromSSRC = function (ssrc) {
344
 XMPP.prototype.getJidFromSSRC = function (ssrc) {
384
     if (!this.isConferenceInProgress())
345
     if (!this.isConferenceInProgress())
385
         return null;
346
         return null;
396
     this.connection.jingle.activecall.peerconnection.removeStream(stream);
357
     this.connection.jingle.activecall.peerconnection.removeStream(stream);
397
 };
358
 };
398
 
359
 
399
-XMPP.prototype.disconnect = function (callback) {
360
+XMPP.prototype.disconnect = function () {
400
     if (this.disconnectInProgress || !this.connection || !this.connection.connected)
361
     if (this.disconnectInProgress || !this.connection || !this.connection.connected)
401
     {
362
     {
402
         this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);
363
         this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);

+ 2
- 1
service/xmpp/XMPPEvents.js View File

49
     ROOM_JOIN_ERROR: 'xmpp.room_join_error',
49
     ROOM_JOIN_ERROR: 'xmpp.room_join_error',
50
     ROOM_CONNECT_ERROR: 'xmpp.room_connect_error',
50
     ROOM_CONNECT_ERROR: 'xmpp.room_connect_error',
51
     // xmpp is connected and obtained user media
51
     // xmpp is connected and obtained user media
52
-    READY_TO_JOIN: 'xmpp.ready_to_join'
52
+    READY_TO_JOIN: 'xmpp.ready_to_join',
53
+    FOCUS_LEFT: "xmpp.focus_left"
53
 };
54
 };
54
 module.exports = XMPPEvents;
55
 module.exports = XMPPEvents;

Loading…
Cancel
Save