app/assets/javascripts/govuk/analytics/analytics.js in govuk_frontend_toolkit-7.2.0 vs app/assets/javascripts/govuk/analytics/analytics.js in govuk_frontend_toolkit-7.3.0

- old
+ new

@@ -1,14 +1,23 @@ ;(function (global) { 'use strict' var GOVUK = global.GOVUK || {} + var EMAIL_PATTERN = /[^\s=/?&]+(?:@|%40)[^\s=/?&]+/g + var POSTCODE_PATTERN = /[A-PR-UWYZ][A-HJ-Z]?[0-9][0-9A-HJKMNPR-Y]?(?:[\s+]|%20)*[0-9][ABD-HJLNPQ-Z]{2}/gi // For usage and initialisation see: // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/docs/analytics.md#create-an-analytics-tracker var Analytics = function (config) { + this.stripPostcodePII = false + if (typeof config.stripPostcodePII !== 'undefined') { + this.stripPostcodePII = (config.stripPostcodePII === true) + // remove the option so we don't pass it to other trackers - it's not + // their concern + delete config.stripPostcodePII + } this.trackers = [] if (typeof config.universalId !== 'undefined') { var universalId = config.universalId delete config.universalId this.trackers.push(new GOVUK.GoogleAnalyticsUniversalTracker(universalId, config)) @@ -18,10 +27,49 @@ delete config.govukTrackerUrl this.trackers.push(new GOVUK.GOVUKTracker(govukTrackerUrl)) } } + Analytics.prototype.stripPII = function (value) { + if (typeof value === 'string') { + return this.stripPIIFromString(value) + } else if (Object.prototype.toString.call(value) === '[object Array]') { + return this.stripPIIFromArray(value) + } else if (typeof value === 'object') { + return this.stripPIIFromObject(value) + } else { + return value + } + } + + Analytics.prototype.stripPIIFromString = function (string) { + var emailStripped = string.replace(EMAIL_PATTERN, '[email]') + if (this.stripPostcodePII === true) { + return emailStripped.replace(POSTCODE_PATTERN, '[postcode]') + } else { + return emailStripped + } + } + + Analytics.prototype.stripPIIFromObject = function (object) { + for (var property in object) { + var value = object[property] + + object[property] = this.stripPII(value) + } + return object + } + + Analytics.prototype.stripPIIFromArray = function (array) { + for (var i = 0, l = array.length; i < l; i++) { + var elem = array[i] + + array[i] = this.stripPII(elem) + } + return array + } + Analytics.prototype.sendToTrackers = function (method, args) { for (var i = 0, l = this.trackers.length; i < l; i++) { var tracker = this.trackers[i] var fn = tracker[method] @@ -34,33 +82,44 @@ Analytics.load = function () { GOVUK.GoogleAnalyticsUniversalTracker.load() GOVUK.GOVUKTracker.load() } + Analytics.prototype.defaultPathForTrackPageview = function () { + // Ignore anchor, but keep query string as per default behaviour of + // GA (see: https://developers.google.com/analytics/devguides/collection/analyticsjs/pages#overview) + // we ignore the possibility of there being campaign variables in the + // anchor because we wouldn't know how to detect and parse them if they + // were present + return this.stripPIIFromString(window.location.href.split('#')[0]) + } + Analytics.prototype.trackPageview = function (path, title, options) { - this.sendToTrackers('trackPageview', arguments) + arguments[0] = arguments[0] || this.defaultPathForTrackPageview() + if (arguments.length === 0) { arguments.length = 1 } + this.sendToTrackers('trackPageview', this.stripPII(arguments)) } /* https://developers.google.com/analytics/devguides/collection/analyticsjs/events options.label – Useful for categorizing events (eg nav buttons) options.value – Values must be non-negative. Useful to pass counts options.nonInteraction – Prevent event from impacting bounce rate */ Analytics.prototype.trackEvent = function (category, action, options) { - this.sendToTrackers('trackEvent', arguments) + this.sendToTrackers('trackEvent', this.stripPII(arguments)) } Analytics.prototype.trackShare = function (network, options) { - this.sendToTrackers('trackSocial', [network, 'share', global.location.pathname, options]) + this.sendToTrackers('trackSocial', this.stripPII([network, 'share', global.location.pathname, options])) } /* The custom dimension index must be configured within the Universal Analytics profile */ Analytics.prototype.setDimension = function (index, value) { - this.sendToTrackers('setDimension', arguments) + this.sendToTrackers('setDimension', this.stripPII(arguments)) } /* Add a beacon to track a page in another GA account on another domain. */