Skip to content

Commit 2ff5b8b

Browse files
committed
add cookie storage as backup for local storage
1 parent c9709ca commit 2ff5b8b

File tree

6 files changed

+173
-11
lines changed

6 files changed

+173
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Unreleased
22

3+
* Migrate cookie data to local storage to address issue where having cookies disabled causes SDK to generate a new deviceId for returning users.
4+
35
## 2.5.0 (September 30, 2015)
46

57
* Add support for user properties operations (set, setOnce, add, unset).

amplitude.js

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,7 +1888,7 @@ if (windowLocalStorageAvailable()) {
18881888
div.load(attrKey);
18891889
localStorage.length = div.XMLDocument.documentElement.attributes.length;
18901890
} else {
1891-
/* Nothing we can do ... */
1891+
localStorage = require('./localstorage-cookie.js');
18921892
}
18931893
}
18941894
if (!localStorage) {
@@ -1909,7 +1909,61 @@ if (!localStorage) {
19091909

19101910
module.exports = localStorage;
19111911

1912-
}, {}],
1912+
}, {"./localstorage-cookie.js":20}],
1913+
20: [function(require, module, exports) {
1914+
/* jshint -W020, unused: false, noempty: false, boss: true */
1915+
/* global escape, unescape */
1916+
1917+
var Cookie = require('./cookie.js');
1918+
1919+
var COOKIE_STORAGE_KEY = 'amplitude_storage';
1920+
1921+
var cookieStorage = {
1922+
length: 0,
1923+
1924+
setItem: function(sKey, sValue) {
1925+
var store = Cookie.get(COOKIE_STORAGE_KEY) || {};
1926+
1927+
// don't add if cookie size exceeds 4k
1928+
var existingValue = '';
1929+
if (store.hasOwnProperty(sKey)) { existingValue = store[sKey]; }
1930+
var itemLength = escape(sKey).length + escape(sValue).length - escape(existingValue).length;
1931+
if (document.cookie.length + itemLength > 4*1024) { return; }
1932+
1933+
if (!store.hasOwnProperty(sKey)) { this.length++; }
1934+
store[sKey] = sValue;
1935+
Cookie.set(COOKIE_STORAGE_KEY, store);
1936+
},
1937+
1938+
getItem: function(sKey) {
1939+
var store = Cookie.get(COOKIE_STORAGE_KEY);
1940+
return store && store.hasOwnProperty(sKey) && store[sKey] || null;
1941+
},
1942+
1943+
removeItem: function(sKey) {
1944+
var store = Cookie.get(COOKIE_STORAGE_KEY);
1945+
if (!store || !store.hasOwnProperty(sKey)) { return; }
1946+
delete store[sKey];
1947+
Cookie.set(COOKIE_STORAGE_KEY, store);
1948+
this.length--;
1949+
},
1950+
1951+
clear: function() {
1952+
Cookie.set(COOKIE_STORAGE_KEY, {});
1953+
this.length = 0;
1954+
},
1955+
1956+
key: function(n) {
1957+
var store = Cookie.get(COOKIE_STORAGE_KEY);
1958+
if (!store) { return null; }
1959+
var keys = Object.keys(store);
1960+
return n < keys.length && keys[n] || null;
1961+
}
1962+
};
1963+
1964+
module.exports = cookieStorage;
1965+
1966+
}, {"./cookie.js":3}],
19131967
7: [function(require, module, exports) {
19141968
/*
19151969
* JavaScript MD5 1.0.1
@@ -2320,8 +2374,8 @@ Request.prototype.send = function(callback) {
23202374

23212375
module.exports = Request;
23222376

2323-
}, {"querystring":20}],
2324-
20: [function(require, module, exports) {
2377+
}, {"querystring":21}],
2378+
21: [function(require, module, exports) {
23252379

23262380
/**
23272381
* Module dependencies.
@@ -2396,8 +2450,8 @@ exports.stringify = function(obj){
23962450
return pairs.join('&');
23972451
};
23982452

2399-
}, {"trim":21,"type":22}],
2400-
21: [function(require, module, exports) {
2453+
}, {"trim":22,"type":23}],
2454+
22: [function(require, module, exports) {
24012455

24022456
exports = module.exports = trim;
24032457

@@ -2417,7 +2471,7 @@ exports.right = function(str){
24172471
};
24182472

24192473
}, {}],
2420-
22: [function(require, module, exports) {
2474+
23: [function(require, module, exports) {
24212475
/**
24222476
* toString ref.
24232477
*/

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/localstorage-cookie.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* jshint -W020, unused: false, noempty: false, boss: true */
2+
/* global escape, unescape */
3+
4+
var Cookie = require('./cookie.js');
5+
6+
var COOKIE_STORAGE_KEY = 'amplitude_storage';
7+
8+
var cookieStorage = {
9+
length: 0,
10+
11+
setItem: function(sKey, sValue) {
12+
var store = Cookie.get(COOKIE_STORAGE_KEY) || {};
13+
14+
// don't add if cookie size exceeds 4k
15+
var existingValue = '';
16+
if (store.hasOwnProperty(sKey)) { existingValue = store[sKey]; }
17+
var itemLength = escape(sKey).length + escape(sValue).length - escape(existingValue).length;
18+
if (document.cookie.length + itemLength > 4*1024) { return; }
19+
20+
if (!store.hasOwnProperty(sKey)) { this.length++; }
21+
store[sKey] = sValue;
22+
Cookie.set(COOKIE_STORAGE_KEY, store);
23+
},
24+
25+
getItem: function(sKey) {
26+
var store = Cookie.get(COOKIE_STORAGE_KEY);
27+
return store && store.hasOwnProperty(sKey) && store[sKey] || null;
28+
},
29+
30+
removeItem: function(sKey) {
31+
var store = Cookie.get(COOKIE_STORAGE_KEY);
32+
if (!store || !store.hasOwnProperty(sKey)) { return; }
33+
delete store[sKey];
34+
Cookie.set(COOKIE_STORAGE_KEY, store);
35+
this.length--;
36+
},
37+
38+
clear: function() {
39+
Cookie.set(COOKIE_STORAGE_KEY, {});
40+
this.length = 0;
41+
},
42+
43+
key: function(n) {
44+
var store = Cookie.get(COOKIE_STORAGE_KEY);
45+
if (!store) { return null; }
46+
var keys = Object.keys(store);
47+
return n < keys.length && keys[n] || null;
48+
}
49+
};
50+
51+
module.exports = cookieStorage;

src/localstorage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ if (windowLocalStorageAvailable()) {
7979
div.load(attrKey);
8080
localStorage.length = div.XMLDocument.documentElement.attributes.length;
8181
} else {
82-
/* Nothing we can do ... */
82+
localStorage = require('./localstorage-cookie.js');
8383
}
8484
}
8585
if (!localStorage) {

test/localstorage.js

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
describe('Localstorage', function() {
22
var Amplitude = require('../src/amplitude.js');
33
var localStorage = require('../src/localstorage.js');
4-
// var cookieStorage = require('../src/localstorage-cookie.js');
4+
var cookieStorage = require('../src/localstorage-cookie.js');
55
var Cookie = require('../src/cookie.js');
66

77
var apiKey = '000000';
@@ -38,6 +38,61 @@ describe('Localstorage', function() {
3838
afterEach(function() {
3939
});
4040

41+
describe('cookie storage', function() {
42+
43+
beforeEach(function() {
44+
cookieStorage.clear();
45+
assert.lengthOf(cookieStorage, 0);
46+
});
47+
48+
it('should store values', function() {
49+
cookieStorage.setItem('key-a', 'value-a');
50+
assert.equal(cookieStorage.getItem('key-a'), 'value-a');
51+
assert.lengthOf(cookieStorage, 1);
52+
});
53+
54+
it('should return null for unstored keys', function() {
55+
assert.isNull(cookieStorage.getItem('bogus_key'));
56+
cookieStorage.setItem('key-a', 'value-a');
57+
assert.isNull(cookieStorage.getItem('bogus_key'));
58+
});
59+
60+
it('should remove values', function() {
61+
cookieStorage.setItem('key-a', 'value-a');
62+
assert.equal(cookieStorage.getItem('key-a'), 'value-a');
63+
assert.lengthOf(cookieStorage, 1);
64+
cookieStorage.removeItem('bogus_key');
65+
assert.lengthOf(cookieStorage, 1);
66+
cookieStorage.removeItem('key-a');
67+
assert.isNull(cookieStorage.getItem('key-a'));
68+
assert.lengthOf(cookieStorage, 0);
69+
});
70+
71+
it('should replace values', function() {
72+
cookieStorage.setItem('key-a', 'value-a');
73+
assert.equal(cookieStorage.getItem('key-a'), 'value-a');
74+
assert.lengthOf(cookieStorage, 1);
75+
cookieStorage.setItem('key-a', 'value-b');
76+
assert.equal(cookieStorage.getItem('key-a'), 'value-b');
77+
assert.lengthOf(cookieStorage, 1);
78+
});
79+
80+
it('should store values with entities', function() {
81+
cookieStorage.setItem('key-a', 'this&that;with=an?<>');
82+
assert.equal(cookieStorage.getItem('key-a'), 'this&that;with=an?<>');
83+
});
84+
85+
it('should not store more than 4k', function() {
86+
var longString = new Array(3*1024).join('a');
87+
88+
cookieStorage.setItem('key-a', longString);
89+
cookieStorage.setItem('key-b', longString);
90+
91+
assert.equal(cookieStorage.getItem('key-a'), longString);
92+
assert.isNull(cookieStorage.getItem('key-b'));
93+
});
94+
});
95+
4196
describe('upgrade', function() {
4297

4398
it('should migrate cookie values to localstorage', function() {

0 commit comments

Comments
 (0)