Skip to content

Commit eb09f56

Browse files
committed
Merge branch 'feature/2.2.0'
2 parents a278bea + 00c0485 commit eb09f56

12 files changed

+107
-47
lines changed

CHANGELOG

+12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
Version 2.2.0 (2014-12-15)
2+
--------------------------
3+
Made trackerDictionary object available in onload callbacks, thanks @murphybob! (#294)
4+
Ensured all page offsets are integers (#291)
5+
Added public method to get duid (#289)
6+
Added public method to get user fingerprint (#288)
7+
Added bundle.js to deploy/.gitignore (#281)
8+
Started using grunt-cloudfront for cache invalidation (#276)
9+
Added ability to disable use of localStorage (#181)
10+
Added ability to disable cookies (#140)
11+
URL encoded custom contexts if base 64 encoding is disabled (#299)
12+
113
Version 2.1.2 (2014-11-15)
214
--------------------------
315
Removed requestEnd field from PerformanceTiming context (#285)

Gruntfile.js

+25-13
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ module.exports = function(grunt) {
171171
grunt.loadNpmTasks('grunt-contrib-concat');
172172
grunt.loadNpmTasks('grunt-yui-compressor');
173173
grunt.loadNpmTasks('grunt-s3');
174-
grunt.loadNpmTasks('grunt-cloudfront-clear');
174+
grunt.loadNpmTasks('grunt-cloudfront');
175175
grunt.loadNpmTasks('grunt-browserify');
176176
grunt.loadNpmTasks('intern');
177177
grunt.loadNpmTasks('grunt-lodash');
@@ -217,25 +217,37 @@ module.exports = function(grunt) {
217217
},
218218
});
219219

220-
grunt.config('cloudfront_clear', {
221-
pinned: {
222-
resourcePaths: ['<%= pkg.pinnedVersion %>/sp.js'],
223-
secret_key: '<%= aws.secret %>',
224-
access_key: '<%= aws.key %>',
225-
dist: '<%= aws.distribution %>'
220+
grunt.config('cloudfront', {
221+
options: {
222+
region: 'eu-east-1',
223+
distributionId: '<%= aws.distribution %>',
224+
listInvalidations: true,
225+
listDistributions: true,
226+
credentials: {
227+
accessKeyId: '<%= aws.key %>',
228+
secretAccessKey: '<%= aws.secret %>'
229+
}
226230
},
227231
not_pinned: {
228-
resourcePaths: ['<%= pkg.version %>/sp.js'],
229-
secret_key: '<%= aws.secret %>',
230-
access_key: '<%= aws.key %>',
231-
dist: '<%= aws.distribution %>'
232+
CallerReference: Date.now().toString(),
233+
Paths: {
234+
Quantity: 1,
235+
Items: ['/<%= pkg.version %>/sp.js']
236+
}
237+
},
238+
pinned: {
239+
CallerReference: Date.now().toString(),
240+
Paths: {
241+
Quantity: 1,
242+
Items: ['/<%= pkg.pinnedVersion %>/sp.js']
243+
}
232244
}
233245
});
234246
});
235247

236248
grunt.registerTask('default', 'Build lodash, Browserify, add banner, and minify', ['lodash', 'browserify:main', 'concat:deploy', 'min:deploy']);
237-
grunt.registerTask('publish', 'Upload to S3 and invalidate Cloudfront (full semantic version only)', ['upload_setup', 'lodash', 'browserify:main', 'concat:deploy', 'min:deploy', 's3:not_pinned', 'cloudfront_clear:not_pinned']);
238-
grunt.registerTask('publish-pinned', 'Upload to S3 and invalidate Cloudfront (full semantic version and semantic major version)', ['upload_setup', 'lodash', 'browserify:main', 'concat:deploy', 'min:deploy', 's3', 'cloudfront_clear']);
249+
grunt.registerTask('publish', 'Upload to S3 and invalidate Cloudfront (full semantic version only)', ['upload_setup', 'lodash', 'browserify:main', 'concat:deploy', 'min:deploy', 's3:not_pinned', 'cloudfront:not_pinned']);
250+
grunt.registerTask('publish-pinned', 'Upload to S3 and invalidate Cloudfront (full semantic version and semantic major version)', ['upload_setup', 'lodash', 'browserify:main', 'concat:deploy', 'min:deploy', 's3', 'cloudfront']);
239251
grunt.registerTask('test', 'Intern tests', ['browserify:test', 'intern']);
240252
grunt.registerTask('travis', 'Intern tests for Travis CI', ['lodash','browserify:test','intern']);
241253
grunt.registerTask('tags', 'Minifiy the Snowplow invocation tag', ['min:tag', 'concat:tag']);

deploy/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# Assembled files
2+
bundle.js
23
snowplow.js
34
sp.js

examples/ads/async.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ <h1>Asynchronous ad tracking examples for snowplow.js</h1>
3838
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
3939
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
4040
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
41-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.1.2/sp.js","adTracker"));
41+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","adTracker"));
4242

4343
window.adTracker('newTracker', rnd, 'd3rkrsqld9gmqf.cloudfront.net', {
4444
'encodeBase64': false
@@ -110,7 +110,7 @@ <h1>Asynchronous ad tracking examples for snowplow.js</h1>
110110
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
111111
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
112112
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
113-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.0.2/sp.js","adTracker"));
113+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","adTracker"));
114114

115115
window.adTracker('newTracker', rnd, 'd3rkrsqld9gmqf.cloudfront.net', {
116116
'encodeBase64': false
@@ -141,7 +141,7 @@ <h1>Asynchronous ad tracking examples for snowplow.js</h1>
141141
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
142142
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
143143
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
144-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.0.2/sp.js","adTracker"));
144+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","adTracker"));
145145

146146
window.adTracker('newTracker', rnd, 'd3rkrsqld9gmqf.cloudfront.net', {
147147
'encodeBase64': false
@@ -154,7 +154,7 @@ <h1>Asynchronous ad tracking examples for snowplow.js</h1>
154154
}
155155

156156
document.getElementById(imageId).addEventListener('click', clickHandler);
157-
}('banner3'));
157+
}('banner3'));
158158

159159
</script>
160160
<!-- Snowplow stops plowing -->

examples/web/async-large.html

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
2424
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
2525
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
26-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.1.2/sp.js","snowplow_1"));
26+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","snowplow_1"));
2727

2828
window.snowplow_1('newTracker', 'cf', 'd3rkrsqld9gmqf.cloudfront.net', { // Initialise a tracker
2929
encodeBase64: false, // Default is true
@@ -55,6 +55,11 @@
5555
}
5656
]);
5757

58+
// Callback executed once the tracker loads which logs the user fingerprint
59+
window.snowplow_1(function () {
60+
console.log(this.cf.getUserFingerprint());
61+
});
62+
5863
</script>
5964
<!-- Snowplow stops plowing -->
6065

examples/web/async-medium.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
2525
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
2626
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
27-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.1.2/sp.js","new_name_here"));
27+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","new_name_here"));
2828

2929
window.new_name_here('newTracker', 'cf', 'd3rkrsqld9gmqf.cloudfront.net', {
3030
encodeBase64: false,

examples/web/async-small.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
;(function(p,l,o,w,i,n,g){if(!p[i]){p.GlobalSnowplowNamespace=p.GlobalSnowplowNamespace||[];
2424
p.GlobalSnowplowNamespace.push(i);p[i]=function(){(p[i].q=p[i].q||[]).push(arguments)
2525
};p[i].q=p[i].q||[];n=l.createElement(o);g=l.getElementsByTagName(o)[0];n.async=1;
26-
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.1.2/sp.js","snowplow"));
26+
n.src=w;g.parentNode.insertBefore(n,g)}}(window,document,"script","//d1fc8wv8zag5ca.cloudfront.net/2.2.0/sp.js","snowplow"));
2727

2828
window.snowplow('newTracker', 'cf', 'd3rkrsqld9gmqf.cloudfront.net', { // Initialise a tracker
2929
encodeBase64: false, // Default is true

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
{
22
"name": "snowplow-tracker",
3-
"version": "2.1.2",
3+
"version": "2.2.0",
44
"devDependencies": {
55
"JSON": "~1.0.0",
66
"browser-cookie-lite": "~0.3.1",
77
"browserify": "~3.28.1",
88
"grunt": "~0.4.2",
99
"grunt-browserify": "~1.3.0",
10-
"grunt-cloudfront-clear": "0.0.1",
10+
"grunt-cloudfront": "^0.2.0",
1111
"grunt-contrib-concat": "~0.3.0",
1212
"grunt-lodash": "~0.3.0",
1313
"grunt-s3": "~0.2.0-alpha.3",

src/js/in_queue.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@
150150
// Arguments is not an array, so we turn it into one
151151
input = Array.prototype.shift.call(parameterArray);
152152

153-
// Custom callback rather than tracker method
153+
// Custom callback rather than tracker method, called with trackerDictionary as the context
154154
if (lodash.isFunction(input)) {
155-
input.apply(this, parameterArray);
155+
input.apply(trackerDictionary, parameterArray);
156156
continue;
157157
}
158158

@@ -172,14 +172,8 @@
172172

173173
namedTrackers = getNamedTrackers(names);
174174

175-
if (lodash.isString(f)) {
176-
for (j = 0; j < namedTrackers.length; j++) {
177-
namedTrackers[j][f].apply(namedTrackers[j], parameterArray);
178-
}
179-
} else {
180-
for (j = 0; j < namedTrackers.length; j++) {
181-
f.apply(namedTrackers[j], parameterArray);
182-
}
175+
for (j = 0; j < namedTrackers.length; j++) {
176+
namedTrackers[j][f].apply(namedTrackers[j], parameterArray);
183177
}
184178
}
185179
}

src/js/lib/detectors.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,14 @@
175175
return isNaN(w) || isNaN(h) ? '' : w + 'x' + h;
176176
};
177177

178-
/*
178+
/**
179179
* Returns browser features (plugins, resolution, cookies)
180+
*
181+
* @param boolean useCookies Whether to test for cookies
182+
* @param string testCookieName Name to use for the test cookie
183+
* @return Object containing browser features
180184
*/
181-
object.detectBrowserFeatures = function(testCookieName) {
185+
object.detectBrowserFeatures = function(useCookies, testCookieName) {
182186
var i,
183187
mimeType,
184188
pluginMap = {
@@ -227,7 +231,9 @@
227231
// Other browser features
228232
features.res = screenAlias.width + 'x' + screenAlias.height;
229233
features.cd = screenAlias.colorDepth;
230-
features.cookie = object.hasCookies(testCookieName);
234+
if (useCookies) {
235+
features.cookie = object.hasCookies(testCookieName);
236+
}
231237

232238
return features;
233239
};

src/js/out_queue.js

+15-4
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,24 @@
4040
localStorageAccessible = require('./lib/detectors').localStorageAccessible(),
4141
object = typeof exports !== 'undefined' ? exports : this; // For eventual node.js environment support
4242

43-
object.OutQueueManager = function (functionName, namespace, mutSnowplowState) {
43+
/**
44+
* Object handling sending events to a collector.
45+
* Instantiated once per tracker instance.
46+
*
47+
* @param string functionName The Snowplow function name (used to generate the localStorage key)
48+
* @param string namespace The tracker instance's namespace (used to generate the localStorage key)
49+
* @param object mutSnowplowState Gives the pageUnloadGuard a reference to the outbound queue
50+
* so it can unload the page when all queues are empty
51+
* @param useLocalStorage Whether to use localStorage at all
52+
* @return object OutQueueManager instance
53+
*/
54+
object.OutQueueManager = function (functionName, namespace, mutSnowplowState, useLocalStorage) {
4455
var queueName = ['snowplowOutQueue', functionName, namespace].join('_'),
4556
executingQueue = false,
4657
configCollectorUrl,
4758
outQueue;
4859

49-
if (localStorageAccessible) {
60+
if (localStorageAccessible && useLocalStorage) {
5061
// Catch any JSON parse errors that might be thrown
5162
try {
5263
outQueue = json2.parse(localStorage.getItem(queueName));
@@ -69,7 +80,7 @@
6980
function enqueueRequest (request, url) {
7081
outQueue.push(request);
7182
configCollectorUrl = url;
72-
if (localStorageAccessible) {
83+
if (localStorageAccessible && useLocalStorage) {
7384
localStorage.setItem(queueName, json2.stringify(outQueue));
7485
}
7586

@@ -106,7 +117,7 @@
106117

107118
image.onload = function () {
108119
outQueue.shift();
109-
if (localStorageAccessible) {
120+
if (localStorageAccessible && useLocalStorage) {
110121
localStorage.setItem(queueName, json2.stringify(outQueue));
111122
}
112123
executeQueue();

src/js/tracker.js

+27-8
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@
7171
* 8. userFingerprintSeed, 123412414
7272
* 9. pageUnloadTimer, 500
7373
* 10. forceSecureTracker, false
74-
* 11. writeCookies, true
74+
* 11. useLocalStorage, true
75+
* 12. useCookies, true
76+
* 13. writeCookies, true
7577
*/
7678
object.Tracker = function Tracker(functionName, namespace, version, mutSnowplowState, argmap) {
7779

@@ -165,12 +167,17 @@
165167
// This forces the tracker to be HTTPS even if the page is not secure
166168
forceSecureTracker = argmap.hasOwnProperty('forceSecureTracker') ? (argmap.forceSecureTracker === true) : false,
167169

170+
// Whether to use localStorage to store events between sessions while offline
171+
useLocalStorage = argmap.hasOwnProperty('useLocalStorage') ? argmap.useLocalStorage : true,
172+
173+
// Whether to use cookies
174+
configUseCookies = argmap.hasOwnProperty('useCookies') ? argmap.useCookies : true,
168175

169176
// Browser language (or Windows language for IE). Imperfect but CloudFront doesn't log the Accept-Language header
170177
browserLanguage = navigatorAlias.userLanguage || navigatorAlias.language,
171178

172179
// Browser features via client-side data collection
173-
browserFeatures = detectors.detectBrowserFeatures(getSnowplowCookieName('testcookie')),
180+
browserFeatures = detectors.detectBrowserFeatures(configUseCookies, getSnowplowCookieName('testcookie')),
174181

175182
// Visitor fingerprint
176183
userFingerprint = (argmap.userFingerprint === false) ? '' : detectors.detectSignature(configUserFingerprintHashSeed),
@@ -213,7 +220,7 @@
213220
formTrackingManager = forms.getFormTrackingManager(core, trackerId),
214221

215222
// Manager for local storage queue
216-
outQueueManager = new requestQueue.OutQueueManager(functionName, namespace, mutSnowplowState);
223+
outQueueManager = new requestQueue.OutQueueManager(functionName, namespace, mutSnowplowState, useLocalStorage);
217224

218225

219226
// Enable base 64 encoding for unstructured events and custom contexts
@@ -326,7 +333,7 @@
326333

327334
for (var contextKey in lowPriorityKeys) {
328335
if (request.hasOwnProperty(contextKey) && lowPriorityKeys.hasOwnProperty(contextKey)) {
329-
querystring += '&' + contextKey + '=' + request[contextKey];
336+
querystring += '&' + contextKey + '=' + encodeURIComponent(request[contextKey]);
330337
}
331338
}
332339

@@ -442,6 +449,9 @@
442449
* Load visitor ID cookie
443450
*/
444451
function loadDomainUserIdCookie() {
452+
if (!configUseCookies) {
453+
return [];
454+
}
445455
var now = new Date(),
446456
nowTs = Math.round(now.getTime() / 1000),
447457
id = getSnowplowCookieValue('id'),
@@ -497,14 +507,14 @@
497507
currentVisitTs = id[4],
498508
lastVisitTs = id[5];
499509

500-
if (configDoNotTrack && configWriteCookies) {
510+
if (configDoNotTrack && configUseCookies && configWriteCookies) {
501511
cookie.cookie(idname, '', -1, configCookiePath, configCookieDomain);
502512
cookie.cookie(sesname, '', -1, configCookiePath, configCookieDomain);
503513
return;
504514
}
505515

506516
// New session?
507-
if (!ses) {
517+
if (!ses && configUseCookies) {
508518
// New session (aka new visit)
509519
visitCount++;
510520
// Update the last visit timestamp
@@ -529,7 +539,7 @@
529539

530540

531541
// Update cookies
532-
if (configWriteCookies) {
542+
if (configUseCookies && configWriteCookies) {
533543
setDomainUserIdCookie(_domainUserId, createTs, visitCount, nowTs, lastVisitTs);
534544
cookie.cookie(sesname, '*', configSessionCookieTimeout, configCookiePath, configCookieDomain);
535545
}
@@ -650,7 +660,7 @@
650660
function logPagePing(pageTitle, context) {
651661
resetMaxScrolls();
652662
core.trackPagePing(purify(configCustomUrl || locationHrefAlias), pageTitle, purify(configReferrerUrl),
653-
minXOffset, maxXOffset, minYOffset, maxYOffset, context);
663+
Math.round(minXOffset), Math.round(maxXOffset), Math.round(minYOffset), Math.round(maxYOffset), context);
654664
}
655665

656666
/**
@@ -785,6 +795,15 @@
785795
return loadDomainUserIdCookie();
786796
},
787797

798+
/**
799+
* Get the user fingerprint
800+
*
801+
* @return string The user fingerprint
802+
*/
803+
getUserFingerprint: function () {
804+
return userFingerprint;
805+
},
806+
788807
/**
789808
* Specify the app ID
790809
*

0 commit comments

Comments
 (0)