

/*
 * ClockCalBinding - 
 *	Binds Clock/Cal fields in order to set default values onchange
 *		...If no due time is set, but user chooses due date,
 * 		   automatically populate associated time field w/ default (workday begin)
 *
 * Example: <input id="idCal" ...
 *	    <input id="idClock" ...
 *	    <script>
 *		ClockCalGroup.add(idCal, idClock);
 *	    </script>
 */
var ClockCalBinding = new function()
{
	this.rgPairs = new Array();

	this.add = function( sIdCalField, sIdClockField )
	{
		if (!sIdCalField || !sIdClockField) return;
		this.rgPairs.push({'idCal' : sIdCalField, 'idClock' : sIdClockField});
	}

	this.init = function()
	{
		for (var ix = 0; ix < this.rgPairs.length; ix++)
			this.associate(ix);
	}

	this.associate = function(ixPair)
	{
		if (!this.rgPairs || !this.rgPairs[ixPair]) return;
		var oCal = 	elById(this.rgPairs[ixPair].idCal);
		var oClock = 	elById(this.rgPairs[ixPair].idClock);
		if (!oCal || !oClock) return;

		oCal.onchange = this.getOnChange(oCal, oClock, true);
		oClock.onchange = this.getOnChange(oCal, oClock, false);
	}

	this.getOnChange = function(oCal, oClock, bCalToClock)
	{
		var oTarget = 	bCalToClock ? oClock : oCal;
		var oSource = 	bCalToClock ? oCal : oClock;
		var sDefault = 	bCalToClock ? GetLocaleTime() : GetLocaleDate();
		return 	function()
			{
				if (oTarget.value.length == 0 && oSource.value.length == 0)
					return; // allow user to remove due date/time
				if (oTarget.value.length == 0 || 
				    oTarget.value == sDefault)
				{
					oTarget.value = bCalToClock ? 
							GetWorkdayStart() : 
							GetToday();
				}
			};
	}
}


/**
 *
 * CALENDAR
 *
 */
