function CCountDown(strInstanceName, blnIsRefreshable, blnShowStatusClock, strRedirectUrl)
{
	// properties
	this.PASSEXPIRY		= "PassExpiry";	// pass exp cookie id
	this.SERVERTIME		= "ServerTime";	// server time cookie id
	this.TIMEDECREMENT	= 1000;			// countdown decrement in millisecs
	this.TIMEBUFFER		= 5000;			// buffer to add to countdown time in millisecs
		
	this.strInstanceName	= strInstanceName;		// window.setinterval needs to know the name of the current instance for it to work
	this.blnIsRefreshable	= blnIsRefreshable;		// refreshable sessions
	this.blnShowStatusClock	= blnShowStatusClock;	// toggle status clock
	this.strUrl				= strRedirectUrl || ''	// redirection url when time reaches 0 - defualt to empty string
	this.gdtmPassExpiry		= null;					// pass expiry date
	this.gdtmServerDate		= null;					// server date, to determine countdown
	this.gstrHours			= "";		
	this.gstrMinutes		= "";
	this.gstrSeconds		= "";
	this.gintTimeRemaining	= 0;
	this.gaobjClockLayers	= new Array();			// collection of clock elements (hrs/mins/sec layers) on page.	
	this.gdtmLastUpdated	= new Date();			// the local time the countdown clock was last updated

	// methods
	this.StartCountdown		= StartCountdown;
	this.UpdateCountdown	= UpdateCountdown;		// updates the time remaining, and recalculates if session exp cookie updated
	this.UpdateClockDisplay = UpdateClockDisplay;
	this.SyncClock			= SyncClock;			// sync relative to local clock. Required for situations that pause the clock, eg. alert boxes
	this.intRoundSeconds	= intRoundSeconds;		// round to the nearest second
	this.GetCookie			= GetCookie;
	
	// the following methods add place holders  
	// for a particular countdown time element to go.
	this.AddHourLayer		= AddHourLayer;
	this.AddMinuteLayer		= AddMinuteLayer;
	this.AddSecondLayer		= AddSecondLayer;
	this.AddClockLayer		= AddClockLayer;
	
	function StartCountdown()
	{	
		var clockTimer = null;

		// countdown time based on servers clock, as stored in cookie
		this.gdtmPassExpiry = new Date(GetCookie(this.PASSEXPIRY));
		this.gdtmServerDate = new Date(GetCookie(this.SERVERTIME));
		this.gintTimeRemaining = (this.gdtmPassExpiry.getTime() - this.gdtmServerDate.getTime()) - this.TIMEBUFFER;

		// activate countdown only if cookies are enabled
		if (GetCookie(this.PASSEXPIRY))
		{
			if (!document.layers) this.UpdateCountdown();
			clockTimer = window.setInterval(this.strInstanceName + '.UpdateCountdown()', this.TIMEDECREMENT);
		}
	}

	function UpdateCountdown()
	{			
		var dtmCurrentPassExpiry;		// current cookie exp time
		var dtmCurrentServerDate;		// current cookie server time
		var dtmCurrentRemainingTime;	// current diff between expiry and server cookie times

		// get latest cookie values
		dtmCurrentPassExpiry = new Date(GetCookie(this.PASSEXPIRY));
		dtmCurrentServerDate = new Date(GetCookie(this.SERVERTIME));	

		this.SyncClock();
			
		// check if the session ttl has been updated
		if (dtmCurrentPassExpiry.getTime() != this.gdtmPassExpiry.getTime())
		{
			// update countdown value
			this.gdtmPassExpiry = dtmCurrentPassExpiry;
			this.gdtmServerDate = dtmCurrentServerDate;
			this.gintTimeRemaining = (this.gdtmPassExpiry.getTime() - this.gdtmServerDate.getTime()) - this.TIMEBUFFER;
		}

		// countdown the remaining time
		this.gintTimeRemaining = this.gintTimeRemaining - this.TIMEDECREMENT;		
		if (this.gintTimeRemaining > 0)
		{			
			dtmCurrentRemainingTime = new Date(this.gintTimeRemaining);
			this.gstrHours = dtmCurrentRemainingTime.getHours();
			this.gstrMinutes = dtmCurrentRemainingTime.getMinutes();
			this.gstrSeconds = dtmCurrentRemainingTime.getSeconds();
		} 
		else
		{
			if (this.strUrl != '') 
			{
				location.href = this.strUrl
			}
			this.gstrHours = "0";
			this.gstrMinutes = "0";
			this.gstrSeconds = "0";
		}
		
		if (this.blnShowStatusClock)
		{
			var strSeconds = this.gstrSeconds;
			if (parseInt(strSeconds) < 10) strSeconds = '0' + strSeconds;
			window.status = "Your Session Time Remaining " + this.gstrMinutes + ":" + strSeconds + " minutes";
		}
		
		// draw clock display
		this.UpdateClockDisplay();
	}
	
	function UpdateClockDisplay()
	{
		for (var i=0; i < this.gaobjClockLayers.length; i++)
		{		
			// determine the correct time unit to update
			
			switch ( (this.gaobjClockLayers[i].strTimeUnitId).toLowerCase() )
			{
				case "hours" :
					this.gaobjClockLayers[i].UpdateLayer(this.gstrHours);
					break;
				case "minutes" :
					this.gaobjClockLayers[i].UpdateLayer(this.gstrMinutes);
					break;
				case "seconds" :
					this.gaobjClockLayers[i].UpdateLayer(this.gstrSeconds);
					break;
				default:
					this.gaobjClockLayers[i].UpdateLayer("");
			}
		}
	}
	
	function SyncClock()
	{
		// not required in netscape/mozilla, these browser automatically sync
		var objAgent = navigator.userAgent.toLowerCase();
		if (objAgent.indexOf("msie") == -1)
		{
			return;
		}
		
		var dtmCurrentLocalTime = new Date();	// current local time
		var intTimeSinceLastUpdate;
		
		intTimeSinceLastUpdate = this.intRoundSeconds(dtmCurrentLocalTime.getTime() - this.gdtmLastUpdated.getTime());
		intTimeSinceLastUpdate -= this.TIMEDECREMENT // just get the time lag, don't count down.
		
		if ( intTimeSinceLastUpdate > 0 )
		{				
			// update only if there is time remaining
			if ( this.gintTimeRemaining > intTimeSinceLastUpdate )
			{				
				this.gintTimeRemaining -= this.intRoundSeconds(intTimeSinceLastUpdate);
			}
			else
			{
				this.gintTimeRemaining = 0;
			}
		}
		
		// record the local time for this update
		this.gdtmLastUpdated = new Date();
	}

	function intRoundSeconds(strSeconds)
	{	
		return parseInt(strSeconds / this.TIMEDECREMENT) * this.TIMEDECREMENT;
	}

	// add a hour display
	function AddHourLayer(strLayerId, strBgColor, strFontSize, strFontFamily, strFontColor, 
		strLayerHeight, strLayerWidth, strNNFontSize)
	{
		this.AddClockLayer(strLayerId, "hour", strBgColor, strFontSize, strFontFamily, strFontColor, 
			strLayerHeight, strLayerWidth, strNNFontSize);
	}
	
	// add a minute display 
	function AddMinuteLayer(strLayerId, strBgColor, strFontSize, strFontFamily, strFontColor, 
		strLayerHeight, strLayerWidth, strNNFontSize)
	{
		this.AddClockLayer(strLayerId, "minutes", strBgColor, strFontSize, strFontFamily, strFontColor, 
			strLayerHeight, strLayerWidth, strNNFontSize);
	}
	
	// add a second display
	function AddSecondLayer(strLayerId, strBgColor, strFontSize, strFontFamily, strFontColor, 
		strLayerHeight, strLayerWidth, strNNFontSize)
	{
		this.AddClockLayer(strLayerId, "seconds", strBgColor, strFontSize, strFontFamily, strFontColor, 
			strLayerHeight, strLayerWidth, strNNFontSize);
	}		
	
	function AddClockLayer(strLayerId, strTimeUnitId, strBgColor, strFontSize, strFontFamily, strFontColor, 
		strLayerHeight, strLayerWidth, strNNFontSize)
	{
		var strLayerTag = "";

		// add layer to collection
		this.gaobjClockLayers[this.gaobjClockLayers.length] = new CClockLayer(strLayerId, strTimeUnitId, strBgColor, strFontSize, 
																	strFontFamily, strFontColor, strLayerHeight, strLayerWidth, strNNFontSize);

		// draw the clock layer placeholders
		if (document.all || document.getElementById)
		{
			// ie
			strLayerTag = "<span id=" + strLayerId + "></span>";
		}
		else
		{		
			// nn4
			// needs this padding, roughly the width of the layer, otherwise following 
			// content starts at the start of layer
			if (1 == this.gaobjClockLayers.length)
				strLayerTag += "<ilayer id=" + strLayerId + " width='" + strLayerWidth + "' height='" + strLayerHeight + "' bgcolor='" + strBgColor + "' visibility='show'><img src='/images/1x1.gif' width='" + strLayerWidth + "' height='" + strLayerHeight + "' />";
			else
				strLayerTag += "<ilayer id=" + strLayerId + " width='" + strLayerWidth + "' height='" + strLayerHeight + "' bgcolor='" + strBgColor + "' visibility='show'><img src='/images/1x1.gif' width='" + strLayerWidth + "' height='1' />";
			
			strLayerTag += "<layer id=" + strLayerId + " width='" + strLayerWidth + "' height='" + strLayerHeight + "' left='0' top='0'></layer></ilayer>"
		}

		document.write(strLayerTag);
	}
	
	// Retrieve the value of the cookie with the specified name.
	function GetCookie(sName)
	{
		// cookies are separated by semicolons
		var aCookie = document.cookie.split("; ");
		for (var i=0; i < aCookie.length; i++)
		{
			// a name/value pair (a crumb) is separated by an equal sign
			var aCrumb = aCookie[i].split("=");
			if (sName == aCrumb[0]) 
			// tac on RE to decode any encoded space characters
			// that remain after unescape()
			return unescape(aCrumb[1]).replace(/\+/g," ");
		}

		// a cookie with the requested name does not exist
		return null;		
	}	
}

