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.
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():
They
don't have any field names (although in fact there's nothing to stop you from
adding them after the objects have been created, by updating their
fieldnames[] arrays).
They don't have expiry dates or paths set, so if you want to write back to one, it's essential that you update its .expiry and .path properties first, as required.
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
For example, in this
code:
userCookie
= new cookieObject("userdetails", 365, "/",
"name", "lastvisit")
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
userCookie = new cookieObject("userdetails", 365, "/", "name", "lastvisit")