﻿/*
 * usCalendar 1.0 - United States Calendar
 *
 * Copyright (c) 2007 Zhiwei Ou (ouzhiwei@gmail.com)
 * licensed under the GPL licenses.
 *
 * Reference：
 * http://www.smart.net/~mmontes/ushols.html
 * http://www.holidayorigins.com/
 *
 * $Date: 2007-08-08 $
 * $Rev: $
 */

//*******************************************************
// Month full names
//*******************************************************
var arrMonthName = {
	1: 'January',
	2: 'February',
	3: 'March',
	4: 'April',
	5: 'May',
	6: 'June',
	7: 'July',
	8: 'August',
	9: 'September',
	10: 'October',
	11: 'November',
	12: 'December'
};

//*******************************************************
// Month short names
//*******************************************************
var arrShortMonthName = {
	1: 'Jan',
	2: 'Feb',
	3: 'Mar',
	4: 'Apr',
	5: 'May',
	6: 'Jun',
	7: 'Jul',
	8: 'Aug',
	9: 'Sep',
	10: 'Oct',
	11: 'Nov',
	12: 'Dec'
};

//*******************************************************
// Weekday names
//*******************************************************
var arrWeekdayName = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

//*******************************************************
// Day alias
//*******************************************************
var arrDayAlias = {
	'4.22': "Earth Day"		//since 1970
};

//*******************************************************
// Zodiac Names
//*******************************************************
var arrZodiac = [
	'Aries',
	'Taurus',
	'Gemini',
	'Cancer',
	'Leo',
	'Virgo',
	'Libra',
	'Scorpius',
	'Sagittarius',
	'Capricornus',
	'Aquarius',
	'Pisces'
];

//*******************************************************
// Get the last day for a specific month
//*******************************************************
function getLastDay(numYear, numMonth) {
	with (new Date(numYear, numMonth, 1, 12)) {
		setDate(0) ; 
		return getDate();
	}
}

//*******************************************************
// Get the date for Easter
//*******************************************************
function getEaster(numYear) {
    var golden = numYear % 19;
    var fullmoon = (golden*(30 - 11)+15) % 30;
    if (numYear > 1582) {
        var century = Math.floor((numYear - 1500)/100);
        var adjust = Math.floor(century/25)*8 + Math.min(8,Math.floor((century%25)/3)) + 3;
        fullmoon = (fullmoon + Math.floor(numYear/100) -Math.floor(numYear/400) - 2 - adjust) % 30;
        
        if (fullmoon == 28 && golden >= 11) {
        	fullmoon--;
        }else if (fullmoon > 28) {
        	fullmoon = 28;
        }
    }
	
	numMonth = 3;
    var adjust = numYear <= 1582 ? 2 : Math.floor(numYear/100) - Math.floor(numYear/400);
    wDay = (numYear + Math.floor(numYear/4) - adjust + Math.floor((13 * numMonth + 8)/5) + 21 + fullmoon) % 7;

    pascha = fullmoon + 7 - wDay

    var numDay = 21 + pascha;
    if (numDay > 31) {
        numMonth++; 
        numDay -= 31;
    }
    
    return numMonth + '.' + numDay;
}

