Skip to content

Commit 1e834f2

Browse files
committed
Merge pull request #5 from amplitude/utmParameters
Automatically gather UTM parameters
2 parents 9def1a4 + 4b6d442 commit 1e834f2

File tree

4 files changed

+157
-4
lines changed

4 files changed

+157
-4
lines changed

amplitude.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,16 @@ Amplitude.prototype._eventId = 0;
157157
Amplitude.prototype._sending = false;
158158
Amplitude.prototype._lastEventTime = null;
159159
Amplitude.prototype._sessionId = null;
160+
Amplitude.prototype._newSession = false;
160161

161162
/**
162163
* Initializes Amplitude.
163164
* apiKey The API Key for your app
164165
* opt_userId An identifier for this user
165166
* opt_config Configuration options
166167
* - saveEvents (boolean) Whether to save events to local storage. Defaults to true.
168+
* - utmParams (string) Optional utm data in query string format.
169+
* Pulled from location.search otherwise.
167170
*/
168171
Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
169172
try {
@@ -211,11 +214,16 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
211214
this.sendEvents();
212215
}
213216

217+
// Parse the utm properties out of cookies and query for adding to user properties.
218+
var utmParams = opt_config && opt_config.utmParams || location.search;
219+
this._utmProperties = Amplitude._getUtmData(Cookie.get('__utmz'), utmParams);
220+
214221
this._lastEventTime = parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_TIME)) || null;
215222
this._sessionId = parseInt(localStorage.getItem(LocalStorageKeys.SESSION_ID)) || null;
216223
this._eventId = localStorage.getItem(LocalStorageKeys.LAST_EVENT_ID) || 0;
217224
var now = new Date().getTime();
218225
if (!this._sessionId || !this._lastEventTime || now - this._lastEventTime > this.options.sessionTimeout) {
226+
this._newSession = true;
219227
this._sessionId = now;
220228
localStorage.setItem(LocalStorageKeys.SESSION_ID, this._sessionId);
221229
}
@@ -226,6 +234,10 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
226234
}
227235
};
228236

237+
Amplitude.prototype.isNewSession = function() {
238+
return this._newSession;
239+
};
240+
229241
Amplitude.prototype.nextEventId = function() {
230242
this._eventId++;
231243
return this._eventId;
@@ -254,6 +266,31 @@ var _saveCookieData = function(scope) {
254266
});
255267
};
256268

269+
Amplitude._getUtmParam = function(name, query) {
270+
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
271+
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
272+
var results = regex.exec(query);
273+
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, " "));
274+
};
275+
276+
Amplitude._getUtmData = function(rawCookie, query) {
277+
// Translate the utmz cookie format into url query string format.
278+
var cookie = rawCookie ? '?' + rawCookie.split('.').slice(-1)[0].replace(/\|/g, '&') : '';
279+
280+
var fetchParam = function (queryName, query, cookieName, cookie) {
281+
return Amplitude._getUtmParam(queryName, query) ||
282+
Amplitude._getUtmParam(cookieName, cookie);
283+
};
284+
285+
return {
286+
utm_source: fetchParam('utm_source', query, 'utmcsr', cookie),
287+
utm_medium: fetchParam('utm_medium', query, 'utmcmd', cookie),
288+
utm_campaign: fetchParam('utm_campaign', query, 'utmccn', cookie),
289+
utm_term: fetchParam('utm_term', query, 'utmctr', cookie),
290+
utm_content: fetchParam('utm_content', query, 'utmcct', cookie),
291+
};
292+
};
293+
257294
Amplitude.prototype.saveEvents = function() {
258295
try {
259296
localStorage.setItem(this.options.unsentKey, JSON.stringify(this._unsentEvents));
@@ -336,6 +373,11 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) {
336373
localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME, this._lastEventTime);
337374
localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID, eventId);
338375