function Clock()
{
	this.txtID = '';
	this.localeTime = GetLocaleTime();
	this.dt = new Date();
}
Clock.prototype.show = function(id) 
{ 
	this.txtID = id; 
	elById('timeTableSelect').size = 20;

	var rgEl = elById('timeTableSelect');
	rgEl.size = 20;
	
	for( var i = 0; i < rgEl.options.length; i++ )
	{
		if ( rgEl.options[i].value == elById(id).value ||
			 rgEl.options[i].value == '0' + elById(id).value )
			rgEl.selectedIndex = i;
	}
	
	theMgr.showPopup('tblTime', elById(id), -2, elById(id).offsetHeight - 2); 
}
Clock.prototype.focus = function(bTxtFieldFocus)
{
	if (bTxtFieldFocus)
		safeFocus(elById(this.txtID));
	else
		safeFocus(elById('timeTableSelect'));
}
Clock.prototype.setTime = function(sTime)
{
	this.dt = new Date(sTime);
}
Clock.prototype.getPrintableTime = function()
{
	var sDate = this.localeTime;
	var sHours = this.dt.getHours();
	var sMinutes = this.dt.getMinutes() + '';
	var sActualAMPM = '';
	var sAMPM = '';

	// case 292053 - PHP's setlocale doesn't always yield a space between time and AM/PM...so search for 'a' or 'p'
	var locAMPM = (sDate.toLowerCase().lastIndexOf('a') != -1) ? sDate.toLowerCase().lastIndexOf('a') : sDate.toLowerCase().lastIndexOf('p');
	if ( locAMPM > 0 )
	{
		sAMPM = sDate.substr(locAMPM);
		sAMPM = trim(sAMPM);
	}

	if ( sAMPM.length > 0 ) // ampm
	{
		if ( this.dt.getHours() == 0 )
			sHours = "12";
		if ( this.dt.getHours() > 12 )
			sHours = this.dt.getHours() - 12;
			
		sActualAMPM = sAMPM;  // locale time always has am in it, swap for pm if we need to
		if ( this.dt.getHours() > 11 )
		{
			sActualAMPM = sAMPM.replace(/a/, 'p');
			sActualAMPM = sActualAMPM.replace(/A/, 'P');
		}
	}

	sDate = sDate.replace(/hh/, sHours);
	sDate = sDate.replace(/mm/, sMinutes.length == 1 ? "0" + sMinutes : sMinutes);
	sDate = sDate.replace(sAMPM, sActualAMPM);
	return sDate;
}
Clock.prototype.hide = function() 
{ 
	var o = elById(this.txtID);
	if (o)
	{
		o.value = this.getPrintableTime(); 
		if (o.onchange && o.onchange.call) o.onchange.call();
	}
	theMgr.hideAllPopups();
}
Clock.prototype.guessTime = function( sTime )
{
	var dt = new Date();
	dt.setSeconds(0);
	dt.setMinutes(0);
	
	var re = /^\s*([012]?\d)[:\.]?([012345]\d)[:\.]?([012345]?\d?)\s*$/;
	var matches = re.exec(sTime);
	if ( matches )
	{
		if ( matches.length > 1 )
			dt.setHours( matches[1] );

		if ( matches.length > 2 )
			dt.setMinutes( matches[2] );

		if ( matches.length > 3 )
		{
			if ( !isNaN(parseInt(matches[3], 10)) )
				dt.setSeconds( parseInt(matches[3], 10) );
		}
		return dt;
	}
	
	re = /^\s*([012]?\d)[:\.]?(\d\d)?[:\.]?(\d\d)?\s?([ap])\.?m?\.?\s*$/i;
	matches = re.exec(sTime);
	if ( matches )
	{
		if ( matches[4].toLowerCase() == "p" && parseInt(matches[1], 10) != 12)
		{	
			var hrs = matches[1];
			if ( hrs.substr(0,1) == "0" )
				hrs = hrs.substr(1);
			dt.setHours(parseInt(hrs, 10) + 12);
		}
		else if ( matches[4].toLowerCase() == "a" && parseInt(matches[1], 10) == 12)
			dt.setHours(0);
		else
			dt.setHours(matches[1]);

		if ( !isNaN(parseInt(matches[2], 10)) )
			dt.setMinutes(matches[2]);
		return dt;
	}


	sTime = trim(sTime.toLowerCase());
	if ( sTime == "noon" )
	{
		dt.setHours(12);
		return dt;
	}
	if ( sTime == "midnight" )
	{
		dt.setHours(0);
		return dt;
	}
	if ( sTime == "now" )
		return new Date();
	
	re = /^\s*(\d+)\s*$/;
	matches = re.exec(sTime);
	
	var n = 0;
	if ( matches != null && matches.length > 1 )
	{
		n = parseInt(matches[1], 10);
		if ( n < 8 )
			dt.setHours(n+12);
		else
			dt.setHours(n);
		return dt;
	}
	
	dt = new Date();
	if ( sTime.indexOf("minute") != -1 || sTime.indexOf("hour") != -1 )
	{
		re = /(\d+)/;
		matches = re.exec(sTime);
		if ( matches != null && matches.length > 1 )
		{
			n = parseInt(matches[1], 10);
		} else {
			for ( var i = 0; i < this.rgNumbers.length; i++ )
			{
				if ( sTime.indexOf(this.rgNumbers[i]) != -1 )
					n = i;
			}			
		}
		if ( sTime.indexOf("minute") != -1 )
			dt.setMinutes(dt.getMinutes() + n);
		else
			dt.setHours(dt.getHours() + n);
		return dt;
	}
	dt.setHours(12);
	dt.setMinutes(0);
	dt.setSeconds(0);
	return dt;
}

