Browse Source

Moves loading of analytics in the library.

master
damencho 9 years ago
parent
commit
234f3384e1

+ 1
- 4
JitsiConference.js View File

8
 var JitsiConferenceErrors = require("./JitsiConferenceErrors");
8
 var JitsiConferenceErrors = require("./JitsiConferenceErrors");
9
 var JitsiParticipant = require("./JitsiParticipant");
9
 var JitsiParticipant = require("./JitsiParticipant");
10
 var Statistics = require("./modules/statistics/statistics");
10
 var Statistics = require("./modules/statistics/statistics");
11
-var AnalyticsAdapter = require("./modules/statistics/AnalyticsAdapter");
12
 var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager');
11
 var JitsiDTMFManager = require('./modules/DTMF/JitsiDTMFManager');
13
 var JitsiTrackEvents = require("./JitsiTrackEvents");
12
 var JitsiTrackEvents = require("./JitsiTrackEvents");
14
 var JitsiTrackErrors = require("./JitsiTrackErrors");
13
 var JitsiTrackErrors = require("./JitsiTrackErrors");
99
         this.statistics = new Statistics(this.xmpp, {
98
         this.statistics = new Statistics(this.xmpp, {
100
             callStatsID: this.options.config.callStatsID,
99
             callStatsID: this.options.config.callStatsID,
101
             callStatsSecret: this.options.config.callStatsSecret,
100
             callStatsSecret: this.options.config.callStatsSecret,
102
-            disableThirdPartyRequests:
103
-                this.options.config.disableThirdPartyRequests,
104
             roomName: this.options.name
101
             roomName: this.options.name
105
         });
102
         });
106
     }
103
     }
787
     // Accept incoming call
784
     // Accept incoming call
788
     this.room.setJingleSession(jingleSession);
785
     this.room.setJingleSession(jingleSession);
789
     this.room.connectionTimes["session.initiate"] = now;
786
     this.room.connectionTimes["session.initiate"] = now;
790
-    AnalyticsAdapter.sendEvent("muc.idle",
787
+    Statistics.analytics.sendEvent("muc.idle",
791
         (now - this.room.connectionTimes["muc.joined"]));
788
         (now - this.room.connectionTimes["muc.joined"]));
