//********************************************************************************************
//* COOKIE.JS - Implements a generic serializing buffer object using cookies for persistance *
//*                                                                                          *
//* TTC: 36hrs                                                                               *
//*                                                                                          *
//* Copyright PCSpectra 2002                                                                 *
//*                                                                                          * 
//* History:                                                                                 *
//* - April 07/2002                                                                          *
//*   Finished first implementation of buffer.  Doesn't include extensive error checking.    *
//* - April 09/2002                                                                          *
//*   Fixed Save(), Don't append deliminator on last element of array.                       *
//*   Fixed Save(), Missing last character of cookie                                         *
//* - April 11/2002                                                                          *
//*   Added date change functionality and improved Save() efficiency                         *
//* - April 12/2002                                                                          *
//*	  Added encoding and decoding to input/output of cookie data                             *
//*                                                                                          *
//* Improvements:                                                                            *
//*   More verbose error checking using exceptions would make using this object less err'd   *
//*   Finding first null element instead of adding a new one to tail would decrease memory   *
//*   Load() should zero all memory and use internal counter instead of length property      *
//*                                                                                          *
//* Public Methods:                                                                          *
//*   Cookie() - Constructor																 *
//*   GetCookieCount() - Returns the named cookie count										 *
//*   Fetch() - Returns a named cookie given an index										 *
//*   Create() - Create a new named cookie													 *
//*   Modify() - Modifies the date of expiry of a named cookie								 *
//*   Delete() - Sets the cookie date to zero so the browser will not store it				 *
//*   GetCount() - Returns the currently named cookie's value item count					 *
//*   AddItem() - Adds an item to a named cookie's value array								 *
//*   GetItem() - Gets an item to named cookie's value array								 *
//*   DelItem() - Deletes an item from a named cookie's value array							 *
//*   Load() - Serializes the buffered cookie data to browser cookie's for persistance		 *
//*   Save() - Serializes the browser cookie data to buffered cookie data					 *
//* Private Methods:																		 *
//*   GetCookieIndex() - Returns the index of the currently named cookie					 *
//********************************************************************************************

//Cookie object

function COOKIEITEM(name, value, days){
	//Initialize variables
	this.m_Name  = name;		//Name of the cookie
	this.m_Value = new Array();	//Array of cookie items

	//Create OR initialize array with data stored in value
	if(value == null){
		var expiryDate = new Date();	//Stores expiry date
		
		this.m_Value[0] = expiryDate.getTime() + days * 86400000; //86400000ms in one day
	}
	else
		this.m_Value = value.split(COOKIEITEM.arguments[3]);
}

// Object instantiation, creation and initialization