//*******************************************************
// USA Federal Holidays and Celebrations
//*******************************************************
var blnElection = false;
function getFederalHoliday(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek) {
	var strTitle = "";
	//Month.Day
	switch( strKey1 ) {
		case '1.1':
			strTitle = "New Year's Day";
			break;
		case '1.20':
			strTitle = (numYear >= 1789 && (numYear - 1789) % 4 == 0) ? "Inauguration Day" : "";
			break;
		case '2.22':
			strTitle = (numYear >= 1782 && numYear < 1971) ? "Washington's Birthday" : "";
			break;
		case '3.4':
			strTitle = (numYear < 1937 && (1937 - numYear) % 4 == 0) ? "Inauguration Day" : "";
			break;
		case '5.30':
			strTitle = (numYear >= 1868 && numYear < 1971) ? "Decoration Day" : ""; //Memorial Day
			break;
		case '6.14':
			strTitle = (numYear >= 1777) ? "Flag Day" : "";
			break;
		case '7.4':
			strTitle = (numYear >= 1948) ? "Independence Day" : "";
			break;
		case '11.11':
			strTitle = (numYear >= 1919 && numYear < 1971) || (numYear > 1977) ? "Veteran's Day" : "";
			break;
		case '12.25':
			strTitle = (numYear >= 300) ? 'Christmas' : '';
			break;
		default:
			break;
	}
	
	//Month.Week.Day
	var strAnd = (strTitle == '') ? '' : '<br />';
	switch( strKey2 ) {
		case '1.w3.1':
			strTitle += (numYear >= 1968) ? strAnd + "Martin Luther King Day" : "";
			break;
		case '2.w3.1':
			strTitle += (numYear >= 1971) ? strAnd + "Washington's Birthday" : "";
			break;
		case '5.w3.6':
			strTitle += (numYear >= 1971) ? strAnd + "Armed Forces Day" : "";
			break;
		case '5.w4.1':
		case '5.w5.1':
			if(blnLastWdayInMonth) {
				strTitle += (numYear >= 1971) ? strAnd + "Memorial Day" : "";
			}
			break;
		case '9.w1.1':
			strTitle += (numYear >= 1882) ? strAnd + "Labor Day" : "";
			break;
		case '10.w2.1':
			strTitle += (numYear >= 1971) ? strAnd + "Columbus Day" : "";
			break;
		case '10.w4.1':
			strTitle = (numYear >= 1971 && numYear <= 1977) ? strAnd + "Veteran's Day (Armistice)" : "";
			break;
		case '11.w1.2':
		case '11.w2.2':
			regMatch = /(.*)\.(.*)/.exec(strKey1);
			if(!blnElection && parseInt(RegExp.$2) >= 2) {
				strTitle += (numYear >= 1971) ? strAnd + "Election Day" : "";
				blnElection = true;
			}
			break;
		case '11.w4.4':
			strTitle = (numYear >= 1621) ? "Thanksgiving Day" : "";
			break;
		default:
			break;
	}

	return strTitle;
}

//*******************************************************
// Other Widely Celebrated Observances
//*******************************************************
var arrEaster = new Array();
function getOtherCelebratedDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek) {
	var strTitle = "";
	
	//Get Easter date and save it to array
	if(!arrEaster[numYear]) {
		arrEaster[numYear] = getEaster(numYear);
	}
	//Month.Day
	switch( strKey1 ) {
		case arrEaster[numYear]:
			strTitle = 'Easter';
			break;
		case '2.2':
			strTitle = 'Groundhog Day';
			break;
		case '2.12':
			strTitle = "Lincoln's Birthday";
			break;
		case '2.14':
			strTitle = "Valentine's Day";
			break;
		case '3.17':
			strTitle = "St. Patrick's Day";
			break;
		case '5.1':
			strTitle = "May Day";
			break;
		case '10.24':
			strTitle =  (numYear >= 1971) ? "United Nations Day" : "";
			break;
		case '10.31':
			strTitle = "Halloween";
			break;
		default:
			break;
	}
	
	return strTitle;
}

//*******************************************************
// Days for Relatives
//*******************************************************
function getRelativesDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek) {
	var strTitle = "";
	
	//Month.Day
	switch( strKey1 ) {
		case '4.1':
			strTitle = "April Fools's Day";
			break;
		default:
			break;
	}
	
	//Month.Week.Day
	var strAnd = (strTitle == '') ? '' : '<br />';
	switch( strKey2 ) {
		case '5.w2.0':
			strTitle += (numYear >= 1907) ? strAnd + "Mothers' day" : "";
			break;
		case '5.w3.0':
		case '5.w4.0':
		case '5.w5.0':
			if(blnLastFullWeek) {
				strTitle += (numYear >= 1955) ? strAnd + "Administrative Assistants' Day" : "";
			}
			break;
		case '6.w3.0':
			strTitle += (numYear >= 1910) ? strAnd + "Fathers' Day" : "";
			break;
		case '7.w4.0':
			strTitle += (numYear >= 1994) ? strAnd + "Parents' Day" : "";
			break;
		case '9.w2.0':
			strTitle += (numYear >= 1973) ? strAnd + "Grandparents' Day" : "";
			break;
		default:
			break;
	}

	return strTitle;
}

