Skip to content

Commit 4e52a25

Browse files
committed
Merge branch 'release/2.6.0'
2 parents f704765 + a847071 commit 4e52a25

File tree

10 files changed

+637
-66
lines changed

10 files changed

+637
-66
lines changed

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ language: node_js
55
install:
66
- npm install
77
- npm install -g grunt-cli
8-
- wget https://api.equinox.io/1/Applications/ap_pJSFC5wQYkAyI0FIVwKYs9h1hW/Updates/Asset/ngrok.zip\?os\=linux\&arch\=386\&channel\=stable
9-
- unzip ./ngrok.zip\?os=linux\&arch=386\&channel=stable
8+
- wget https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
9+
- unzip ./ngrok-stable-linux-amd64.zip
1010
before_script:
1111
- node tests/integration/request_recorder.js &
1212

1313
# Create a random ngrok subdomain
1414
- export SUBDOMAIN=$RANDOM$RANDOM
15-
- ./ngrok -authtoken $NGROK_AUTH -subdomain $SUBDOMAIN -log=stdout 8500 > /dev/null &
15+
- ./ngrok http -authtoken $NGROK_AUTH -subdomain $SUBDOMAIN -log=stdout 8500 > /dev/null &
1616
script:
1717
- grunt travis
1818
after_script:

