/**
 * ************************************************************************************************
 *
 * This JavaScript code dynamically builds a one-month calendar, by default for the current month
 * It was written for the Haverford Township Hilltop Baseball, Softball and Challenger League
 * http://www.hilltopleague.com
 *
 * @author Louis V. Lattanzio lvlattanzio@gmail.com
 *
 * ************************************************************************************************
 */

var CalendarEvents = new Object();
CalendarEvents.events = new Array();
//  Load the calendar data
//      This will only occur once, when the page loads
//      Navigating between months will not cause another AJAX request
loadCalendarItems();

/**
 * This function builds a month-view calendar for the date specified.
 * If no date is specified, the current date is used
 *
 * @param containerDivId The element ID of the container div for the calendar
 * @param startDate A JavaScript date object
 */
function buildCalendar(containerDivId,startDate)
{
	//  Get necessary values for calculations
	var today = startDate ? startDate : new Date();
	var year = today.getFullYear();
	var month = today.getMonth();
	var date = today.getDate();
	var day = today.getDay();

	//  Get the first and last date of the current month
	var first = new Date(year, month, 1);
	var last = getLastDateOfMonth(year, month);

	//  Get a handle on the container element
	var container = document.getElementById(containerDivId);
	container.innerHTML = "Please wait, the calendar is loading.";

	//  Get the calendar wrapper and put it in the container
	var calDiv = getCalendarWrapper();
	//container.appendChild(calDiv);

	//  Get this month's events from the data
	var thisMonthEvents = extractThisMonthEvents(year, month);

	//  Build the month header
	calDiv.appendChild(getMonthHeader(month,year));

	//  Build the day column header row
	calDiv.appendChild(getDaysOfWeekHeader());

	var currDate = 1;
	var currWeek = 0;

	//  Build the calendar days
	while(currDate <= last.getDate())
	{
		var weekDiv = getWeekWrapper(currWeek);
		calDiv.appendChild(weekDiv);

		var currDay = 0;
		while(currDay < CalendarConstants.CLND.NUM_DAYS_OF_WEEK)
		{
			//  Get an instance of a day cell
			var dayDiv = getDayCell(currWeek, currDay);
			weekDiv.appendChild(dayDiv);

			//  Check for unused day cells and format accordingly
			if((currWeek == 0 && currDay < first.getDay()) || currDate > last.getDate())
			{
				dayDiv.appendChild(document.createTextNode(CalendarConstants.CHAR.EMPTY_STR));
				dayDiv.style.backgroundColor = CalendarConstants.EMPTY_COLOR;
			}
			//  Otherwise format it as a valid day of the month cell
			else
			{
				//  Store this day cell's date as a property of itself for future access
				dayDiv.myDate = currDate;
				dayDiv.myJsDate = new Date(year,month,currDate);
				dayDiv.title = dayDiv.myJsDate.toLocaleDateString();

				var present = new Date();
				//if((year == present.getFullYear()) && (month == present.getMonth()) && (currDate == present.getDate()))
				if(dayDiv.myJsDate.toLocaleDateString() == present.toLocaleDateString())
				{
					dayDiv.style.backgroundColor = CalendarConstants.TODAY_COLOR;
					dayDiv.title = "Today";
				}

				var numEvents = countEvents(thisMonthEvents, currDate);
				if(numEvents > 0)
				{
					//dayDiv.style.backgroundColor = CalendarConstants.HAS_EVENTS_COLOR;
					dayDiv.style.backgroundColor = 
						dayDiv.myJsDate.toLocaleDateString() == present.toLocaleDateString() ? 
						blendColors(CalendarConstants.TODAY_COLOR,CalendarConstants.HAS_EVENTS_COLOR) : 
						CalendarConstants.HAS_EVENTS_COLOR;

					dayDiv.onmouseover = function(){
						mouseOver(thisMonthEvents, this);
						};
					dayDiv.onmouseout = function(){
						var layer = document.getElementById(CalendarConstants.EIDS.EVENTS_LAYER);
						layer.style.display = CalendarConstants.DIV_HIDE;
						};
					dayDiv.onclick = function(){
						//mouseOver(thisMonthEvents, this);
						flyOutLayer(this, calDiv)
						};
				}
				dayDiv.appendChild(getDayCellDate(currDate));

				dayDiv.appendChild(getDayCellContent(year, month, currDate, numEvents));

				currDate = currDate + 1;
			}
			currDay = currDay + 1;
		}
		weekDiv.appendChild(getFloatClearDiv());

		currWeek = currWeek + 1;
	}
	//  Navigation links
	calDiv.appendChild(getNavigationBar(year, month, date, containerDivId));

	//  Add the calendar to the container AFTER it has finished building
	container.innerHTML = '';
	container.appendChild(calDiv);

	//  Append the calendar caption to the container
	container.appendChild(getCalendarCaption());
}

/**
 * This function calculates the last date of the specified month and year
 *
 * @param year The year of the month for which to calculate the last date
 * @param month The month of for which to calculate the last date
 * @return Returns a JavaScript date object for the last date of the specified month and year
 */
function getLastDateOfMonth(year, month)
{
	var last = new Date(year, month + 1, 1);
	last.setDate(last.getDate() - 1);

	return last;
}

/**
 * This function will be called when a day with events is moused over and will display the events for that day
 *
 * @param thisMonthEvents An array of the events for the current month
 * @param elem The div element being moused over
 */
function mouseOver(thisMonthEvents, elem)
{
	//  Extract today's events
	var todayEvents = extractTodayEvents(thisMonthEvents, elem.myDate);

	//  Only allow one events layer at a time
	if(layer = document.getElementById(CalendarConstants.EIDS.EVENTS_LAYER))
	{
		document.body.removeChild(layer);
	}

	var eventsText = getEventsLayer(todayEvents, elem);

	document.body.appendChild(eventsText);

	eventsText.style.display = CalendarConstants.DIV_DISPLAY;
}

/**
 * This function causes the events layer to "fly out" above the calendar
 *
 * @param elem The div element of the day that was clicked
 * @param calDiv The div element containing the calendar
 */
function flyOutLayer(elem, calDiv)
{
	//  Get the events layer and format it as a fly-out
	var layer = getEventsLayerForFlyout(calDiv);

	//  Get the flyout header
	var flyoutHeader = getFlyoutHeader(elem);

	layer.insertBefore(flyoutHeader, layer.firstChild);

	var actions = document.createElement(CalendarConstants.TAGS.DIV);
	actions.style.textAlign = CalendarConstants.TEXT_ALIGN_RIGHT;

	var over = elem.onmouseover;
	var out = elem.onmouseout;
	
	elem.onmouseover = null;
	elem.onmouseout = null;
	layer.onmouseout = null;

	var innerHTML = layer.innerHTML;

	//  Add the print link
	actions.appendChild(getFlyoutPrintLink(innerHTML));

	//  Add the links delimeter
	//actions.appendChild(document.createTextNode(CalendarConstants.CHAR.SPACE + CalendarConstants.CHAR.PIPE + CalendarConstants.CHAR.SPACE));
	actions.appendChild(getNavigationLinksDelimeter());

	//  Add the close link
	actions.appendChild(getFlyoutCloseLink(layer, elem, over, out));

	
	layer.insertBefore(actions,layer.firstChild);
}