//*******************************************************
// Notable Dates for planning the Year
//*******************************************************
var strTaxKey = null;
function getNotableDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek) {
	var strTitle = '';
	
	regMatch = /(\w+)\.(\w+)\.(\w+)/.exec(strKey2);
	strWday = '';

	//Month.Day
	switch( strKey1 ) {
		case '4.15':
			if(numYear >= 1918) {
				if( RegExp.$3 == 6 ) {
					strTaxKey = '4.w' + (parseInt(RegExp.$2.substr(-1)) + 1) + '.1';
				} else if (RegExp.$3 == 0 ) {
					strTaxKey = '4.' + RegExp.$2 + '.1';
				} else {
					strTitle += "Federal Income Taxes are due";
				}
			}
			break;
		default:
			break;
	}
	
	//Month.Week.Day
	var strAnd = (strTitle == '') ? '' : '<br />';
	var numSundayCnt = 0;
	switch( strKey2 ) {
		case '4.w1.0':
			if(numYear == 2006) {
				strTitle += strAnd + "Daylight time begins";
			}
			break;
		case '3.w2.0':
			if(numYear > 2006) {
				strTitle += strAnd + "Daylight time begins";
			}
			break;
		case strTaxKey:
			strTitle += strAnd + "Federal Income Taxes are due";
			break;
		case '10.w4.0':
		case '10.w5.0':
			if(blnLastWdayInMonth && numYear == 2006) {
				strTitle += strAnd + "Daylight Time ends";
			}
			break;
		case '11.w1.0':
			if(numYear > 2006) {
				strTitle += strAnd + "Daylight time ends";
			}
			blnDltEnd = true;
		default:
			break;
	}

	return strTitle;
}

//*******************************************************
// Get Zodiac Name
//*******************************************************
function getZodiac(numMonth, numDay) {
	var numTmp = numMonth * 100 + numDay;
	var numIndex;

	if ((numTmp >= 321) && (numTmp <= 419)) {
		numIndex = 0;
	} else if ((numTmp >= 420) && (numTmp <= 520)) {
		numIndex = 1;
	} else if ((numTmp >= 521) && (numTmp <= 621)) {
		numIndex = 2;
	} else if ((numTmp >= 622) && (numTmp <= 722)) {
		numIndex = 3;
	} else if ((numTmp >= 723) && (numTmp <= 822)) {
		numIndex = 4;
	} else if ((numTmp >= 823) && (numTmp <= 922)) {
		numIndex = 5;
	} else if ((numTmp >= 923) && (numTmp <= 1022)) {
		numIndex = 6;
	} else if ((numTmp >= 1023) && (numTmp <= 1121)) {
		numIndex = 7;
	} else if ((numTmp >= 1122) && (numTmp <= 1221)) {
		numIndex = 8;
	} else if ((numTmp >= 1222) || (numTmp <= 119)) {
		numIndex = 9;
	} else if ((numTmp >= 120) && (numTmp <= 218)) {
		numIndex = 10;
	} else if ((numTmp >= 219) && (numTmp <= 320)) {
		numIndex = 11;
	} else {
		return '';
	}
	
	return arrZodiac[numIndex];
}

