JavaScript Cookie API

Adam Barth proposed a new Cookie API for browser JavaScript over on the WHATWG list (API details)

To validate the API I took a stab at implementing it in ECMAScript using the existing Cookie API (document.cookie). The good news is that you can mostly replicate the functionality today, which means that if you like how the API looks you could try it out today.

After some consideration, Hixie put it to sleep (and I agree, it's not really worth it - Web Storage is a more robust direction for the future). But here's the sample implementation if anyone wants to use a close approximation.

Caveats: this is very rough and could probably do a higher fidelity emulation. The intent was not a rock-solid implementation, but to explore the edges of the proposed specification - i.e. is there really a new CookieList type intended? Is there any notification mechanism if an asynchronous cookie lookup fails to find a match?
/*jslint browser: true */
/*global Cookie */

(function() {

// Intentional export to global namespace
if (typeof this.Cookie !== "function") {
this.Cookie = function() {
this.name = "";
this.value = "";
this.domain = ".";
this.expires = "";
this.path = "";
this.secure = false;
this.httpOnly = false;
};
}


// Internal
function getCookieList() {
var list = [];
var carray = document.cookie.split('; ');
for (var i = 0; i < carray.length; i += 1) {
var parts = carray[i].split('=');
if (parts.length === 2) {
var cookie = new Cookie();
cookie.name = decodeURIComponent(parts[0]);
cookie.value = decodeURIComponent(parts[1]);
// NOTE: Other fields (like "expires" are not set)
list.push(cookie);
}
}

return list;
}

// Internal
function getCookie(name) {
var cookies = getCookieList();

for (var i = 0; i < cookies.length; i += 1) {
var cookie = cookies[i];
if (cookie.name === name) {
return cookie;
}
}

return null;
}

// Internal
function setCookie(name, value, expires, path) {

var str = encodeURIComponent(name) + "=" + encodeURIComponent(value);
str += expires ? ("; expires=" + expires) : "";
str += "; path=" + (path ? encodeURI(path) : "/");

document.cookie = str;
}

// Internal
function deleteCookie(name) {
var dt = new Date();
dt.setTime(dt.getTime() + (-1 * 24 * 60 * 60 * 1000)); // yesterday

setCookie(name, "", dt.toGMTString());
}


// void getCookie(in DOMString name, in CookieCallback callback);
if (document && typeof document.getCookie !== "function") {
document.getCookie = function(name, callback) {

var cookie = getCookie(name);

if (cookie) {
// ISSUE: API refers to callback object with handleEvent function
// whereas example shows callback as function
if (typeof callback === "function") {
setTimeout(function() {
callback(cookie);
}, 0);
}
}
// ISSUE: If not found?
};
}

// void getAllCookies(in CookieListCallback callback);
if (document && typeof document.getAllCookies !== "function") {
document.getAllCookies = function(callback) {

var cookies = getCookieList();

// ISSUE: Return type in document is a new type
// 'CookieList' which is not defined

// ISSUE: API refers to callback object with handleEvent function
// whereas example shows function directly
if (typeof callback === "function") {
setTimeout(function() {
callback(cookies);
}, 0);
}
};
}

// void setCookie(in Cookie cookie, in optional VoidCallback error);
if (document && typeof document.setCookie !== "function") {
document.setCookie = function(cookie, errorCallback) {
// ISSUE: Should 'cookie.expires' be a Date or string?

if (navigator.cookieEnabled) {
setCookie(cookie.name, cookie.value, cookie.expires, cookie.path);

// TODO: Appropriate sprinkles of toString()
var readback = getCookie(cookie.name);
if (!readback || readback.value !== cookie.value) {
errorCallback();
}
}
else if (typeof errorCallback === "function") {
errorCallback();
}
};
}

// void deleteCookie(in DOMString name, in optional VoidCallback error);
if (document && typeof document.deleteCookie !== "function") {
document.deleteCookie = function(name, errorCallback) {

if (navigator.cookieEnabled) {
deleteCookie(name);

if (getCookie(name)) {
errorCallback();
}
}
else if (typeof errorCallback === "function") {
errorCallback();
}
};
}

})();

Comments