Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit e3f760f

Browse files
committed
Adding cookie service
- Browser.cookies() - MockBrowser - $cookie service - $sessionStore
1 parent 07699b1 commit e3f760f

File tree

6 files changed

+438
-4
lines changed

6 files changed

+438
-4
lines changed

src/AngularPublic.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ angularService('$browser', function browserFactory(){
66
jqLite(window.document),
77
jqLite(window.document.getElementsByTagName('head')[0]));
88
browserSingleton.startUrlWatcher();
9+
browserSingleton.startCookieWatcher();
910
browserSingleton.bind();
1011
}
1112
return browserSingleton;

src/Browser.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,50 @@ function Browser(location, document, head) {
2222

2323
this.location = location;
2424
this.document = document;
25+
var rawDocument = document[0];
2526
this.head = head;
2627
this.idCounter = 0;
28+
29+
this.cookies = cookies;
30+
this.watchCookies = function(fn){ cookieListeners.push(fn); };
31+
32+
// functions
33+
var lastCookies = {};
34+
var lastCookieString = '';
35+
var cookieListeners = [];
36+
/**
37+
* cookies() -> hash of all cookies
38+
* cookies(name, value) -> set name to value
39+
* if value is undefined delete it
40+
* cookies(name) -> should get value, but deletes (no one calls it right now that way)
41+
*/
42+
function cookies(name, value){
43+
if (name) {
44+
if (value === _undefined) {
45+
delete lastCookies[name];
46+
rawDocument.cookie = escape(name) + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
47+
} else {
48+
rawDocument.cookie = escape(name) + '=' + escape(lastCookies[name] = ''+value);
49+
}
50+
} else {
51+
if (rawDocument.cookie !== lastCookieString) {
52+
lastCookieString = rawDocument.cookie;
53+
var cookieArray = lastCookieString.split("; ");
54+
lastCookies = {};
55+
56+
for (var i = 0; i < cookieArray.length; i++) {
57+
var keyValue = cookieArray[i].split("=");
58+
if (keyValue.length === 2) { //ignore nameless cookies
59+
lastCookies[unescape(keyValue[0])] = unescape(keyValue[1]);
60+
}
61+
}
62+
foreach(cookieListeners, function(fn){
63+
fn(lastCookies);
64+
});
65+
}
66+
return lastCookies;
67+
}
68+
}
2769
}
2870

2971
Browser.prototype = {
@@ -132,6 +174,14 @@ Browser.prototype = {
132174
})();
133175
},
134176

