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

This commit is contained in:
Lyubomir Marinov 2016-01-15 15:42:04 +02:00
parent 4a6e8e5e6e
commit b49a08c485
4 changed files with 69 additions and 32 deletions

View File

@ -222,24 +222,5 @@
<a id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]feedback"><i class="fa fa-heart"></i></a>
</div>
</div>
<script type="text/javascript">
if (!config.disableThirdPartyRequests) {
[
// FIXME The implementation provided by analytics.js starts an
// asynchronous download of the Google Analytics integration.
// Unfortunately, modules/statistics/AnalyticsAdapter.js has already
// made the decision to not use it at the current point of the
// execution and has initialized itself with the NoopAnalytics
// implementation i.e. the implementation of the method
// AnalyticsAdapter.sendEvent will amount to nothing.
'analytics.js?v=1'
].forEach(function(extSrc) {
var extScript = document.createElement('script');
extScript.src = extSrc;
extScript.async = false;
document.head.appendChild(extScript);
});
}
</script>
</body>
</html>

View File

@ -1,15 +1,43 @@
var ScriptUtil = require('../util/ScriptUtil');
// Load the integration of a third-party analytics API such as Google Analytics.
// Since we cannot guarantee the quality of the third-party service (e.g. their
// server may take noticeably long time to respond), it is in our best interest
// (in the sense that the intergration of the analytics API is important to us
// but not enough to allow it to prevent people from joining a conference) to
// download the API asynchronously. Additionally, Google Analytics will download
// its implementation asynchronously anyway so it makes sense to append the
// loading on our side rather than prepend it.
if (config.disableThirdPartyRequests !== true) {
ScriptUtil.loadScript(
'analytics.js?v=1',
/* async */ true,
/* prepend */ false);
}
// NoopAnalytics
function NoopAnalytics() {}
NoopAnalytics.prototype.sendEvent = function () {};
// AnalyticsAdapter
function AnalyticsAdapter() {
var AnalyticsImpl = window.Analytics || NoopAnalytics;
this.analytics = new AnalyticsImpl();
// XXX Since we asynchronously load the integration of the analytics API and
// the analytics API may asynchronously load its implementation (e.g. Google
// Analytics), we cannot make the decision with respect to which analytics
// implementation we will use here and we have to postpone it i.e. we will
// make a lazy decision.
}
AnalyticsAdapter.prototype.sendEvent = function (action, data) {
var a = this.analytics;
if (a === null || typeof a === 'undefined') {
this.analytics = a = new (window.Analytics || NoopAnalytics)();
}
try {
this.analytics.sendEvent.apply(this.analytics, arguments);
a.sendEvent.apply(a, arguments);
} catch (ignored) {}
};
module.exports = new AnalyticsAdapter();
module.exports = new AnalyticsAdapter();

View File

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

View File

@ -0,0 +1,32 @@
/**
* Implements utility functions which facilitate the dealing with scripts such
* as the download and execution of a JavaScript file.
*/
var ScriptUtil = {
/**
* Loads a script from a specific source.
*
* @param src the source from the which the script is to be (down)loaded
* @param async true to asynchronously load the script or false to
* synchronously load the script
* @param prepend true to schedule the loading of the script as soon as
* possible or false to schedule the loading of the script at the end of the
* scripts known at the time
*/
loadScript: function (src, async, prepend) {
var d = document;
var tagName = 'script';
var script = d.createElement(tagName);
var referenceNode = d.getElementsByTagName(tagName)[0];
script.async = async;
script.src = src;
if (prepend) {
referenceNode.parentNode.insertBefore(script, referenceNode);
} else {
referenceNode.parentNode.appendChild(script);
}
},
};
module.exports = ScriptUtil;