Object-based JavaScript cookie handling functions 

Version 2.1

Introduction
Using cookie objects
NEW Using cookie data fields
Forms and variables
Cookie List functions
Reference (properties and methods)
Source code
Release notes (v2.1 bug fix)

Instant example:

// Create cookie object and attempt to read it from disk
userCookie  = new cookieObject("userdetails", 365, "/", "name", "lastvisit")
// if cookie exists, display its data fields
if (userCookie.found) {
      document.write("Hello " + userCookie.get("name") + ", you last visited on  " + userCookie.get("lastvisit"))
}

newDate = new Date()
// update cookie's "lastvisit" field
userCookie.put("lastvisit", newDate.toGMTString())
// write cookie back to disk
userCookie.write()



Introduction

These functions take a different approach to cookie handling from the 'classic' function set. The major differences are:

Adding the functions to your web pages.
There are two ways to build these functions into your web pages:

This is version 2.1 of the object-based cookie library. It's backward-compatible with previous versions, so existing application code will work unchanged with it. This version is identified by "version 2.1" in the comment block at the start of the library file pcpcookielib.js.

Using cookie objects.

A cookie object isn't the same thing as an actual cookie on disk - it's a block of memory managed by javascript, into which you can read an existing cookie, or create a new cookie from scratch. You can update a cookie object's data, and write it back to disk.  

Whether the cookie already exists or not, the first step is create a new cookie object, like this:

objectVar = new cookieObject(name, expiry, path, fieldname1, fieldname2....)

Parameter Type             
name  string The cookie name
expiry (optional) integer
or
date object
The number of days to expiry
or
The expiry date
path (optional) string The access path for the cookie (tip - specify "/" to make the cookie available to all pages in your website)

The object doesn't support domain paths directly, but you can add one to the path parameter, e.g.

"/; domain=myserver.com"
fieldname1, fieldname2....fieldnameN (optional) string Optional field names, for use with the get() and put() methods (see below). 

Example:

userCookie  = new cookieObject("userdetails", 365, "/", "name", "age", "gender")

Important - use this code to read an existing cookie from disk, as well as to create a new cookie from scratch.

When you create a cookie object, the system automatically attempts to read a cookie of that name from disk. In the example above, if there's already a cookie called "userdetails" on disk, then the system will read it and make its data value(s) available via the cookie object's get() and put() methods.

To see if a cookie already exists, check the cookie object's .found property immediate after creating the object, like this:

