pyc-website
main website for pyc inc.
git clone https://9o.is/git/pyc-website.git
commit a2f0d93eeece123d50a531f6ae58c2a9a18afdcd parent 22107e9ae2a61813660bc7b106e7f8493ed15c1e Author: Jul <jul@9o.is> Date: Wed, 4 Jun 2014 22:42:07 -0400 using angular-google-analytics so ui-router page changes are recorded Diffstat:
| M | build.config.js | | | 3 | ++- |
| M | src/main/scala/bootstrap/liftweb/Boot.scala | | | 2 | +- |
| M | src/main/scala/inc/pyc/config/GoogleAnalytics.scala | | | 23 | +++++++++++++++++++++-- |
| M | src/main/webapp/app/App.js | | | 2 | +- |
| A | src/main/webapp/vendor/angular-google-analytics.js | | | 346 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 371 insertions(+), 5 deletions(-)
diff --git a/build.config.js b/build.config.js @@ -63,7 +63,8 @@ module.exports = { "<%= dirs.vendor %>/angular-google-maps.min.js", "<%= dirs.vendor %>/ui-mask.js", "<%= dirs.vendor %>/liftAjax.js", - "<%= dirs.vendor %>/angular-file-upload.js" + "<%= dirs.vendor %>/angular-file-upload.js", + "<%= dirs.vendor %>/angular-google-analytics.js" ], css: [ ], diff --git a/src/main/scala/bootstrap/liftweb/Boot.scala b/src/main/scala/bootstrap/liftweb/Boot.scala @@ -81,7 +81,7 @@ class Boot extends Loggable { Mailer.testModeSend.default.set((m: MimeMessage) => logger.info("Test mode message:\n" + prettyPrintMime(m))) // Google Analytics - GoogleAnalytics.init + GoogleAnalytics.angularInit() LiftRules.statelessDispatch.append(inc.pyc.snippet.Sitemap) diff --git a/src/main/scala/inc/pyc/config/GoogleAnalytics.scala b/src/main/scala/inc/pyc/config/GoogleAnalytics.scala @@ -2,7 +2,7 @@ package inc.pyc package config import net.liftweb.util.Props -import net.liftweb.common.Loggable +import net.liftweb.common._ import net.liftweb.http.js.JsCmd import net.liftweb.http.Req import net.liftweb.http.LiftSession @@ -17,10 +17,12 @@ import net.liftweb.http.S * https://github.com/d6y/liftmodules-googleanalytics/issues/4 */ object GoogleAnalytics extends Loggable { + + val id: Box[String] = Props.get("google.analytics.id") def init: Unit = init( ()⇒true ) - def init(includeTest: () ⇒ Boolean): Unit = Props.get("google.analytics.id") map Async.headJs foreach { js => + def init(includeTest: () ⇒ Boolean): Unit = id map Async.headJs foreach { js => def addTracking(s: LiftSession, r: Req) : Unit = if (includeTest()) S.putInHead(js) LiftSession.onBeginServicing = addTracking _ :: LiftSession.onBeginServicing } @@ -43,6 +45,23 @@ object GoogleAnalytics extends Loggable { } } + /** + * Configures an angular module with angular-google-analytics + * https://github.com/revolunet/angular-google-analytics + */ + def angularInit(moduleName: String = "app", analytics: Boolean = true): Unit = id foreach { id => + val config = + moduleName + """.config(function(AnalyticsProvider) { + AnalyticsProvider.setAccount('"""+id+"""'); + AnalyticsProvider.useAnalytics("""+analytics.toString+"""); + AnalyticsProvider.setPageEvent('$stateChangeSuccess'); + }); + """ + + moduleName + ".run(function(Analytics){});" + + def addTracking(s: LiftSession, r: Req) : Unit = S.putAtEndOfBody(<script>{config}</script>) + LiftSession.onBeginServicing = addTracking _ :: LiftSession.onBeginServicing + } } diff --git a/src/main/webapp/app/App.js b/src/main/webapp/app/App.js @@ -1,4 +1,4 @@ -var app = angular.module("app", ['google-maps', 'ui.bootstrap', 'ui.router', 'ui.mask', 'angularFileUpload']); +var app = angular.module("app", ['google-maps', 'ui.bootstrap', 'ui.router', 'ui.mask', 'angularFileUpload', 'angular-google-analytics']); var ZIP_CODE_REGEXP = /^(\d{5}(-\d{4})?|[A-Z]\d[A-Z] *\d[A-Z]\d)$/; var PASSWORD_REGEXP = /^(?=.*[^a-zA-Z])\S{8,}$/; diff --git a/src/main/webapp/vendor/angular-google-analytics.js b/src/main/webapp/vendor/angular-google-analytics.js @@ -0,0 +1,346 @@ +/* global angular, console */ + +'use strict'; + +angular.module('angular-google-analytics', []) + .provider('Analytics', function() { + var created = false, + trackRoutes = true, + accountId, + trackPrefix = '', + domainName, + analyticsJS = false, + pageEvent = '$routeChangeSuccess', + cookieConfig = 'auto', + ecommerce = false, + enhancedLinkAttribution = false, + removeRegExp, + experimentId, + ignoreFirstPageLoad = false; + + this._logs = []; + + // config methods + this.setAccount = function(id) { + accountId = id; + return true; + }; + this.trackPages = function(doTrack) { + trackRoutes = doTrack; + return true; + }; + + this.trackPrefix = function(prefix) { + trackPrefix = prefix; + return true; + }; + + this.setDomainName = function(domain) { + domainName = domain; + return true; + }; + + this.useAnalytics = function(val) { + analyticsJS = !!val; + return true; + }; + + this.useEnhancedLinkAttribution = function (val) { + enhancedLinkAttribution = !!val; + return true; + }; + + this.setPageEvent = function(name) { + pageEvent = name; + return true; + }; + + this.setCookieConfig = function (config) { + cookieConfig = config; + return true; + }; + + this.useECommerce = function (val) { + ecommerce = !!val; + return true; + }; + + this.setRemoveRegExp = function (regex) { + if (regex instanceof RegExp) { + removeRegExp = regex; + return true; + } + return false; + }; + + this.setExperimentId = function (id) { + experimentId = id; + return true; + }; + + this.ignoreFirstPageLoad = function (val) { + ignoreFirstPageLoad = !!val; + }; + + // public service + this.$get = ['$document', '$rootScope', '$location', '$window', function($document, $rootScope, $location, $window) { + var getUrl = function () { + var url = $location.path(); + if (removeRegExp) { + return url.replace(removeRegExp, ''); + } + return url; + }; + + // private methods + function _createScriptTag() //noinspection JSValidateTypes + { + // inject the google analytics tag + if (!accountId) return; + $window._gaq = []; + $window._gaq.push(['_setAccount', accountId]); + if (enhancedLinkAttribution) { + $window._gaq.push(['_require', 'inpage_linkid', '//www.google-analytics.com/plugins/ga/inpage_linkid.js']); + } + if (trackRoutes && !ignoreFirstPageLoad) { + if (removeRegExp) { + $window._gaq.push(['_trackPageview', getUrl()]); + } else { + $window._gaq.push(['_trackPageview']); + } + } + if(domainName) $window._gaq.push(['_setDomainName', domainName]); + (function() { + var document = $document[0]; + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = ('https:' === document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); + })(); + created = true; + } + function _createAnalyticsScriptTag() { + if (!accountId) { + return console.warn('No account id set for Analytics.js'); + } + + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments);},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m); + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + $window.ga('create', accountId, cookieConfig); + + if (trackRoutes && !ignoreFirstPageLoad) { + $window.ga('send', 'pageview', getUrl()); + } + + if ($window.ga) { + if (ecommerce) { + $window.ga('require', 'ecommerce', 'ecommerce.js'); + } + if (enhancedLinkAttribution) { + $window.ga('require', 'linkid', 'linkid.js'); + } + if (experimentId) { + var expScript = document.createElement('script'), + s = document.getElementsByTagName('script')[0]; + expScript.src = "//www.google-analytics.com/cx/api.js?experiment=" + experimentId; + s.parentNode.insertBefore(expScript, s); + } + + } + + } + this._log = function() { + // for testing + //console.info('analytics log:', arguments); + this._logs.push(arguments); + }; + this._trackPage = function(url, title) { + title = title ? title : $document[0].title; + if (trackRoutes && !analyticsJS && $window._gaq) { + // http://stackoverflow.com/questions/7322288/how-can-i-set-a-page-title-with-google-analytics + $window._gaq.push(["_set", "title", title]); + $window._gaq.push(['_trackPageview', trackPrefix + url]); + this._log('_trackPageview', arguments); + } else if (trackRoutes && analyticsJS && $window.ga) { + $window.ga('send', 'pageview', { + 'page': trackPrefix + url, + 'title': title + }); + this._log('pageview', arguments); + } + }; + this._trackEvent = function(category, action, label, value) { + if (!analyticsJS && $window._gaq) { + $window._gaq.push(['_trackEvent', category, action, label, value]); + this._log('trackEvent', arguments); + } else if ($window.ga) { + $window.ga('send', 'event', category, action, label, value); + this._log('event', arguments); + } + + }; + + /** + * Add transaction + * https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addTrans + * https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addTrans + * @param transactionId + * @param affiliation + * @param total + * @param tax + * @param shipping + * @param city + * @param state + * @param country + * @private + */ + this._addTrans = function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) { + if (!analyticsJS && $window._gaq) { + $window._gaq.push(['_addTrans', transactionId, affiliation, total, tax, shipping, city, state, country]); + this._log('_addTrans', arguments); + } else if ($window.ga) { + if (!ecommerce) { + console.warn('ecommerce no set. Use AnalyticsProvider.setECommerce(true);'); + } else { + $window.ga('ecommerce:addTransaction', { + id: transactionId, + affiliation: affiliation, + revenue: total, + tax: tax, + shipping: shipping, + currency: currency || 'USD' + }); + this._log('ecommerce:addTransaction', arguments); + } + + } + }; + + /** + * Add item to transaction + * https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._addItem + * https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#addItem + * @param transactionId + * @param sku + * @param name + * @param category + * @param price + * @param quantity + * @private + */ + this._addItem = function (transactionId, sku, name, category, price, quantity) { + if (!analyticsJS && $window._gaq) { + $window._gaq.push(['_addItem', transactionId, sku, name, category, price, quantity]); + this._log('_addItem', arguments); + } else if ($window.ga) { + $window.ga('ecommerce:addItem', { + id: transactionId, + name: name, + sku: sku, + category: category, + price: price, + quantity: quantity + }); + this._log('ecommerce:addItem', arguments); + } + }; + + /** + * Track transaction + * https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiEcommerce#_gat.GA_Tracker_._trackTrans + * https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#sendingData + * @private + */ + this._trackTrans = function () { + if (!analyticsJS && $window._gaq) { + $window._gaq.push(['_trackTrans']); + this._log('_trackTrans', arguments); + } else if ($window.ga) { + $window.ga('ecommerce:send'); + this._log('ecommerce:send', arguments); + } + + }; + + /** + * Clear transaction + * https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce#clearingData + * + * @private + */ + this._clearTrans = function () { + if ($window.ga) { + $window.ga('ecommerce:clear'); + this._log('ecommerce:clear', arguments); + } + }; + + /** + * Send custom events + * https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#implementation + * https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions#implementation + * + * @param obj + * @private + */ + this._send = function (obj) { + if ($window.ga) { + $window.ga('send', obj); + this._log('send', obj); + } + }; + + + + // creates the ganalytics tracker + if (analyticsJS) { + _createAnalyticsScriptTag(); + } else { + _createScriptTag(); + } + + + var me = this; + + // activates page tracking + if (trackRoutes) $rootScope.$on(pageEvent, function() { + me._trackPage(getUrl()); + }); + + return { + _logs: me._logs, + cookieConfig: cookieConfig, + ecommerce: ecommerce, + enhancedLinkAttribution: enhancedLinkAttribution, + getUrl: getUrl, + experimentId: experimentId, + ignoreFirstPageLoad: ignoreFirstPageLoad, + trackPage: function(url, title) { + // add a page event + me._trackPage(url, title); + }, + trackEvent: function(category, action, label, value) { + // add an action event + me._trackEvent(category, action, label, value); + }, + addTrans: function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) { + me._addTrans(transactionId, affiliation, total, tax, shipping, city, state, country, currency); + }, + addItem: function (transactionId, sku, name, category, price, quantity) { + me._addItem(transactionId, sku, name, category, price, quantity); + }, + trackTrans: function () { + me._trackTrans(); + }, + clearTrans: function () { + me._clearTrans(); + }, + send: function (obj) { + me._send(obj); + } + }; + }]; + + });