From 62c1ed7752a95e87455a9f014abfc49290a8e024 Mon Sep 17 00:00:00 2001 From: Dan Wilkerson Date: Sun, 28 Aug 2016 10:03:40 -0400 Subject: [PATCH 1/5] beginning to add update --- lib/angulartics-ga.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/angulartics-ga.js b/lib/angulartics-ga.js index c6e44ff..54e96cc 100644 --- a/lib/angulartics-ga.js +++ b/lib/angulartics-ga.js @@ -16,6 +16,12 @@ angular.module('angulartics.google.analytics', ['angulartics']) // Set the default settings for this module $analyticsProvider.settings.ga = { additionalAccountNames: undefined, + // Select hits to send to all additional accounts + additionalAccountConfig: { + pageview: true, + event: true, + + }, disableEventTracking: null, disablePageTracking: null, userId: null @@ -36,6 +42,19 @@ angular.module('angulartics.google.analytics', ['angulartics']) } } + function dispatchToGa(cmd) { + + if (detectUniversalAnalytics()) { + + + + } else if (detectClassicAnalytics()) { + + + } + + } + $analyticsProvider.registerPageTrack(function (path, properties) { // Do nothing if page tracking is disabled From aa0a779de8fd0b46463816e3d0b24511e102f9d2 Mon Sep 17 00:00:00 2001 From: Dan Wilkerson Date: Sun, 28 Aug 2016 17:46:16 -0400 Subject: [PATCH 2/5] Refactored code, added configs for duplicating hits --- lib/angulartics-ga.js | 376 ++++++++++++++++++++++++++++++------------ 1 file changed, 273 insertions(+), 103 deletions(-) diff --git a/lib/angulartics-ga.js b/lib/angulartics-ga.js index 54e96cc..83689c5 100644 --- a/lib/angulartics-ga.js +++ b/lib/angulartics-ga.js @@ -17,65 +17,41 @@ angular.module('angulartics.google.analytics', ['angulartics']) $analyticsProvider.settings.ga = { additionalAccountNames: undefined, // Select hits to send to all additional accounts - additionalAccountConfig: { + additionalAccountHitTypes: { pageview: true, event: true, - + error: false, + timing: false, + setUserProperties: false, + userId: false }, disableEventTracking: null, disablePageTracking: null, userId: null }; - function dimensionsAndMetrics(properties) { - if (window.ga) { - // add custom dimensions and metrics - var customData = {}; - var key; - - for (key in properties) { - if (!key.indexOf('dimension') || !key.indexOf('metric')) { - customData[key] = properties[key]; - } - } - return customData; - } - } - - function dispatchToGa(cmd) { - - if (detectUniversalAnalytics()) { - - - - } else if (detectClassicAnalytics()) { - - - } - - } - + /** + * Track Pageview in GA + * @name pageTrack + * + * @param {string} path value of Page dimension stored with hit e.g. '/home' + * @param {object} properties Object with optional addtional Custom Dimensions/Metrics + * + * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/pages + * @link https://developers.google.com/analytics/devguides/collection/gajs/ + */ $analyticsProvider.registerPageTrack(function (path, properties) { + properties = properties || {}; + // Do nothing if page tracking is disabled if ($analyticsProvider.settings.ga.disablePageTracking) return; - if (window._gaq) { - _gaq.push(['_trackPageview', path]); - angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ - _gaq.push([accountName + '._trackPageview', path]); - }); - } - if (window.ga) { - var dimsAndMets = dimensionsAndMetrics(properties); - if ($analyticsProvider.settings.ga.userId) { - ga('set', 'userId', $analyticsProvider.settings.ga.userId); - } - ga('send', 'pageview', path, dimsAndMets); - angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ - ga(accountName +'.send', 'pageview', path, dimsAndMets); - }); - } + dispatchToGa('send', angular.extend(dimensionsAndMetrics(properties), { + hitType: 'pageview', + page: path + })); + }); /** @@ -89,18 +65,19 @@ angular.module('angulartics.google.analytics', ['angulartics']) * * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events */ - $analyticsProvider.registerEventTrack(eventTrack); - - function eventTrack (action, properties) { + $analyticsProvider.registerEventTrack(function(action, properties) { // Do nothing if event tracking is disabled if ($analyticsProvider.settings.ga.disableEventTracking) return; - // Google Analytics requires an Event Category - if (!properties || !properties.category) { - properties = properties || {}; - properties.category = 'Event'; + if (!action && action + '' !== '0') { + return console.log('Missing required argument action'); } + + // Sets default properties + properties = properties || {}; + properties.category = properties.category || 'Event'; + // GA requires that eventValue be an integer, see: // https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#eventValue // https://github.com/luisfarzati/angulartics/issues/81 @@ -117,43 +94,20 @@ angular.module('angulartics.google.analytics', ['angulartics']) // Making nonInteraction parameter more intuitive, includes backwards compatibilty // https://github.com/angulartics/angulartics-google-analytics/issues/49 - if (!properties.hasOwnProperty('nonInteraction')) { - properties.nonInteraction = properties.noninteraction; - } + properties.nonInteraction = properties.nonInteraction || properties.noninteraction; + + dispatchToGa('send', angular.extend(dimensionsAndMetrics(properties), { + hitType: 'event', + eventCategory: properties.category, + eventAction: action, + eventLabel: properties.label, + eventValue: properties.value, + nonInteraction: properties.nonInteraction, + page: properties.page || window.location.hash.substring(1) || window.location.pathname, + hitCallback: properties.hitCallback, + })); - if (window.ga) { - - var eventOptions = { - eventCategory: properties.category, - eventAction: action, - eventLabel: properties.label, - eventValue: properties.value, - nonInteraction: properties.nonInteraction, - page: properties.page || window.location.hash.substring(1) || window.location.pathname, - userId: $analyticsProvider.settings.ga.userId, - hitCallback: properties.hitCallback - }; - - // Round up any dimensions and metrics for this hit - var dimsAndMets = dimensionsAndMetrics(properties); - angular.extend(eventOptions, dimsAndMets); - - // Add transport settings - if($analyticsProvider.settings.ga.transport) { - angular.extend(eventOptions, $analyticsProvider.settings.ga.transport); - } - - ga('send', 'event', eventOptions); - - angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ - ga(accountName +'.send', 'event', eventOptions); - }); - - } else if (window._gaq) { - _gaq.push(['_trackEvent', properties.category, action, properties.label, properties.value, properties.nonInteraction]); - } - - } + }); /** * Exception Track Event in GA @@ -166,10 +120,14 @@ angular.module('angulartics.google.analytics', ['angulartics']) * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events */ $analyticsProvider.registerExceptionTrack(function (error, cause) { - eventTrack(error.toString(), { - category: 'Exceptions', - label: error.stack, - nonInteraction: true + dispatchToGa('send', { + hitType: 'event', + eventCategory: 'Exceptions', + eventAction: error.toString(), + eventLabel: error.stack, + nonInteraction: true, + page: window.location.hash.substring(1) || window.location.pathname, + isException: true }); }); @@ -194,11 +152,11 @@ angular.module('angulartics.google.analytics', ['angulartics']) * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#customs */ $analyticsProvider.registerSetUserProperties(function (properties) { - if(properties) { - // add custom dimensions and metrics to each hit - var dimsAndMets = dimensionsAndMetrics(properties); - ga('set', dimsAndMets); + + if (properties) { + dispatchToGa('set', dimensionsAndMetrics(properties)); } + }); /** @@ -215,15 +173,227 @@ angular.module('angulartics.google.analytics', ['angulartics']) * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings */ $analyticsProvider.registerUserTimings(function (properties) { - if (!properties || !properties.timingCategory || !properties.timingVar || typeof properties.timingValue === 'undefined') { - console.log('Properties timingCategory, timingVar, and timingValue are required to be set.'); - return; - } - if(window.ga) { - ga('send', 'timing', properties); + if (!angular.isObject(properties) || angular.isArray(properties)) { + return console.log('Required argument properties is missing or not an object'); } + + angular.forEach(['timingCategory', 'timingVar', 'timingValue'], function(prop) { + if (angular.isUndefined(properties[prop])) { + return console.log('Argument properties missing required property ' + prop); + } + }); + + dispatchToGa('send', { + hitType: 'timing', + timingCategory: properties.timingCategory, + timingVar: properties.timingVar, + timingValue: properties.timingVar, + timingLabel: properties.timingLabel, + page: properties.page || window.location.hash.substring(1) || window.location.pathname, + }); + }); + /** + * Detects if Universal Analytics is installed + * + * @name detectUniversalAnalytics + */ + function detectUniversalAnalytics() { + + // Use the GoogleAnalyticsObject property set by the default GA snippet + // to correctly determine the namespace of the GA global + var gaNamespace = window.GoogleAnalyticsObject; + return gaNamespace && window[gaNamespace]; + + } + + /** + * Detects if Classic Analytics is installed + * + * @name detectClassicAnalytics + */ + function detectClassicAnalytics() { + + // If _gaq is undefined, we're trusting Classic Analytics to be there + return typeof window._gaq !== 'undefined'; + + } + + /** + * Extract Custom Data for a hit + * @name dimensionsAndMetrics + * + * @param {object} properties properties object from an API call that is filtered for Custom Dimensions & Metrics + * + * @returns {object} customData object with only Custom Dimensions/Metrics from properties argument + */ + function dimensionsAndMetrics(properties) { + // add custom dimensions and metrics + var customData = {}; + var key; + + for (key in properties) { + // Keys must be dimensionXX or metricXX, e.g. dimension1, metric155, so + // if those strings aren't at zero (which evaluates to falsey), ignore + // the key + if (!key.indexOf('dimension') || !key.indexOf('metric')) { + customData[key] = properties[key]; + } + } + return customData; + } + + /** + * Handler for hits to GA. Dynamically adjusts syntax for + * targeted version based on global detection. + * + * @name dispatchToGa + * + * @param {string} command Standard Universal Analytics command (create, send, set) + * @param {object} fieldsObj object with hit-specific fields. Fields are whitelisted in handler - non-supported fields are ignored. + * + */ + var dispatchToGa = (function() { + + var handler; + + if (detectClassicAnalytics()) { + handler = dispatchToClassic_; + } + + if (detectUniversalAnalytics()) { + handler = dispatchToUniversal_; + } + + // If neither has been detected, GA is not above the angular code + if (!handler) { + console.log('Error: neither Classic nor Universal Analytics detected at bootstrap. Angulartics-GA will ignore all commands!'); + return angular.noop; + } + + return function(command, fieldsObj) { + + var hitType = fieldsObj.hitType === 'event' && fieldsObj.isException ? 'error' : fieldsObj.hitType; + var shouldCopyHit = $analyticsProvider.settings.ga.additionalAccountHitTypes[hitType]; + console.log(hitType); + console.log(shouldCopyHit); + console.log($analyticsProvider.settings.ga.additionalAccountHitTypes); + + handler(command, fieldsObj, shouldCopyHit); + + } + + /** + * Dispatches a hit using Universal syntax + * + * @name dispatchToUniversal_ + * @private + * + * @param {string} command Standard Universal Analytics command (create, send, set) + * @param {object} fieldsObj object with hit-specific fields. Fields are whitelisted in handler - non-supported fields are ignored. + * @param {boolean} shouldCopyHit should hit be propogated to all trackers + */ + function dispatchToUniversal_(command, fieldsObj, shouldCopyHit) { + + var gaNamespace = window.GoogleAnalyticsObject; + var userId = $analyticsProvider.settings.ga.userId; + + if (userId) fieldsObj.userId = userId; + + window[gaNamespace](command, fieldsObj); + + if (shouldCopyHit) { + + // If the userId shouldn't be duplicated, remove from the fieldsObj + if (userId && !$analyticsProvider.settings.ga.additionalAccountHitTypes.userId) { + delete fieldsObj.userId; + } + + angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ + + var accountCommand = accountName + '.' + command; + window[gaNamespace](accountCommand, fieldsObj) + + }); + + } + + } + + /** + * Dispatches a hit using Classic syntax + * Translates Universal Syntax to Classic syntax + * + * @name dispatchToClassic_ + * @private + * + * @param {string} command Standard Universal Analytics command (create, send, set) + * @param {object} fieldsObj object with hit-specific fields. Fields are whitelisted in handler - non-supported fields are ignored. + * @param {boolean} shouldCopyHit should hit be propogated to all trackers + */ + function dispatchToClassic_(command, fieldsObj, shouldCopyHit) { + + if (command === 'set') { + return console.log('Classic Analytics does not support the "set" command or Custom Dimensions. Command ignored.'); + } + + var classicCommand; + + // Transpose our syntax from Universal Analytics to Classic Analytics + // Currently we only support 'send' style commands + if (command === 'send') { + + switch(fieldsObj.hitType) { + case 'pageview': + classicCommand = ['_trackPageview', fieldsObj.page]; + break; + case 'event': + classicCommand = [ + '_trackEvent', + fieldsObj.category, + fieldsObj.action, + fieldsObj.label, + fieldsObj.value, + fieldsObj.nonInteraction + ]; + break; + case 'timing': + classicCommand = [ + '_trackTiming', + fieldsObj.timingCategory, + fieldsObj.timingVar, + fieldsObj.timingValue, + fieldsObj.timingLabel + ]; + break; + } + + } + + if (!classicCommand) { + console.log('Unable to find command ' + command + '. Hit ignored.'); + } + + // Issue our command to GA + window._gaq.push(classicCommand); + + if (shouldCopyHit) { + + angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ + + // Namespace the command as required per: + classicCommand[0] = accountName + '.' + classicCommand[0]; + window._gaq.push(classicCommand); + + }); + + } + + } + + })(); + }]); })(window, window.angular); From 48f61ed6881fcfb6baf87b13bc01d098411b19b5 Mon Sep 17 00:00:00 2001 From: Dan Wilkerson Date: Sun, 28 Aug 2016 18:01:14 -0400 Subject: [PATCH 3/5] Moved to using angular.isXXX funcs, rebuilt --- dist/angulartics-ga.min.js | 2 +- dist/angulartics-ga.min.js.map | 2 +- lib/angulartics-ga.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dist/angulartics-ga.min.js b/dist/angulartics-ga.min.js index dc728d0..b2645a0 100644 --- a/dist/angulartics-ga.min.js +++ b/dist/angulartics-ga.min.js @@ -1,2 +1,2 @@ -!function(window,angular,undefined){"use strict";angular.module("angulartics.google.analytics",["angulartics"]).config(["$analyticsProvider",function($analyticsProvider){function dimensionsAndMetrics(properties){if(window.ga){var key,customData={};for(key in properties)key.indexOf("dimension")&&key.indexOf("metric")||(customData[key]=properties[key]);return customData}}function eventTrack(action,properties){if(!$analyticsProvider.settings.ga.disableEventTracking){if(properties&&properties.category||(properties=properties||{},properties.category="Event"),properties.value){var parsed=parseInt(properties.value,10);properties.value=isNaN(parsed)?0:parsed}if(properties.hitCallback&&"function"!=typeof properties.hitCallback&&(properties.hitCallback=null),properties.hasOwnProperty("nonInteraction")||(properties.nonInteraction=properties.noninteraction),window.ga){var eventOptions={eventCategory:properties.category,eventAction:action,eventLabel:properties.label,eventValue:properties.value,nonInteraction:properties.nonInteraction,page:properties.page||window.location.hash.substring(1)||window.location.pathname,userId:$analyticsProvider.settings.ga.userId,hitCallback:properties.hitCallback},dimsAndMets=dimensionsAndMetrics(properties);angular.extend(eventOptions,dimsAndMets),$analyticsProvider.settings.ga.transport&&angular.extend(eventOptions,$analyticsProvider.settings.ga.transport),ga("send","event",eventOptions),angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){ga(accountName+".send","event",eventOptions)})}else window._gaq&&_gaq.push(["_trackEvent",properties.category,action,properties.label,properties.value,properties.nonInteraction])}}$analyticsProvider.settings.pageTracking.trackRelativePath=!0,$analyticsProvider.settings.ga={additionalAccountNames:undefined,disableEventTracking:null,disablePageTracking:null,userId:null},$analyticsProvider.registerPageTrack(function(path,properties){if(!$analyticsProvider.settings.ga.disablePageTracking&&(window._gaq&&(_gaq.push(["_trackPageview",path]),angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){_gaq.push([accountName+"._trackPageview",path])})),window.ga)){var dimsAndMets=dimensionsAndMetrics(properties);$analyticsProvider.settings.ga.userId&&ga("set","userId",$analyticsProvider.settings.ga.userId),ga("send","pageview",path,dimsAndMets),angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){ga(accountName+".send","pageview",path,dimsAndMets)})}}),$analyticsProvider.registerEventTrack(eventTrack),$analyticsProvider.registerExceptionTrack(function(error,cause){eventTrack(error.toString(),{category:"Exceptions",label:error.stack,nonInteraction:!0})}),$analyticsProvider.registerSetUsername(function(userId){$analyticsProvider.settings.ga.userId=userId}),$analyticsProvider.registerSetUserProperties(function(properties){if(properties){var dimsAndMets=dimensionsAndMetrics(properties);ga("set",dimsAndMets)}}),$analyticsProvider.registerUserTimings(function(properties){return properties&&properties.timingCategory&&properties.timingVar&&"undefined"!=typeof properties.timingValue?void(window.ga&&ga("send","timing",properties)):void console.log("Properties timingCategory, timingVar, and timingValue are required to be set.")})}])}(window,window.angular); +!function(window,angular,undefined){"use strict";angular.module("angulartics.google.analytics",["angulartics"]).config(["$analyticsProvider",function($analyticsProvider){function detectUniversalAnalytics(){var gaNamespace=window.GoogleAnalyticsObject;return gaNamespace&&window[gaNamespace]}function detectClassicAnalytics(){return angular.isUndefined(window._gaq)}function dimensionsAndMetrics(properties){var key,customData={};for(key in properties)key.indexOf("dimension")&&key.indexOf("metric")||(customData[key]=properties[key]);return customData}$analyticsProvider.settings.pageTracking.trackRelativePath=!0,$analyticsProvider.settings.ga={additionalAccountNames:undefined,additionalAccountHitTypes:{pageview:!0,event:!0,error:!1,timing:!1,setUserProperties:!1,userId:!1},disableEventTracking:null,disablePageTracking:null,userId:null},$analyticsProvider.registerPageTrack(function(path,properties){properties=properties||{},$analyticsProvider.settings.ga.disablePageTracking||dispatchToGa("send",angular.extend(dimensionsAndMetrics(properties),{hitType:"pageview",page:path}))}),$analyticsProvider.registerEventTrack(function(action,properties){if(!$analyticsProvider.settings.ga.disableEventTracking){if(!action&&action+""!="0")return console.log("Missing required argument action");if(properties=properties||{},properties.category=properties.category||"Event",properties.value){var parsed=parseInt(properties.value,10);properties.value=isNaN(parsed)?0:parsed}angular.isFunction(properties.hitCallback)||(properties.hitCallback=null),properties.nonInteraction=properties.nonInteraction||properties.noninteraction,dispatchToGa("send",angular.extend(dimensionsAndMetrics(properties),{hitType:"event",eventCategory:properties.category,eventAction:action,eventLabel:properties.label,eventValue:properties.value,nonInteraction:properties.nonInteraction,page:properties.page||window.location.hash.substring(1)||window.location.pathname,hitCallback:properties.hitCallback}))}}),$analyticsProvider.registerExceptionTrack(function(error,cause){dispatchToGa("send",{hitType:"event",eventCategory:"Exceptions",eventAction:error.toString(),eventLabel:error.stack,nonInteraction:!0,page:window.location.hash.substring(1)||window.location.pathname,isException:!0})}),$analyticsProvider.registerSetUsername(function(userId){$analyticsProvider.settings.ga.userId=userId}),$analyticsProvider.registerSetUserProperties(function(properties){properties&&dispatchToGa("set",dimensionsAndMetrics(properties))}),$analyticsProvider.registerUserTimings(function(properties){return!angular.isObject(properties)||angular.isArray(properties)?console.log("Required argument properties is missing or not an object"):(angular.forEach(["timingCategory","timingVar","timingValue"],function(prop){if(angular.isUndefined(properties[prop]))return console.log("Argument properties missing required property "+prop)}),void dispatchToGa("send",{hitType:"timing",timingCategory:properties.timingCategory,timingVar:properties.timingVar,timingValue:properties.timingVar,timingLabel:properties.timingLabel,page:properties.page||window.location.hash.substring(1)||window.location.pathname}))});var dispatchToGa=function(){function dispatchToUniversal_(command,fieldsObj,shouldCopyHit){var gaNamespace=window.GoogleAnalyticsObject,userId=$analyticsProvider.settings.ga.userId;userId&&(fieldsObj.userId=userId),window[gaNamespace](command,fieldsObj),shouldCopyHit&&(userId&&!$analyticsProvider.settings.ga.additionalAccountHitTypes.userId&&delete fieldsObj.userId,angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){var accountCommand=accountName+"."+command;window[gaNamespace](accountCommand,fieldsObj)}))}function dispatchToClassic_(command,fieldsObj,shouldCopyHit){if("set"===command)return console.log('Classic Analytics does not support the "set" command or Custom Dimensions. Command ignored.');var classicCommand;if("send"===command)switch(fieldsObj.hitType){case"pageview":classicCommand=["_trackPageview",fieldsObj.page];break;case"event":classicCommand=["_trackEvent",fieldsObj.category,fieldsObj.action,fieldsObj.label,fieldsObj.value,fieldsObj.nonInteraction];break;case"timing":classicCommand=["_trackTiming",fieldsObj.timingCategory,fieldsObj.timingVar,fieldsObj.timingValue,fieldsObj.timingLabel]}classicCommand||console.log("Unable to find command "+command+". Hit ignored."),window._gaq.push(classicCommand),shouldCopyHit&&angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){classicCommand[0]=accountName+"."+classicCommand[0],window._gaq.push(classicCommand)})}var handler;return detectClassicAnalytics()&&(handler=dispatchToClassic_),detectUniversalAnalytics()&&(handler=dispatchToUniversal_),handler?function(command,fieldsObj){var hitType="event"===fieldsObj.hitType&&fieldsObj.isException?"error":fieldsObj.hitType,shouldCopyHit=$analyticsProvider.settings.ga.additionalAccountHitTypes[hitType];console.log(hitType),console.log(shouldCopyHit),console.log($analyticsProvider.settings.ga.additionalAccountHitTypes),handler(command,fieldsObj,shouldCopyHit)}:(console.log("Error: neither Classic nor Universal Analytics detected at bootstrap. Angulartics-GA will ignore all commands!"),angular.noop)}()}])}(window,window.angular); //# sourceMappingURL=../dist/angulartics-ga.min.js.map \ No newline at end of file diff --git a/dist/angulartics-ga.min.js.map b/dist/angulartics-ga.min.js.map index c498618..512229b 100644 --- a/dist/angulartics-ga.min.js.map +++ b/dist/angulartics-ga.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["lib/angulartics-ga.js"],"names":["window","angular","undefined","module","config","$analyticsProvider","dimensionsAndMetrics","properties","ga","key","customData","indexOf","eventTrack","action","settings","disableEventTracking","category","value","parsed","parseInt","isNaN","hitCallback","hasOwnProperty","nonInteraction","noninteraction","eventOptions","eventCategory","eventAction","eventLabel","label","eventValue","page","location","hash","substring","pathname","userId","dimsAndMets","extend","transport","forEach","additionalAccountNames","accountName","_gaq","push","pageTracking","trackRelativePath","disablePageTracking","registerPageTrack","path","registerEventTrack","registerExceptionTrack","error","cause","toString","stack","registerSetUsername","registerSetUserProperties","registerUserTimings","timingCategory","timingVar","timingValue","console","log"],"mappings":"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","file":"dist/angulartics-ga.min.js","sourceRoot":".."} \ No newline at end of file +{"version":3,"sources":["lib/angulartics-ga.js"],"names":["window","angular","undefined","module","config","$analyticsProvider","detectUniversalAnalytics","gaNamespace","GoogleAnalyticsObject","detectClassicAnalytics","isUndefined","_gaq","dimensionsAndMetrics","properties","key","customData","indexOf","settings","pageTracking","trackRelativePath","ga","additionalAccountNames","additionalAccountHitTypes","pageview","event","error","timing","setUserProperties","userId","disableEventTracking","disablePageTracking","registerPageTrack","path","dispatchToGa","extend","hitType","page","registerEventTrack","action","console","log","category","value","parsed","parseInt","isNaN","isFunction","hitCallback","nonInteraction","noninteraction","eventCategory","eventAction","eventLabel","label","eventValue","location","hash","substring","pathname","registerExceptionTrack","cause","toString","stack","isException","registerSetUsername","registerSetUserProperties","registerUserTimings","isObject","isArray","forEach","prop","timingCategory","timingVar","timingValue","timingLabel","dispatchToUniversal_","command","fieldsObj","shouldCopyHit","accountName","accountCommand","dispatchToClassic_","classicCommand","push","handler","noop"],"mappings":"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","file":"dist/angulartics-ga.min.js","sourceRoot":".."} \ No newline at end of file diff --git a/lib/angulartics-ga.js b/lib/angulartics-ga.js index 83689c5..2d8ad8b 100644 --- a/lib/angulartics-ga.js +++ b/lib/angulartics-ga.js @@ -88,7 +88,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) // GA requires that hitCallback be an function, see: // https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#hitcallback - if (properties.hitCallback && (typeof properties.hitCallback !== 'function')) { + if (!angular.isFunction(properties.hitCallback)) { properties.hitCallback = null; } @@ -217,7 +217,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) function detectClassicAnalytics() { // If _gaq is undefined, we're trusting Classic Analytics to be there - return typeof window._gaq !== 'undefined'; + return angular.isUndefined(window._gaq); } From 64468c8237ca03ecfaa522f476b879d2e8c5dbd4 Mon Sep 17 00:00:00 2001 From: Dan Wilkerson Date: Sun, 28 Aug 2016 18:02:01 -0400 Subject: [PATCH 4/5] bumped minor, added myself to contribs --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 46eb899..85bb03e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angulartics-google-analytics", - "version": "0.2.1", + "version": "0.3.0", "description": "Google Analytics plugin for Angulartics", "keywords": [ "google", @@ -27,7 +27,8 @@ "Blake Miller (https://github.com/blak3mill3r)", "Hannah Fouasnon (https://github.com/fouasnon)", "Alex Logvynovskiy (https://github.com/dr-axly)", - "Fil Maj (https://github.com/filmaj)" + "Fil Maj (https://github.com/filmaj)", + "Dan Wilkerson (https://github.com/danwilkerson)" ], "main": "lib/index.js", "scripts": { From a36e75dd8f928db7f74af72405f616c13ed69f0e Mon Sep 17 00:00:00 2001 From: Dan Wilkerson Date: Sun, 4 Sep 2016 12:47:54 -0400 Subject: [PATCH 5/5] Updated ecommerce tracking to use dispatchToGa syntax, misc bugfixes --- dist/angulartics-ga.min.js | 2 +- dist/angulartics-ga.min.js.map | 2 +- lib/angulartics-ga.js | 223 +++++++++++++++++++++++++++++---- 3 files changed, 199 insertions(+), 28 deletions(-) diff --git a/dist/angulartics-ga.min.js b/dist/angulartics-ga.min.js index b2645a0..39f4505 100644 --- a/dist/angulartics-ga.min.js +++ b/dist/angulartics-ga.min.js @@ -1,2 +1,2 @@ -!function(window,angular,undefined){"use strict";angular.module("angulartics.google.analytics",["angulartics"]).config(["$analyticsProvider",function($analyticsProvider){function detectUniversalAnalytics(){var gaNamespace=window.GoogleAnalyticsObject;return gaNamespace&&window[gaNamespace]}function detectClassicAnalytics(){return angular.isUndefined(window._gaq)}function dimensionsAndMetrics(properties){var key,customData={};for(key in properties)key.indexOf("dimension")&&key.indexOf("metric")||(customData[key]=properties[key]);return customData}$analyticsProvider.settings.pageTracking.trackRelativePath=!0,$analyticsProvider.settings.ga={additionalAccountNames:undefined,additionalAccountHitTypes:{pageview:!0,event:!0,error:!1,timing:!1,setUserProperties:!1,userId:!1},disableEventTracking:null,disablePageTracking:null,userId:null},$analyticsProvider.registerPageTrack(function(path,properties){properties=properties||{},$analyticsProvider.settings.ga.disablePageTracking||dispatchToGa("send",angular.extend(dimensionsAndMetrics(properties),{hitType:"pageview",page:path}))}),$analyticsProvider.registerEventTrack(function(action,properties){if(!$analyticsProvider.settings.ga.disableEventTracking){if(!action&&action+""!="0")return console.log("Missing required argument action");if(properties=properties||{},properties.category=properties.category||"Event",properties.value){var parsed=parseInt(properties.value,10);properties.value=isNaN(parsed)?0:parsed}angular.isFunction(properties.hitCallback)||(properties.hitCallback=null),properties.nonInteraction=properties.nonInteraction||properties.noninteraction,dispatchToGa("send",angular.extend(dimensionsAndMetrics(properties),{hitType:"event",eventCategory:properties.category,eventAction:action,eventLabel:properties.label,eventValue:properties.value,nonInteraction:properties.nonInteraction,page:properties.page||window.location.hash.substring(1)||window.location.pathname,hitCallback:properties.hitCallback}))}}),$analyticsProvider.registerExceptionTrack(function(error,cause){dispatchToGa("send",{hitType:"event",eventCategory:"Exceptions",eventAction:error.toString(),eventLabel:error.stack,nonInteraction:!0,page:window.location.hash.substring(1)||window.location.pathname,isException:!0})}),$analyticsProvider.registerSetUsername(function(userId){$analyticsProvider.settings.ga.userId=userId}),$analyticsProvider.registerSetUserProperties(function(properties){properties&&dispatchToGa("set",dimensionsAndMetrics(properties))}),$analyticsProvider.registerUserTimings(function(properties){return!angular.isObject(properties)||angular.isArray(properties)?console.log("Required argument properties is missing or not an object"):(angular.forEach(["timingCategory","timingVar","timingValue"],function(prop){if(angular.isUndefined(properties[prop]))return console.log("Argument properties missing required property "+prop)}),void dispatchToGa("send",{hitType:"timing",timingCategory:properties.timingCategory,timingVar:properties.timingVar,timingValue:properties.timingVar,timingLabel:properties.timingLabel,page:properties.page||window.location.hash.substring(1)||window.location.pathname}))});var dispatchToGa=function(){function dispatchToUniversal_(command,fieldsObj,shouldCopyHit){var gaNamespace=window.GoogleAnalyticsObject,userId=$analyticsProvider.settings.ga.userId;userId&&(fieldsObj.userId=userId),window[gaNamespace](command,fieldsObj),shouldCopyHit&&(userId&&!$analyticsProvider.settings.ga.additionalAccountHitTypes.userId&&delete fieldsObj.userId,angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){var accountCommand=accountName+"."+command;window[gaNamespace](accountCommand,fieldsObj)}))}function dispatchToClassic_(command,fieldsObj,shouldCopyHit){if("set"===command)return console.log('Classic Analytics does not support the "set" command or Custom Dimensions. Command ignored.');var classicCommand;if("send"===command)switch(fieldsObj.hitType){case"pageview":classicCommand=["_trackPageview",fieldsObj.page];break;case"event":classicCommand=["_trackEvent",fieldsObj.category,fieldsObj.action,fieldsObj.label,fieldsObj.value,fieldsObj.nonInteraction];break;case"timing":classicCommand=["_trackTiming",fieldsObj.timingCategory,fieldsObj.timingVar,fieldsObj.timingValue,fieldsObj.timingLabel]}classicCommand||console.log("Unable to find command "+command+". Hit ignored."),window._gaq.push(classicCommand),shouldCopyHit&&angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){classicCommand[0]=accountName+"."+classicCommand[0],window._gaq.push(classicCommand)})}var handler;return detectClassicAnalytics()&&(handler=dispatchToClassic_),detectUniversalAnalytics()&&(handler=dispatchToUniversal_),handler?function(command,fieldsObj){var hitType="event"===fieldsObj.hitType&&fieldsObj.isException?"error":fieldsObj.hitType,shouldCopyHit=$analyticsProvider.settings.ga.additionalAccountHitTypes[hitType];console.log(hitType),console.log(shouldCopyHit),console.log($analyticsProvider.settings.ga.additionalAccountHitTypes),handler(command,fieldsObj,shouldCopyHit)}:(console.log("Error: neither Classic nor Universal Analytics detected at bootstrap. Angulartics-GA will ignore all commands!"),angular.noop)}()}])}(window,window.angular); +!function(window,angular,undefined){"use strict";angular.module("angulartics.google.analytics",["angulartics"]).config(["$analyticsProvider",function($analyticsProvider){function detectUniversalAnalytics(){var gaNamespace=window.GoogleAnalyticsObject;return gaNamespace&&window[gaNamespace]}function detectClassicAnalytics(){return!angular.isUndefined(window._gaq)}function dimensionsAndMetrics(properties){var key,customData={};for(key in properties)key.indexOf("dimension")&&key.indexOf("metric")||(customData[key]=properties[key]);return customData}$analyticsProvider.settings.pageTracking.trackRelativePath=!0,$analyticsProvider.settings.ga={additionalAccountNames:undefined,additionalAccountHitTypes:{pageview:!0,event:!0,exception:!1,ecommerce:!1,userTiming:!1,setUserProperties:!1,userId:!1},disableEventTracking:null,disablePageTracking:null,userId:null,enhancedEcommerce:!1},$analyticsProvider.registerPageTrack(function(path,properties){properties=properties||{},$analyticsProvider.settings.ga.disablePageTracking||dispatchToGa("pageview","send",angular.extend(dimensionsAndMetrics(properties),{hitType:"pageview",page:path}))}),$analyticsProvider.registerEventTrack(function(action,properties){if(!$analyticsProvider.settings.ga.disableEventTracking){if(!action&&action+""!="0")return console.log("Missing required argument action");if(properties=properties||{},properties.category=properties.category||"Event",properties.value){var parsed=parseInt(properties.value,10);properties.value=isNaN(parsed)?0:parsed}angular.isFunction(properties.hitCallback)||(properties.hitCallback=null),properties.nonInteraction=properties.nonInteraction||properties.noninteraction,dispatchToGa("event","send",angular.extend(dimensionsAndMetrics(properties),{hitType:"event",eventCategory:properties.category,eventAction:action,eventLabel:properties.label,eventValue:properties.value,nonInteraction:properties.nonInteraction,page:properties.page||window.location.hash.substring(1)||window.location.pathname,hitCallback:properties.hitCallback}))}}),$analyticsProvider.registerExceptionTrack(function(error,cause){dispatchToGa("exception","send",{hitType:"event",eventCategory:"Exceptions",eventAction:error.toString(),eventLabel:error.stack,nonInteraction:!0,page:window.location.hash.substring(1)||window.location.pathname,isException:!0})}),$analyticsProvider.registerSetUsername(function(userId){$analyticsProvider.settings.ga.userId=userId}),$analyticsProvider.registerSetUserProperties(function(properties){properties&&dispatchToGa("setUserProperties","set",dimensionsAndMetrics(properties))}),$analyticsProvider.registerUserTimings(function(properties){return!angular.isObject(properties)||angular.isArray(properties)?console.log("Required argument properties is missing or not an object"):(angular.forEach(["timingCategory","timingVar","timingValue"],function(prop){if(angular.isUndefined(properties[prop]))return console.log("Argument properties missing required property "+prop)}),void dispatchToGa("userTiming","send",{hitType:"timing",timingCategory:properties.timingCategory,timingVar:properties.timingVar,timingValue:properties.timingValue,timingLabel:properties.timingLabel,optSampleRate:properties.optSampleRate,page:properties.page||window.location.hash.substring(1)||window.location.pathname}))}),$analyticsProvider.registerTransactionTrack(function(transaction){var product,i;if(dispatchToGa("ecommerce","require","ecommerce"),dispatchToGa("ecommerce","ecommerce:addTransaction",transaction),transaction.products)for(i=0;i-1&&$analyticsProvider.settings.ga.enhancedEcommerce)switch(command){case"ecommerce:addTransaction":command=["ec:setAction","purchase"];break;case"ecommerce:addItem":command="ec:addProduct",fieldsObj.id=fieldsObj.sku;break;case"ecommerce:send":command="send",fieldsObj.hitType="event",fieldsObj.eventCategory="Angulartics Enhanced Ecommerce",fieldsObj.eventAction="Purchase",fieldsObj.nonInteraction=!0}"require"===command&&"ecommerce"===fieldsObj&&$analyticsProvider.settings.ga.enhancedEcommerce&&(fieldsObj="ec"),uaCommand=command instanceof Array?command.concat(fieldsObj):[command,fieldsObj],window[gaNamespace].apply(this,uaCommand),shouldCopyHit&&(userId&&!$analyticsProvider.settings.ga.additionalAccountHitTypes.userId&&delete fieldsObj.userId,angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){var uaCommandClone=[].slice.call(uaCommand);uaCommandClone[0]=accountName+"."+uaCommandClone[0],window[gaNamespace].apply(this,uaCommandClone)}))}function dispatchToClassic_(command,fieldsObj,shouldCopyHit){if("set"===command)return console.log('Classic Analytics does not support the "set" command or Custom Dimensions. Command ignored.');var classicCommand;if("send"===command)switch(fieldsObj.hitType){case"pageview":classicCommand=["_trackPageview",fieldsObj.page];break;case"event":classicCommand=["_trackEvent",fieldsObj.category,fieldsObj.action,fieldsObj.label,fieldsObj.value,fieldsObj.nonInteraction];break;case"timing":classicCommand=["_trackTiming",fieldsObj.timingCategory,fieldsObj.timingVar,fieldsObj.timingValue,fieldsObj.timingLabel,fieldsObj.optSampleRate]}return"ecommerce:addTransaction"===command&&(classicCommand=["_addTrans",fieldsObj.id,fieldsObj.affiliation,fieldsObj.revenue,fieldsObj.tax,fieldsObj.shipping,fieldsObj.billingCity,fieldsObj.billingRegion,fieldsObj.billingCountry]),"ecommerce:addItem"===command&&(classicCommand=["_addItem",fieldsObj.id,fieldsObj.sku,fieldsObj.name,fieldsObj.category,fieldsObj.price,fieldsObj.quantity]),"_set"===command&&(classicCommand=["_set","currencyCode",fieldsObj]),"ecommerce:send"===command&&(classicCommand=["_trackTrans"]),classicCommand?(window._gaq.push(classicCommand),void(shouldCopyHit&&angular.forEach($analyticsProvider.settings.ga.additionalAccountNames,function(accountName){var classicCommandClone=[].slice.call(classicCommand);classicCommandClone[0]=accountName+"."+classicCommandClone[0],window._gaq.push(classicCommandClone)}))):console.log("Unable to find command "+command+" or fieldsObj missing required properties. Command ignored.")}var handler;return detectClassicAnalytics()&&(handler=dispatchToClassic_),detectUniversalAnalytics()&&(handler=dispatchToUniversal_),handler?function(method,command,fieldsObj){var shouldCopyHit=$analyticsProvider.settings.ga.additionalAccountHitTypes[method];handler(command,fieldsObj,shouldCopyHit)}:(console.log("Error: neither Classic nor Universal Analytics detected at bootstrap. Angulartics-GA will ignore all commands!"),angular.noop)}()}])}(window,window.angular); //# sourceMappingURL=../dist/angulartics-ga.min.js.map \ No newline at end of file diff --git a/dist/angulartics-ga.min.js.map b/dist/angulartics-ga.min.js.map index 512229b..4db0ba9 100644 --- a/dist/angulartics-ga.min.js.map +++ b/dist/angulartics-ga.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["lib/angulartics-ga.js"],"names":["window","angular","undefined","module","config","$analyticsProvider","detectUniversalAnalytics","gaNamespace","GoogleAnalyticsObject","detectClassicAnalytics","isUndefined","_gaq","dimensionsAndMetrics","properties","key","customData","indexOf","settings","pageTracking","trackRelativePath","ga","additionalAccountNames","additionalAccountHitTypes","pageview","event","error","timing","setUserProperties","userId","disableEventTracking","disablePageTracking","registerPageTrack","path","dispatchToGa","extend","hitType","page","registerEventTrack","action","console","log","category","value","parsed","parseInt","isNaN","isFunction","hitCallback","nonInteraction","noninteraction","eventCategory","eventAction","eventLabel","label","eventValue","location","hash","substring","pathname","registerExceptionTrack","cause","toString","stack","isException","registerSetUsername","registerSetUserProperties","registerUserTimings","isObject","isArray","forEach","prop","timingCategory","timingVar","timingValue","timingLabel","dispatchToUniversal_","command","fieldsObj","shouldCopyHit","accountName","accountCommand","dispatchToClassic_","classicCommand","push","handler","noop"],"mappings":"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","file":"dist/angulartics-ga.min.js","sourceRoot":".."} \ No newline at end of file +{"version":3,"sources":["lib/angulartics-ga.js"],"names":["window","angular","undefined","module","config","$analyticsProvider","detectUniversalAnalytics","gaNamespace","GoogleAnalyticsObject","detectClassicAnalytics","isUndefined","_gaq","dimensionsAndMetrics","properties","key","customData","indexOf","settings","pageTracking","trackRelativePath","ga","additionalAccountNames","additionalAccountHitTypes","pageview","event","exception","ecommerce","userTiming","setUserProperties","userId","disableEventTracking","disablePageTracking","enhancedEcommerce","registerPageTrack","path","dispatchToGa","extend","hitType","page","registerEventTrack","action","console","log","category","value","parsed","parseInt","isNaN","isFunction","hitCallback","nonInteraction","noninteraction","eventCategory","eventAction","eventLabel","label","eventValue","location","hash","substring","pathname","registerExceptionTrack","error","cause","toString","stack","isException","registerSetUsername","registerSetUserProperties","registerUserTimings","isObject","isArray","forEach","prop","timingCategory","timingVar","timingValue","timingLabel","optSampleRate","registerTransactionTrack","transaction","product","i","products","length","sku","id","currencyCode","dispatchToUniversal_","command","fieldsObj","shouldCopyHit","uaCommand","Array","concat","apply","this","accountName","uaCommandClone","slice","call","dispatchToClassic_","classicCommand","affiliation","revenue","tax","shipping","billingCity","billingRegion","billingCountry","name","price","quantity","push","classicCommandClone","handler","method","noop"],"mappings":"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","file":"dist/angulartics-ga.min.js","sourceRoot":".."} \ No newline at end of file diff --git a/lib/angulartics-ga.js b/lib/angulartics-ga.js index 2d8ad8b..3671657 100644 --- a/lib/angulartics-ga.js +++ b/lib/angulartics-ga.js @@ -20,14 +20,16 @@ angular.module('angulartics.google.analytics', ['angulartics']) additionalAccountHitTypes: { pageview: true, event: true, - error: false, - timing: false, + exception: false, + ecommerce: false, + userTiming: false, setUserProperties: false, userId: false }, disableEventTracking: null, disablePageTracking: null, - userId: null + userId: null, + enhancedEcommerce: false }; /** @@ -47,7 +49,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) // Do nothing if page tracking is disabled if ($analyticsProvider.settings.ga.disablePageTracking) return; - dispatchToGa('send', angular.extend(dimensionsAndMetrics(properties), { + dispatchToGa('pageview', 'send', angular.extend(dimensionsAndMetrics(properties), { hitType: 'pageview', page: path })); @@ -96,7 +98,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) // https://github.com/angulartics/angulartics-google-analytics/issues/49 properties.nonInteraction = properties.nonInteraction || properties.noninteraction; - dispatchToGa('send', angular.extend(dimensionsAndMetrics(properties), { + dispatchToGa('event', 'send', angular.extend(dimensionsAndMetrics(properties), { hitType: 'event', eventCategory: properties.category, eventAction: action, @@ -120,7 +122,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/events */ $analyticsProvider.registerExceptionTrack(function (error, cause) { - dispatchToGa('send', { + dispatchToGa('exception', 'send', { hitType: 'event', eventCategory: 'Exceptions', eventAction: error.toString(), @@ -154,7 +156,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) $analyticsProvider.registerSetUserProperties(function (properties) { if (properties) { - dispatchToGa('set', dimensionsAndMetrics(properties)); + dispatchToGa('setUserProperties', 'set', dimensionsAndMetrics(properties)); } }); @@ -169,6 +171,8 @@ angular.module('angulartics.google.analytics', ['angulartics']) * 'timingValue' (number) * Properties can also have the optional fields: * 'timingLabel' (string) + * 'optSampleRate' (number) Classic Analytics only - determines % of users to collect data from, handled serverside by UA + * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings#sampling_considerations * * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/user-timings */ @@ -184,17 +188,99 @@ angular.module('angulartics.google.analytics', ['angulartics']) } }); - dispatchToGa('send', { + dispatchToGa('userTiming', 'send', { hitType: 'timing', timingCategory: properties.timingCategory, timingVar: properties.timingVar, - timingValue: properties.timingVar, + timingValue: properties.timingValue, timingLabel: properties.timingLabel, + optSampleRate: properties.optSampleRate, // Classic Analytics only page: properties.page || window.location.hash.substring(1) || window.location.pathname, }); }); + /** + * Ecommerce Tracking in GA + * @name transactionTrack + * + * @param {object} transaction comprised of following fields: + * 'id': 'T12345', // Transaction ID. Required for purchases and refunds. + * 'affiliation': 'Online Store', + * 'revenue': '35.43', // Total transaction value (incl. tax and shipping) + * 'tax':'4.90', + * 'shipping': '5.99', + * 'coupon': 'SUMMER_SALE', // Enhanced Ecommerce Only + * 'dimension1': 'Card ID #1234', // Hit, Session, or User-level Custom Dimension(s) + * 'metric1': 1, // Custom Metric(s) + * 'currencyCode': 'EUR', // Currency Code to track the transaction with. Recognized codes: https://support.google.com/analytics/answer/6205902?hl=en#supported-currencies + * 'billingCity': 'San Francisco', // Classic Analytics only + * 'billingRegion': 'California', // Classic Analytics only + * 'billingCountry': 'USA', // Classic Analytics only + * 'products': [{ // List of products + * 'name': 'Triblend Android T-Shirt', // Name or ID is required. + * 'id': '12345', // Product SKU + * 'price': '15.25', + * 'brand': 'Google', // Enhanced Ecommerce only + * 'category': 'Apparel', + * 'variant': 'Gray', // Enhanced Ecommerce only + * 'quantity': 1, + * 'coupon': '', // Enhanced Ecommerce only. + * 'currencyCode': 'BRL', // Product-level currency code, Enhanced Ecommerce only + * 'dimension2': 'Clearance', // Product-level Custom Dimension + * 'metric2': 1 // Product-level Custom Metric + * }, + * ... + * ] + * + * @param {object] properties comprised of custom dimensions and metrics to + * send with the transaction hit + * Utilizes traditional ecommerce tracking by default. To used Enhanced Ecommerce, + * set the $analytics.settings.ga.enhancedEcommerce flag to true + * + * Docs on traditional ecommerce (UA): + * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/ecommerce + * + * Docs on Enhanced Ecommerce + * @link https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce + * + * Docs on Classic Ecommerce (_gaq) + * @link https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingEcommerce + **/ + $analyticsProvider.registerTransactionTrack(function(transaction) { + + var product; + var i; + + // Universal Analytics splits off the ecommerce code into a separate + // library we must include by using the "require" command + dispatchToGa('ecommerce', 'require', 'ecommerce'); + dispatchToGa('ecommerce', 'ecommerce:addTransaction', transaction); + + if (transaction.products) { + for (i = 0; i < transaction.products.length; i++) { + + product = transaction.products[i]; + + // GA uses a SKU property and stores the transaction ID in the ID Field + product.sku = product.id; + product.id = transaction.id; + + dispatchToGa('ecommerce', 'ecommerce:addItem', transaction.products[i]); + + } + } + + if (transaction.currencyCode) { + + dispatchToGa('ecommerce', '_set', transaction.currencyCode); // Classic Anayltics only - UA uses fieldsObj.currencyCode instead + + } + + dispatchToGa('ecommerce', 'ecommerce:send', dimensionsAndMetrics(transaction)); + + }); + /** * Detects if Universal Analytics is installed * @@ -217,7 +303,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) function detectClassicAnalytics() { // If _gaq is undefined, we're trusting Classic Analytics to be there - return angular.isUndefined(window._gaq); + return !angular.isUndefined(window._gaq); } @@ -251,6 +337,7 @@ angular.module('angulartics.google.analytics', ['angulartics']) * * @name dispatchToGa * + * @param {string} method Name of angulartics method for checking if hits should be duplicated * @param {string} command Standard Universal Analytics command (create, send, set) * @param {object} fieldsObj object with hit-specific fields. Fields are whitelisted in handler - non-supported fields are ignored. * @@ -273,14 +360,9 @@ angular.module('angulartics.google.analytics', ['angulartics']) return angular.noop; } - return function(command, fieldsObj) { - - var hitType = fieldsObj.hitType === 'event' && fieldsObj.isException ? 'error' : fieldsObj.hitType; - var shouldCopyHit = $analyticsProvider.settings.ga.additionalAccountHitTypes[hitType]; - console.log(hitType); - console.log(shouldCopyHit); - console.log($analyticsProvider.settings.ga.additionalAccountHitTypes); + return function(method, command, fieldsObj) { + var shouldCopyHit = $analyticsProvider.settings.ga.additionalAccountHitTypes[method]; handler(command, fieldsObj, shouldCopyHit); } @@ -299,10 +381,46 @@ angular.module('angulartics.google.analytics', ['angulartics']) var gaNamespace = window.GoogleAnalyticsObject; var userId = $analyticsProvider.settings.ga.userId; + var uaCommand; - if (userId) fieldsObj.userId = userId; + if (userId && angular.isObject(fieldsObj)) fieldsObj.userId = userId; - window[gaNamespace](command, fieldsObj); + if (command.indexOf('ecommerce:') > -1 && $analyticsProvider.settings.ga.enhancedEcommerce) { + + switch (command) { + case 'ecommerce:addTransaction': + command = ['ec:setAction', 'purchase']; + break; + case 'ecommerce:addItem': + command = 'ec:addProduct'; + // Enhanced Ecommerce reverts to using the ID property for the SKU, + // so we swap them back here + fieldsObj.id = fieldsObj.sku; + break; + case 'ecommerce:send': + command = 'send'; + fieldsObj.hitType = 'event'; + fieldsObj.eventCategory = 'Angulartics Enhanced Ecommerce'; + fieldsObj.eventAction = 'Purchase'; + fieldsObj.nonInteraction = true; + break; + } + + } + + if (command === 'require' && fieldsObj === 'ecommerce') { + + if ($analyticsProvider.settings.ga.enhancedEcommerce) { + + fieldsObj = 'ec'; + + } + + } + + uaCommand = command instanceof Array ? command.concat(fieldsObj) : [command, fieldsObj]; + + window[gaNamespace].apply(this, uaCommand); if (shouldCopyHit) { @@ -313,8 +431,10 @@ angular.module('angulartics.google.analytics', ['angulartics']) angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ - var accountCommand = accountName + '.' + command; - window[gaNamespace](accountCommand, fieldsObj) + var uaCommandClone = [].slice.call(uaCommand); + uaCommandClone[0] = accountName + '.' + uaCommandClone[0]; + + window[gaNamespace].apply(this, uaCommandClone); }); @@ -365,15 +485,64 @@ angular.module('angulartics.google.analytics', ['angulartics']) fieldsObj.timingCategory, fieldsObj.timingVar, fieldsObj.timingValue, - fieldsObj.timingLabel + fieldsObj.timingLabel, + fieldsObj.optSampleRate ]; break; } } + if (command === 'ecommerce:addTransaction') { + + classicCommand = [ + '_addTrans', + fieldsObj.id, + fieldsObj.affiliation, + fieldsObj.revenue, + fieldsObj.tax, + fieldsObj.shipping, + fieldsObj.billingCity, + fieldsObj.billingRegion, + fieldsObj.billingCountry + ]; + + } + + if (command === 'ecommerce:addItem') { + + classicCommand = [ + '_addItem', + fieldsObj.id, + fieldsObj.sku, + fieldsObj.name, + fieldsObj.category, + fieldsObj.price, + fieldsObj.quantity + ]; + + } + + if (command === '_set') { + + classicCommand = [ + '_set', + 'currencyCode', + fieldsObj + ]; + + } + + if (command === 'ecommerce:send') { + + classicCommand = [ + '_trackTrans' + ]; + + } + if (!classicCommand) { - console.log('Unable to find command ' + command + '. Hit ignored.'); + return console.log('Unable to find command ' + command + ' or fieldsObj missing required properties. Command ignored.'); } // Issue our command to GA @@ -383,9 +552,11 @@ angular.module('angulartics.google.analytics', ['angulartics']) angular.forEach($analyticsProvider.settings.ga.additionalAccountNames, function (accountName){ - // Namespace the command as required per: - classicCommand[0] = accountName + '.' + classicCommand[0]; - window._gaq.push(classicCommand); + var classicCommandClone = [].slice.call(classicCommand); + // Namespace the command as required + classicCommandClone[0] = accountName + '.' + classicCommandClone[0]; + + window._gaq.push(classicCommandClone); });