function Cookie(delim)
{
	//Deliminator used to split cookie value string
	this.m_delim = delim || ",";	

	//Internal arrays used for buffering input/output 
	this.m_Cookie = new Array(); 	

	//Return index of named cookie
	function GetCookieIndex(name)
	{
		for(var i=0; i<this.m_Cookie.length; i++){
			if(this.m_Cookie[i].m_Name == name)	
				return i;
		}
		return -1;	//failure
	}

	// Cookie methods
	function Fetch(index)		//Fetch the named cookie at index
	{ return this.m_Cookie[index].m_Name; }

	function GetCookieCount()
	{ return this.m_Cookie.length; }

	function Create(name, days)	//Create a named cookie entry with null value
	{ this.m_Cookie[this.m_Cookie.length] = new COOKIEITEM(name, null, days); }
	
	function Modify(name, days)	//Modify the days before named cookie expires
	{ this.m_Cookie[this.GetCookieIndex(name)].m_Value[0] = days; }
	
	function Delete(name)		//Change expiry date on named cookie to expire at
	{ this.Modify(name, 0); }	//end of browser session.
	
	// Cookie item methods

	function GetCount(name)
	{ return this.m_Cookie[this.GetCookieIndex(name)].m_Value.length-1; }

	function AddItem(name, value)
	{ 
		var nIndex  = this.GetCookieIndex(name);			
		var nItems	= this.m_Cookie[nIndex].m_Value.length;	

		//Add item to named cookie array of values
		this.m_Cookie[nIndex].m_Value[nItems] = value; 
	}
	
	function GetItem(name, index)
	{ return this.m_Cookie[this.GetCookieIndex(name)].m_Value[index+1]; }
	
	function DelItem(name, index)  //null values are empty arrays and will not be saved
	{ this.m_Cookie[this.GetCookieIndex(name)].m_Value[index+1] = null; }	

	
	//************************************Serialization***********************************

	/* Load cookie from browser database */

	function Load()
	{ 
		//Retrieve cookie data and remove special encoding
		var cookieData = unescape(document.cookie);

		//Load cookie data if cookies aren't empty
		if(cookieData.length){
			var NameValue = new Array();	//Split extracted cookie array 0=Name, 1=Value
			var extCookie;					//Extracted cookie (name=value) pair
			var start=0;					//starting index of next parse iteration			

			//Parse the cookie item(s) and store in buffer array
			for(i=0; i<cookieData.length+2; i++){
				//Extract single cookie from string
				extCookie = cookieData.substring(start, i-1);
				
				//Add to cookie to array when parsed at deliminator
				if(cookieData.charAt(i-1) == ";"){
					start = i+1;	//Update starting index for next iteration
					
					//Split string into name value pairs
					NameValue = extCookie.split("=");

					//Add cookie to tail of array
					this.m_Cookie[this.m_Cookie.length] = new COOKIEITEM(NameValue[0], NameValue[1], null, this.m_delim);	
				}
			}

			//Split string into name value pairs
			NameValue = extCookie.split("=");

			//Add Name=Value pairs to cookie array
			this.m_Cookie[this.m_Cookie.length] = new COOKIEITEM(NameValue[0], NameValue[1], null, this.m_delim);	
			
			return true;	//Cookies parsed correctly
		}

		//No cookies available
		return false;	
	}

	/* Write cookie to browser database */

	function Save()
	{ 
		//Create cookie's from array
		for(var i=0; i<this.m_Cookie.length; i++){
			//If days is NULL don't save new cookies with date so cookie keeps old expiry date
			//If days is zero save cookie with previous expires date so browser removes cookie
			//If value is null ignore when concatenating array into single string

			var expires = new Date();
			expires.setTime(this.m_Cookie[i].m_Value[0]);	//Time until expiry (milli-seconds)

			var newName = this.m_Cookie[i].m_Name;		  
			
			//Report critical error when cookie name is NULL and exit function
			if(newName == null){
				alert("Error! Save() cannot serialize un-named cookies.  Your cookies may not function properly now");
				return;
			}

			//Concatenate cookie item array into single string newValue
			var newValue = "";	//If you don't initialize first item is undefined
			
			//Iterate arrays and create cookie string	
			for(var j=0; j<this.m_Cookie[i].m_Value.length; j++){
				if(this.m_Cookie[i].m_Value[j]){  //If cookie item array element NOT null	
					//Don't append deliminator on last element (FIXED)
					if(this.m_Cookie[i].m_Value.length-1 == j)
						newValue += this.m_Cookie[i].m_Value[j];
					else
						newValue += this.m_Cookie[i].m_Value[j] + this.m_delim;	
				}
			}

			//Persist cookie to browser database
			document.cookie = escape(newName)+"="+escape(newValue)+"; expires="+expires;
		}
	}

	//Map methods to 'this' class
	this.Fetch  = Fetch;
	this.Create = Create;
	this.Modify = Modify;
	this.Delete = Delete;
									 
	this.GetCookieIndex = GetCookieIndex;
	this.GetCookieCount = GetCookieCount;
	this.GetCount = GetCount;

	this.AddItem = AddItem;
	this.GetItem = GetItem;
	this.DelItem = DelItem;
	this.Load    = Load;
	this.Save    = Save;
}

