function CCountDown(strInstanceName, blnIsRefreshable, blnShowStatusClock, strRedirectUrl, expiresCookieName, sysTimeCookieName)
{
	// properties
	this.PASSEXPIRY = expiresCookieName; // pass exp cookie id
	this.SERVERTIME = sysTimeCookieName; // 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;

	this.ClockTimerIntervalID = null;

	// Hide non-script version and show script version of session info
	var defaultSpan = document.getElementById("sessionTimerDefault");
	if (defaultSpan != null) defaultSpan.style.display = "none";
	var countdownSpan = document.getElementById("sessionTimerCountDown");
	if (countdownSpan != null) countdownSpan.style.display = "inline";
		
	function StartCountdown()
	{	
		// countdown time based on servers clock, as stored in cookie
		this.gdtmPassExpiry = parseDate(GetCookie(this.PASSEXPIRY));
		this.gdtmServerDate = parseDate(GetCookie(this.SERVERTIME));
		this.gintTimeRemaining = (this.gdtmPassExpiry.getTime() - this.gdtmServerDate.getTime()) - this.TIMEBUFFER;
				
		// activate countdown only if cookies are enabled
		if (GetCookie(this.PASSEXPIRY))
		{
			this.UpdateCountdown();
			this.ClockTimerIntervalID = 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 = parseDate(GetCookie(this.PASSEXPIRY));
		dtmCurrentServerDate = parseDate(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 a redirect url has been supplied and not submitting the payment, then bounce
			if (this.strUrl != '' && !gblnProcessing) 
			{
				window.clearInterval(this.ClockTimerIntervalID);
				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, strStyle, unitsText)
	{
		this.AddClockLayer(strLayerId, "hour", strStyle, unitsText);
	}
	
	// add a minute display
	function AddMinuteLayer(strLayerId, strStyle, unitsText)
	{
		this.AddClockLayer(strLayerId, "minutes", strStyle, unitsText);
	}
	
	// add a second display
	function AddSecondLayer(strLayerId, strStyle, unitsText)
	{
		this.AddClockLayer(strLayerId, "seconds", strStyle, unitsText);
	}

	function AddClockLayer(strLayerId, strTimeUnitId, strStyle, unitsText)
	{
		var strLayerTag = "";

		// add layer to collection
		this.gaobjClockLayers[this.gaobjClockLayers.length] = new CClockLayer(strLayerId, strTimeUnitId, strStyle);

		// draw the clock layer placeholders
		var elem = document.createElement("span");
		elem.id = strLayerId;
		var parent = document.getElementById("sessionTimerClock");
		parent.appendChild(elem)
		parent.appendChild(document.createTextNode(" " + unitsText + " "));
	}
	
	// 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;
	}

	function parseDate(dateStr)
	{
		//yyyy-MM-dd HH:mm:ss
		try
		{
			var d = Date.parse(dateStr);
			if (!isNaN(d)) return new Date(d);
		}
		catch (e) { }
		var da = new Date(
			getDatePart(dateStr, 0, 4),
			(getDatePart(dateStr, 5, 7) - 1),
			getDatePart(dateStr, 8, 10),
			getDatePart(dateStr, 11, 13),
			getDatePart(dateStr, 14, 16),
			getDatePart(dateStr, 17, 19));
		return da;
	}

	function getDatePart(dateStr, start, end)
	{
		return parseInt(dateStr.substring(start, end), 10);
	}
}

// CClockLayer is responsible for the display of a unit of 
// time (hours, mins or secs) counting down on the page.
function CClockLayer(strLayerId, strTimeUnitId, strStyle)
{
	// properties
	this.strLayerId	= strLayerId;
	this.strTimeUnitId = strTimeUnitId;	// identifies the time unit displayed h/m/s
	// style properties...
	this.strStyle = strStyle;		
	
	// methods
	this.UpdateLayer = UpdateLayer;
		
	// show updated time
	function UpdateLayer(strTimeValue)
	{
		if (parseInt(strTimeValue) < 10 && this.strTimeUnitId == 'seconds') 
		{
			strTimeValue = "0" + strTimeValue;
		}
		var objLayerRef = document.getElementById(this.strLayerId);		
		objLayerRef.innerHTML = "<span class='" + this.strStyle + "'>" + strTimeValue + "</span>";				
	}
}