792
     try{
789
     try{
793
         jingleSession.initialize(false /* initiator */,this.room);
790
         jingleSession.initialize(false /* initiator */,this.room);

+ 6
- 7
JitsiConferenceEventManager.js View File

8
 var AuthenticationEvents =
8
 var AuthenticationEvents =
9
     require("./service/authentication/AuthenticationEvents");
9
     require("./service/authentication/AuthenticationEvents");
10
 var Statistics = require("./modules/statistics/statistics");
10
 var Statistics = require("./modules/statistics/statistics");
11
-var AnalyticsAdapter = require("./modules/statistics/AnalyticsAdapter");
12
 var MediaType = require("./service/RTC/MediaType");
11
 var MediaType = require("./service/RTC/MediaType");
13
 
12
 
14
 /**
13
 /**
99
         {
98
         {
100
             for (var ckey in chatRoom.connectionTimes){
99
             for (var ckey in chatRoom.connectionTimes){
101
                 var cvalue = chatRoom.connectionTimes[ckey];
100
                 var cvalue = chatRoom.connectionTimes[ckey];
102
-                AnalyticsAdapter.sendEvent('conference.' + ckey, cvalue);
101
+                Statistics.analytics.sendEvent('conference.' + ckey, cvalue);
103
             }
102
             }
104
             for (var xkey in chatRoom.xmpp.connectionTimes){
103
             for (var xkey in chatRoom.xmpp.connectionTimes){
105
                 var xvalue = chatRoom.xmpp.connectionTimes[xkey];
104
                 var xvalue = chatRoom.xmpp.connectionTimes[xkey];
106
-                AnalyticsAdapter.sendEvent('xmpp.' + xkey, xvalue);
105
+                Statistics.analytics.sendEvent('xmpp.' + xkey, xvalue);
107
             }
106
             }
108
         });
107
         });
109
 
108
 
132
         JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);
131
         JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE);
133
     chatRoom.addListener(XMPPEvents.BRIDGE_DOWN,
132
     chatRoom.addListener(XMPPEvents.BRIDGE_DOWN,
134
         function (){
133
         function (){
135
-            AnalyticsAdapter.sendEvent('conference.bridgeDown');
134
+            Statistics.analytics.sendEvent('conference.bridgeDown');
136
         });
135
         });
137
 
136
 
138
     this.chatRoomForwarder.forward(XMPPEvents.RESERVATION_ERROR,
137
     this.chatRoomForwarder.forward(XMPPEvents.RESERVATION_ERROR,
171
 
170
 
172
     chatRoom.addListener(XMPPEvents.FOCUS_LEFT,
171
     chatRoom.addListener(XMPPEvents.FOCUS_LEFT,
173
         function () {
172
         function () {
174
-            AnalyticsAdapter.sendEvent('conference.focusLeft');
173
+            Statistics.analytics.sendEvent('conference.focusLeft');
175
             if(!conference.connection._reload())
174
             if(!conference.connection._reload())
176
                 conference.eventEmitter.emit(
175
                 conference.eventEmitter.emit(
177
                     JitsiConferenceEvents.CONFERENCE_FAILED,
176
                     JitsiConferenceEvents.CONFERENCE_FAILED,
187
         JitsiConferenceEvents.CONNECTION_INTERRUPTED);
186
         JitsiConferenceEvents.CONNECTION_INTERRUPTED);
188
     chatRoom.addListener(XMPPEvents.CONNECTION_INTERRUPTED,
187
     chatRoom.addListener(XMPPEvents.CONNECTION_INTERRUPTED,
189
         function () {
188
         function () {
190
-            AnalyticsAdapter.sendEvent('connection.interrupted');
189
+            Statistics.analytics.sendEvent('connection.interrupted');
191
         });
190
         });
192
 
191
 
193
     this.chatRoomForwarder.forward(XMPPEvents.RECORDER_STATE_CHANGED,
192
     this.chatRoomForwarder.forward(XMPPEvents.RECORDER_STATE_CHANGED,
451
         var now = window.performance.now();
450
         var now = window.performance.now();
452
         logger.log("(TIME) data channel opened ", now);
451
         logger.log("(TIME) data channel opened ", now);
453
         conference.room.connectionTimes["data.channel.opened"] = now;
452
         conference.room.connectionTimes["data.channel.opened"] = now;
454
-        AnalyticsAdapter.sendEvent('conference.dataChannel.open', now);
453
+        Statistics.analytics.sendEvent('conference.dataChannel.open', now);
455
     });
454
     });
456
 
455
 
457
     this.rtcForwarder.forward(RTCEvents.LASTN_CHANGED,
456
     this.rtcForwarder.forward(RTCEvents.LASTN_CHANGED,

+ 4
- 4
JitsiConnection.js View File

2
 var XMPP = require("./modules/xmpp/xmpp");
2
 var XMPP = require("./modules/xmpp/xmpp");
3
 var JitsiConnectionEvents = require("./JitsiConnectionEvents");
3
 var JitsiConnectionEvents = require("./JitsiConnectionEvents");
4
 var JitsiConnectionErrors = require("./JitsiConnectionErrors");
4
 var JitsiConnectionErrors = require("./JitsiConnectionErrors");
5
-var AnalyticsAdapter = require("./modules/statistics/AnalyticsAdapter");
6
 
5
 
7
 /**
6
 /**
8
  * Creates new connection object for the Jitsi Meet server side video conferencing service. Provides access to the
7
  * Creates new connection object for the Jitsi Meet server side video conferencing service. Provides access to the
26
 
25
 
27
     this.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED,
26
     this.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED,
28
         function (errType, msg) {
27
         function (errType, msg) {
29
-            AnalyticsAdapter.sendEvent('connection.failed.' + errType);
28
+            Statistics.analytics.sendEvent('connection.failed.' + errType);
30
             if(errType === JitsiConnectionErrors.OTHER_ERROR &&
29
             if(errType === JitsiConnectionErrors.OTHER_ERROR &&
31
                 (msg === "item-not-found" || msg === "host-unknown")) {
30
                 (msg === "item-not-found" || msg === "host-unknown")) {
32
                     // FIXME: don't report the error if we are going to reload
31
                     // FIXME: don't report the error if we are going to reload
40
             // and then there are no msgs, but we want to log only disconnects
39
             // and then there are no msgs, but we want to log only disconnects
41
             // when there is real error
40
             // when there is real error
42
             if(msg)
41
             if(msg)
43
-                AnalyticsAdapter.sendEvent('connection.disconnected.' + msg);
42
+                Statistics.analytics.sendEvent(
43
+                    'connection.disconnected.' + msg);
44
         });
44
         });
45
 }
45
 }
46
 
46
 
73
 JitsiConnection.prototype._reload = function () {
73
 JitsiConnection.prototype._reload = function () {
74
     if(this.retryOnFail === 0)
74
     if(this.retryOnFail === 0)
75
         return false;
75
         return false;
76
-    AnalyticsAdapter.sendEvent('connection.reload');
76
+    Statistics.analytics.sendEvent('connection.reload');
77
     this.retryOnFail--;
77
     this.retryOnFail--;
78
     var states = {};
78
     var states = {};
79
     for(var name in this.conferences) {
79
     for(var name in this.conferences) {

+ 3
- 7
JitsiMeetJS.js View File

16
 var RTC = require("./modules/RTC/RTC");
16
 var RTC = require("./modules/RTC/RTC");
17
 var RTCUIHelper = require("./modules/RTC/RTCUIHelper");
17
 var RTCUIHelper = require("./modules/RTC/RTCUIHelper");
18
 var Statistics = require("./modules/statistics/statistics");
18
 var Statistics = require("./modules/statistics/statistics");
19
-var AnalyticsAdapter = require("./modules/statistics/AnalyticsAdapter");
20
 var Resolutions = require("./service/RTC/Resolutions");
19
 var Resolutions = require("./service/RTC/Resolutions");
21
 var ScriptUtil = require("./modules/util/ScriptUtil");
20
 var ScriptUtil = require("./modules/util/ScriptUtil");
22
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
21
 var GlobalOnErrorHandler = require("./modules/util/GlobalOnErrorHandler");
67
     },
66
     },
68
     logLevels: Logger.levels,
67
     logLevels: Logger.levels,
69
     mediaDevices: JitsiMediaDevices,
68
     mediaDevices: JitsiMediaDevices,
70
-    analytics: AnalyticsAdapter,
69
+    analytics: null,
71
     init: function (options) {
70
     init: function (options) {
72
         var logObject, attr;
71
         var logObject, attr;
73
-        Statistics.audioLevelsEnabled = !options.disableAudioLevels;
74
-
75
-        if(typeof options.audioLevelsInterval === 'number') {
76
-            Statistics.audioLevelsInterval = options.audioLevelsInterval;
77
-        }
72
+        Statistics.init(options);
73
+        this.analytics = Statistics.analytics;
78
 
74
 
79
         if (options.enableWindowOnErrorHandler) {
75
         if (options.enableWindowOnErrorHandler) {
80
             GlobalOnErrorHandler.addHandler(
76
             GlobalOnErrorHandler.addHandler(

+ 1
- 2
doc/API.md View File

50
     9. disableAudioLevels - boolean property. Enables/disables audio levels.
50
     9. disableAudioLevels - boolean property. Enables/disables audio levels.
51
     10. disableSimulcast - boolean property. Enables/disables simulcast.
51
     10. disableSimulcast - boolean property. Enables/disables simulcast.
52
     11. enableWindowOnErrorHandler - boolean property (default false). Enables/disables attaching global onerror handler (window.onerror).
52
     11. enableWindowOnErrorHandler - boolean property (default false). Enables/disables attaching global onerror handler (window.onerror).
53
+    12. disableThirdPartyRequests - if true - callstats will be disabled and the callstats API won't be included.
53
 
54
 
54
 * ```JitsiMeetJS.JitsiConnection``` - the ```JitsiConnection``` constructor. You can use that to create new server connection.
55
 * ```JitsiMeetJS.JitsiConnection``` - the ```JitsiConnection``` constructor. You can use that to create new server connection.
55
 
56
 
214
         3. jirecon
215
         3. jirecon
215
         4. callStatsID - callstats credentials
216
         4. callStatsID - callstats credentials
216
         5. callStatsSecret - callstats credentials
217
         5. callStatsSecret - callstats credentials
217
-        6. disableThirdPartyRequests - if true - callstats will be disabled and
218
-        the callstats API won't be included.
219
         **NOTE: if 4 and 5 are set the library is going to send events to callstats. Otherwise the callstats integration will be disabled.**
218
         **NOTE: if 4 and 5 are set the library is going to send events to callstats. Otherwise the callstats integration will be disabled.**
220
 
219
 
221
 5. addEventListener(event, listener) - Subscribes the passed listener to the event.
220
 5. addEventListener(event, listener) - Subscribes the passed listener to the event.

+ 1
- 2
modules/RTC/JitsiRemoteTrack.js View File

1
 var JitsiTrack = require("./JitsiTrack");
1
 var JitsiTrack = require("./JitsiTrack");
2
 var JitsiTrackEvents = require("../../JitsiTrackEvents");
2
 var JitsiTrackEvents = require("../../JitsiTrackEvents");
3
 var RTCBrowserType = require("./RTCBrowserType");
3
 var RTCBrowserType = require("./RTCBrowserType");
4
-var AnalyticsAdapter = require("../statistics/AnalyticsAdapter");
5
 
4
 
6
 var ttfmTrackerAudioAttached = false;
5
 var ttfmTrackerAudioAttached = false;
7
 var ttfmTrackerVideoAttached = false;
6
 var ttfmTrackerVideoAttached = false;
120
             - this.conference.getConnectionTimes()["muc.joined"]);
119
             - this.conference.getConnectionTimes()["muc.joined"]);
121
         this.conference.getConnectionTimes()[type + ".ttfm"] = ttfm;
120
         this.conference.getConnectionTimes()[type + ".ttfm"] = ttfm;
122
         console.log("(TIME) TTFM " + type + ":\t", ttfm);
121
         console.log("(TIME) TTFM " + type + ":\t", ttfm);
123
-        AnalyticsAdapter.sendEvent(type +'.ttfm', ttfm);
122
+        Statistics.analytics.sendEvent(type +'.ttfm', ttfm);
124
     }.bind(this));
123
     }.bind(this));
125
 };
124
 };
126
 
125
 

+ 7
- 10
modules/statistics/AnalyticsAdapter.js View File

3
 function NoopAnalytics() {}
3
 function NoopAnalytics() {}
4
 NoopAnalytics.prototype.sendEvent = function () {};
4
 NoopAnalytics.prototype.sendEvent = function () {};
5
 
5
 
6
+function AnalyticsAdapter() {
7
+    this.browserActionSuffix = '.' + RTCBrowserType.getBrowserName();
8
+}
9
+
6
 // XXX Since we asynchronously load the integration of the analytics API and the
10
 // XXX Since we asynchronously load the integration of the analytics API and the
7
 // analytics API may asynchronously load its implementation (e.g. Google
11
 // analytics API may asynchronously load its implementation (e.g. Google
8
 // Analytics), we cannot make the decision with respect to which analytics
12
 // Analytics), we cannot make the decision with respect to which analytics
9
 // implementation we will use here and we have to postpone it i.e. we will make
13
 // implementation we will use here and we have to postpone it i.e. we will make
10
 // a lazy decision.
14
 // a lazy decision.
11
-
12
-function AnalyticsAdapter() {
13
-    this.browserActionSuffix = '.' + RTCBrowserType.getBrowserName();
14
-}
15
-
16
 AnalyticsAdapter.prototype.sendEvent = function (action, data)
15
 AnalyticsAdapter.prototype.sendEvent = function (action, data)
17
 {
16
 {
18
-    var a = this.analytics;
19
-
20
-    if (a === null || typeof a === 'undefined') {
17
+    if (this.analytics === null || typeof this.analytics === 'undefined') {
21
         var AnalyticsImpl = window.Analytics || NoopAnalytics;
18
         var AnalyticsImpl = window.Analytics || NoopAnalytics;
22
 
19
 
23
-        this.analytics = a = new AnalyticsImpl();
20
+        this.analytics = new AnalyticsImpl();
24
     }
21
     }
25
     try {
22
     try {
26
-        a.sendEvent(action + this.browserActionSuffix, data);
23
+        this.analytics.sendEvent(action + this.browserActionSuffix, data);
27
     } catch (ignored) {}
24
     } catch (ignored) {}
28
 };
25
 };
29
 
26
 

+ 36
- 1
modules/statistics/statistics.js View File

4
 var RTPStats = require("./RTPStatsCollector.js");
4
 var RTPStats = require("./RTPStatsCollector.js");
5
 var EventEmitter = require("events");
5
 var EventEmitter = require("events");
6
 var StatisticsEvents = require("../../service/statistics/Events");
6
 var StatisticsEvents = require("../../service/statistics/Events");
7
+var AnalyticsAdapter = require("./AnalyticsAdapter");
7
 var CallStats = require("./CallStats");
8
 var CallStats = require("./CallStats");
8
 var ScriptUtil = require('../util/ScriptUtil');
9
 var ScriptUtil = require('../util/ScriptUtil');
9
 var JitsiTrackError = require("../../JitsiTrackError");
10
 var JitsiTrackError = require("../../JitsiTrackError");
32
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
33
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
33
 }
34
 }
34
 
35
 
36
+// Load the integration of a third-party analytics API such as Google Analytics.
37
+// Since we cannot guarantee the quality of the third-party service (e.g. their
38
+// server may take noticeably long time to respond), it is in our best interest
39
+// (in the sense that the intergration of the analytics API is important to us
40
+// but not enough to allow it to prevent people from joining a conference) to
41
+// download the API asynchronously. Additionally, Google Analytics will download
42
+// its implementation asynchronously anyway so it makes sense to append the
43
+// loading on our side rather than prepend it.
44
+function loadAnalytics() {
45
+    JitsiMeetJS.util.ScriptUtil.loadScript(
46
+        'analytics.js?v=1',
47
+        /* async */ true,
48
+        /* prepend */ false);
49
+}
50
+
35
 /**
51
 /**
36
  * Log stats via the focus once every this many milliseconds.
52
  * Log stats via the focus once every this many milliseconds.
37
  */