/**
 * This function gets the events layer from the page and formats it as a fly-out
 *
 * @param calDiv The div of the day that was clicked to generate the events layer
 * @return Returns the events layer formatted as a fly-out
 */
function getEventsLayerForFlyout(calDiv)
{
	var mLayer = document.getElementById(CalendarConstants.EIDS.EVENTS_LAYER);

	var coords = locateElement(calDiv);
	mLayer.style.left = coords[0] + CalendarConstants.STYL.PX;
	mLayer.style.top = coords[1] + CalendarConstants.STYL.PX;
	mLayer.style.width = calDiv.clientWidth + CalendarConstants.STYL.PX;
	mLayer.style.height = 
		(calDiv.clientHeight + 2 * CalendarConstants.CELL_PADDING) 
		+ CalendarConstants.STYL.PX;

	return mLayer;
}

/**
 * This function gets the flyout header for a given day on the calendar
 *
 * @param elem The div element of the day that was clicked on the calendar
 * @return Returns the flyout header for the day that was clicked on the calendar
 */
function getFlyoutHeader(elem)
{
	var flyoutHeader = document.createElement(CalendarConstants.TAGS.HEADER_THREE);

	flyoutHeader.style.textAlign = CalendarConstants.TEXT_ALIGN_CENTER;
	flyoutHeader.style.fontFamily = CalendarConstants.FONT_FAMILY;

	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CONT.FLYOUT_HEADER_TEXT));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CHAR.SPACE));
	flyoutHeader.innerHTML += CalendarConstants.ENTY.MDASH;
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CHAR.SPACE));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CLND.DAYS_OF_WEEK[elem.myJsDate.getDay()]));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CHAR.COMMA + CalendarConstants.CHAR.SPACE));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CLND.MONTHS[elem.myJsDate.getMonth()]));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CHAR.SPACE));
	flyoutHeader.appendChild(document.createTextNode(elem.myJsDate.getDate()));
	flyoutHeader.appendChild(document.createTextNode(CalendarConstants.CHAR.COMMA + CalendarConstants.CHAR.SPACE));
	flyoutHeader.appendChild(document.createTextNode(elem.myJsDate.getFullYear()));

	return flyoutHeader;
}

/**
 * This function generates a link that will print the events flyout layer
 *
 * @param innerHTML The innerHTML of the flyout layer that is to be printed
 * @return Returns a link that will print the events layer flyout
 */
function getFlyoutPrintLink(innerHTML)
{
	var printLink = document.createElement(CalendarConstants.TAGS.ANCHOR);

	printLink.style.color = CalendarConstants.NAV_LINK_COLOR;

	printLink.href = CalendarConstants.CHAR.HASH;
	printLink.title = CalendarConstants.CONT.PRINT_LINK_TITLE;

	printLink.onclick = function(){
		//window.print();
		win = window.open();
		self.focus();
		win.document.open();
		win.document.write(innerHTML);
		win.document.close();
		win.print();
		win.close();
		};

	printLink.appendChild(document.createTextNode(CalendarConstants.CONT.PRINT_LINK));

	return printLink;
}

/**
 * This function gets the link that will close the events layer flyout
 *
 * @param layer The layer to close
 * @param elem The div element of the day that was clicked to generate the flyout
 * @param over An onmouseover function to re-apply to elem
 * @param out An onmouseout function to re-apply to elem
 * @return Returns a link that will close the events layer flyout
 */
function getFlyoutCloseLink(layer, elem, over, out)
{
	var close = document.createElement(CalendarConstants.TAGS.ANCHOR);

	close.style.color = CalendarConstants.NAV_LINK_COLOR;

	close.href = CalendarConstants.CHAR.HASH;
	close.title = CalendarConstants.CONT.CLOSE_LINK_TITLE;

	close.onclick = function(){
		document.body.removeChild(layer);
		elem.onmouseover = over;
		elem.onmouseout = out;
		};

	close.appendChild(document.createTextNode(CalendarConstants.CONT.CLOSE_LINK));

	return close;
}

/**
 * This function gets the events layer for a given day on the calendar
 *
 * @param todayEvents The array of events for the given day
 * @param elem The div element being moused over
 * @return Returns a div element containing the events for the day
 */
function getEventsLayer(todayEvents, elem)
{
	var eventsText = document.createElement(CalendarConstants.TAGS.DIV);
	eventsText.id = CalendarConstants.EIDS.EVENTS_LAYER;

	eventsText.style.textAlign = CalendarConstants.EVENT_ALIGN;
	eventsText.style.fontFamily = CalendarConstants.FONT_FAMILY;
	eventsText.style.fontSize = CalendarConstants.FONT_SIZE;

	for(i = 0; i < todayEvents.length; i ++)
	{
		var eventWrapper = document.createElement(CalendarConstants.TAGS.DIV);
		eventWrapper.id = CalendarConstants.EIDS.EVENT_WRAPPER_PREFIX + i;
		eventWrapper.style.fontFamily = CalendarConstants.FONT_FAMILY;

		//  Add the event title
		eventWrapper.appendChild(getTitleString(todayEvents[i]));

		//  Add the event time
		eventWrapper.appendChild(getTimeString(todayEvents[i]));

		//  Add the event location
		eventWrapper.appendChild(getLocationHTML(todayEvents[i]));

		//  TODO - ADD ADDITIONAL EVENT INFORMATION ITEMS

		//  Add a bottom border if this isn't the last event for this day
		if(i < todayEvents.length - 1)
		{
			eventWrapper.style.borderBottomWidth = (CalendarConstants.BORDER_WIDTH * 2) + CalendarConstants.STYL.PX;
			eventWrapper.style.borderBottomStyle = CalendarConstants.EV_DIVIDER_STYLE;
			eventWrapper.style.borderBottomColor = CalendarConstants.BORDER_COLOR;
			eventWrapper.style.marginBottom = CalendarConstants.CELL_PADDING + CalendarConstants.STYL.PX;
		}
		eventsText.appendChild(eventWrapper);
	}

	var coords = locateElement(elem);

	eventsText.style.position = CalendarConstants.EV_LAYER_POS_ABS;
	eventsText.style.left = coords[0] + CalendarConstants.STYL.PX;
	eventsText.style.top = (coords[1] + CalendarConstants.CELL_SIZE) + CalendarConstants.STYL.PX;
	eventsText.style.zIndex = elem.style.zIndex + 1;
	eventsText.style.display = CalendarConstants.DIV_HIDE;
	eventsText.style.backgroundColor = CalendarConstants.EVENTS_BGCOLOR;
	eventsText.style.borderColor = CalendarConstants.BORDER_COLOR;
	eventsText.style.borderWidth = CalendarConstants.BORDER_WIDTH * 2;
	eventsText.style.borderStyle = CalendarConstants.BORDER_STYLE;
	eventsText.style.padding = CalendarConstants.CELL_PADDING + CalendarConstants.STYL.PX;

	eventsText.onmouseover = function(){
		this.style.display = CalendarConstants.DIV_DISPLAY;
		};
	eventsText.onmouseout = function(){
		this.style.display = CalendarConstants.DIV_HIDE;
		};
	/*eventsText.ondblclick = function(){
		this.style.display = CalendarConstants.DIV_HIDE;
		};*/

	return eventsText;
}

