govuk_frontend_toolkit 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/app/assets/CHANGELOG.md +4 -0
- data/app/assets/Gruntfile.js +4 -1
- data/app/assets/LICENCE +1 -1
- data/app/assets/README.md +29 -834
- data/app/assets/VERSION.txt +1 -1
- data/app/assets/docs/analytics.md +158 -0
- data/app/assets/docs/javascript.md +206 -0
- data/app/assets/docs/mixins.md +624 -0
- data/app/assets/javascripts/govuk/analytics/error-tracking.js +22 -0
- data/app/assets/javascripts/govuk/analytics/google-analytics-classic-tracker.js +111 -0
- data/app/assets/javascripts/govuk/analytics/google-analytics-universal-tracker.js +104 -0
- data/app/assets/javascripts/govuk/analytics/print-intent.js +31 -0
- data/app/assets/javascripts/govuk/analytics/tracker.js +61 -0
- data/app/assets/package.json +1 -1
- data/app/assets/spec/unit/MultivariateTestSpec.js +11 -11
- data/app/assets/spec/unit/SelectionButtonSpec.js +4 -4
- data/app/assets/spec/unit/analytics/GoogleAnalyticsClassicTrackerSpec.js +124 -0
- data/app/assets/spec/unit/analytics/GoogleAnalyticsUniversalTrackerSpec.js +122 -0
- data/app/assets/spec/unit/analytics/TrackerSpec.js +74 -0
- metadata +46 -15
@@ -0,0 +1,22 @@
|
|
1
|
+
// Extension to track errors using google analytics as a data store.
|
2
|
+
(function() {
|
3
|
+
|
4
|
+
"use strict";
|
5
|
+
var trackJavaScriptError = function (e) {
|
6
|
+
var errorSource = e.filename + ': ' + e.lineno;
|
7
|
+
GOVUK.analytics.trackEvent('JavaScript Error', e.message, {
|
8
|
+
label: errorSource,
|
9
|
+
value: 1,
|
10
|
+
nonInteraction: true
|
11
|
+
});
|
12
|
+
};
|
13
|
+
|
14
|
+
if (window.addEventListener) {
|
15
|
+
window.addEventListener('error', trackJavaScriptError, false);
|
16
|
+
} else if (window.attachEvent) {
|
17
|
+
window.attachEvent('onerror', trackJavaScriptError);
|
18
|
+
} else {
|
19
|
+
window.onerror = trackJavaScriptError;
|
20
|
+
}
|
21
|
+
|
22
|
+
}());
|
@@ -0,0 +1,111 @@
|
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
3
|
+
window.GOVUK = window.GOVUK || {};
|
4
|
+
|
5
|
+
var GoogleAnalyticsClassicTracker = function(id, cookieDomain) {
|
6
|
+
window._gaq = window._gaq || [];
|
7
|
+
configureProfile(id, cookieDomain);
|
8
|
+
allowCrossDomainTracking();
|
9
|
+
anonymizeIp();
|
10
|
+
|
11
|
+
function configureProfile(id, cookieDomain) {
|
12
|
+
_gaq.push(['_setAccount', id]);
|
13
|
+
_gaq.push(['_setDomainName', cookieDomain]);
|
14
|
+
}
|
15
|
+
|
16
|
+
function allowCrossDomainTracking() {
|
17
|
+
_gaq.push(['_setAllowLinker', true]);
|
18
|
+
}
|
19
|
+
|
20
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApi_gat#_gat._anonymizeIp
|
21
|
+
function anonymizeIp() {
|
22
|
+
_gaq.push(['_gat._anonymizeIp']);
|
23
|
+
}
|
24
|
+
};
|
25
|
+
|
26
|
+
GoogleAnalyticsClassicTracker.load = function() {
|
27
|
+
var ga = document.createElement('script'),
|
28
|
+
s = document.getElementsByTagName('script')[0];
|
29
|
+
|
30
|
+
ga.async = true;
|
31
|
+
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
32
|
+
s.parentNode.insertBefore(ga, s);
|
33
|
+
};
|
34
|
+
|
35
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/asyncMigrationExamples#VirtualPageviews
|
36
|
+
GoogleAnalyticsClassicTracker.prototype.trackPageview = function(path) {
|
37
|
+
var pageview = ['_trackPageview'];
|
38
|
+
|
39
|
+
if (typeof path === "string") {
|
40
|
+
pageview.push(path);
|
41
|
+
}
|
42
|
+
|
43
|
+
_gaq.push(pageview);
|
44
|
+
};
|
45
|
+
|
46
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide
|
47
|
+
GoogleAnalyticsClassicTracker.prototype.trackEvent = function(category, action, options) {
|
48
|
+
var value,
|
49
|
+
options = options || {},
|
50
|
+
hasLabel = false,
|
51
|
+
hasValue = false,
|
52
|
+
evt = ["_trackEvent", category, action];
|
53
|
+
|
54
|
+
// Label is optional
|
55
|
+
if (typeof options.label === "string") {
|
56
|
+
hasLabel = true;
|
57
|
+
evt.push(options.label);
|
58
|
+
}
|
59
|
+
|
60
|
+
// Value is optional, but when used must be an
|
61
|
+
// integer, otherwise the event will be invalid
|
62
|
+
// and not logged
|
63
|
+
if (options.value || options.value === 0) {
|
64
|
+
value = parseInt(options.value, 10);
|
65
|
+
if (typeof value === "number" && !isNaN(value)) {
|
66
|
+
hasValue = true;
|
67
|
+
|
68
|
+
// Push an empty label if not set for correct final argument order
|
69
|
+
if (!hasLabel) {
|
70
|
+
evt.push('');
|
71
|
+
}
|
72
|
+
|
73
|
+
evt.push(value);
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
// Prevents an event from affecting bounce rate
|
78
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/eventTrackerGuide#non-interaction
|
79
|
+
if (options.nonInteraction) {
|
80
|
+
|
81
|
+
// Push empty label/value if not already set, for correct final argument order
|
82
|
+
if (!hasValue) {
|
83
|
+
if (!hasLabel) {
|
84
|
+
evt.push('');
|
85
|
+
}
|
86
|
+
evt.push(0);
|
87
|
+
}
|
88
|
+
|
89
|
+
evt.push(true);
|
90
|
+
}
|
91
|
+
|
92
|
+
_gaq.push(evt);
|
93
|
+
};
|
94
|
+
|
95
|
+
/*
|
96
|
+
https://developers.google.com/analytics/devguides/collection/gajs/methods/gaJSApiSocialTracking
|
97
|
+
network – The network on which the action occurs (e.g. Facebook, Twitter)
|
98
|
+
action – The type of action that happens (e.g. Like, Send, Tweet)
|
99
|
+
target – The text value that indicates the subject of the action
|
100
|
+
*/
|
101
|
+
GoogleAnalyticsClassicTracker.prototype.trackSocial = function(network, action, target) {
|
102
|
+
_gaq.push(['_trackSocial', network, action, target]);
|
103
|
+
};
|
104
|
+
|
105
|
+
// https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingCustomVariables
|
106
|
+
GoogleAnalyticsClassicTracker.prototype.setCustomVariable = function(index, value, name, scope) {
|
107
|
+
_gaq.push(['_setCustomVar', index, name, String(value), scope]);
|
108
|
+
};
|
109
|
+
|
110
|
+
GOVUK.GoogleAnalyticsClassicTracker = GoogleAnalyticsClassicTracker;
|
111
|
+
})();
|
@@ -0,0 +1,104 @@
|
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
3
|
+
window.GOVUK = window.GOVUK || {};
|
4
|
+
|
5
|
+
var GoogleAnalyticsUniversalTracker = function(id, cookieDomain) {
|
6
|
+
configureProfile(id, cookieDomain);
|
7
|
+
anonymizeIp();
|
8
|
+
|
9
|
+
function configureProfile(id, cookieDomain) {
|
10
|
+
sendToGa('create', id, {'cookieDomain': cookieDomain});
|
11
|
+
}
|
12
|
+
|
13
|
+
function anonymizeIp() {
|
14
|
+
// https://developers.google.com/analytics/devguides/collection/analyticsjs/advanced#anonymizeip
|
15
|
+
sendToGa('set', 'anonymizeIp', true);
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
GoogleAnalyticsUniversalTracker.load = function() {
|
20
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
21
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
22
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
23
|
+
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
24
|
+
};
|
25
|
+
|
26
|
+
// https://developers.google.com/analytics/devguides/collection/analyticsjs/pages
|
27
|
+
GoogleAnalyticsUniversalTracker.prototype.trackPageview = function(path, title) {
|
28
|
+
if (typeof path === "string") {
|
29
|
+
var pageviewObject = {
|
30
|
+
page: path
|
31
|
+
};
|
32
|
+
|
33
|
+
if (typeof title === "string") {
|
34
|
+
pageviewObject.title = title;
|
35
|
+
}
|
36
|
+
sendToGa('send', 'pageview', pageviewObject);
|
37
|
+
} else {
|
38
|
+
sendToGa('send', 'pageview');
|
39
|
+
}
|
40
|
+
};
|
41
|
+
|
42
|
+
// https://developers.google.com/analytics/devguides/collection/analyticsjs/events
|
43
|
+
GoogleAnalyticsUniversalTracker.prototype.trackEvent = function(category, action, options) {
|
44
|
+
var value,
|
45
|
+
options = options || {},
|
46
|
+
evt = {
|
47
|
+
hitType: 'event',
|
48
|
+
eventCategory: category,
|
49
|
+
eventAction: action
|
50
|
+
};
|
51
|
+
|
52
|
+
// Label is optional
|
53
|
+
if (typeof options.label === "string") {
|
54
|
+
evt.eventLabel = options.label;
|
55
|
+
}
|
56
|
+
|
57
|
+
// Value is optional, but when used must be an
|
58
|
+
// integer, otherwise the event will be invalid
|
59
|
+
// and not logged
|
60
|
+
if (options.value || options.value === 0) {
|
61
|
+
value = parseInt(options.value, 10);
|
62
|
+
if (typeof value === "number" && !isNaN(value)) {
|
63
|
+
evt.eventValue = value;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
// Prevents an event from affecting bounce rate
|
68
|
+
// https://developers.google.com/analytics/devguides/collection/analyticsjs/events#implementation
|
69
|
+
if (options.nonInteraction) {
|
70
|
+
evt.nonInteraction = 1;
|
71
|
+
}
|
72
|
+
|
73
|
+
sendToGa('send', evt);
|
74
|
+
};
|
75
|
+
|
76
|
+
/*
|
77
|
+
https://developers.google.com/analytics/devguides/collection/analyticsjs/social-interactions
|
78
|
+
network – The network on which the action occurs (e.g. Facebook, Twitter)
|
79
|
+
action – The type of action that happens (e.g. Like, Send, Tweet)
|
80
|
+
target – Specifies the target of a social interaction.
|
81
|
+
This value is typically a URL but can be any text.
|
82
|
+
*/
|
83
|
+
GoogleAnalyticsUniversalTracker.prototype.trackSocial = function(network, action, target) {
|
84
|
+
sendToGa('send', {
|
85
|
+
'hitType': 'social',
|
86
|
+
'socialNetwork': network,
|
87
|
+
'socialAction': action,
|
88
|
+
'socialTarget': target
|
89
|
+
});
|
90
|
+
};
|
91
|
+
|
92
|
+
// https://developers.google.com/analytics/devguides/collection/analyticsjs/custom-dims-mets
|
93
|
+
GoogleAnalyticsUniversalTracker.prototype.setDimension = function(index, value) {
|
94
|
+
sendToGa('set', 'dimension' + index, String(value));
|
95
|
+
};
|
96
|
+
|
97
|
+
function sendToGa() {
|
98
|
+
if (typeof window.ga === "function") {
|
99
|
+
ga.apply(window, arguments);
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
GOVUK.GoogleAnalyticsUniversalTracker = GoogleAnalyticsUniversalTracker;
|
104
|
+
})();
|
@@ -0,0 +1,31 @@
|
|
1
|
+
// Extension to monitor attempts to print pages.
|
2
|
+
(function () {
|
3
|
+
|
4
|
+
"use strict";
|
5
|
+
var printAttempt = (function () {
|
6
|
+
GOVUK.analytics.trackEvent('Print Intent', document.location.pathname);
|
7
|
+
});
|
8
|
+
|
9
|
+
// Most browsers
|
10
|
+
if (window.matchMedia) {
|
11
|
+
var mediaQueryList = window.matchMedia('print'),
|
12
|
+
mqlListenerCount = 0;
|
13
|
+
mediaQueryList.addListener(function (mql) {
|
14
|
+
if (!mql.matches && mqlListenerCount === 0) {
|
15
|
+
printAttempt();
|
16
|
+
mqlListenerCount++;
|
17
|
+
// If we try and print again in 3 seconds, don't log it
|
18
|
+
window.setTimeout(function () {
|
19
|
+
mqlListenerCount = 0;
|
20
|
+
// printing will be tracked again now
|
21
|
+
}, 1e3);
|
22
|
+
}
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
// IE < 10
|
27
|
+
if (window.onafterprint) {
|
28
|
+
window.onafterprint = printAttempt;
|
29
|
+
}
|
30
|
+
|
31
|
+
}());
|
@@ -0,0 +1,61 @@
|
|
1
|
+
(function() {
|
2
|
+
"use strict";
|
3
|
+
window.GOVUK = window.GOVUK || {};
|
4
|
+
|
5
|
+
// For usage and initialisation see:
|
6
|
+
// https://github.com/alphagov/govuk_frontend_toolkit/blob/master/docs/analytics.md#create-an-analytics-tracker
|
7
|
+
|
8
|
+
var Tracker = function(config) {
|
9
|
+
this.universal = new GOVUK.GoogleAnalyticsUniversalTracker(config.universalId, config.cookieDomain);
|
10
|
+
this.classic = new GOVUK.GoogleAnalyticsClassicTracker(config.classicId, config.cookieDomain);
|
11
|
+
};
|
12
|
+
|
13
|
+
Tracker.load = function() {
|
14
|
+
GOVUK.GoogleAnalyticsClassicTracker.load();
|
15
|
+
GOVUK.GoogleAnalyticsUniversalTracker.load();
|
16
|
+
};
|
17
|
+
|
18
|
+
Tracker.prototype.trackPageview = function(path, title) {
|
19
|
+
this.classic.trackPageview(path);
|
20
|
+
this.universal.trackPageview(path, title);
|
21
|
+
};
|
22
|
+
|
23
|
+
/*
|
24
|
+
https://developers.google.com/analytics/devguides/collection/analyticsjs/events
|
25
|
+
options.label – Useful for categorizing events (eg nav buttons)
|
26
|
+
options.value – Values must be non-negative. Useful to pass counts
|
27
|
+
options.nonInteraction – Prevent event from impacting bounce rate
|
28
|
+
*/
|
29
|
+
Tracker.prototype.trackEvent = function(category, action, options) {
|
30
|
+
this.classic.trackEvent(category, action, options);
|
31
|
+
this.universal.trackEvent(category, action, options);
|
32
|
+
};
|
33
|
+
|
34
|
+
Tracker.prototype.trackShare = function(network) {
|
35
|
+
var target = location.pathname;
|
36
|
+
this.classic.trackSocial(network, 'share', target);
|
37
|
+
this.universal.trackSocial(network, 'share', target);
|
38
|
+
};
|
39
|
+
|
40
|
+
/*
|
41
|
+
Assumes that the index of the dimension is the same for both classic and universal.
|
42
|
+
Check this for your app before using this
|
43
|
+
*/
|
44
|
+
Tracker.prototype.setDimension = function(index, value, name, scope) {
|
45
|
+
var PAGE_LEVEL_SCOPE = 3;
|
46
|
+
scope = scope || PAGE_LEVEL_SCOPE;
|
47
|
+
|
48
|
+
if (typeof index !== "number") {
|
49
|
+
index = parseInt(index, 10);
|
50
|
+
}
|
51
|
+
|
52
|
+
if (typeof scope !== "number") {
|
53
|
+
scope = parseInt(scope, 10);
|
54
|
+
}
|
55
|
+
|
56
|
+
this.universal.setDimension(index, value);
|
57
|
+
this.classic.setCustomVariable(index, value, name, scope);
|
58
|
+
};
|
59
|
+
|
60
|
+
GOVUK.Tracker = Tracker;
|
61
|
+
})();
|
data/app/assets/package.json
CHANGED
@@ -6,7 +6,7 @@ describe("MultivariateTest", function() {
|
|
6
6
|
|
7
7
|
describe("#run", function() {
|
8
8
|
it("should pick a random cohort on first run", function() {
|
9
|
-
GOVUK.cookie.
|
9
|
+
GOVUK.cookie.and.returnValue(null);
|
10
10
|
var fooSpy = jasmine.createSpy('fooSpy');
|
11
11
|
var barSpy = jasmine.createSpy('barSpy');
|
12
12
|
var test = new GOVUK.MultivariateTest({
|
@@ -18,9 +18,9 @@ describe("MultivariateTest", function() {
|
|
18
18
|
}
|
19
19
|
});
|
20
20
|
|
21
|
-
expect(GOVUK.cookie.
|
22
|
-
expect(GOVUK.cookie.
|
23
|
-
if (GOVUK.cookie.
|
21
|
+
expect(GOVUK.cookie.calls.count()).toEqual(2);
|
22
|
+
expect(GOVUK.cookie.calls.argsFor(1)[0]).toEqual('multivariatetest_cohort_stuff');
|
23
|
+
if (GOVUK.cookie.calls.argsFor(1)[1] == 'foo') {
|
24
24
|
expect(fooSpy).toHaveBeenCalled();
|
25
25
|
}
|
26
26
|
else {
|
@@ -29,7 +29,7 @@ describe("MultivariateTest", function() {
|
|
29
29
|
});
|
30
30
|
|
31
31
|
it("should use an existing cohort choice on subsequent runs", function() {
|
32
|
-
GOVUK.cookie.
|
32
|
+
GOVUK.cookie.and.returnValue('foo');
|
33
33
|
var fooSpy = jasmine.createSpy('fooSpy');
|
34
34
|
var barSpy = jasmine.createSpy('barSpy');
|
35
35
|
var test = new GOVUK.MultivariateTest({
|
@@ -44,7 +44,7 @@ describe("MultivariateTest", function() {
|
|
44
44
|
});
|
45
45
|
|
46
46
|
it("should set a custom var", function() {
|
47
|
-
GOVUK.cookie.
|
47
|
+
GOVUK.cookie.and.returnValue('foo');
|
48
48
|
var test = new GOVUK.MultivariateTest({
|
49
49
|
name: 'stuff',
|
50
50
|
cohorts: {
|
@@ -73,7 +73,7 @@ describe("MultivariateTest", function() {
|
|
73
73
|
});
|
74
74
|
|
75
75
|
it("should set html for a cohort", function() {
|
76
|
-
GOVUK.cookie.
|
76
|
+
GOVUK.cookie.and.returnValue('foo');
|
77
77
|
var $el = $('<div>');
|
78
78
|
var test = new GOVUK.MultivariateTest({
|
79
79
|
name: 'stuff',
|
@@ -90,7 +90,7 @@ describe("MultivariateTest", function() {
|
|
90
90
|
it("should call the callback for a cohort", function() {
|
91
91
|
var fooSpy = jasmine.createSpy('fooSpy');
|
92
92
|
var barSpy = jasmine.createSpy('barSpy');
|
93
|
-
GOVUK.cookie.
|
93
|
+
GOVUK.cookie.and.returnValue('bar');
|
94
94
|
var $el = $('<div>');
|
95
95
|
var test = new GOVUK.MultivariateTest({
|
96
96
|
name: 'stuff',
|
@@ -105,7 +105,7 @@ describe("MultivariateTest", function() {
|
|
105
105
|
});
|
106
106
|
|
107
107
|
it("should call the callback for a cohort if it is a string", function() {
|
108
|
-
GOVUK.cookie.
|
108
|
+
GOVUK.cookie.and.returnValue('foo');
|
109
109
|
var test = new GOVUK.MultivariateTest({
|
110
110
|
name: 'stuff',
|
111
111
|
customVarIndex: 1,
|
@@ -123,7 +123,7 @@ describe("MultivariateTest", function() {
|
|
123
123
|
it("should assign a new random cohort if the assigned cohort does not exist", function() {
|
124
124
|
var fooSpy = jasmine.createSpy('fooSpy');
|
125
125
|
var barSpy = jasmine.createSpy('barSpy');
|
126
|
-
GOVUK.cookie.
|
126
|
+
GOVUK.cookie.and.returnValue('baz');
|
127
127
|
var test = new GOVUK.MultivariateTest({
|
128
128
|
name: 'stuff',
|
129
129
|
customVarIndex: 1,
|
@@ -132,7 +132,7 @@ describe("MultivariateTest", function() {
|
|
132
132
|
bar: {callback: barSpy}
|
133
133
|
}
|
134
134
|
});
|
135
|
-
if (GOVUK.cookie.
|
135
|
+
if (GOVUK.cookie.calls.argsFor(1)[1] == 'foo') {
|
136
136
|
expect(fooSpy).toHaveBeenCalled();
|
137
137
|
}
|
138
138
|
else {
|
@@ -694,7 +694,7 @@ describe("selection-buttons", function () {
|
|
694
694
|
clickCallbackCancelled = false,
|
695
695
|
focusBlurCallbackCancelled = false;
|
696
696
|
|
697
|
-
spyOn($.fn, "on").
|
697
|
+
spyOn($.fn, "on").and.callFake(function (evt, callback) {
|
698
698
|
if (this === $radioButtons) {
|
699
699
|
if (evt === "click") {
|
700
700
|
clickCallbackBound = callback;
|
@@ -706,7 +706,7 @@ describe("selection-buttons", function () {
|
|
706
706
|
return this;
|
707
707
|
});
|
708
708
|
|
709
|
-
spyOn($.fn, "off").
|
709
|
+
spyOn($.fn, "off").and.callFake(function (evt, callback) {
|
710
710
|
if (this === $radioButtons) {
|
711
711
|
if (evt === "click") {
|
712
712
|
clickCallbackCancelled = callback;
|
@@ -731,7 +731,7 @@ describe("selection-buttons", function () {
|
|
731
731
|
clickCallbackCancelled = false,
|
732
732
|
focusBlurCallbackCancelled = false;
|
733
733
|
|
734
|
-
spyOn($.fn, "on").
|
734
|
+
spyOn($.fn, "on").and.callFake(function (evt, selector, callback) {
|
735
735
|
if ((this[0] === document) && (selector === "label.selectable input[type='checkbox']")) {
|
736
736
|
if (evt === "click") {
|
737
737
|
clickCallbackBound = callback;
|
@@ -743,7 +743,7 @@ describe("selection-buttons", function () {
|
|
743
743
|
return this;
|
744
744
|
});
|
745
745
|
|
746
|
-
spyOn($.fn, "off").
|
746
|
+
spyOn($.fn, "off").and.callFake(function (evt, selector, callback) {
|
747
747
|
if ((this[0] === document) && (selector === "label.selectable input[type='checkbox']")) {
|
748
748
|
if (evt === "click") {
|
749
749
|
clickCallbackCancelled = callback;
|