53
  */
66
     return err;
82
     return err;
67
 }
83
 }
68
 
84
 
85
+/**
86
+ * Init statistic options
87
+ * @param options
88
+ */
89
+Statistics.init = function (options) {
90
+    Statistics.audioLevelsEnabled = !options.disableAudioLevels;
91
+
92
+    if(typeof options.audioLevelsInterval === 'number') {
93
+        Statistics.audioLevelsInterval = options.audioLevelsInterval;
94
+    }
95
+
96
+    Statistics.disableThirdPartyRequests = options.disableThirdPartyRequests;
97
+
98
+    if (Statistics.disableThirdPartyRequests !== true)
99
+        loadAnalytics();
100
+}
101
+
69
 function Statistics(xmpp, options) {
102
 function Statistics(xmpp, options) {
70
     this.rtpStats = null;
103
     this.rtpStats = null;
71
     this.eventEmitter = new EventEmitter();
104
     this.eventEmitter = new EventEmitter();
76
             // Even though AppID and AppSecret may be specified, the integration
109
             // Even though AppID and AppSecret may be specified, the integration
77
             // of callstats.io may be disabled because of globally-disallowed
110
             // of callstats.io may be disabled because of globally-disallowed
78
             // requests to any third parties.
111
             // requests to any third parties.
79
-            && (this.options.disableThirdPartyRequests !== true);
112
+            && (Statistics.disableThirdPartyRequests !== true);
80
     if(this.callStatsIntegrationEnabled)
113
     if(this.callStatsIntegrationEnabled)
81
         loadCallStatsAPI();
114
         loadCallStatsAPI();
82
     this.callStats = null;
115
     this.callStats = null;
88
 }
121
 }