/**
 * This function generates the event title, if one exists.
 * If the event has a title HREF, the title is created as a link to that HREF.
 *
 * @param todayEvent The event for which to get the title
 * @return Returns the title of the event
 */
function getTitleString(todayEvent)
{
	var title = document.createElement(CalendarConstants.TAGS.DIV);

	title.style.fontWeight = CalendarConstants.FONT_WT_BOLD;

	var evTitle = todayEvent.getTitle();
	var titleHREF = todayEvent.getTitleHREF();

	if(evTitle)
	{
		if(titleHREF)
		{
			var a = createHREF(evTitle,titleHREF,CalendarConstants.CONT.TITLE_HREF_TITLE + CalendarConstants.CHAR.SPACE + evTitle);
			a.style.color = CalendarConstants.BORDER_COLOR;

			title.appendChild(a);
		}
		else
		{
			title.appendChild(document.createTextNode(evTitle));
		}
	}

	return title;
}

/**
 * This function builds a time string for a given event.
 * If the event has a duration, the time string includes and end time.
 *
 * @param todayEvent The event for which to build a time string
 * @return Returns the time string for the event
 */
function getTimeString(todayEvent)
{
	var time = document.createElement(CalendarConstants.TAGS.DIV);

	time.style.fontStyle = CalendarConstants.FONT_STYLE_ITALIC;

	var timeStr = CalendarConstants.CHAR.EMPTY_STR;

	var hrs = todayEvent.getStartTime().getHours();
	if(hrs != 0)
	{
	var mins = todayEvent.getStartTime().getMinutes();
	var duration = todayEvent.getDurationMins();

	timeStr += formatTime(hrs, mins);

	//  Add the end time to the time string if the event has a duration
	if(duration != null && duration > 0)
	{
		var str = todayEvent.getStartTime().toString();
		var endTime = new Date(str);
		
		var endMins = endTime.getMinutes();
		endMins += duration;
		endTime.setMinutes(endMins);

		timeStr += ' - ';

		timeStr += formatTime(endTime.getHours(), endTime.getMinutes());
	}
	}

	time.appendChild(document.createTextNode(timeStr));

	return time;
}

/**
 * This function formats a time into common readable format
 * 
 * @param hrs The hours of the time in 24-hour format
 * @param mins The minutes of the time
 * @return Returns a time string in 'hh:mm A.M.|P.M.' format
 */
function formatTime(hrs, mins)
{
	var timeStr = CalendarConstants.CHAR.EMPTY_STR;
	if(hrs != null && hrs >= 0)
	{
		if(hrs == 0)
		{
			timeStr += 12;
		}
		else if(hrs > 12)
		{
			timeStr += (hrs - 12);
		}
		else
		{
			timeStr += hrs;
		}
		timeStr += CalendarConstants.CHAR.TIME_DELIM;
		if(mins != null && mins >= 0)
		{
			timeStr += (mins < 10) ? (CalendarConstants.CHAR.MINS_PAD + mins) : mins;
		}
		else
		{
			timeStr += CalendarConstants.CHAR.MINS_PAD + CalendarConstants.CHAR.MINS_PAD;
		}
		timeStr +=
			(hrs < 12) ? 
			CalendarConstants.CHAR.SPACE + CalendarConstants.CONT.TIME_AM : 
			CalendarConstants.CHAR.SPACE + CalendarConstants.CONT.TIME_PM;
	}

	return timeStr;
}

/**
 * This function gets the event location, if one exists.
 * If the event has a location HREF, the location is built as a link to that HREF.
 *
 * @param todayEvent The event for which to get the location
 * @return Returns the location of the event
 */
function getLocationHTML(todayEvent)
{
	var loc = document.createElement(CalendarConstants.TAGS.DIV);

	var location = todayEvent.getLocation();
	var locationHREF = todayEvent.getLocationHREF();

	if(location)
	{
		if(locationHREF)
		{
			var a = createHREF(location,locationHREF,CalendarConstants.CONT.LOCATION_HREF_TITLE + CalendarConstants.CHAR.SPACE + location);
			a.style.color = CalendarConstants.BORDER_COLOR;

			loc.appendChild(a);
		}
		else
		{
			loc.appendChild(document.createTextNode(location));
		}
	}

	return loc;
}

/**
 * This function creates a DOM <a> object.  By default the target attribute is set to "_blank".
 *
 * @param text The text of the link
 * @param href The URI to which this link will point
 * @param title The title of the link
 * @param newTarget Indicates if the link should be in a blank target. Only false will disable the blank target.
 * @return Returns an <a> object
 */
function createHREF(text,href,title,newTarget)
{
	var a = document.createElement('a');

	if(newTarget != false)
	{
		a.setAttribute('target','_blank');
	}

	a.setAttribute('href',href);

	a.setAttribute('title','');
	if(title)
	{
		a.setAttribute('title',title);
	}

	a.appendChild(document.createTextNode(text));

	return a;
}

/**
 * This function returns the coordinates of an element on the page
 *
 * @param obj The element to locate
 * @return Returns the x,y coordinates of obj
 */
function locateElement(obj)
{         
	var xCoord = 0;
	var yCoord = 0;

	if(obj.offsetParent)
	{
		do
		{   
			xCoord += obj.offsetLeft || 0;
			yCoord += obj.offsetTop || 0;
		}
		while((obj = obj.offsetParent));

		return [xCoord, yCoord];
	}
}

/**
 * This function instantiates and returns a browser-specific XMLHttpRequest object
 */
function getXMLHttpRequest()
{
	var xmlHttpObj = false;

	//	Firefox, Safari, etc.
	if (window.XMLHttpRequest)
	{
		xmlHttpObj = new XMLHttpRequest();
	}
	//	Internet Explorer
	else if (window.ActiveXObject)
	{
		xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
	}

	return xmlHttpObj;
}