function Calendar()
{
	this.rgMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
	this.rgDays = GetWeekdays();
	this.rgNumbers = ['zero','one','two','three','four','five','six','seven','eight','nine','ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen','twenty','twentyone','twentytwo','twentythree','twentyfour','twentyfive','twentysix','twentyseven','twentyeight','twentynine','thirty'];
	this.iFirstDayOfWeek = 0;
	this.dt = new Date();
	this.dtShowing = new Date(this.dt);
	this.layout = [[1,2,3,4,5,6,7],[8,9,10,11,12,13,14],[15,16,17,18,19,20,21],[22,23,24,25,26,27,28],[29,30,31,1,2,3,4],[5,6,7,8,9,10,11]];
	this.bPastOK = false;
	this.txtID = '';
	this.txtTimeID = '';
	this.localeDate = GetLocaleDate();
}

Calendar.prototype.drawMe = function() {
	// Write names of days in week row
	var i;
	for(i=0; i<this.rgDays.length; i++)
		elById('weekday' + i.toString()).innerHTML = this.rgDays[(i + this.iFirstDayOfWeek)%(this.rgDays.length)].toString().substr(0,1);

	// find out which day of week the first of the month is
	var dtFirst = new Date(this.dtShowing);
	dtFirst.setDate(1);
	dtFirst.setHours(0, 0, 1);
	
	var iDaysToRollBack = 0;
	if ( dtFirst.getDay() > this.iFirstDayOfWeek )
		iDaysToRollBack = dtFirst.getDay() - this.iFirstDayOfWeek
	else if ( dtFirst.getDay() < this.iFirstDayOfWeek )
		iDaysToRollBack = dtFirst.getDay() + ( 6 - this.iFirstDayOfWeek ) + 1;
	else
		iDaysToRollBack = 7;

	dtFirst.setDate( dtFirst.getDate() - iDaysToRollBack ); // roll back enough to start filling in table
	var dtToday = new Date();
	dtToday.setHours(0, 0, 0);

	for(i=0; i<6; i++)
	{
		for( var j=0; j<=6; j++ )
		{
			this.layout[i][j] = new Date(dtFirst);
			var el = elById(i.toString() + ',' + j.toString());
			el.innerHTML = this.layout[i][j].getDate();
			if ( dtFirst.getMonth() == this.dtShowing.getMonth() )
			{
				if ( !this.bPastOK && dtFirst < dtToday )
					el.style.color = '#777777';
				else
					el.style.color = '#000000';
			}
			else
				el.style.color = '#777777';
				
			if ( dtFirst.getYear() == dtToday.getYear() && dtFirst.getMonth() == dtToday.getMonth() && dtFirst.getDate() == dtToday.getDate() )
				el.style.border = '1px solid black';
			else
				el.style.border = '';
			if ( dtFirst.getYear() == this.dt.getYear() && dtFirst.getMonth() == this.dt.getMonth() && dtFirst.getDate() == this.dt.getDate() )
				el.style.backgroundColor = '#cccccc';
			else
				el.style.backgroundColor = '#ffffff';
				

			dtFirst.setDate( dtFirst.getDate() + 1 );
		}
	}

	elById('MonthTitle').innerHTML = this.rgMonths[this.dtShowing.getMonth()] + "&nbsp;" + (this.dtShowing.getFullYear());
	return true; 
}
Calendar.prototype.fwdMonth = function() { if ( this.dtShowing.getDate() > 28 ) { this.dtShowing.setDate(1); } this.dtShowing.setMonth( this.dtShowing.getMonth() + 1 ); this.drawMe(); }
Calendar.prototype.backMonth = function() { if ( this.dtShowing.getDate() > 28 ) { this.dtShowing.setDate(1); } this.dtShowing.setMonth( this.dtShowing.getMonth() - 1 ); this.drawMe(); }
Calendar.prototype.setDay = function(id)
{ 
	elById(id).blur(); 

	var rg = id.split(',',2); 
	var dt = new Date(this.layout[rg[0]][rg[1]]);
	dt.setHours(24, 59, 59);
	var dtToday = new Date();
	if ( !this.bPastOK && dt < dtToday )
		return;

	this.dt = new Date(this.layout[rg[0]][rg[1]]);
	this.dtShowing = new Date(this.dt);
	this.drawMe();
	this.hide();
}
Calendar.prototype.show = function(id, timeID) 
{ 
	this.txtID = id; 
	this.txtTimeID = timeID;
	
	this.drawMe();
	theMgr.showPopup('tblCalendar', elById(id), -2, elById(id).offsetHeight - 2); 
}
Calendar.prototype.getPrintableDate = function()
{
	var sDate = this.localeDate;
	sDate = sDate.replace(/dd/, this.dt.getDate());
	sDate = sDate.replace(/mm/, this.dt.getMonth() + 1);
	sDate = sDate.replace(/yyyy/, this.dt.getFullYear());
	return sDate;
}
Calendar.prototype.showTips = function()
{
	if (trim(elById('calTips').innerHTML) == "")
		elById('calTips').innerHTML = GetCalendarTip();
	else
		elById('calTips').innerHTML = "";
}
Calendar.prototype.hide = function() 
{ 
	var o = elById(this.txtID);
	if (o) 
	{
		o.value = this.getPrintableDate(); 
		if (o.onchange && o.onchange.call) o.onchange.call();
	}
	o = elById('calTips');
	if (o) o.innerHTML = '';
	theMgr.hideAllPopups();
	theMgr.unmaskClicks();
}
Calendar.prototype.setDate = function( sDate )
{			
	this.dt = new Date(sDate);
	this.dtShowing = new Date(this.dt);
}
Calendar.prototype.guessDate = function( sDate )
{

	var dt = new Date(sDate);
	var dtToday = new Date();
	var re, matches, theYear;
	// lasse.karlsen
	// see case 147999
    if ( sDate.match(/^\d?\d$/) )
    {
        var theDay = parseInt(sDate, 10);
        var theMonth = dtToday.getMonth();
        theYear = dtToday.getFullYear();

        if (theDay < dtToday.getDate())
        {
            theMonth++;
            if (theMonth >= 12)
            {
                theMonth -= 12;
                theYear++;
            }
        }
        dt = new Date(theYear, theMonth, theDay);
        if ( !isNaN(dt) )
            return dt;
    }

	if ( sDate.match(/^\d\d\d\d[\:\-\.\/]\d?\d[\:\-\.\/]\d?\d$/) )
	{
		re = /^(\d\d\d\d)[\:\-\.\/](\d?\d)[\:\-\.\/](\d?\d)$/;
		matches = re.exec(sDate);
		
		dt = new Date(parseInt(matches[1], 10), parseInt(matches[2], 10) - 1, parseInt(matches[3], 10));

		if ( !this.bPastOK && dt < dtToday && !(dt.getYear() == dtToday.getYear() && dt.getMonth() == dtToday.getMonth() && dt.getDate() == dtToday.getDate() ))
		{
			dt = new Date(dtToday);
			alert( 'The due date must in the future.  It has been reset to today.' );
		}
		if ( !isNaN(dt) )
			return dt;		
	}	

	if ( sDate.match(/^\d?\d[\:\-\.\/]\d?\d[\:\-\.\/]?\d?\d?\d?\d?$/) )
	{
		// need to figure out which is month and which is day
		re = /^\s*(\d?\d)[\:\-\.\/](\d?\d)[\:\-\.\/]?((\d\d)?\d\d)?\s*$/;
		matches = re.exec(sDate);

		if ( matches )
		{
			theYear = dtToday.getFullYear();
			if ( matches.length > 2)
			{
				if ( matches[3].length == 2 )
					theYear = "20" + matches[3];
				else if ( matches[3].length == 4 )
					theYear = matches[3];
			}
			
			if ( this.localeDate.indexOf("mm") == 0 )
			{
				dt = new Date(theYear, parseInt(matches[1], 10) - 1, parseInt(matches[2], 10));
			}
			else
			{
				dt = new Date(theYear, parseInt(matches[2], 10) - 1, parseInt(matches[1], 10));
			}

			if ( !this.bPastOK && dt < dtToday && !(dt.getYear() == dtToday.getYear() && dt.getMonth() == dtToday.getMonth() && dt.getDate() == dtToday.getDate() ))
			{
				dt = new Date(dtToday);
				alert( 'The due date must in the future.  It has been reset to today.' );
			}
			if ( !isNaN(dt) )
				return dt;
		}
	}
	
	var dtGuess = new Date();
	sDate = trim(sDate.toLowerCase());
	if ( sDate == "today" )
			return new Date();
	
	if ( sDate == "tomorrow" )
	{
		dtGuess.setDate( dtGuess.getDate() + 1 );
		return dtGuess;
	}
	
	if ( sDate == "day after tomorrow" || sDate == "the day after tomorrow" )
	{
		dtGuess.setDate( dtGuess.getDate() + 2 );
		return dtGuess;
	}
	
	if ( sDate == "yesterday" )
	{
		dtGuess.setDate( dtGuess.getDate() - 1 );
		return dtGuess;
	}
	var i, n;
	for( i=0; i < this.rgDays.length; i++ )
	{
		// look for first 3 letters of day name mon, tue, wed (but don't confuse mon with month
		if ( sDate.indexOf(this.rgDays[i].toLowerCase().substr(0,3)) != -1 &&
				sDate.indexOf("month") == -1 )
		{
			var iDayOfWeekToday = dtGuess.getDay();
			if( i > iDayOfWeekToday )
				dtGuess.setDate( dtGuess.getDate() + (i - iDayOfWeekToday) );
			else if ( i < iDayOfWeekToday )
				dtGuess.setDate( dtGuess.getDate() + (i + 1 + (6 - iDayOfWeekToday)) );
			else if ( sDate.indexOf("next") != -1 )
				dtGuess.setDate( dtGuess.getDate() + 7 );
			return dtGuess;
		}
	}

	for( i=0; i < this.rgMonths.length; i++ )
	{
		re = /(\d+)/;
		n = 0;
		matches = re.exec(sDate);
		
		if ( matches && matches.length > 1 )
			n = parseInt(matches[1], 10);
		
		if ( sDate.indexOf(this.rgMonths[i].toLowerCase().substr(0,3)) != -1 )
		{
			if ( n != 0 ) // march 26 (in the future from today)
			{
				dtGuess.setMonth(i);
				dtGuess.setDate(n);
				if ( dtGuess < dtToday )
					dtGuess.setFullYear( dtGuess.getFullYear() + 1 );
			}
			else
			{
				var iMonthToday = dtGuess.getMonth();
				if( i > iMonthToday )
					dtGuess.setMonth( dtGuess.getMonth() + (i - iMonthToday) );
				else if ( i < iMonthToday )
					dtGuess.setMonth( dtGuess.getMonth() + (i + 1 + (11 - iMonthToday)) );
				else
					dtGuess.setMonth( dtGuess.getMonth() + 12 );
					
				dtGuess.setDate(1);
			}
			return dtGuess;
		}
	}

	if ( sDate.indexOf("day") != -1 
		|| sDate.indexOf("week") != -1 
		|| sDate.indexOf("month") != -1
		|| sDate.indexOf("year") != -1 )
	{
		re = /(\d+)/;
		n = 0;
		matches = re.exec(sDate);
		
		// no digits, assume "next"
		if (matches == null)
			n = 1;
		else if ( matches.length > 1 )
			n = parseInt(matches[1], 10);

		if ( sDate.indexOf("day") != -1 )
			dtGuess.setDate( dtGuess.getDate() + n );
		else if ( sDate.indexOf("week") != -1 )
			dtGuess.setDate( dtGuess.getDate() + (7 * n) );
		else if ( sDate.indexOf("month") != -1 )
			dtGuess.setMonth( dtGuess.getMonth() + n );
		else if ( sDate.indexOf("year") != -1 )
			dtGuess.setFullYear( dtGuess.getFullYear() + n );
		return dtGuess;
	}

	dtGuess = new Date(sDate);
	// case 221578
	// Mac Safari: Year gets set to 1901 or 1969 for Date(NaN) or Date(empty string), respectively.
	// We don't want to guess that the user is setting due date to 1901 or 1969, so we guess Today.
	if ( isNaN(dtGuess.getDate()) || dtGuess.getYear() == 1 || dtGuess.getYear() == 69)
		return new Date(); 
}