CHANGELOG

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
1+
Version 2.6.0 (2016-03-03)
2+
--------------------------
3+
Bumped ngrok version used in CI to 2.x (#460)
4+
Allowed random upload path in grunt task (#461)
5+
Ensured that PerformanceTiming context doesn't contain properties inherited from Object.prototype (#458)
6+
Added `forceUnsecureTracker` Tracker argument, thanks @bloodyowl! (#374)
7+
Added subset of Augur data as a new context (#386)
8+
Added deprecation warning to setSessionCookieTimeout (#394)
9+
Added setting to automatically use top-level domain for duid (#409)
10+
Added Optimizely contexts (#448)
11+
Added trackEnhancedEcommerceAction() method (#452)
12+
Added addEnhancedEcommerceActionContext() method (#453)
13+
Added addEnhancedEcommerceImpressionContext() method (#454)
14+
Added addEnhancedEcommerceProductContext() method (#455)
15+
Added addEnhancedEcommercePromoContext() method (#456)
16+
Made domainUserId a UUID (#274)
17+
Attached device sent timestamp (stm) to events at last possible moment (#355)
18+
Attempting to create a new tracker using an existing namespace should do nothing (#411)
19+
Using a different library to publish to S3 (#422)
20+
Prevented error running grunt-cloudfront (#426)
21+
Respected doNotTrack in IE 11 and Safari 7.1.3+, thanks @grzegorzewald! (#440)
22+
123
Version 2.5.3 (2015-11-10)
224
--------------------------
325
Bumped Node version to 4.1.2 in .travis.yml (#420)

Gruntfile.js

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,7 @@ module.exports = function(grunt) {
181181

182182
grunt.loadNpmTasks('grunt-contrib-concat');
183183
grunt.loadNpmTasks('grunt-yui-compressor');
184-
grunt.loadNpmTasks('grunt-s3');
185-
grunt.loadNpmTasks('grunt-cloudfront');
184+
grunt.loadNpmTasks('grunt-aws');
186185
grunt.loadNpmTasks('grunt-browserify');
187186
grunt.loadNpmTasks('intern');
188187
grunt.loadNpmTasks('grunt-lodash');
@@ -194,63 +193,60 @@ module.exports = function(grunt) {
194193

195194
grunt.config('s3', {
196195
options: {
197-
key: '<%= aws.key %>',
198-
secret: '<%= aws.secret %>',
196+
accessKeyId: '<%= aws.key %>',
197+
secretAccessKey: '<%= aws.secret %>',
199198
bucket: '<%= aws.bucket %>',
200199
access: 'public-read',
201-
gzip: true
200+
region: '<%= aws.region %>',
201+
gzip: true,
202+
cache: false
202203
},
203204
not_pinned: {
204205
options: {
205206
headers: {
206-
'Cache-Control': 'max-age=315360000'
207+
CacheControl: "max-age=315360000"
207208
}
208209
},
209-
upload: [
210+
files: [
210211
{
211-
src: 'dist/sp.js',
212-
dest: '<%= pkg.version %>/sp.js'
212+
src: ["dist/sp.js"],
213+
dest: "<%= aws.uploadPath %>"
213214
}
214215
]
215216
},
216217
pinned: {
217218
options: {
218219
headers: {
219-
'Cache-Control': 'max-age=3600'
220+
CacheControl: "max-age=3600"
220221
}
221222
},
222-
upload: [
223+
files: [
223224
{
224-
src: 'dist/sp.js',
225-
dest: '<%= pkg.pinnedVersion %>/sp.js'
226-
}
225+
src: ["dist/sp.js"],
226+
dest: "<%= aws.uploadPath %>"
227+
}
227228
]
228-
},
229+
}
229230
});
230231

231232
grunt.config('cloudfront', {
232233
options: {
233-
region: 'eu-east-1',
234-
distributionId: '<%= aws.distribution %>',
235-
listInvalidations: true,
236-
listDistributions: true,
237-
credentials: {
238-
accessKeyId: '<%= aws.key %>',
239-
secretAccessKey: '<%= aws.secret %>'
240-
}
234+
accessKeyId: '<%= aws.key %>',
235+
secretAccessKey: '<%= aws.secret %>',
236+
distributionId: '<%= aws.distribution %>'
241237
},
242238
not_pinned: {
243-
CallerReference: Date.now().toString(),
244-
Paths: {
245-
Quantity: 1,
246-
Items: ['/<%= pkg.version %>/sp.js']
239+
options: {
240+
invalidations: [
241+
'/<%= aws.uploadPath %>'
242+
]
247243
}
248244
},
249245
pinned: {
250-
CallerReference: Date.now().toString(),
251-
Paths: {
252-
Quantity: 1,
253-
Items: ['/<%= pkg.pinnedVersion %>/sp.js']
246+
options: {
247+
invalidations: [
248+
'/<%= aws.uploadPath %>'
249+
]
254250
}
255251
}
256252
});

aws.sample.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@
22
"key": "ADD HERE",
33
"secret": "ADD HERE",
44
"bucket": "ADD HERE",
5-
"distribution": "ADD HERE"
5+
"region": "ADD HERE",
6+
"distribution": "ADD HERE",
7+
"uploadPath": "ADD HERE"
68
}

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
{
22
"name": "snowplow-tracker",
3-
"version": "2.5.3",
3+
"version": "2.6.0",
44
"devDependencies": {
55
"JSON": "~1.0.0",
66
"browser-cookie-lite": "~0.3.1",
77
"grunt": "~0.4.2",
88
"grunt-browserify": "~4.0.1",
9-
"grunt-cloudfront": "^0.2.0",
109
"grunt-contrib-concat": "~0.3.0",
1110
"grunt-lodash": "~0.3.0",
12-
"grunt-s3": "~0.2.0-alpha.3",
11+
"grunt-aws": "~0.6.1",
1312
"grunt-yui-compressor": "git://github.com/snowplow/grunt-yui-compressor.git#0.4.0",
1413
"intern": "^3.0.6",
1514
"jstimezonedetect": "1.0.5",
@@ -25,7 +24,8 @@
2524
"Alex Dean",
2625
"Simon Andersson",
2726
"Anthon Pang",
28-
"Fred Blundun"
27+
"Fred Blundun",
28+
"Joshua Beemster"
2929
],
3030
"description": "JavaScript tracker for Snowplow",
3131
"repository": {

src/js/in_queue.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,13 @@
112112
*/
113113
function createNewNamespace(namespace, endpoint, argmap) {
114114
argmap = argmap || {};
115-
trackerDictionary[namespace] = new TrackerConstructor(functionName, namespace, version, pageViewId, mutSnowplowState, argmap);
116-
trackerDictionary[namespace].setCollectorUrl(endpoint);
115+
116+
if (!trackerDictionary.hasOwnProperty(namespace)) {
117+
trackerDictionary[namespace] = new TrackerConstructor(functionName, namespace, version, pageViewId, mutSnowplowState, argmap);
118+
trackerDictionary[namespace].setCollectorUrl(endpoint);
119+
} else {
120+
helpers.warn('Tracker namespace ' + namespace + ' already exists.');
121+
}
117122
}
118123

119124
/**

src/js/lib/helpers.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
var
3737
lodash = require('../lib_managed/lodash'),
38+
cookie = require('browser-cookie-lite'),
3839

3940
object = typeof exports !== 'undefined' ? exports : this; // For eventual node.js environment support
4041

@@ -290,4 +291,102 @@
290291
}
291292
};
292293

294+
/**
295+
* Finds the root domain
296+
*/
297+
object.findRootDomain = function () {
298+
var cookiePrefix = '_sp_root_domain_test_';
299+
var cookieName = cookiePrefix + new Date().getTime();
300+
var cookieValue = '_test_value_' + new Date().getTime();
301+
302+
var split = window.location.hostname.split('.');
303+
var position = split.length - 1;
304+
while (position >= 0) {
305+
var currentDomain = split.slice(position, split.length).join('.');
306+
cookie.cookie(cookieName, cookieValue, 0, '/', currentDomain);
307+
if (cookie.cookie(cookieName) === cookieValue) {
308+
309+
// Clean up created cookie(s)
310+
object.deleteCookie(cookieName, currentDomain);
311+
var cookieNames = object.getCookiesWithPrefix(cookiePrefix);
312+
for (var i = 0; i < cookieNames.length; i++) {
313+
object.deleteCookie(cookieNames[i], currentDomain);
314+
}
315+
316+
return currentDomain;
317+
}
318+
position -= 1;
319+
}
320+
321+
// Cookies cannot be read
322+
return window.location.hostname;
323+
};
324+
325+
/**
326+
* Checks whether a value is present within an array
327+
*
328+
* @param val The value to check for
329+
* @param array The array to check within
330+
* @return boolean Whether it exists
331+
*/
332+
object.isValueInArray = function (val, array) {
333+
for (var i = 0; i < array.length; i++) {
334+
if (array[i] === val) {
335+
return true;
336+
}
337+
}
338+
return false;
339+
}
340+
341+
/**
342+
* Deletes an arbitrary cookie by setting the expiration date to the past
343+
*
344+
* @param cookieName The name of the cookie to delete
345+
* @param domainName The domain the cookie is in
346+
*/
347+
object.deleteCookie = function (cookieName, domainName) {
348+
cookie.cookie(cookieName, '', -1, '/', domainName);
349+
};
350+
351+
/**
352+
* Fetches the name of all cookies beginning with a certain prefix
353+
*
354+
* @param cookiePrefix The prefix to check for
355+
* @return array The cookies that begin with the prefix
356+
*/
357+
object.getCookiesWithPrefix = function (cookiePrefix) {
358+
var cookies = document.cookie.split("; ");
359+
var cookieNames = [];
360+
for (var i = 0; i < cookies.length; i++) {
361+
if (cookies[i].startsWith(cookiePrefix)) {
362+
cookieNames.push(cookies[i]);
363+
}
364+
}
365+
return cookieNames;
366+
};
367+
368+
/**
369+
* Parses an object and returns either the
370+
* integer or undefined.
371+
*
372+
* @param obj The object to parse
373+
* @return the result of the parse operation
374+
*/
375+
object.parseInt = function (obj) {
376+
var result = parseInt(obj);
377+
return isNaN(result) ? undefined : result;
378+
}
379+
380+
/**
381+
* Parses an object and returns either the
382+
* number or undefined.
383+
*
384+
* @param obj The object to parse
385+
* @return the result of the parse operation
386+
*/
387+
object.parseFloat = function (obj) {
388+
var result = parseFloat(obj);
389+
return isNaN(result) ? undefined : result;
390+
}
391+
293392
}());

src/js/out_queue.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@
179179
if (body.bytes >= maxPostBytes) {
180180
helpers.warn("Event of size " + body.bytes + " is too long - the maximum size is " + maxPostBytes);
181181
var xhr = initializeXMLHttpRequest(configCollectorUrl);
182-
xhr.send(encloseInPayloadDataEnvelope([body.evt]));
182+
xhr.send(encloseInPayloadDataEnvelope(attachStmToEvent([body.evt])));
183183
return;
184184
} else {
185185
outQueue.push(body);
@@ -268,8 +268,9 @@
268268
var batch = lodash.map(outQueue.slice(0, numberToSend), function (x) {
269269
return x.evt;
270270
});
271+
271272
if (batch.length > 0) {
272-
xhr.send(encloseInPayloadDataEnvelope(batch));
273+
xhr.send(encloseInPayloadDataEnvelope(attachStmToEvent(batch)));
273274
}
274275

275276
} else {
@@ -288,7 +289,7 @@
288289
executingQueue = false;
289290
};
290291

291-
image.src = configCollectorUrl + nextRequest;
292+
image.src = configCollectorUrl + nextRequest.replace('?', '?stm=' + new Date().getTime() + '&');
292293
}
293294
}
294295

@@ -319,6 +320,19 @@
319320
});
320321
}
321322

323+
/**
324+
* Attaches the STM field to outbound POST events.
325+
*
326+
* @param events the events to attach the STM to
327+
*/
328+
function attachStmToEvent(events) {
329+
var stm = new Date().getTime().toString();
330+
for (var i = 0; i < events.length; i++) {
331+
events[i]['stm'] = stm;
332+
}
333+
return events
334+
}
335+
322336
return {
323337
enqueueRequest: enqueueRequest,
324338
executeQueue: executeQueue

0 commit comments

Comments
 (0)