diff --git a/static/cosmopolite.js b/static/cosmopolite.js index 175b160..6b4f0c1 100644 --- a/static/cosmopolite.js +++ b/static/cosmopolite.js @@ -43,8 +43,9 @@ String.prototype.hashCode = function() { * @param {?Cosmopolite.typeCallbacks=} opt_callbacks * @param {?string=} opt_urlPrefix * @param {?string=} opt_namespace + * @param {?string=} opt_trackingID */ -var Cosmopolite = function(opt_callbacks, opt_urlPrefix, opt_namespace) { +var Cosmopolite = function(opt_callbacks, opt_urlPrefix, opt_namespace, opt_trackingID) { /** * @type {Cosmopolite.typeCallbacks} * @private @@ -58,7 +59,8 @@ var Cosmopolite = function(opt_callbacks, opt_urlPrefix, opt_namespace) { this.urlPrefix_ = opt_urlPrefix || '/cosmopolite'; /** * @type {string} - * @private */ + * @private + */ this.namespace_ = opt_namespace || 'cosmopolite'; /** @@ -119,18 +121,45 @@ var Cosmopolite = function(opt_callbacks, opt_urlPrefix, opt_namespace) { } /** @type {Array.} */ - var scriptUrls = [ + var scriptURLs = [ '/_ah/channel/jsapi' ]; + if (opt_trackingID) { + scriptURLs.push('https://www.google-analytics.com/analytics.js'); + /** + * @type {string} + * @private + */ + this.analyticsObjName_ = this.uuid_(); + window['GoogleAnalyticsObject'] = this.analyticsObjName_; + var settings = { + 'storage': 'none', + 'clientId': localStorage[this.namespace_ + ':tracking_client_id'] + }; + var saveCallback = function(analytics) { + localStorage[this.namespace_ + ':tracking_client_id'] = + analytics.get('clientId'); + }.bind(this); + window[this.analyticsObjName_] = { + 'l': 1 * new Date(), + 'q': [ + ['create', opt_trackingID, settings], + [saveCallback], + ['send', 'event', 'cosmopolite', 'load'] + ] + }; + } + /** * @type {number} * @private */ - this.numScriptsToLoad_ = scriptUrls.length; - scriptUrls.forEach(function(scriptUrl) { + this.numScriptsToLoad_ = scriptURLs.length; + scriptURLs.forEach(function(scriptURL) { /** @type {Node} */ var script = document.createElement('script'); - script.src = scriptUrl; + script.src = scriptURL; + script.async = true; script.onload = this.onLoad_.bind(this); document.body.appendChild(script); }, this); @@ -285,17 +314,22 @@ Cosmopolite.prototype.subscribe = function(subject, opt_messages, opt_last_id) { } this.sendRPC_('subscribe', args, function(response) { - // unsubscribe may have been called since we sent the RPC. That's racy - // without waiting for the promise, but do our best - if (subjectString in this.subscriptions_) { - this.subscriptions_[subjectString].state = - Cosmopolite.SubscriptionState_.ACTIVE; - } /** @type {string} */ var result = response['result']; if (result == 'ok') { + // unsubscribe may have been called since we sent the RPC. That's racy + // without waiting for the promise, but do our best + if (subjectString in this.subscriptions_) { + this.subscriptions_[subjectString].state = + Cosmopolite.SubscriptionState_.ACTIVE; + } resolve(); + if (this.analyticsObj_) { + this.analyticsObj_( + 'send', 'event', 'cosmopolite', 'subscribe', subjectString); + } } else { + delete this.subscriptions_[subjectString]; reject(); } }); @@ -577,6 +611,14 @@ Cosmopolite.prototype.onLoad_ = function() { // Shutdown during startup return; } + if (this.analyticsObjName_) { + /** + * @type {Object} + * @private + */ + this.analyticsObj_ = window[this.analyticsObjName_]; + delete window[this.analyticsObjName_]; + } this.registerMessageHandlers_(); this.createChannel_(); }; @@ -1183,6 +1225,9 @@ Cosmopolite.prototype.onServerEvent_ = function(e) { if (e['profile']) { /** @type {string} */ this.profile_ = e['profile']; + if (this.analyticsObj_) { + this.analyticsObj_('set', 'userId', this.profile_); + } this.profilePromises_.forEach(function(resolve) { resolve(this.profile_); }, this); diff --git a/static/debug.js b/static/debug.js index 79a1144..c34aea5 100644 --- a/static/debug.js +++ b/static/debug.js @@ -32,7 +32,7 @@ var onReady = function() { 'onPin': onPin, 'onUnpin': onUnpin, } - cosmo = new Cosmopolite(callbacks); + cosmo = new Cosmopolite(callbacks, null, null, 'UA-37845853-3'); elements['messageText'].addEventListener('keypress', messageKeyPress); elements['pinText'].addEventListener('keypress', pinKeyPress);