376+
// Add the utm properties, if any, onto the user properties.
377+
var userProperties = {};
378+
object.merge(userProperties, this.options.userProperties || {}, this._utmProperties);
379+
object.merge(userProperties, this._utmProperties);
380+
339381
eventProperties = eventProperties || {};
340382
var event = {
341383
device_id: this.options.deviceId,
@@ -351,7 +393,7 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) {
351393
device_model: ua.os.family,
352394
language: this.options.language,
353395
event_properties: eventProperties,
354-
user_properties: this.options.userProperties || {},
396+
user_properties: userProperties,
355397
uuid: UUID(),
356398
library: {
357399
name: 'amplitude-js',

amplitude.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/amplitude.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Amplitude.prototype._newSession = false;
5555
* opt_userId An identifier for this user
5656
* opt_config Configuration options
5757
* - saveEvents (boolean) Whether to save events to local storage. Defaults to true.
58+
* - utmParams (string) Optional utm data in query string format.
59+
* Pulled from location.search otherwise.
5860
*/
5961
Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
6062
try {
@@ -102,6 +104,10 @@ Amplitude.prototype.init = function(apiKey, opt_userId, opt_config) {
102104
this.sendEvents();
103105
}
104106

107+
// Parse the utm properties out of cookies and query for adding to user properties.
108+
var utmParams = opt_config && opt_config.utmParams || location.search;
109+
this._utmProperties = Amplitude._getUtmData(Cookie.get('__utmz'), utmParams);
110+
105111
this._lastEventTime = parseInt(localStorage.getItem(LocalStorageKeys.LAST_EVENT_TIME)) || null;
106112
this._sessionId = parseInt(localStorage.getItem(LocalStorageKeys.SESSION_ID)) || null;
107113
this._eventId = localStorage.getItem(LocalStorageKeys.LAST_EVENT_ID) || 0;
@@ -150,6 +156,31 @@ var _saveCookieData = function(scope) {
150156
});
151157
};
152158

159+
Amplitude._getUtmParam = function(name, query) {
160+
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
161+
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
162+
var results = regex.exec(query);
163+
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, " "));
164+
};
165+
166+
Amplitude._getUtmData = function(rawCookie, query) {
167+
// Translate the utmz cookie format into url query string format.
168+
var cookie = rawCookie ? '?' + rawCookie.split('.').slice(-1)[0].replace(/\|/g, '&') : '';
169+
170+
var fetchParam = function (queryName, query, cookieName, cookie) {
171+
return Amplitude._getUtmParam(queryName, query) ||
172+
Amplitude._getUtmParam(cookieName, cookie);
173+
};
174+
175+
return {
176+
utm_source: fetchParam('utm_source', query, 'utmcsr', cookie),
177+
utm_medium: fetchParam('utm_medium', query, 'utmcmd', cookie),
178+
utm_campaign: fetchParam('utm_campaign', query, 'utmccn', cookie),
179+
utm_term: fetchParam('utm_term', query, 'utmctr', cookie),
180+
utm_content: fetchParam('utm_content', query, 'utmcct', cookie),
181+
};
182+
};
183+
153184
Amplitude.prototype.saveEvents = function() {
154185
try {
155186
localStorage.setItem(this.options.unsentKey, JSON.stringify(this._unsentEvents));
@@ -232,6 +263,11 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) {
232263
localStorage.setItem(LocalStorageKeys.LAST_EVENT_TIME, this._lastEventTime);
233264
localStorage.setItem(LocalStorageKeys.LAST_EVENT_ID, eventId);
234265

266+
// Add the utm properties, if any, onto the user properties.
267+
var userProperties = {};
268+
object.merge(userProperties, this.options.userProperties || {});
269+
object.merge(userProperties, this._utmProperties);
270+
235271
eventProperties = eventProperties || {};
236272
var event = {
237273
device_id: this.options.deviceId,
@@ -247,7 +283,7 @@ Amplitude.prototype.logEvent = function(eventType, eventProperties) {
247283
device_model: ua.os.family,
248284
language: this.options.language,
249285
event_properties: eventProperties,
250-
user_properties: this.options.userProperties || {},
286+
user_properties: userProperties,
251287
uuid: UUID(),
252288
library: {
253289
name: 'amplitude-js',

test/amplitude.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,81 @@ describe('Amplitude', function() {
187187
});
188188
});
189189

190+
describe('gatherUtm', function() {
191+
beforeEach(function() {
192+
amplitude.init(apiKey);
193+
});
194+
195+
afterEach(function() {
196+
reset();
197+
});
198+
199+
it('should add utm params to the user properties', function() {
200+
cookie.set('__utmz', '133232535.1424926227.1.1.utmcct=top&utmccn=new');
201+
202+
reset();
203+
amplitude.init(apiKey, undefined, {
204+
utmParams: '?utm_source=amplitude&utm_medium=email&utm_term=terms'
205+
});
206+
207+
amplitude.setUserProperties({user_prop: true});
208+
amplitude.logEvent('UTM Test Event', {});
209+
210+
assert.lengthOf(server.requests, 1);
211+
var events = JSON.parse(querystring.parse(server.requests[0].requestBody).e);
212+
assert.deepEqual(events[0].user_properties, {
213+
user_prop: true,
214+
utm_campaign: 'new',
215+
utm_content: 'top',
216+
utm_medium: 'email',
217+
utm_source: 'amplitude',
218+
utm_term: 'terms'
219+
});
220+
221+
});
222+
223+
it('should get utm params from the query string', function() {
224+
var query = '?utm_source=amplitude&utm_medium=email&utm_term=terms' +
225+
'&utm_content=top&utm_campaign=new';
226+
var utms = Amplitude._getUtmData('', query);
227+
assert.deepEqual(utms, {
228+
utm_campaign: 'new',
229+
utm_content: 'top',
230+
utm_medium: 'email',
231+
utm_source: 'amplitude',
232+
utm_term: 'terms'
233+
});
234+
});
235+
236+
it('should get utm params from the cookie string', function() {
237+
var cookie = '133232535.1424926227.1.1.utmcsr=google|utmccn=(organic)' +
238+
'|utmcmd=organic|utmctr=(none)|utmcct=link';
239+
var utms = Amplitude._getUtmData(cookie, '');
240+
assert.deepEqual(utms, {
241+
utm_campaign: '(organic)',
242+
utm_content: 'link',
243+
utm_medium: 'organic',
244+
utm_source: 'google',
245+
utm_term: '(none)'
246+
});
247+
});
248+
249+
it('should prefer utm params from the query string', function() {
250+
var query = '?utm_source=amplitude&utm_medium=email&utm_term=terms' +
251+
'&utm_content=top&utm_campaign=new';
252+
var cookie = '133232535.1424926227.1.1.utmcsr=google|utmccn=(organic)' +
253+
'|utmcmd=organic|utmctr=(none)|utmcct=link';
254+
var utms = Amplitude._getUtmData(cookie, query);
255+
assert.deepEqual(utms, {
256+
utm_campaign: 'new',
257+
utm_content: 'top',
258+
utm_medium: 'email',
259+
utm_source: 'amplitude',
260+
utm_term: 'terms'
261+
});
262+
});
263+
});
264+
190265
describe('sessionId', function() {
191266

192267
var clock;

0 commit comments

Comments
 (0)