////////////////////////////////////////////////////////////////////////////////
//
//	cookiejar - an HTTP Cookie utility class
//
//	2009-2011, Josh Moyer <JMoyer@nodomain.net>.  Public domain, no warrantee.
//
////////////////////////////////////////////////////////////////////////////////

// returns a CookieJar object
//
CookieJar = function()
	{
	this.print = function(s) {};
	
	this.print("CookieJar()");

	var a;		// array
	var nvp;	// name/value pair

	this.cookies = new Object();

	this.dc = document.cookie;
	this.print("this.dc=" + this.dc);

	if (this.dc)
		{
		a = this.dc.split(";");

		for (var i=0; i<a.length; i++)
			{
			nvp = a[i].split("=");

			nvp[0] = decodeURIComponent(nvp[0]);
			nvp[1] = decodeURIComponent(nvp[1]);

			if (nvp[0].charAt(0) == " ") nvp[0] = nvp[0].substring(1);
			if (nvp[1] == "") nvp[1] = null;

			this.cookies[nvp[0]] = nvp[1];
			this.print("cookies." + nvp[0] + "=" + this.cookies[nvp[0]]);
			}
		}
	}

// adds a cookie to the document.
// returns true if an error occurs, false on success.
//
CookieJar.prototype.add = function(
	cn,		// name
	cv,		// value
	cma,	// max-age
	cd,		// domain
	cp,		// path
	cs)		// secure?
	{
	this.print("CookieJar.add(" +
		cn + ", " +
		cv + ", " +
		cma + ", " +
		cd + ", " +
		cp + ", " +
		cs + ")");

	if (!cn) throw new Error("Cookies MUST have a name.");

	if (cv == undefined) cv = "";

	var cnu = cn; // unencoded
	var cvu = cv; // unencoded

	cn = encodeURIComponent(cn);
	if (cv != "") cv = encodeURIComponent(cv);
	if (cma) cma = "; max-age=" + cma; else cma = "";
	if (cd) cd = "; domain=" + cd; else cd = "";
	if (cp) cp = "; path=" + cp; else cp = "";
	if (cs) cs = "; secure"; else cs = "";

	this.dc = cn + "=" + cv + cma + cp + cd + cs;

	this.print(this.dc + " will be added");

	document.cookie = this.dc;
	this.dc = document.cookie;

	this.print("this.dc=" + this.dc);

	if (this.peek(cn))
		{
		if (cvu == "") cvu = null;
	   	this.cookies[cnu] = cvu;
		}
	else
		{
		this.print("ERROR: CookieJar.add()");
		return true;
		}

	this.print("cookies." + cnu + "=" + this.cookies[cnu]);
	return false;
	}

// deletes the specified cookie.
// returns true if an error occurs, false on success.
//
CookieJar.prototype.eat = function(
	cn, // name
	cd, // domain
	cp) // path
	{
	this.print("CookieJar.eat(" + cn + ", " + cd + ", " + cp + ")");

	if (!cn) throw new Error("Cookies MUST have a name.");

	var cnu = cn; // unencoded 

	cn = encodeURIComponent(cn);

	if (!this.peek(cn)) 
		{
		this.print("ERROR: CookieJar.eat() called on non-existant cookie");
		return true;
		}
	
	if (cd) cd = "; domain=" + cd; else cd = "";
	if (cp) cp = "; path=" + cp; else cp = "";

	this.dc = cn + "=" + cd + cp + "; max-age=0";

	this.print(this.dc + " will be eaten");

	document.cookie = this.dc;
	this.dc = document.cookie;

	this.print("this.dc=" + this.dc);

	if (this.peek(cn))
		{
		this.print("ERROR: CookieJar.eat()");
		return true;
		}

	delete this.cookies[cnu];
	this.print("cookies." + cnu + "=" + this.cookies[cnu]);
	return false;
	}

// returns true if specified cookie exists, false otherwise.
//
CookieJar.prototype.peek = function(
	cn,		// cookie name
	cc,		// private variable, leave undefined
	ci,		// private variable, leave undefined
	cli,	// private variable, leave undefined
	cnl)	// private variable, leave undefined

	{
	this.print("CookieJar.peek(" +
		cn + ", cc, " +
		ci + ", " +
		cli + ", " +
		cnl + ")");

	if (!cn) throw new Error("Cookies MUST have a name.");
	
	// initialize
	if (!cli)
		{
		cc = this.dc;
		ci = 0;
		cli = cc.lastIndexOf(cn);
		if (cli == -1) return false;
		cnl = cn.length;
		}

	// peek from our starting position...
	ci = cc.indexOf(cn, ci);

	// is this a delimited match?
	var dcscl = cc.charAt(ci - 1); // separator char left
	var dcscr = cc.charAt(ci + cnl); // separator char right

	if ((dcscl == "" || dcscl == ";" || dcscl == " ") &&
	   (dcscr == "=" || dcscr == ";" || dcscr == ""))
		{
		return true;
		}
	else
		{
		// this was a match but not a delimited one.  if we are at the
		// end of the string recurse into ourselves otherwise return
		if (ci < cli) return this.peek(cn, cc, ci+1, cli, cnl);
		else return false;
		}	
	}