function loadCalendarItems()
{
	//  This block parses an XML string
	
	/*if(window.DOMParser)
	{
		parser = new DOMParser();
		xmlDoc = parser.parseFromString(xmlString,"text/xml")
	}
	else if(ActiveXObject)
	{
		xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		xmlDoc.async = "false";
		xmlDoc.loadXML(xmlString);
	}*/
	/**/
	//  Clear out the current CalendarEvents
	//CalendarEvents = new Object();
	//CalendarEvents.events = new Array();

	xmlobj = getXMLHttpRequest();
	xmlobj.open("GET","htCal.xml",true);
	xmlobj.onreadystatechange = function(){if(xmlobj.readyState == 4){
	/**/

	//  Get the root node
	//var rootNode = xmlDoc.documentElement;
	var rootNode = xmlobj.responseXML.documentElement;

	//  Extract the calendar items
	////var items = rootNode.getElementsByTagName('htCal:calendarItem');
	var items = getTagSet(rootNode, 'htCal:calendarItem');

	//  Loop through the data for each item
	for(i=0;i<items.length;i++)
	{
		var item = items[i];

		//var title = getTagData(item.getElementsByTagName('htCal:title').item(0));
		var title = getTagData(getTag(item,'htCal:title'));

		//var description = getTagData(item.getElementsByTagName('htCal:description').item(0));
		var description = getTagData(getTag(item, 'htCal:description'));

		//var titleHREF = getTagData(item.getElementsByTagName('htCal:titleHREF').item(0));
		var titleHREF = getTagData(getTag(item, 'htCal:titleHREF'));

		//var occurrences = item.getElementsByTagName('htCal:occurrences').item(0).getElementsByTagName('htCal:occurrence');
		var occurrences = getTagSet(getTag(item, 'htCal:occurrences'), 'htCal:occurrence');

		//  For each occurrence of this event create a HtCalendarItem
		for(j=0;j<occurrences.length;j++)
		{
			var occurrence = occurrences[j];

			//var startTime = getTagData(occurrence.getElementsByTagName('htCal:startTime').item(0));
			var startTime = getTagData(getTag(occurrence, 'htCal:startTime'));

			//var durationMins = getTagData(occurrence.getElementsByTagName('htCal:durationMins').item(0));
			var durationMins = getTagData(getTag(occurrence, 'htCal:durationMins'));
			durationMins = durationMins != '' ? parseInt(durationMins,10) : 0;

			//var location = getTagData(occurrence.getElementsByTagName('htCal:location').item(0));
			var location = getTagData(getTag(occurrence, 'htCal:location'));

			//var locationHREF = getTagData(occurrence.getElementsByTagName('htCal:locationHREF').item(0));
			var locationHREF = getTagData(getTag(occurrence, 'htCal:locationHREF'));

			//  Create a HtCalendarItem object for each
			var calItem = new HtCalendarItem(title,startTime,durationMins,location,description,titleHREF,locationHREF);
			CalendarEvents.events.push(calItem);
			//log(calItem.toString());
		}
	}
	/**/
	}}
	xmlobj.send(null);
	/**/
}

function getTagSet(prnt, tagName)
{
	tags = prnt.getElementsByTagName(tagName);

	if(!tags || tags == null || tags.length == 0)
	{
		if(tagName.indexOf(':') > -1)
		{
			tags = prnt.getElementsByTagName(tagName.substring(tagName.indexOf(':') + 1));
		}
	}

	return tags;
}

function getTag(prnt, tagName)
{
	return getTagSet(prnt, tagName).item(0);
}

function getTagData(tag)
{
	try{
	return tag.firstChild.data;
	}
	catch(err)
	{
	return '';}
}

function log(msg, sev)
{
	//  Replace any new line characters with pipes
	msg = msg.replace(new RegExp('\\n','g'),' | ');

	//  Color code messages
	color = !sev || sev=='info'?'#bbbbbb':'#dd5555';

	//  Write the log message as a table
	document.getElementById('debug').innerHTML += 
		'<table border="0" style="color:'+color+';"><tr><td style="white-space:nowrap;vertical-align:top;">'+new Date() 
		+ '</td><td style="vertical-align:top;">::</td><td>' + msg 
		+ '</td></tr><tr><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td></tr></table>';
}

function blendColors(col1,col2)
{
	var pattern = /#[0-9a-f]{6}/gi;

	if(col1.match(pattern) && col2.match(pattern))
	{
		var rgb1 = new Array();
		var rgb2 = new Array();
		//rgb1['r'] = parseInt(col1.substring(1,2),16)*16+parseInt(col1.substring(2,3),16);
		rgb1['r'] = parseInt(col1.substring(1,3),16);
		rgb1['g'] = parseInt(col1.substring(3,4),16)*16+parseInt(col1.substring(4,5),16);
		rgb1['b'] = parseInt(col1.substring(5,6),16)*16+parseInt(col1.substring(6,7),16);
		rgb2['r'] = parseInt(col2.substring(1,2),16)*16+parseInt(col2.substring(2,3),16);
		rgb2['g'] = parseInt(col2.substring(3,4),16)*16+parseInt(col2.substring(4,5),16);
		rgb2['b'] = parseInt(col2.substring(5,6),16)*16+parseInt(col2.substring(6,7),16);

		var rgb = new Array();
		rgb['r'] = Math.round((rgb1['r']+rgb2['r'])/2);
		rgb['g'] = Math.round((rgb1['g']+rgb2['g'])/2);
		rgb['b'] = Math.round((rgb1['b']+rgb2['b'])/2);

		return '#'+rgb['r'].toString(16)+rgb['g'].toString(16)+rgb['b'].toString(16);
	}
	else
	{
		//Invalid color(s) - return white
		return '#ffffff';
	}
}

/**
 * This function extracts events from the data for the specified month
 *
 * @param year The year of the month being built
 * @param month The month being built
 * @return Returns an array of events for the specified month
 */
function extractThisMonthEvents(year, month)
{
	var thisMonthEvents = new Array();

	for(i = 0; i < CalendarEvents.events.length; i ++)
	{
		var ev = CalendarEvents.events[i];
		//if(ev[0] == year && ev[1] == (month + 1))
		if(ev.getYear() == year && ev.getMonth() == month)
		{
			thisMonthEvents.push(ev);
		}
	}
	return thisMonthEvents;
}

/**
 * This function counts the number of events on a given date of the month
 *
 * @param thisMonthEvents An array of events for the current month being viewed
 * @param currDate The date of the month for which to count events
 * @return Returns the number of events on the given date of this month
 */
function countEvents(thisMonthEvents, currDate)
{
	var count = 0;

	for(i = 0; i < thisMonthEvents.length; i ++)
	{
		//if(thisMonthEvents[i][2] == currDate)
		if(thisMonthEvents[i].getDayOfMonth() == currDate)
		{
			count ++;
		}
	}
	return count;
}

/**
 * This function returns the events on a given date of the month
 *
 * @param thisMonthEvents An array of events for the current month being viewed
 * @param currDate The date of the month for which to extract events
 * @return Returns the events for the given date of this month
 */
function extractTodayEvents(thisMonthEvents, currDate)
{
	var todayEvents = new Array();

	for(i = 0; i < thisMonthEvents.length; i ++)
	{
		var ev = thisMonthEvents[i];
		//if(ev[2] == currDate)
		if(ev.getDayOfMonth() == currDate)
		{
			todayEvents.push(ev);
			//thisMonthEvents.pop(ev);
		}
	}
	bubbleSortTodayEvents(todayEvents);

	return todayEvents;
}

/**
 * This function sorts a day's events in ascending order by time of day
 *
 * @param todayEvents An array of events for a given day
 */
function bubbleSortTodayEvents(data)
{
	var numEvents = data.length;

	for(i = 0; i < numEvents; i ++)
	{
		var isSorted = true;

		for(j = 1; j < numEvents - i; j ++)
		{
			var curr = data[j].getStartTime();

			var prev = data[j - 1].getStartTime();

			if(curr < prev)
			{
				var tmp = data[j];
				data[j] = data[j - 1];
				data[j - 1] = tmp;

				isSorted = false;
			}
		}

		if(isSorted)
		{
			break;
		}
	}
}

/**
 * This function builds the wrapper div for the calendar components
 *
 * @return A formatted div to contain the calendar components
 */