userCookie  = new cookieObject("userdetails", 365, "/", "name", "age", "gender")
if (userCookie.found) {....

if .found is true, then the cookie exists.

TIP - Always check whether a cookie has been found; never just assume that it exists. Cookies can disappear for a variety of reasons, or the user may have switched to a different browser or computer.

Updating data values and writing cookies to disk
Once you have a cookie object (new or read-in from disk), you can update its data value(s) and write it back to disk. Data values (fields) are explained below, but here's a simple example:

userCookie.put("name", "Jane Smith")
userCookie.put("age", "25-35")
userCookie.put("gender","F")

userCookie.write()

Note that the .write() method takes no parameters. You set up the cookie object's name, data values, expiry date and access path by other means (such as the .put() method in this example), then call .write() to send those values to disk.

It's important to remember the difference between the cookie object in memory, and the cookie on disk. In the example above, the .put() method calls update the cookie's data values in memory, but do not affect its value on disk. Only when you call .write() are the data values written to disk, overwriting any previous values. You can overwrite an on-disk cookie via its .write() method as often as you like.

Mind those dates!
If you are reading an existing cookie from disk and intend to write it back to disk later, then it's essential to specify an expiry date/period and path for the cookie.
This is because the browser does not supply these values when it reads the cookie in from disk; they're effectively lost. If you overwrite the cookie on disk without specifying a new date and/or path, the rewritten cookie will default to being temporary (deleted at the end of the browser session), and/or accessible only by pages from the current server folder and its children.

The easiest way to specify expiry date/period and path is when you're defining the cookie object (the "365" and "/" parameters in the example above). You can, however, change a cookie object's .expires and .accessPath properties at any time prior to calling its .write() method, like this:

userCookie.expires = 365
userCookie.accessPath  = "/"


Using cookie data fields.

A cookie object's data fields are stored in an array called (appropriately enough) .fields[]. You can access the elements of .fields[] directly, like this:

userCookie  = new cookieObject("userdetails", 365, "/")
if (userCookie.found() ) {
  alert("Hello " + userCookie.fields[0] + ", you are " + userCookie.fields[1] + " and " +
userCookie.fields[2])
}

However it's better to give names to the elements of fields, then access them via the cookie object's get()  method, like this:

userCookie  = new cookieObject("userdetails", 365, "/", "name", "age", "gender")
if (userCookie.found() ) {
  alert("Hello " + userCookie.get("name") + ", you are " + userCookie.get("age") + " and " +
userCookie.get("gender"))
}

The second version is more self-explanatory, so less likely to lead to programming errors.

There's a similar choice when updating fields. You can either say

userCookie.fields[0] = "Jane Smith"

or

userCookie.put("name", "Jane Smith")

The value of the put() method's second parameter (the value to write into the field) can be anything you'd put on the right-hand side of a normal variable assignment, e.g. a variable, a form field's .value property or the result of a function call such as stringVar.toUpperCase().

TIP -
Don't forget that put() only updates the .cookie object's fields[] array - you still need to call the object's write() method to save its data to disk.


Mixing it

You can use the direct (".fields[0]") approach even if you've defined field names. For example:

ageVar = userCookie.fields[1]
and
ageVar = userCookie.get("age")

will have the same effect, as will

userCookie.fields[1] = ageVar 
and
userCookie.put("age", ageVar)

The third way!
Yes, there is a third way of accessing cookie fields! This combines the direct and named approaches. It uses the cookie object's
namepos() method, like this:

ageVar = userCookie.fields[userCookie.namepos("age")]
userCookie.fields[userCookie.namepos("age")] = ageVar

This technique allows you to perform arithmetic and other operations directly on named cookie fields, like this:

userCookie.fields[userCookie.namepos("age")]++


IMPORTANT - be consistent when defining field names
It's essential that you always declare the field names in the same order for a particular cookie type. 

If, for example,
you defined a cookie like this in one page:

userCookie  = new cookieObject("lastvisit", 365, "/", "name", "date")

then like this in another:

userCookie  = new cookieObject("lastvisit", 365, "/", "date", "name")

the second page would read the name into the date field, and vice versa. To avoid this, always copy and paste the object definition from its original page to any other pages that use it, or store it in an external script library (.js) file.  


ALSO IMPORTANT - when specifying field names, don't omit the expiry and path parameters.
Javascript allows only trailing parameters to be omitted from function calls, so this statement:

userCookie  = new cookieObject("lastvisit", , , "name", "date")

will cause an error. To select default values for the expiry and path parameters, specify the reserved value null, like this:

userCookie  = new cookieObject("lastvisit", null, null, "name", "date")


The direct approach has its uses
In most cases it's best to use field names, but accessing  the .fields[] array directly does have its uses. First, it lets you read in a cookie with an unknown number of fields, then step through them. Here's an example:

var i
for (i = 0; i < mysteryCookie.fields.length; i++) {
   document.write(mysteryCookie.fields[i] + "<br>")
}

It also lets you store array values in a cookie, like this:

var i
for (i = 0; i < myArray.length; i++) {
 mysteryCookie.fields[i] = myArray[i]
}

Using cookie data with forms and variables.

Cookie data is often taken from fields in on-screen forms, and conversely loaded from cookies into form fields for editing. 

To store data items from a form to a cookie, use code like this: 

userCookie.put("name", myform.name.value) // Store form field value to cookie field
userCookie.put ("age", myform.age.value)
userCookie.put("gender", myform.gender.value)

userCookie.write()

To place items from a cookie into a form, you use code like this: 

if (userCookie.found) { 
myform.name.value = userCookie.get("name") 
myform.age.value = userCookie.get("age")
myform.gender.value = userCookie.get("gender") 
}

Variables
If you don't want to keep calling a cookie object's get() and put() methods in your code, copy the field's value to a variable, use that, then update the field's value before writing back to disk. Here's an example:

totalwins = userCookie.get("totalwins") * 1

.... (code at various places in the page, e.g. "totalwins++",
" if (disqualified) {totalwins--}")....

userCookie.put("totalwins", totalwins)
userCookie.write()



Cookie field data types 

Fields that have been read in from existing cookies are always text strings, so you may need to convert them to other data types. That's why the code example above is:

totalwins = userCookie.get("totalwins") * 1

- the "* 1" is a crude (but effective!) way of making JavaScript convert the text value in the cookie object's totalwins field to a number.

When writing cookie values to disk, JavaScript does simple data type conversions automatically, so you can say:

userCookie.put("totalwins", totalwins)

and leave JavaScript to convert the number  to text. 

However you can't store boolean values, objects or arrays directly in cookie fields. 

For boolean (true/false) variables, do this:

userCookie.put ("useFlash", (boolvar == true) ? "y" :  "n") // store boolean as y or n
boolvar = (userCookie.get("useFlash") == "y") ? true : false  // create boolean value from field

To save the value of a date object:

myDate = new Date() // Create new date containing current date and time
userCookie.put("dateField", myDate.toUTCString())  // Stores date's value as a
string

Then to restore it to a Date() object:

savedDate = new Date(userCookie.get("dateField")) // Creates a new Date object with saved date's value


Example application

A simple site-visit tracking cookie might  be defined like this:

userCookie  = new cookieObject("lastvisit", 365, "/", "name", "date")

Here's some code for handling this cookie, taking into account whether the cookie already exists or not.

<script>
userCookie = new cookieObject("userdetails", 365,
"/", "name", "date")
// executing new cookiObject automatically executes the object's read() method

if (userCookie.found) {
    document.write('<p>Hi there '+userCookie.get("name")+', you last visited us on '+ userCookie.get("date")+'</p>')
} else {
    userCookie.put("name", prompt("Please type your name here: "))
    document.write('Welcome, '+ userCookie.get("name")+'!')
}
vdate = new Date()
userCookie.put ("date",  vdate.toGMTString())
userCookie.write()
</script>

Having created the cookie object, the code checks to see if a cookie with that name ("userdetails") already exists on disk ( if (userCookie.found) ). If it does, the code displays a "Welcome back" message; if not, it prompts the user to type in their name, which it stores in the cookie object's "name" field.

Either way, the code then updates the cookie object's "date" field with today's date, and writes the cookie, complete with new/existing name and current date, to disk. 


Cookie subclasses
If you want to be really organised, you can define subclasses of cookie object with pre-set field names, like this:

function userCookie(cookiename) {
  x  = new cookieObject(cookiename, 365, "/", "name", "age", "gender")
  return x


Now you can create multiple instances of the subclass, like this:

user1 = new userCookie("user1")
user2 = new userCookie("user2")
user3 = new userCookie("user3")

user2.put("age", 23)
user2.write()
myVar = user1.get("name")

Cookie List functions

Sometimes you won't know which cookies are available to your page, and it can be useful to get a list of them. The object-based cookie library provides a cookieList() object type and a findCookieObject() function, which help you to deal with multiple cookies.

A cookieList() object is an array of cookie objects, one for each existing cookie that's available to the  page. Here's an example:

cookList = new cookieList()

cookList is an array of objects (each of the cookieObject class), and you can access individual objects like this:

cookList[0].write() // Write the first cookie in the list to disk
x = cookList[1].name  // access the .name property of the second cookie
cookList[0].fields[1] = "Hello World"  // Update the second field of the first cookie

Two important points about cookie objects created by cookieList():

One use for cookieList() would be to let you keep cookies about multiple users on the same computer. If you named these cookies "userdata01", "userdata02" and so on, then when the page loaded you could create a cookieList array and step through it, looking for the cookies whose names began with "userdata".

findCookieObject() finds a named cookie in a cookieList() array, and returns a pointer to it, or null if no cookie of that name is found. It takes two parameters; the cookie name, and the object array created by cookieList(). Here's an example:

userCookie = findCookieObject("userdata", cookList)

You can now use userCookie like an ordinary object name, e.g.

userCookie.fields[1] = 23

Note that 

userCookie = findCookieObject("userdata", cookList)
if (userCookie != null) {...

is equivalent to saying:

userCookie = new cookieObject("userdata")
if (userCookie.found) {....


Cookie object reference

Properties exposed by a cookieObject object:

 Property    

Type Usage
name string Cookie name (set when you create the object, but can be updated)
fields[] array The data items stored in the cookie. You manipulate the cookie's contents by reading and updating the elements of this array. For a single-item cookie, just use fields[0].
fieldnames[] array The names given to the elements of the fields[] array. Optional - fieldnames.length will be zero if no names have been specified.
found bool True if the last read() method call was successful, otherwise false. This value is also returned by the read() method. Note that the object executes its read() method automatically during the creation process, so .found immediately indicates whether the cookie exists or not.
fieldSeparator string Used to separate the elements of the .fields[] array in the concatenated cookie string. Default value is #. Note - your data can include the # character, as the array values are processed using the escape() and unescape() methods during concatenating/splitting.
expires integer
or
date object
The number of days to expiry
or
The expiry date

This is set when you create the object, but you can update it later.

accessPath string The access path for the cookie (tip - specify "/" to make the cookie available to all pages in your website)

This is set when you create the object, but you can update it later.

rawValue string The multi-item (concatenated) string representing the .fields[] array elements. Set by the last read() or write() method call.

 

Methods exposed by a cookieObject object.
The basic cookieObject methods take no parameters - to adjust the cookie's expiry period, path, contents etc, update the object's properties (see above). 

Method Parameters Action
read() none Attempts to read the cookie from disk. Returns true if the cookie is found, false if not. Called automatically by the new cookieObject() statement.
write() none Writes the cookie to disk. Returns null.
remove() none Removes the cookie from disk. Returns false if the cookie was successfully deleted.
get(fieldname)

string fieldname Returns the value of a named field, e.g.
userCookie.get("age")

Returns "BadFieldName!" if the name doesn't exist.
put(fieldname, value)

string fieldname

string or number value
Updates the value of a named field, e.g.
userCookie.put("age", 23)

Returns false if the field name doesn't exist, true if it does.

namepos(fieldname)

string fieldname Returns the element number of fields[] referenced by fieldname, e.g.

i = userCookie.namepos("age")

Returns -1 if the name doesn't exist. Used by get() and put(), but can be called externally.


The source code!

If you're using Internet Explorer, then you can click here to download the library file to your hard disk (choose 'Save this program to disk' from the pop-up dialog).

Alternatively, you can copy the code from here (select it and choose Edit..Copy from your browser's menu). Paste it into a plain text editor (e.g. Windows Notepad), then either save it to disk as "pcpcookielib.js", or copy and paste it into your web page source.

CODE STARTS BELOW:

/*

DISCLAIMER: THESE JAVASCRIPT FUNCTIONS ARE SUPPLIED 'AS IS', WITH
NO WARRANTY EXPRESSED OR IMPLIED. YOU USE THEM AT YOUR OWN RISK.
PAUL STEPHENS DOES NOT ACCEPT ANY LIABILITY FOR
ANY LOSS OR DAMAGE RESULTING FROM THEIR USE, HOWEVER CAUSED.

Paul Stephens' cookie-handling object library

version 2.0

www.paulspages.co.uk

(C) Paul Stephens, 2001-2003. Feel free to use this code, but please leave this comment block in. This code must not be sold, either alone or as part of an application, without the consent of the author.

*/

function cookieObject(name, expires, accessPath) {
var i, j
this.name = name
this.fieldSeparator = "#"
this.found = false
this.expires = expires
this.accessPath = accessPath
this.rawValue = ""
this.fields = new Array()
this.fieldnames = new Array()
if (arguments.length > 3) { // field name(s) specified
  j = 0
  for (i = 3; i < arguments.length; i++) {
    this.fieldnames[j] = arguments[i]   
    j++
  }
}
this.read = ucRead

this.write = ucWrite

this.remove = ucDelete
this.get = ucFieldGet
this.put = ucFieldPut
this.namepos = ucNamePos
this.read()
}


function ucFieldGet(fieldname) {
var i = this.namepos(fieldname)
if (i >=0) {
  return this.fields[i]
} else {
  return "BadFieldName!"
}
}

function ucFieldPut (fieldname, fieldval) {
var i = this.namepos(fieldname)
if (i >=0) {
  this.fields[i] = fieldval
  return true
} else {
  return false
}
}

function ucNamePos(fieldname) {
var i
for (i = 0; i < this.fieldnames.length; i++) {
  if (fieldname == this.fieldnames[i]) {
    return i
  }
}
return -1
}


function ucWrite() {     
  var cookietext = this.name + "="

// concatenate array elements into cookie string

// Special case - single-field cookie, so write without # terminator
if (this.fields.length == 1) {
  cookietext += escape(this.fields[0])
  } else { // multi-field cookie
    for (i in this.fields) {
      cookietext += escape(this.fields[i]) + this.fieldSeparator }
  }


// Set expiry parameter, if specified
    if (this.expires != null) { 
      if (typeof(this.expires) == "number") { // Expiry period in days specified 
        var today=new Date()    
        var expiredate = new Date()     
        expiredate.setTime(today.getTime() + 1000*60*60*24*this.expires)
        cookietext += "; expires=" + expiredate.toGMTString()
      } else { // assume it's a date object
        cookietext +=  "; expires=" + this.expires.toGMTString()
      } // end of typeof(this.expires) if
    } // end of this.expires != null if
  
// add path, if specified
   if (this.accessPath != null) {
   cookietext += "; PATH="+this.accessPath }

// write cookie
   // alert("writing "+cookietext)
   document.cookie = cookietext
   return null 
}


function ucRead() {
  var search = this.name + "="                      
  var CookieString = document.cookie           
  this.rawValue = null
  this.found = false    
  if (CookieString.length > 0) {               
    offset = CookieString.indexOf(search)      
    if (offset != -1) {                        
      offset += search.length                  
      end = CookieString.indexOf(";", offset)  
      if (end == -1) {  // cookie is last item in the string, so no terminator                       
       end = CookieString.length }             
      this.rawValue = CookieString.substring(offset, end)                                  
      this.found = true
      }
    }
  
if (this.rawValue != null) { // unpack into fields

  var sl = this.rawValue.length
  var startidx = 0
  var endidx = 0
  var i = 0

// Special case - single-field cookies written by other functions,
// so without a '#' terminator

if (this.rawValue.substr(sl-1, 1) != this.fieldSeparator) {
  this.fields[0] = unescape(this.rawValue)
  } else { // separate fields

  do 
  {
   endidx = this.rawValue.indexOf(this.fieldSeparator, startidx)
   if (endidx !=-1) {
     this.fields[i] = unescape(this.rawValue.substring(startidx, endidx))
     i++
     startidx = endidx + 1}
  }
  while (endidx !=-1 & endidx != (this.rawValue.length -1));
}
} // end of unpack into fields if block
  return this.found
} // end of function


function ucDelete() {
  this.expires = -10
  this.write()
  return this.read()
}


// IT'S OK TO REMOVE THE CODE BELOW HERE IF YOUR PAGE
// DOESN'T USE cookieList() OBJECTS OR THE findCookieObject() FUNCTION.


function findCookieObject(cName, cObjArray) {
/*
This function finds a named cookie among the objects
pointed to by a cookieList array (see below).

Parameters are the cookie name to search for (a string), and an array created with
the new cookieList() constructor (see below)

NOTE - if you're only dealing with a specific, named cookie, then it's
more efficient to ceate a single cookieObject directly with that name,
and check its .found property to see if it already exists on this client.

This function is for when you've created an all-cookies array anyway,
and now want to check whether a specific cookie is present.

It returns a pointer to the cookieObject if found, or null if not found.
*/

var cpointer = null, i
for (i in cObjArray) {
  if (cName == cObjArray[i].name) {
    cpointer = cObjArray[i]
  }
}
return cpointer
}


function cookieList() {
/*
This constructor function creates a cookieObject object (see below)
for each cookie in document.cookie,
and returns an array of pointers to the objects.

You can use it to load all the cookies available to a page, then walk through them.

Example usage:

cookList = new cookieList()
for (i in cookList) {
 document.write(cookList[i].name + " " + cookList[i].fields[0] + "
")
}

*/

var i = 0, rawstring, offset = 0, start, newname
cpointers = new Array()
rawstring = document.cookie
if (rawstring.length > 0) {
  do {
   start = rawstring.indexOf("=", offset)
   if (start != -1) { // another cookie found in string
     // get cookie string up to end of current cookie name
     newname = rawstring.substring(0, start)
     if (offset > 0) {
       // if not first cookie in string, remove previous cookie data from substring
       // subsequent cookie names have a space before them (just a little browser foible!)
       newname = newname.substring(newname.lastIndexOf(";")+2, start)
     }    
     cpointers[i] = new cookieObject(newname)
     offset = start + 1
     i++
   }
  } while (start != -1)
} // end rawstring.length > 0
return cpointers
} //end function
 


Release notes

Version 2.1 fixes a bug where undefined .fields[] array elements would not be written to disk.

For example, in this code:

userCookie  = new cookieObject("userdetails", 365, "/", "name", "lastvisit")
newDate = new Date()
userCookie.put("lastvisit", newDate.toGMTString())
userCookie.write()


if the cookie "userdetails" doesn't already exist, then .fields[0] (its "name" field) is undefined.
Because no data is written to the array element, it's still undefined when the object's write() method is called.

In previous versions of the library, when the cookie's write() method was called, no data (not even a field delimiter) would be written for the "name" field. In the code example above, this would cause the "lastvisit" data to appear where the "name" data should be.

In v2.1 of the library, a text value of "undefined" (plus delimiter) is written for
all fields which have been given names, but not assigned values. So this code:

userCookie  = new cookieObject("userdetails", 365, "/", "name", "lastvisit")
userCookie.write()

would write a cookie with "name" and "lastvisit" values of "undefined", if the cookie didn't already exist.