177+
startCookieWatcher: function() {
178+
var self = this;
179+
(function poll() {
180+
self.cookies();
181+
self.setTimeout(poll, self.delay);
182+
})();
183+
},
184+
135185
setUrl: function(url) {
136186
var existingURL = this.location.href;
137187
if (!existingURL.match(/#/)) existingURL += '#';

src/services.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,3 +392,67 @@ angularService('$resource', function($xhr){
392392
var resource = new ResourceFactory($xhr);
393393
return bind(resource, resource.route);
394394
}, {inject: ['$xhr.cache']});
395+
396+
397+
angularService('$cookies', function($browser) {
398+
var cookies = {}, rootScope = this;
399+
$browser.watchCookies(function(newCookies){
400+
copy(newCookies, cookies);
401+
rootScope.$eval();
402+
});
403+
this.$onEval(PRIORITY_FIRST, update);
404+
this.$onEval(PRIORITY_LAST, update);
405+
return cookies;
406+
407+
function update(){
408+
var name, browserCookies = $browser.cookies();
409+
for(name in cookies) {
410+
if (browserCookies[name] !== cookies[name]) {
411+
$browser.cookies(name, browserCookies[name] = cookies[name]);
412+
}
413+
}
414+
for(name in browserCookies) {
415+
if (browserCookies[name] !== cookies[name]) {
416+
$browser.cookies(name, _undefined);
417+
//TODO: write test for this delete
418+
//delete cookies[name];
419+
}
420+
}
421+
};
422+
}, {inject: ['$browser']});
423+
424+
425+
angularService('$sessionStore', function($store) {
426+
427+
function SessionStore() {}
428+
429+
SessionStore.prototype.get = function(key) {
430+
return fromJson($store[key]);
431+
};
432+
433+
SessionStore.prototype.getAll = function() {
434+
var all = {},
435+
key;
436+
437+
for (key in $store) {
438+
if (!$store.hasOwnProperty(key)) continue;
439+
all[key] = fromJson($store[key]);
440+
}
441+
442+
return all;
443+
};
444+
445+
446+
SessionStore.prototype.put = function(key, value) {
447+
$store[key] = toJson(value);
448+
};
449+
450+
451+
SessionStore.prototype.remove = function(key) {
452+
delete $store[key];
453+
};
454+
455+
456+
return new SessionStore();
457+
458+
}, {inject: ['$cookies']});

test/BrowserSpecs.js

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ describe('browser', function(){
2727
browser.startUrlWatcher();
2828
});
2929

30+
it('should contain cookie cruncher', function() {
31+
expect(browser.cookies).toBeDefined();
32+
});
33+
34+
it('should be able to start cookie watcher', function() {
35+
browser.delay = 1;
36+
expectAsserts(2);
37+
browser.watchCookies(function(cookies){
38+
assertEquals({'foo':'bar'}, cookies);
39+
});
40+
browser.setTimeout = function(fn, delay){
41+
assertEquals(1, delay);
42+
document.cookie = 'foo=bar';
43+
browser.setTimeout = function(fn, delay) {};
44+
fn();
45+
};
46+
browser.startCookieWatcher();
47+
});
48+
3049
describe('outstading requests', function(){
3150
it('should process callbacks immedietly with no outstanding requests', function(){
3251
var callback = jasmine.createSpy('callback');
@@ -67,4 +86,193 @@ describe('browser', function(){
6786
});
6887
});
6988

89+
90+
describe('cookies', function() {
91+
92+
function deleteAllCookies() {
93+
var cookies = document.cookie.split(";");
94+
95+
for (var i = 0; i < cookies.length; i++) {
96+
var cookie = cookies[i];
97+
var eqPos = cookie.indexOf("=");
98+
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
99+
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
100+
}
101+
};
102+
103+
var browser;
104+
105+
beforeEach(function() {
106+
deleteAllCookies();
107+
browser = new Browser({}, jqLite(document));
108+
expect(document.cookie).toEqual('');
109+
});
110+
111+
112+
afterEach(function() {
113+
deleteAllCookies();
114+
expect(document.cookie).toEqual('');
115+
});
116+
117+
118+
describe('remove all via (null)', function() {
119+
120+
it('should do nothing when no cookies are set', function() {
121+
browser.cookies(null);
122+
expect(document.cookie).toEqual('');
123+
expect(browser.cookies()).toEqual({});
124+
});
125+
126+
});
127+
128+
describe('remove via (cookieName, undefined)', function() {
129+
130+
it('should remove a cookie when it is present', function() {
131+
document.cookie = 'foo=bar';
132+
133+
browser.cookies('foo', undefined);
134+
135+
expect(document.cookie).toEqual('');
136+
expect(browser.cookies()).toEqual({});
137+
});
138+
139+
140+
it('should do nothing when an nonexisting cookie is being removed', function() {
141+
browser.cookies('doesntexist', undefined);
142+
expect(document.cookie).toEqual('');
143+
expect(browser.cookies()).toEqual({});
144+
});
145+
});
146+
147+
148+
describe('put via (cookieName, string)', function() {
149+
150+
it('should create and store a cookie', function() {
151+
browser.cookies('cookieName', 'cookieValue');
152+
expect(document.cookie).toEqual('cookieName=cookieValue');
153+
expect(browser.cookies()).toEqual({'cookieName':'cookieValue'});
154+
});
155+
156+
157+
it('should overwrite an existing unsynced cookie', function() {
158+
document.cookie = "cookie=new";
159+
160+
var oldVal = browser.cookies('cookie', 'newer');
161+
162+
expect(document.cookie).toEqual('cookie=newer');
163+
expect(browser.cookies()).toEqual({'cookie':'newer'});
164+
expect(oldVal).not.toBeDefined();
165+
});
166+
167+
it('should escape both name and value', function() {
168+
browser.cookies('cookie1=', 'val;ue');
169+
browser.cookies('cookie2=bar;baz', 'val=ue');
170+
171+
var rawCookies = document.cookie.split("; "); //order is not guaranteed, so we need to parse
172+
expect(rawCookies.length).toEqual(2);
173+
expect(rawCookies).toContain('cookie1%3D=val%3Bue');
174+
expect(rawCookies).toContain('cookie2%3Dbar%3Bbaz=val%3Due');
175+
});
176+
});
177+
178+
179+
describe('get via (cookieName)', function() {
180+
181+
it('should return undefined for nonexistent cookie', function() {
182+
expect(browser.cookies('nonexistent')).not.toBeDefined();
183+
});
184+
185+
186+
it ('should return a value for an existing cookie', function() {
187+
document.cookie = "foo=bar";
188+
browser.cookies(true);
189+
expect(browser.cookies().foo).toEqual('bar');
190+
});
191+
192+
193+
it ('should unescape cookie values that were escaped by puts', function() {
194+
document.cookie = "cookie2%3Dbar%3Bbaz=val%3Due";
195+
browser.cookies(true);
196+
expect(browser.cookies()['cookie2=bar;baz']).toEqual('val=ue');
197+
});
198+
199+
200+
it('should preserve leading & trailing spaces in names and values', function() {
201+
browser.cookies(' cookie name ', ' cookie value ');
202+
expect(browser.cookies()[' cookie name ']).toEqual(' cookie value ');
203+
expect(browser.cookies()['cookie name']).not.toBeDefined();
204+
});
205+
});
206+
207+
208+
describe('getAll', function() {
209+
210+
it('should return cookies as hash', function() {
211+
document.cookie = "foo1=bar1";
212+
document.cookie = "foo2=bar2";
213+
expect(browser.cookies()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
214+
});
215+
216+
217+
it('should return empty hash if no cookies exist', function() {
218+
expect(browser.cookies()).toEqual({});
219+
});
220+
});
221+
222+
223+
describe('watch', function() {
224+
225+
it('should allow listeners to be registered', function() {
226+
expectAsserts(1);
227+
228+
browser.watchCookies(function(cookies) {
229+
assertEquals({'aaa':'bbb'}, cookies);
230+
});
231+
232+
browser.cookies('aaa','bbb');
233+
browser.cookies();
234+
});
235+
236+
237+
it('should fire listeners when cookie changes are discovered', function() {
238+
expectAsserts(1);
239+
240+
browser.watchCookies(function(cookies) {
241+
assertEquals({'foo':'bar'}, cookies);
242+
});
243+
244+
document.cookie = 'foo=bar';
245+
browser.cookies();
246+
});
247+
248+
249+
it('should not fire listeners when no cookies were changed', function() {
250+
expectAsserts(0);
251+
252+
browser.cookies(function(cookies) {
253+
assertEquals({'shouldnt':'fire'}, cookies);
254+
});
255+
256+
browser.cookies(true);
257+
});
258+
});
259+
260+
261+
it('should pick up external changes made to browser cookies', function() {
262+
browser.cookies('oatmealCookie', 'drool');
263+
expect(browser.cookies()).toEqual({'oatmealCookie':'drool'});
264+
265+
document.cookie = 'oatmealCookie=changed';
266+
browser.cookies(true);
267+
expect(browser.cookies().oatmealCookie).toEqual('changed');
268+
});
269+
270+
271+
it('should initialize cookie cache with existing cookies', function() {
272+
document.cookie = "existingCookie=existingValue";
273+
expect(browser.cookies()).toEqual({'existingCookie':'existingValue'});
274+
});
275+
276+
});
70277
});
278+

0 commit comments

Comments
 (0)