Browse Source

Decides whether to use analytics after the analytics API has been given a chance to load.

j8
Lyubomir Marinov 9 years ago
parent
commit
b49a08c485
4 changed files with 69 additions and 32 deletions
  1. 0
    19
      index.html
  2. 32
    4
      modules/statistics/AnalyticsAdapter.js
  3. 5
    9
      modules/statistics/CallStats.js
  4. 32
    0
      modules/util/ScriptUtil.js

+ 0
- 19
index.html View File

222
             <a id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]feedback"><i class="fa fa-heart"></i></a>
222
             <a id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]feedback"><i class="fa fa-heart"></i></a>
223
         </div>
223
         </div>
224
     </div>
224
     </div>
225
-    <script type="text/javascript">
226
-      if (!config.disableThirdPartyRequests) {
227
-        [
228
-          // FIXME The implementation provided by analytics.js starts an
229
-          // asynchronous download of the Google Analytics integration.
230
-          // Unfortunately, modules/statistics/AnalyticsAdapter.js has already
231
-          // made the decision to not use it at the current point of the
232
-          // execution and has initialized itself with the NoopAnalytics
233
-          // implementation i.e. the implementation of the method
234
-          // AnalyticsAdapter.sendEvent will amount to nothing.
235
-          'analytics.js?v=1'
236
-        ].forEach(function(extSrc) {
237
-          var extScript = document.createElement('script');
238
-          extScript.src = extSrc;
239
-          extScript.async = false;
240
-          document.head.appendChild(extScript);
241
-        });
242
-      }
243
-    </script>
244
   </body>
225
   </body>
245
 </html>
226
 </html>

+ 32
- 4
modules/statistics/AnalyticsAdapter.js View File

1
+var ScriptUtil = require('../util/ScriptUtil');
2
+
3
+// Load the integration of a third-party analytics API such as Google Analytics.
4
+// Since we cannot guarantee the quality of the third-party service (e.g. their
5
+// server may take noticeably long time to respond), it is in our best interest
6
+// (in the sense that the intergration of the analytics API is important to us
7
+// but not enough to allow it to prevent people from joining a conference) to
8
+// download the API asynchronously. Additionally, Google Analytics will download
9
+// its implementation asynchronously anyway so it makes sense to append the
10
+// loading on our side rather than prepend it.
11
+if (config.disableThirdPartyRequests !== true) {
12
+    ScriptUtil.loadScript(
13
+            'analytics.js?v=1',
14
+            /* async */ true,
15
+            /* prepend */ false);
16
+}
17
+
18
+// NoopAnalytics
1
 function NoopAnalytics() {}
19
 function NoopAnalytics() {}
20
+
2
 NoopAnalytics.prototype.sendEvent = function () {};
21
 NoopAnalytics.prototype.sendEvent = function () {};
3
 
22
 
23
+// AnalyticsAdapter
4
 function AnalyticsAdapter() {
24
 function AnalyticsAdapter() {
5
-  var AnalyticsImpl = window.Analytics || NoopAnalytics;
6
-  this.analytics = new AnalyticsImpl();
25
+    // XXX Since we asynchronously load the integration of the analytics API and
26
+    // the analytics API may asynchronously load its implementation (e.g. Google
27
+    // Analytics), we cannot make the decision with respect to which analytics
28
+    // implementation we will use here and we have to postpone it i.e. we will
29
+    // make a lazy decision.
7
 }
30
 }
8
 
31
 
9
 AnalyticsAdapter.prototype.sendEvent = function (action, data) {
32
 AnalyticsAdapter.prototype.sendEvent = function (action, data) {
33
+  var a = this.analytics;
34
+
35
+  if (a === null || typeof a === 'undefined') {
36
+      this.analytics = a = new (window.Analytics || NoopAnalytics)();
37
+  }
10
   try {
38
   try {
11
-    this.analytics.sendEvent.apply(this.analytics, arguments);
39
+      a.sendEvent.apply(a, arguments);
12
   } catch (ignored) {}
40
   } catch (ignored) {}
13
 };
41
 };
14
 
42
 
15
-module.exports = new AnalyticsAdapter();
43
+module.exports = new AnalyticsAdapter();

+ 5
- 9
modules/statistics/CallStats.js View File

1
 /* global config, $, APP, Strophe, callstats */
1
 /* global config, $, APP, Strophe, callstats */
2
 
2
 
3
 var Settings = require('../settings/Settings');
3
 var Settings = require('../settings/Settings');
4
+var ScriptUtil = require('../util/ScriptUtil');
4
 var jsSHA = require('jssha');
5
 var jsSHA = require('jssha');
5
 var io = require('socket.io-client');
6
 var io = require('socket.io-client');
6
 var callStats = null;
7
 var callStats = null;
49
     // enough to allow it to prevent people from joining a conference) to (1)
50
     // enough to allow it to prevent people from joining a conference) to (1)
50
     // start downloading their API as soon as possible and (2) do the
51
     // start downloading their API as soon as possible and (2) do the
51
     // downloading asynchronously.
52
     // downloading asynchronously.
52
-    (function (d, src) {
53
-        var elementName = 'script';
54
-        var newScript = d.createElement(elementName);
55
-        var referenceNode = d.getElementsByTagName(elementName)[0];
56
-
57
-        newScript.async = true;
58
-        newScript.src = src;
59
-        referenceNode.parentNode.insertBefore(newScript, referenceNode);
60
-    })(document, 'https://api.callstats.io/static/callstats.min.js');
53
+    ScriptUtil.loadScript(
54
+            'https://api.callstats.io/static/callstats.min.js',
55
+            /* async */ true,
56
+            /* prepend */ true);
61
     // FIXME At the time of this writing, we hope that the callstats.io API will
57
     // FIXME At the time of this writing, we hope that the callstats.io API will
62
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
58
     // have loaded by the time we needed it (i.e. CallStats.init is invoked).
63
 }
59
 }

+ 32
- 0
modules/util/ScriptUtil.js View File

1
+/**
2
+ * Implements utility functions which facilitate the dealing with scripts such
3
+ * as the download and execution of a JavaScript file.
4
+ */
5
+var ScriptUtil = {
6
+    /**
7
+     * Loads a script from a specific source.
8
+     *
9
+     * @param src the source from the which the script is to be (down)loaded
10
+     * @param async true to asynchronously load the script or false to
11
+     * synchronously load the script
12
+     * @param prepend true to schedule the loading of the script as soon as
13
+     * possible or false to schedule the loading of the script at the end of the
14
+     * scripts known at the time
15
+     */
16
+    loadScript: function (src, async, prepend) {
17
+        var d = document;
18
+        var tagName = 'script';
19
+        var script = d.createElement(tagName);
20
+        var referenceNode = d.getElementsByTagName(tagName)[0];
21
+
22
+        script.async = async;
23
+        script.src = src;
24
+        if (prepend) {
25
+            referenceNode.parentNode.insertBefore(script, referenceNode);
26
+        } else {
27
+            referenceNode.parentNode.appendChild(script);
28
+        }
29
+    },
30
+};
31
+
32
+module.exports = ScriptUtil;

Loading…
Cancel
Save