89
 Statistics.audioLevelsEnabled = false;
122
 Statistics.audioLevelsEnabled = false;
90
 Statistics.audioLevelsInterval = 200;
123
 Statistics.audioLevelsInterval = 200;
124
+Statistics.disableThirdPartyRequests = false;
125
+Statistics.analytics = AnalyticsAdapter;
91
 
126
 
92
 /**
127
 /**
93
  * Array of callstats instances. Used to call Statistics static methods and
128
  * Array of callstats instances. Used to call Statistics static methods and

+ 2
- 2
modules/xmpp/JingleSessionPC.js View File

12
 var RTCBrowserType = require("../RTC/RTCBrowserType");
12
 var RTCBrowserType = require("../RTC/RTCBrowserType");
13
 var RTC = require("../RTC/RTC");
13
 var RTC = require("../RTC/RTC");
14
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
14
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
15
-var AnalyticsAdapter = require("../statistics/AnalyticsAdapter");
15
+var Statistics = require("../statistics/statistics");
16
 
16
 
17
 /**
17
 /**
18
  * Constant tells how long we're going to wait for IQ response, before timeout
18
  * Constant tells how long we're going to wait for IQ response, before timeout
133
             self.peerconnection.iceConnectionState] = now;
133
             self.peerconnection.iceConnectionState] = now;
134
         logger.log("(TIME) ICE " + self.peerconnection.iceConnectionState +
134
         logger.log("(TIME) ICE " + self.peerconnection.iceConnectionState +
135
                     ":\t", now);
135
                     ":\t", now);
136
-        AnalyticsAdapter.sendEvent(
136
+        Statistics.analytics.sendEvent(
137
             'ice.' + self.peerconnection.iceConnectionState, now);
137
             'ice.' + self.peerconnection.iceConnectionState, now);
138
         switch (self.peerconnection.iceConnectionState) {
138
         switch (self.peerconnection.iceConnectionState) {
139
             case 'connected':
139
             case 'connected':

+ 5
- 4
modules/xmpp/strophe.jingle.js View File

6
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
6
 var XMPPEvents = require("../../service/xmpp/XMPPEvents");
7
 var RTCBrowserType = require("../RTC/RTCBrowserType");
7
 var RTCBrowserType = require("../RTC/RTCBrowserType");
8
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
8
 var GlobalOnErrorHandler = require("../util/GlobalOnErrorHandler");
9
-var AnalyticsAdapter = require("../statistics/AnalyticsAdapter");
9
+var Statistics = require("../statistics/statistics");
10
 
10
 
11
 module.exports = function(XMPP, eventEmitter) {
11
 module.exports = function(XMPP, eventEmitter) {
12
     Strophe.addConnectionPlugin('jingle', {
12
     Strophe.addConnectionPlugin('jingle', {
101
                             .up();
101
                             .up();
102
                         this.terminate(sess.sid);
102
                         this.terminate(sess.sid);
103
                     }
103
                     }
104
-                    AnalyticsAdapter.sendEvent('xmpp.session-initiate', now);
104
+                    Statistics.analytics.sendEvent(
105
+                        'xmpp.session-initiate', now);
105
                     break;
106
                     break;
106
                 case 'session-terminate':
107
                 case 'session-terminate':
107
                     logger.log('terminating...', sess.sid);
108
                     logger.log('terminating...', sess.sid);
117
                 case 'transport-replace':
118
                 case 'transport-replace':
118
                     var now = window.performance.now();
119
                     var now = window.performance.now();
119
                     logger.info("(TIME) Start transport replace", now);
120
                     logger.info("(TIME) Start transport replace", now);
120
-                    AnalyticsAdapter.sendEvent(
121
+                    Statistics.analytics.sendEvent(
121
                         'xmpp.transport-replace.start', now);
122
                         'xmpp.transport-replace.start', now);
122
 
123
 
123
                     sess.replaceTransport($(iq).find('>jingle'),
124
                     sess.replaceTransport($(iq).find('>jingle'),
125
                             var now = window.performance.now();
126
                             var now = window.performance.now();
126
                             logger.info(
127
                             logger.info(
127
                                 "(TIME) Transport replace success!", now);
128
                                 "(TIME) Transport replace success!", now);
128
-                            AnalyticsAdapter.sendEvent(
129
+                            Statistics.analytics.sendEvent(
129
                                 'xmpp.transport-replace.success', now);
130
                                 'xmpp.transport-replace.success', now);
130
                         },
131
                         },
131
                         function(error) {
132
                         function(error) {

Loading…
Cancel
Save