function getCalendarWrapper()
{
	var calDiv = document.createElement(CalendarConstants.TAGS.DIV);
	calDiv.id = CalendarConstants.EIDS.CAL_DIV;

	calDiv.style.width = calculateCalendarFixedWidth() + CalendarConstants.STYL.PX;
	//calDiv.style.width = calculateCalendarFixedWidthForBrowser() + CalendarConstants.STYL.PX;
	calDiv.style.borderColor = CalendarConstants.BORDER_COLOR;
	calDiv.style.borderWidth = CalendarConstants.BORDER_WIDTH;
	calDiv.style.borderStyle = CalendarConstants.BORDER_STYLE;
	calDiv.style.marginLeft = CalendarConstants.MARGIN_LEFT;
	calDiv.style.marginRight = CalendarConstants.MARGIN_RIGHT;
	calDiv.style.fontFamily = CalendarConstants.FONT_FAMILY;
	calDiv.style.fontSize = CalendarConstants.FONT_SIZE;

	return calDiv;
}

/**
 * This function calculates the fixed width of the calendar
 *
 * @return The fixed witdth of the calendar wrapper div
 */
function calculateCalendarFixedWidth()
{
	return (CalendarConstants.CLND.NUM_DAYS_OF_WEEK * (CalendarConstants.CELL_SIZE + (2 * CalendarConstants.BORDER_WIDTH)));
}

/**
 * This function builds the month header in the format Month YYYY
 *
 * @param month The index of the calendar month (0-11)
 * @param year The four-digit calendar year
 * @return A div representing the month header
 */
function getMonthHeader(month,year)
{
	var monthHead = document.createElement(CalendarConstants.TAGS.DIV);

	monthHead.appendChild(document.createTextNode(CalendarConstants.CLND.MONTHS[month] + CalendarConstants.CHAR.SPACE + year));

	monthHead.style.borderWidth = CalendarConstants.BORDER_WIDTH + CalendarConstants.STYL.PX;
	monthHead.style.borderStyle = CalendarConstants.BORDER_STYLE;
	monthHead.style.borderColor = CalendarConstants.BORDER_COLOR;
	monthHead.style.overflow = CalendarConstants.OVERFLOW_HIDDEN;
	monthHead.style.fontFamily = CalendarConstants.MONTH_FONT_FAMILY;
	monthHead.style.fontSize = CalendarConstants.MONTH_FONT_SIZE;

	return monthHead;
}

/**
 * This function builds the days of the week header
 *
 * @return A div representing the days of the week header
 */
function getDaysOfWeekHeader()
{
	var daysDiv = document.createElement(CalendarConstants.TAGS.DIV);
	daysDiv.id = CalendarConstants.EIDS.DAYS_HEAD_DIV;

	for(i = 0; i < CalendarConstants.CLND.NUM_DAYS_OF_WEEK; i++)
	{
		var dayHead = document.createElement(CalendarConstants.TAGS.DIV);
		dayHead.id = CalendarConstants.EIDS.DAY_HEAD_DIV_PREFIX + i;

		dayHead.style.cssFloat= CalendarConstants.FLOAT_LEFT;
		dayHead.style.styleFloat= CalendarConstants.FLOAT_LEFT;
		dayHead.style.width = CalendarConstants.CELL_SIZE + CalendarConstants.STYL.PX;
		dayHead.style.borderWidth = CalendarConstants.BORDER_WIDTH + CalendarConstants.STYL.PX;
		dayHead.style.borderStyle = CalendarConstants.BORDER_STYLE;
		dayHead.style.borderColor = CalendarConstants.BORDER_COLOR;
		dayHead.style.overflow = CalendarConstants.OVERFLOW_HIDDEN;

		dayHead.appendChild(document.createTextNode(CalendarConstants.CLND.DAYS_OF_WEEK[i]));
		daysDiv.appendChild(dayHead);
	}
	return daysDiv;
}

/**
 * This function builds the wrapper div for a given week
 *
 * @param currWeek The index of the current week being built
 * @return A div to contain the days of the week
 */
function getWeekWrapper(currWeek)
{
	var weekDiv = document.createElement(CalendarConstants.TAGS.DIV);
	weekDiv.id = CalendarConstants.EIDS.WEEK_DIV_PREFIX + currWeek;
	weekDiv.style.clear = CalendarConstants.FLOAT_CLEAR;

	return weekDiv;
}

/**
 * This function builds an individual day cell
 *
 * @param currWeek The index of the current week of the month being built
 * @param currDay The index of the current day of the week being build
 * @return A div representing a day cell
 */
function getDayCell(currWeek, currDay)
{
	var dayDiv = document.createElement(CalendarConstants.TAGS.DIV);
	dayDiv.id = CalendarConstants.EIDS.DAY_DIV_PREFIX
		+ currWeek + CalendarConstants.CHAR.USCORE + currDay;

	dayDiv.style.cssFloat = CalendarConstants.FLOAT_LEFT;
	dayDiv.style.styleFloat = CalendarConstants.FLOAT_LEFT;
	dayDiv.style.width = CalendarConstants.CELL_SIZE + CalendarConstants.STYL.PX;
	dayDiv.style.height = CalendarConstants.CELL_SIZE + CalendarConstants.STYL.PX;
	dayDiv.style.borderWidth = CalendarConstants.BORDER_WIDTH + CalendarConstants.STYL.PX;
	dayDiv.style.borderStyle = CalendarConstants.BORDER_STYLE;
	dayDiv.style.borderColor = CalendarConstants.BORDER_COLOR;
	dayDiv.style.overflow = CalendarConstants.OVERFLOW_HIDDEN;

	return dayDiv;
}

/**
 * This function builds the day of the month div for a day cell
 *
 * @param currDate The current day of the month being built
 * @return A div representing the day of the month
 */
function getDayCellDate(currDate)
{
	var dateNumDiv = document.createElement(CalendarConstants.TAGS.DIV);

	dateNumDiv.appendChild(document.createTextNode(currDate));
	dateNumDiv.style.textAlign = CalendarConstants.DATE_ALIGN;
	dateNumDiv.style.padding = CalendarConstants.CELL_PADDING + CalendarConstants.STYL.PX;

	return dateNumDiv;
}

/**
 * This function builds the content for the given day
 *
 * @param year The current year of the month being viewed
 * @param month The current month being viewed
 * @param currDate The current day of the month being built
 * @return A div representing the content for the given day
 */
function getDayCellContent(year, month, currDate, numEvents)
{
	var dateEventDiv = document.createElement(CalendarConstants.TAGS.DIV);

	var str = CalendarConstants.CHAR.EMPTY_STR;
	if(numEvents > 0)
	{
		str += numEvents + CalendarConstants.CHAR.SPACE + CalendarConstants.CONT.EVENT;
		str += numEvents > 1 ? CalendarConstants.CONT.PLURAL : CalendarConstants.CHAR.EMPTY_STR;
	}

	dateEventDiv.appendChild(document.createTextNode(str));
	dateEventDiv.style.textAlign = CalendarConstants.EVENT_ALIGN;
	dateEventDiv.style.padding = CalendarConstants.CELL_PADDING + CalendarConstants.STYL.PX;

	return dateEventDiv;
}

/**
 * This function builds a div to clear the css float:left in FireFox
 *
 * @return A div to clear the css float:left in FireFox
 */