// CClockLayer is responsible for the display of a unit of 
// time (hours, mins or secs) counting down on the page.
function CClockLayer(strLayerId, strTimeUnitId, strBgColor, strFontSize, strFontFamily, strFontColor, strLayerHeight, strLayerWidth, strNNFontSize)
{
	// properties
	this.strLayerId		= strLayerId;
	this.strTimeUnitId	= strTimeUnitId;	// identifies the time unit displayed h/m/s
	// style properties...
	this.strBgColor		= strBgColor;		
	this.strFontSize	= strFontSize;
	this.strFontFamily	= strFontFamily;
	this.strFontColor	= strFontColor;
	
	// these are NN specific style values,
	// need to adjust for proper display in NN
	this.strLayerHeight = strLayerHeight;
	this.strLayerWidth	= strLayerWidth;
	this.strNNFontSize	= strNNFontSize;

	// methods
	this.UpdateLayer = UpdateLayer;
		
	// show updated time
	function UpdateLayer(strTimeValue)
	{
		if (parseInt(strTimeValue) < 10 && this.strTimeUnitId == 'seconds') 
			strTimeValue = "0" + strTimeValue;
		
	
		if (document.all || document.getElementById)
		{
			// ie
			var objLayerRef = document.getElementById(this.strLayerId);
			objLayerRef.innerHTML = "<span style='font-family:" + this.strFontFamily + 
				"; font-size:" + this.strFontSize + 
				"; color: " + this.strFontColor + 
				"; background-color:" + this.strBgColor + ";'>" + strTimeValue + "</span>";
		}
		else
		{
			// nn4
			// write to layer
			var strFont = '<p align=\"right\"><font face=\"' + this.strFontFamily + '\" color=\"' + this.strFontColor + '\" size=\"' + this.strNNFontSize + '\">' + strTimeValue + '</font></p>';
			var strBuffer = 'document.' + this.strLayerId + '.document.' + this.strLayerId + '.document.write(\'' + strFont + '\');'
			
			eval(strBuffer);

			// close layer
			eval("document." + this.strLayerId + ".document." + this.strLayerId + ".document.close();");
		}
		
	}
}