//*******************************************************
// generate calendar html for specific Year-Month
//*******************************************************
function getCalendarHtml(numYear, numMonth) {
	var strYear = numYear + '';
	var strMonth = arrShortMonthName[numMonth];
	
	var startDate = new Date(numYear, numMonth - 1, 1, 0, 0, 0);
	var numOffset = startDate.getDay() - 1;
	var numLastDay = getLastDay(numYear, numMonth);

	var objLastDate = new Date(numYear, numMonth - 1, numLastDay, 0, 0, 0);
	var numLastWeekDay =  objLastDate.getDay();

	var strAnd = '<br />';

	var strMonthTip = arrMonthName[numMonth] + ' ' + strYear;
	
	// HTML code for the month
	var strHtml = '	    <table cellpadding="2" cellspacing="2" border="0">\n'
				+ '	      <tr>\n'
				+ '	        <td colspan="7" title="' + strMonthTip + '"><center>\n'
				+ '	          <b>' + strMonth + '&nbsp;' + strYear + '</b>\n'
				+ '	        </center></td>\n'
				+ '	      </tr>\n'
				+ '	      <tr>\n'
				+ '	        <th align="right"><font color="#DD0022">Su</font></th>\n'
				+ '	        <th align="right">Mo</th>\n'
				+ '	        <th align="right">Tu</th>\n'
				+ '	        <th align="right">We</th>\n'
				+ '	        <th align="right">Th</th>\n'
				+ '	        <th align="right">Fr</th>\n'
				+ '	        <th align="right"><font color="#DD0022">Sa</font></th>\n'
				+ '	      </tr>\n';

	var numCount = 0;
	var arrWdayCnt = [0, 0, 0, 0, 0, 0, 0];
	for( var iRow = 0; iRow < 6; iRow++) {
		strHtml += '	      <tr>\n';
		for( var iCol = 0; iCol < 7; iCol++) {
			var numDay = numCount - numOffset;

			var strText = ''
			var strDayTip = '';
			var strBgColor = '';
			var strTextColor = (iCol == 0 || iCol == 6) ? '#DD0022' : '';

			if( numDay > 0 && numDay <= numLastDay ) {
				var objDate = new Date(numYear, numMonth - 1, numDay, 0, 0, 0);
				strText = '' + numDay;

				strDayTip = arrMonthName[numMonth] + ' ' + numDay + ', ' + numYear;
				
				var numWeek = iRow + 1;
				var numWeekDay = objDate.getDay();
				arrWdayCnt[numWeekDay] += 1;

				var strKey1 = numMonth + '.' + numDay;
				var strKey2 = numMonth + '.w' + arrWdayCnt[numWeekDay] + '.' + numWeekDay;
				
				var blnLastWdayInMonth = false;
				if( (numLastDay - numDay) <= numLastWeekDay ) {
					blnLastWdayInMonth = true;
				}

				var blnLastFullWeek = false;

				var tmpDiff = 0;
				if( numLastWeekDay == 6) {
					tmpDiff = numLastDay - numDay;
				} else {
					tmpDiff = numLastDay - numLastWeekDay - 1 - numDay;
				}

				if ( tmpDiff >= 0 && tmpDiff <= 6 ) {
					blnLastFullWeek = true;
				}

				// Day alias
				if( arrDayAlias[strKey1] ) {
					strDayTip += strAnd + arrDayAlias[strKey1];
					strTextColor = '#336600';
					strBgColor = '#F1FAE8';
				}

				// Notable Day
				var strNotableDay = getNotableDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek);
				if( strNotableDay != '') {
					strDayTip += strAnd + strNotableDay;
					strTextColor = '#2F4F4F';
					strBgColor = '#F0F8FF';
				}

				// Other Celebrated Day ( for Relatives )
				var strRelativesDay = getRelativesDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek);
				if( strRelativesDay != '') {
					strDayTip += strAnd + strRelativesDay;
					strTextColor = '#660099';
					strBgColor = '#F6ECF6';
				}

				// Other Celebrated Day
				var strOtherCelebratedDay = getOtherCelebratedDay(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek);
				if( strOtherCelebratedDay != '') {
					strDayTip += strAnd + strOtherCelebratedDay;
					strTextColor = '#808000';
					strBgColor = '#FAFAD2';
				}

				//Federal Holiday
				var strFederalHoliday = getFederalHoliday(numYear, strKey1, strKey2, blnLastWdayInMonth, blnLastFullWeek);
				if( strFederalHoliday != '') {
					strDayTip += strAnd + strFederalHoliday;
					strTextColor = '#DD0022';
					strBgColor = '#FFE4E1';
				}
				
				//Zodiac Star Sign
				var strZodiac = getZodiac(numMonth, numDay);
				strDayTip += strAnd + strAnd + '~ Star Sign ~';
				strDayTip += strAnd + strZodiac;

			} else {
				strText = '&nbsp;';
			}

			strTextColor = (strTextColor == '') ? '#000000' : strTextColor;
			
			strHtml +='	        <td title="'+ strDayTip + '" bgcolor="'+ strBgColor + '" align="right"><font color="' + strTextColor + '">'+ strText +'</font></td>\n';
			numCount++;
		}
		strHtml += '	      </tr>\n';
	}
	
	strHtml += '	    </table>\n';
    return strHtml;
}