function getFloatClearDiv()
{
	var clearDiv = document.createElement(CalendarConstants.TAGS.DIV);

	clearDiv.style.clear = CalendarConstants.FLOAT_CLEAR;
	//clearDiv.style.width = "0px";
	//clearDiv.appendChild(document.createTextNode(CalendarConstants.CHAR.EMPTY_STR));

	return clearDiv;
}

/**
 * This function generates the calendar navigation bar
 *
 * @param year The year of the month to display
 * @param month The month to display
 * @param day The day of the month
 * @param containerDivId The ID of the container holding the calendar
 * @param The navigation bar for the calendar
 */
function getNavigationBar(year, month, day, containerDivId)
{
	var navDiv = document.createElement(CalendarConstants.TAGS.DIV);

	navDiv.style.borderWidth = CalendarConstants.BORDER_WIDTH + CalendarConstants.STYL.PX;
	navDiv.style.borderStyle = CalendarConstants.BORDER_STYLE;
	navDiv.style.borderColor = CalendarConstants.BORDER_COLOR;

	//  Previous month link
	navDiv.appendChild(getPreviousMonthLink(containerDivId, year, month, day));

	//  Navigation link delimeter
	navDiv.appendChild(getNavigationLinksDelimeter());

	//  Previous year link
	navDiv.appendChild(getPreviousYearLink(containerDivId, year, month, day));

	//  Navigation link delimeter
	navDiv.appendChild(getNavigationLinksDelimeter());

	//  Today link
	navDiv.appendChild(getTodayLink(containerDivId,false));

	//  Navigation link delimeter
	navDiv.appendChild(getNavigationLinksDelimeter());

	//  Next year link
	navDiv.appendChild(getNextYearLink(containerDivId, year, month, day));

	//  Navigation link delimeter
	navDiv.appendChild(getNavigationLinksDelimeter());

	//  Next month link
	navDiv.appendChild(getNextMonthLink(containerDivId, year, month, day));

	return navDiv;
}

/**
 * This function generates the Previous month link for the current month being viewed
 *
 * @param containerDivId The element ID of the container in the body
 * @param year The year of the current month being viewed
 * @param month The current month being viewed
 * @param day The day of the current month being viewed
 * @return The link to view the previous month's calendar
 */
function getPreviousMonthLink(containerDivId, year, month, day)
{
	var prevMoLink = document.createElement(CalendarConstants.TAGS.ANCHOR);
	var prevMonth = CalendarConstants.CLND.MONTHS[month == 0 ? 11 : month - 1];

	prevMoLink.onclick = function(){
		document.getElementById(containerDivId).innerHTML = CalendarConstants.CHAR.EMPTY_STR;
		buildCalendar(containerDivId, new Date(new Date(year, month, day).setMonth(month - 1)));
		};
	prevMoLink.href = CalendarConstants.CHAR.HASH;
	prevMoLink.style.color = CalendarConstants.NAV_LINK_COLOR;
	prevMoLink.style.textDecoration = CalendarConstants.TEXT_DECOR_NONE;
	prevMoLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + prevMonth + CalendarConstants.CHAR.SPACE + year);

	prevMoLink.innerHTML += CalendarConstants.ENTY.LAQUO + CalendarConstants.ENTY.NBSP;

	var span = document.createElement(CalendarConstants.TAGS.SPAN);
	span.style.textDecoration = CalendarConstants.TEXT_DECOR_UNDERLN;

	span.innerHTML = prevMonth;

	prevMoLink.appendChild(span);

	return prevMoLink;
}

/**
 * This function generates the Navigation links delimeter
 *
 * @return The Navigation links delimeter
 */
function getNavigationLinksDelimeter()
{
	var delim = document.createTextNode(CalendarConstants.CHAR.SPACE 
		+ CalendarConstants.CHAR.PIPE
		+ CalendarConstants.CHAR.SPACE);

	return delim;
}

/**
 * This function generates the Previous year link for the current month being viewed
 *
 * @param containerDivId The element ID of the container in the body
 * @param year The year of the current month being viewed
 * @param month The current month being viewed
 * @param day The day of the current month being viewed
 * @return The link to view the current month's calendar in the previous year
 */
function getPreviousYearLink(containerDivId, year, month, day)
{
	var prevYrLink = document.createElement(CalendarConstants.TAGS.ANCHOR);
	var prevYear = year - 1;

	prevYrLink.onclick = function(){
		document.getElementById(containerDivId).innerHTML = CalendarConstants.CHAR.EMPTY_STR;
		buildCalendar(containerDivId, new Date(new Date(year, month, day).setYear(year - 1)));
		};
	prevYrLink.href = CalendarConstants.CHAR.HASH;
	prevYrLink.style.color = CalendarConstants.NAV_LINK_COLOR;
	prevYrLink.style.textDecoration = CalendarConstants.TEXT_DECOR_NONE;
	prevYrLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + CalendarConstants.CLND.MONTHS[month] + CalendarConstants.CHAR.SPACE + prevYear);

	prevYrLink.innerHTML = CalendarConstants.ENTY.LAQUO + CalendarConstants.ENTY.LAQUO + CalendarConstants.ENTY.NBSP;

	var span = document.createElement(CalendarConstants.TAGS.SPAN);
	span.style.textDecoration = CalendarConstants.TEXT_DECOR_UNDERLN;

	span.innerHTML += prevYear;

	prevYrLink.appendChild(span);

	return prevYrLink;
}

/**
 * This function generates the today link
 *
 * @param containerDivId The element ID of the container in the body
 * @param isButton A boolean to determine if the link should be an HTML button
 * @return The link that will display the current month's calendar
 */
function getTodayLink(containerDivId, isButton)
{
	var today = new Date();

	var todayLink = document.createElement(
		isButton ? CalendarConstants.TAGS.BUTTON
		: CalendarConstants.TAGS.ANCHOR);

	todayLink.onclick = function(){
		document.getElementById(containerDivId).innerHTML = CalendarConstants.CHAR.EMPTY_STR;
		buildCalendar(containerDivId, today);
		};

	if(isButton)
	{
		todayLink.value = CalendarConstants.CONT.TODAY;
		todayLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + CalendarConstants.CLND.MONTHS[today.getMonth()] + CalendarConstants.CHAR.SPACE + today.getFullYear());
	}
	else
	{
		todayLink.href = CalendarConstants.CHAR.HASH;

		todayLink.style.color = CalendarConstants.NAV_LINK_COLOR;
		todayLink.style.textDecoration = CalendarConstants.TEXT_DECOR_NONE;
		todayLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + CalendarConstants.CLND.MONTHS[today.getMonth()] + CalendarConstants.CHAR.SPACE + today.getFullYear());

		todayLink.innerHTML = CalendarConstants.ENTY.RAQUO + CalendarConstants.ENTY.NBSP;

		var span = document.createElement(CalendarConstants.TAGS.SPAN);
		span.style.textDecoration = CalendarConstants.TEXT_DECOR_UNDERLN;

		span.innerHTML = CalendarConstants.CONT.TODAY;

		todayLink.appendChild(span);

		todayLink.innerHTML += CalendarConstants.ENTY.NBSP + CalendarConstants.ENTY.LAQUO;
	}
	return todayLink;
}

/**
 * This function generates the Next year link for the current month being viewed
 *
 * @param containerDivId The element ID of the container in the body
 * @param year The year of the current month being viewed
 * @param month The current month being viewed
 * @param day The day of the current month being viewed
 * @return The link to view the current month's calendar in the following year
 */
function getNextYearLink(containerDivId, year, month, day)
{
	var nextYrLink = document.createElement(CalendarConstants.TAGS.ANCHOR);
	var nextYear = year + 1

	nextYrLink.onclick = function(){
		document.getElementById(containerDivId).innerHTML = CalendarConstants.CHAR.EMPTY_STR;
		buildCalendar(containerDivId, new Date(new Date(year, month, day).setYear(year + 1)));
		};
	nextYrLink.href = CalendarConstants.CHAR.HASH;
	nextYrLink.style.color = CalendarConstants.NAV_LINK_COLOR;
	nextYrLink.style.textDecoration = CalendarConstants.TEXT_DECOR_NONE;
	nextYrLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + CalendarConstants.CLND.MONTHS[month] + CalendarConstants.CHAR.SPACE + nextYear);

	var span = document.createElement(CalendarConstants.TAGS.SPAN);
	span.style.textDecoration = CalendarConstants.TEXT_DECOR_UNDERLN;

	span.innerHTML = nextYear;

	nextYrLink.appendChild(span);

	nextYrLink.innerHTML += CalendarConstants.ENTY.NBSP + CalendarConstants.ENTY.RAQUO + CalendarConstants.ENTY.RAQUO;

	return nextYrLink;
}

/**
 * This function generates the Next month link for the current month being viewed
 *
 * @param containerDivId The element ID of the container in the body
 * @param year The year of the current month being viewed
 * @param month The current month being viewed
 * @param day The day of the current month being viewed
 * @return The link to view the next month's calendar
 */
function getNextMonthLink(containerDivId, year, month, day)
{
	var nextMoLink = document.createElement(CalendarConstants.TAGS.ANCHOR);
	var nextMonth = CalendarConstants.CLND.MONTHS[month == 11 ? 0 : month + 1];

	nextMoLink.onclick = function(){
		document.getElementById(containerDivId).innerHTML = CalendarConstants.CHAR.EMPTY_STR;
		buildCalendar(containerDivId, new Date(new Date(year, month, 1).setMonth(month + 1)));
		};
	nextMoLink.href = CalendarConstants.CHAR.HASH;
	nextMoLink.style.color = CalendarConstants.NAV_LINK_COLOR;
	nextMoLink.style.textDecoration = CalendarConstants.TEXT_DECOR_NONE;
	nextMoLink.setAttribute('title',CalendarConstants.CONT.NAV_LINK_TITLE + CalendarConstants.CHAR.SPACE + nextMonth + CalendarConstants.CHAR.SPACE + year);

	var span = document.createElement(CalendarConstants.TAGS.SPAN);
	span.style.textDecoration = CalendarConstants.TEXT_DECOR_UNDERLN;

	span.innerHTML = nextMonth;

	nextMoLink.appendChild(span);

	nextMoLink.innerHTML += CalendarConstants.ENTY.NBSP + CalendarConstants.ENTY.RAQUO;

	return nextMoLink;
}

/**
 * This function gets the caption for the calendar
 * 
 * @return The caption for this calendar
 */
function getCalendarCaption()
{
	var caption = document.createElement(CalendarConstants.TAGS.DIV);
	caption.style.fontSize = CalendarConstants.CAPTION_FONT_SIZE;
	caption.style.color = CalendarConstants.CAPTION_COLOR;
	caption.style.fontStyle = CalendarConstants.FONT_STYLE_ITALIC;
	caption.appendChild(document.createTextNode(CalendarConstants.CONT.CAPTION_TEXT));

	return caption;
}

/**
 * This function calculates the day of the month based on the ID of the div element for that day
 * There was no better way to obtain the day number at interaction time
 *
 * @deprecated The need for this function was replaced by adding the date as a property of the element of elemId
 * @param elemId The id of the div element for the day
 * @return The day of the month
 */
function calculateDateFromId(elemId)
{
	return parseInt(elemId.charAt(7)) * 7 + parseInt(elemId.charAt(9)) + 1;
}

/**
 * This function calculates the fixed width of the calendar for specific browsers
 * 
 * @deprecated This is no longer needed since cell padding was removed from the calendar cells
 * @return The fixed width of the calendar for the browser being used
 */
function calculateCalendarFixedWidthForBrowser()
{
	if(document.all)
	{
		return ((CalendarConstants.CLND.NUM_DAYS_OF_WEEK * CalendarConstants.CELL_SIZE) + (2 * CalendarConstants.BORDER_WIDTH));
	}
	else
	{
		return (CalendarConstants.CLND.NUM_DAYS_OF_WEEK * (CalendarConstants.CELL_SIZE + (2 * CalendarConstants.BORDER_WIDTH)));
	}
}

/**
 * This function animates an element vertically
 *
 * @deprecated Good function to have, but too complicated to animate right now
 * @param elem The element to animate
 * @param ht The actual height of the element before animation
 * @param currHt The start height, or the current height during animation
 */
function animateLayer(elem, ht, currHt)
{
	if(currHt < ht)
	{
		var a = getPixelValue(elem.style.height);

		elem.style.height = (a + 5)+"px";

		setTimeout(function(){
			animateLayer(elem, ht, elem.clientHeight)
			;}
			,0);
	}
}

/**
 * This function parses a pixel value string and returns the number of pixels
 *
 * @param str The pixel value string, e.g. "5px"
 * @return Returns the number of pixels
 */
function getPixelValue(str)
{
	return parseInt(str ? 
		str.substring(0,str.indexOf(CalendarConstants.STYL.PX) ? str.indexOf(CalendarConstants.STYL.PX) : str)
		 : 0);
}

/**
 * This function will invoke functionality currently in development
 * It is the first onload function invoked
 */
function test()
{
	//alert('this function will invoke functionality currently in development');

	//var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

	var item = new HtCalendarItem('mtitle','3/1/2010 19:00');
	item.setTitle("test");
	CalendarEvents.events.push(item);
	//alert(item.toString());
	//alert(CalendarConstants.TAGS.SPAN);
}
/**
 * This is the HtCalendarItem object.
 * It represents an event to be displayed on the calendar.
 * 
 * @param title The title of the event
 * @param startTime The start time of the event; a valid, JS parseable date string
 * @param durationMins The length of the event in minutes; a positive integer or a string representing the same
 * @param location The location of the event
 * @param description A description of the event
 * @param titleHREF A valid URI to use as a link for the title text
 * @param locationHREF A valid URI to use as a link for the location text
 */
function HtCalendarItem(title,startTime,durationMins,location,description,titleHREF,locationHREF)
{
	var _title = title ? title : null;
	var _titleHREF = titleHREF ? titleHREF : null;
	var _startTime = startTime && !isNaN(new Date(startTime)) ? new Date(startTime) : null;
	var _durationMins = durationMins && !isNaN(durationMins) && durationMins > 0 ? durationMins : null;
	var _location = location ? location : null;
	//var _locationHREF = locationHREF && isNaN(locationHREF) ? locationHREF : null;
	var _locationHREF = locationHREF ? locationHREF : null;//alert(isNaN(locationHREF));
	var _description = description && isNaN(description) ? description : null;

	this.getTitle = function(){return _title;};
	this.setTitle = function(title){_title = title;};
	this.getTitleHREF = function(){return _titleHREF;};
	this.setTitleHREF = function(titleHREF){_titleHREF = titleHREF;};
	this.getStartTime = function(){return _startTime;};
	this.setStartTime = function(startTime){_startTime = startTime;};
	this.getDurationMins = function(){return _durationMins;};
	this.setDurationMins = function(durationMins){_durationMins = durationMins;};
	this.getLocation = function(){return _location;};
	this.setLocation = function(location){_location = location;};
	this.getLocationHREF = function(){return _locationHREF;};
	this.setLocationHREF = function(locationHREF){_locationHREF = locationHREF;};
	this.getDescription = function(){return _description;};
	this.setDescription = function(description){_description = description;};
	this.getYear = function(){return _startTime.getFullYear();};
	this.getMonth = function(){return _startTime.getMonth();};
	this.getDayOfMonth = function(){return _startTime.getDate();};
	this.getTimeString = function(){return _startTime.toLocaleTimeString();};
	this.toString = function()
	{
		return 'title'+ '\t: ' + this.getTitle() + '\n' + 
			'startTime' + '\t: ' + this.getStartTime() + '\n' + 
			'durMins' + '\t: ' + this.getDurationMins() + '\n' + 
			'location' + '\t: ' + this.getLocation() + '\n' + 
			'descr' + '\t: ' + this.getDescription() + '\n' + 
			'titleHREF' + '\t: ' + this.getTitleHREF() + '\n' + 
			'locHREF' + '\t: ' + this.getLocationHREF() + '\n' + 
			'';
	};
}

/**
 * This is the CalendarConstants object
 * It contains all of the "constants" for this JavaScript code
 */
function CalendarConstants()
{
	//  Constant grouping container objects
	this.CHAR = new Object();  //  Character constants
	this.CLND = new Object();  //  Calendar constants
	this.CONT = new Object();  //  Page content and other text constants
	this.EIDS = new Object();  //  Element ID constants
	this.ENTY = new Object();  //  HTML Entity constants
	this.STYL = new Object();  //  Formatting/Styling constants
	this.TAGS = new Object();  //  HTML/DOM Tag constants

	//  Character constants
	this.CHAR.COMMA = ",";
	this.CHAR.EMPTY_STR = "";
	this.CHAR.HASH = "#";
	this.CHAR.HYPHEN = "-";
	this.CHAR.MINS_PAD = "0";
	this.CHAR.PIPE = "|";
	this.CHAR.SPACE = " ";
	this.CHAR.TIME_DELIM = ":";
	this.CHAR.USCORE = "_";

	//  Calendar constants
	this.CLND.DAYS_OF_WEEK = new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
	this.CLND.MONTHS = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
	this.CLND.NUM_DAYS_OF_WEEK = 7;

	//  Page content and other text constants
	this.CONT.CAPTION_TEXT = "Place your mouse over a day to see its events. If using a mobile device, click the day instead.";
	this.CONT.CLOSE_LINK = "Close";
	this.CONT.CLOSE_LINK_TITLE = "Back to calendar";
	this.CONT.EVENT = "event";
	this.CONT.FLYOUT_HEADER_TEXT = "Hilltop League Events";
	this.CONT.LOCATION_HREF_TITLE = "Map and Directions to";
	this.CONT.NEXT_MONTH = "Next month";
	this.CONT.NAV_LINK_TITLE = "See the calendar for";
	this.CONT.NEXT_YEAR = "Next year";
	this.CONT.PREV_MONTH = "Previous month";
	this.CONT.PREV_YEAR = "Previous year";
	this.CONT.PLURAL = "s";
	this.CONT.PRINT_LINK = "Print";
	this.CONT.PRINT_LINK_TITLE = "Print today's events";
	this.CONT.TIME_AM = "A.M.";
	this.CONT.TIME_PM = "P.M.";
	this.CONT.TITLE_HREF_TITLE = "Get more information about";
	this.CONT.TODAY = "Today";

	//  Element ID constants
	this.EIDS.CAL_DIV = "cal";
	this.EIDS.DAY_DIV_PREFIX = "dayDiv_";
	this.EIDS.DAY_HEAD_DIV_PREFIX = "day_head_";
	this.EIDS.DAYS_HEAD_DIV = "days_header_div";
	this.EIDS.EVENT_WRAPPER_PREFIX = "event_wrapper_";
	this.EIDS.EVENTS_LAYER = "eventsLayer";
	this.EIDS.WEEK_DIV_PREFIX = "weekDiv_";

	//  HTML Entity constants
	this.ENTY.LAQUO = "&laquo;";
	this.ENTY.MDASH = "&mdash;";
	this.ENTY.NBSP = "&nbsp;";
	this.ENTY.RAQUO = "&raquo;";
	this.ENTY.MIDDOT = "&middot;";

	//  Formatting/Styling constants
	this.STYL.PX = "px";
	//  TODO - FINISH CATEGORIZING THESE CONSTANTS
	this.BORDER_COLOR = "#9f0000";
	this.BORDER_STYLE = "solid";
	this.BORDER_WIDTH = 1;
	this.FONT_SIZE = "11pt";
	this.FONT_FAMILY = "Tahoma";
	this.MONTH_FONT_SIZE = "14pt";
	this.MONTH_FONT_FAMILY = "Tahoma";
	this.FONT_WT_BOLD = "bold";
	this.FONT_STYLE_ITALIC = "italic";
	this.CAPTION_FONT_SIZE = "10pt";
	this.CAPTION_COLOR = "#666666";
	this.MARGIN_LEFT = "auto";
	this.MARGIN_RIGHT = "auto";
	this.FLOAT_LEFT = "left";
	this.FLOAT_CLEAR = "both";
	this.OVERFLOW_HIDDEN = "hidden";
	this.CELL_PADDING = 5;
	this.DATE_ALIGN = "right";
	this.EVENT_ALIGN = "left";
	this.TEXT_DECOR_NONE = "none";
	this.TEXT_DECOR_UNDERLN = "underline";
	this.DIV_DISPLAY = "block";
	this.DIV_HIDE = "none";
	this.EV_DIVIDER_STYLE = "dashed";
	this.EV_LAYER_POS_ABS = "absolute";
	this.TEXT_ALIGN_CENTER = "center";
	this.TEXT_ALIGN_RIGHT = "right";
	this.CELL_SIZE = 80;
	this.EMPTY_COLOR = "#cccccc";
	this.NAV_LINK_COLOR = "#9f0000";
	this.HAS_EVENTS_COLOR = "#99ff66";
	this.TODAY_COLOR = "#ffff66";
	this.EVENTS_BGCOLOR = "#ffffff";

	//  HTML/DOM Tag constants
	this.TAGS.DIV = 'div';
	this.TAGS.SPAN = 'span';
	this.TAGS.ANCHOR = 'a';
	this.TAGS.BUTTON = 'button';
	this.TAGS.HEADER_THREE = 'h3';
}
//  Globally instantiate the CalendarConstants object
var CalendarConstants = new CalendarConstants();