CURRENT PROJECTS
loading
CATEGORIES AND POSTS
loading
overset
DEVELOPMENT LOG FOR JIM PALMER
Posted 07/10/2007 in dhtml


I've seen my share of Dynamic HTML Date Pickers, Javascript Calendar controls, and the like. Each of which have a more horrifically ugly interface and are bloated with features. I found a need to create a simple calender piece so that a user could add a product to their cart when clicking on a specific date.

The example below shows two instantiated calendar's each automatically showing the current month as well as blocking out the days before the current day in the previous month and blocking out days after the current day in the next month.

Update 10/31/2007 - v0.4 Update 9/24/2007 - v0.3 Update 9/21/2007 - v0.2 The javascript Calendar "class" I came up with offer these features: This was minimally tested on IE 6+, FF, and Safari 2.X.

The source for instantiating the test cases of the Calendar Javascript "class":
<html>
	<head>
		<script language="JavaScript" type="text/javascript" src="calendar.js"></script>
		<script>
			var __calendars = new Array();
			__calendars.push(
				new Calendar(
					0,
					'_calendar0',
					new Date( (new Date()).getMonth() + '/' + (new Date()).getDate() + '/' + (new Date()).getFullYear() ),
					new Date( ((new Date()).getMonth() + 2) + '/' + (new Date()).getDate() + '/' + (new Date()).getFullYear() )
				)
			);
			__calendars.push(
				new Calendar(
					1,
					'_calendar1',
					new Date( (new Date()).getMonth() + '/' + (new Date()).getDate() + '/' + (new Date()).getFullYear() ),
					new Date( ((new Date()).getMonth() + 2) + '/' + (new Date()).getDate() + '/' + (new Date()).getFullYear() )
				)
			);
			function initCal() {
				// draw the current month's calendar
				document.getElementById('calContainer1').innerHTML = __calendars[0].drawCal(0);
				document.getElementById('calContainer2').innerHTML = __calendars[1].drawCal(0);
			}
		</script>
	</head>
	<body style="background:#ff9900;" onLoad="initCal();" topmargin=0 bottommargin=0 leftmargin=0 rightmargin=0 marginheight=0 marginwidth=0>
		<span id="calContainer1">Loading...</span><span id="calContainer2">Loading...</span>
	</body>
</html>

The source for the Calendar Javascript "class":
var __monthslong = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var __monthsshort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

function Calendar (arrayID, uniqueName, dateFrom, dateTo) {
	this.currDate = new Date();
	this.uniqueName = uniqueName;
	this.arrayID = arrayID;
	this.products = new Array();
	this.selectedDays = new Array();
	this.prodDesc = '';
	this.dateFrom = dateFrom;
	this.dateTo = dateTo;
	this.startTally = new Array();
	this.maxDays = 10;
}

Calendar.prototype.drawCal = function (nudgeMonth, alreadyLoaded) {

	this.currDate.setMonth((this.currDate.getMonth() + nudgeMonth));
	var tmpNextMo = new Date();
	tmpNextMo.setDate(1);
	tmpNextMo.setFullYear(this.currDate.getFullYear());
	tmpNextMo.setMonth(parseInt(this.currDate.getMonth()) + 1);

/* flawed next month display above calendar - 
 * if next month has less days than first month, 
 * it'll skip the correct month */
//	var tmpNextMo = new Date(this.currDate.getFullYear(), (this.currDate.getMonth() + 1), this.currDate.getDate());

	var _currDate = new Date();
	_currDate.setFullYear(this.currDate.getFullYear());
	_currDate.setMonth(this.currDate.getMonth());
	
	var calStr = 
		'<table cellpadding=0 cellspacing=0 border=0>' +
			'<tr>' +
				'<td style="background:url(ul.gif) no-repeat; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>' +
				'<td style="background:url(ur.gif) right top no-repeat; height:6px; width:6px;"><img width="6" height="6" border=0 src="_blank.gif"></td>' +
			'</tr>' +
			'<tr>' +
				'<td style="background:url(ml.gif) left top repeat-y; text-align:center; font-family:Tahoma; font-size:8pt; color:#808080; overflow:auto; padding-left:6px;" id="__calMessage">' +
					'<table cellpadding=0 cellspacing=0 border=0>' +
						'<tr>' +
							'<td width="100%" colspan=2 align=middle style="padding-bottom:5px;">' +
								'<table cellpadding=0 cellspacing=0 border=0 width="100%">' +
									'<tr>' +
										'<td align=left style="font-size:8pt; color:#000000; text-align:left; cursor:pointer;" ' +
											'onClick="__calendars[' + this.arrayID + '].drawCal((-1), true);">' +
											'<img border=0 src="_left.gif">' +
										'</td>' +
										'<td width="50%" align=left style="padding:0px 16px 0px 0px; font-size:8pt; color:#000000; text-align:center; cursor:default;">' +
											'<nobr>' + __monthslong[this.currDate.getMonth()] + ' ' + this.currDate.getFullYear() + '</nobr>' + 
										'</td>' +
										'<td width="50%" align=right style="padding:0px 0px 0px 16px; font-size:8pt; color:#000000; text-align:center; cursor:default;">' +
											'<nobr>' + __monthslong[tmpNextMo.getMonth()] + ' ' + tmpNextMo.getFullYear() + '</nobr>' +
										'</td>' +
										'<td align=right style="font-size:8pt; color:#000000; text-align:right; cursor:pointer;" ' +
											'onClick="__calendars[' + this.arrayID + '].drawCal(1, true);">' +
											'<img border=0 src="_right.gif">' +
										'</td>' +
									'</tr>' +
								'</table>' +
							'</td>' +
						'</tr>' +
						'<tr>' +
							'<td valign=top>' +
								'<table cellpadding=0 cellspacing=0 border=0>' +
									'<tr>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">M</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">W</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">F</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>' +
									'</tr>' +
									'<tr>';

	// set to the first day
	this.currDate.setDate(1);

	var pMonth = new Date();
	pMonth.setFullYear(this.currDate.getFullYear());
	pMonth.setMonth(this.currDate.getMonth());
	pMonth.setDate((this.currDate.getDate() - 1));
	
	// get current month days count
	var dCount = new Date();
	dCount.setDate(1);
	dCount.setFullYear(this.currDate.getFullYear());
	dCount.setMonth((this.currDate.getMonth() + 1));
	dCount.setDate(dCount.getDate() - 1);
/* FLAWED
	var dCount = new Date();
	dCount.setFullYear(this.currDate.getFullYear());
	dCount.setMonth((this.currDate.getMonth() + 1));
	dCount.setDate((this.currDate.getDate() - 1));
*/
	var dayCounter = 0;

	// perform the previous months last days
	for (var pMo=1; pMo <= this.currDate.getDay(); pMo++) {
		calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;">' + 
								(pMonth.getDate() - this.currDate.getDay() + pMo) + 
							'</td>';
		dayCounter++;
	}

	// now display all days in current month
	for (var cMD=0; cMD < dCount.getDate(); cMD++) {
			
			var validFrom = true;
			// find if this is a valid day or not
			if (typeof(this.dateFrom) != 'undefined') {
				if (this.dateFrom.getTime() > (new Date((this.currDate.getMonth() + 1) + '/' + (this.currDate.getDate() + cMD) + '/' + this.currDate.getFullYear())).getTime()) {
					validFrom = false;
				}
			}
			var validTo = true;
			// find if this is a valid day or not
			if (typeof(this.dateTo) != 'undefined') {
				if (this.dateTo.getTime() < (new Date((this.currDate.getMonth() + 1) + '/' + (this.currDate.getDate() + cMD) + '/' + this.currDate.getFullYear())).getTime()) {
					validTo = false;
				}
			}
			var dayInList = false;
			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == this.currDate.getFullYear() && 
					this.selectedDays[dInd][1] == this.currDate.getMonth() && 
					this.selectedDays[dInd][2] == (this.currDate.getDate() + cMD)) {
					dayInList = true;
				}
			}
			
			if (validFrom == false || validTo == false) {
				calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; background:#EFEFEF; font-size:8pt; color:#808080; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;';
			} else {
				calStr +=
							'<td id="__cal' + this.arrayID + '__' + this.currDate.getFullYear() + '-' + this.currDate.getMonth() + '-' + (this.currDate.getDate() + cMD) + '" ' +
								'align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:pointer;';
				if (dayInList) {
					calStr += ' background:#006699; color:#FFFFFF;';
				} else {
					calStr += ' background:#FFFFFF; color:#808080;';
				}
			}

			if (validFrom == false || validTo == false) {
				calStr += ' text-decoration:line-through;';
			}

			if (validFrom == true && validTo == true) {
				calStr += '" ' +
								'onMouseOver="__calendars[' + this.arrayID + '].toggleDayHilight([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, true);" ';
			}

			if (validFrom == true && validTo == true) {
//								'onClick="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' + 
				calStr += '" ' +
								'onMouseOut="__calendars[' + this.arrayID + '].toggleDayHilight([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, false);" ' +
								'onMouseDown="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'down\');" ' + 
								'onMouseUp="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' + 
								(this.currDate.getDate() + cMD) + 
							'</td>';
			} else {
				calStr += '">' + (this.currDate.getDate() + cMD) + '</td>';
			}
		
		dayCounter++;
		
		if ((dayCounter % 7) == 0) {
			calStr +=
						'</tr>';
			if (cMD != dCount.getDate()) {
						'<tr>';
			}
		}
	}

	// display the tail end of the month
	for (var eMo=1; eMo <= (6 - dCount.getDay()); eMo++) {
		calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #FFFFFF; text-align:center; cursor:default;">' + 
								eMo + 
							'</td>';
		dayCounter++;
	}
	
	calStr +=
								'</table>' +
							'</td>' +
							'<td valign=top style="padding-left:5px;">' +
								'<table cellpadding=0 cellspacing=0 border=0>' +
									'<tr>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">M</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">W</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">T</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">F</td>' +
										'<td align=middle style="background:#EEEEEE url(lightgray-gradient-ee.gif) bottom repeat-x; padding:1px; font-family:Tahoma; font-size:7pt; color:#000000; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; border-top:1px solid #EEEEEE; text-align:center; cursor:default;">S</td>' +
									'</tr>' +
									'<tr>';

	// set to the first day
	this.currDate.setMonth((this.currDate.getMonth() + 1));
	this.currDate.setDate(1);

	var pMonth = new Date();
	pMonth.setFullYear(this.currDate.getFullYear());
	pMonth.setMonth(this.currDate.getMonth());
	pMonth.setDate((this.currDate.getDate() - 1));
	
	// get current month days count
	var dCount = new Date();
	dCount.setDate(1);
	dCount.setFullYear(this.currDate.getFullYear());
	dCount.setMonth((this.currDate.getMonth() + 1));
	dCount.setDate(dCount.getDate() - 1);
/* FLAWED
	var dCount = new Date();
	dCount.setFullYear(this.currDate.getFullYear());
	dCount.setMonth((this.currDate.getMonth() + 1));
	dCount.setDate((this.currDate.getDate() - 1));
*/
	var dayCounter = 0;

	// perform the previous months last days
	for (var pMo=1; pMo <= this.currDate.getDay(); pMo++) {
		calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;">' + 
								(pMonth.getDate() - this.currDate.getDay() + pMo) + 
							'</td>';
		dayCounter++;
	}

	// now display all days in current month
	for (var cMD=0; cMD < dCount.getDate(); cMD++) {
			
			var validFrom = true;
			// find if this is a valid day or not
			if (typeof(this.dateFrom) != 'undefined') {
				if (this.dateFrom.getTime() > (new Date((this.currDate.getMonth() + 1) + '/' + (this.currDate.getDate() + cMD) + '/' + this.currDate.getFullYear())).getTime()) {
					validFrom = false;
				}
			}
			var validTo = true;
			// find if this is a valid day or not
			if (typeof(this.dateTo) != 'undefined') {
				if (this.dateTo.getTime() < (new Date((this.currDate.getMonth() + 1) + '/' + (this.currDate.getDate() + cMD) + '/' + this.currDate.getFullYear())).getTime()) {
					validTo = false;
				}
			}
			var dayInList = false;
			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == this.currDate.getFullYear() && 
					this.selectedDays[dInd][1] == this.currDate.getMonth() && 
					this.selectedDays[dInd][2] == (this.currDate.getDate() + cMD)) {
					dayInList = true;
				}
			}
			
			if (validFrom == false || validTo == false) {
				calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; background:#EFEFEF; font-size:8pt; color:#808080; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:default;';
			} else {
				calStr +=
							'<td id="__cal' + this.arrayID + '__' + this.currDate.getFullYear() + '-' + this.currDate.getMonth() + '-' + (this.currDate.getDate() + cMD) + '" ' +
								'align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #EEEEEE; text-align:center; cursor:pointer;';
				if (dayInList) {
					calStr += ' background:#006699; color:#FFFFFF;';
				} else {
					calStr += ' background:#FFFFFF; color:#808080;';
				}
			}

			if (validFrom == false || validTo == false) {
				calStr += ' text-decoration:line-through;';
			}

			if (validFrom == true && validTo == true) {
				calStr += '" ' +
								'onMouseOver="__calendars[' + this.arrayID + '].toggleDayHilight([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, true);" ';
			}

			if (validFrom == true && validTo == true) {
//								'onClick="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' + 
				calStr += '" ' +
								'onMouseOut="__calendars[' + this.arrayID + '].toggleDayHilight([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, false);" ' +
								'onMouseDown="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'down\');" ' + 
								'onMouseUp="__calendars[' + this.arrayID + '].toggleDay([' + this.currDate.getFullYear() + ',' + this.currDate.getMonth() + ',' + (this.currDate.getDate() + cMD) + '], this, \'up\');">' + 
								(this.currDate.getDate() + cMD) + 
							'</td>';
			} else {
				calStr += '">' + (this.currDate.getDate() + cMD) + '</td>';
			}
		
		dayCounter++;
		
		if ((dayCounter % 7) == 0) {
			calStr +=
						'</tr>';
			if (cMD != dCount.getDate()) {
						'<tr>';
			}
		}
	}

	// display the tail end of the month
	for (var eMo=1; eMo <= (6 - dCount.getDay()); eMo++) {
		calStr +=
							'<td align=middle style="padding:2px; font-family:Tahoma; font-size:8pt; color:#AAAAAA; border-bottom:1px solid #CCCCCC; border-right:1px solid #CCCCCC; border-left:1px solid #FFFFFF; text-align:center; cursor:default;">' + 
								eMo + 
							'</td>';
		dayCounter++;
	}
	
	calStr +=
								'</table>' +
							'</td>' +
						'</tr>' +
						'<tr>' +
							'<td colspan=2 id="__cal' + this.arrayID + '__message" align="left" style="font-family:Tahoma; font-size:7pt; padding:2px;">' +
								'Select a date from the calendar above' +
							'</td>' +
						'</tr>' +
					'</table>' +
				'</td>' +
				'<td style="background:url(mr.gif) right top repeat-y; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>' +
			'</tr>' +
			'<tr>' +
				'<td style="background:url(bl.gif) no-repeat; height:6px;"><img width="1" height="6" border=0 src="_blank.gif"></td>' +
				'<td style="background:url(br.gif) right top no-repeat; height:6px; width:6px;"><img width="6" height="6" border=0 src="_blank.gif"></td>' +
			'</tr>' +
		'</table>';
		
	this.currDate.setMonth((this.currDate.getMonth() - 1));

	if (typeof(alreadyLoaded) != 'undefined' && alreadyLoaded == true) {
		document.getElementById(this.uniqueName).innerHTML = calStr;
	} else {
		return '<div id="' + this.uniqueName + '">' + calStr + '</div>';
	}
	
}

Calendar.prototype.toggleDay = function (day, divBlock, buttonEvent) {

	var dayInList = (-1);

	for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
		if (this.selectedDays[dInd][0] == day[0] && 
			this.selectedDays[dInd][1] == day[1] && 
			this.selectedDays[dInd][2] == day[2]) {
			dayInList = dInd;
		}
	}

	if (dayInList == (-1) && this.startTally.length == 0 && buttonEvent == 'down') {
	
		divBlock.style.background='#006699';
		divBlock.style.color='#FFFFFF';
		this.selectedDays.push(day);
		this.startTally = day;
		this.toggleDayHilight(day, divBlock, true);
		
		this.selectedDays.sort(function(a, b) { return (new Date(a[0], a[1], a[2])).getTime() - (new Date(b[0], b[1], b[2])).getTime() })
		
	} else if (this.startTally.length > 0 && buttonEvent == 'up') {

		var countDays = 1;
		var tallyDest = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
		var tallyNew = new Date(this.startTally[0], (parseInt(this.startTally[1]) + 0), this.startTally[2]);
		var tallyPivot = new Date(this.startTally[0], (parseInt(this.startTally[1]) + 0), this.startTally[2]);

		while (tallyNew.getFullYear() != tallyDest.getFullYear() ||
			tallyNew.getMonth() != tallyDest.getMonth() ||
			tallyNew.getDate() != tallyDest.getDate()) {

			countDays++;

			if (tallyNew.getTime() < tallyDest.getTime()) {
				tallyNew.setDate(tallyNew.getDate() + 1);
			} else {
				tallyNew.setDate(tallyNew.getDate() - 1);
			}

			var newDayInList = false;
			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == tallyNew.getFullYear() && 
					this.selectedDays[dInd][1] == tallyNew.getMonth() && 
					this.selectedDays[dInd][2] == tallyNew.getDate()) {
					newDayInList = true;
				}
			}

			var newDivBlock = document.getElementById('__cal' + this.arrayID + '__' + tallyNew.getFullYear() + '-' + tallyNew.getMonth() + '-' + tallyNew.getDate());

			if (!newDayInList && countDays <= this.maxDays && newDivBlock) {
				this.selectedDays.push([tallyNew.getFullYear(), tallyNew.getMonth(), tallyNew.getDate()]);

				this.selectedDays.sort(function(a, b) { return (new Date(a[0], a[1], a[2])).getTime() - (new Date(b[0], b[1], b[2])).getTime() })

				newDivBlock.style.background='#006699';
				newDivBlock.style.color='#FFFFFF';
			} else if (!newDayInList && newDivBlock) {
				newDivBlock.style.background='#FFFFFF';
				newDivBlock.style.color='#808080';
			}
		}

		this.startTally = new Array();

	} else if (buttonEvent == 'up') {

		// if first day - remove whole block...
		var tallyNew = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
		var tallyPrev = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
		tallyPrev.setDate(tallyPrev.getDate() - 1);
		var prevDayInList = false;
		var remDayInList = true;
		var remDayInListInd = (-1);

		for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
			if (this.selectedDays[dInd][0] == tallyPrev.getFullYear() && 
				this.selectedDays[dInd][1] == tallyPrev.getMonth() && 
				this.selectedDays[dInd][2] == tallyPrev.getDate()) {
				prevDayInList = true;
			}
		}

		if (!prevDayInList) {
			while (remDayInList) {

				tallyNew.setDate(tallyNew.getDate() + 1);

				var newDivBlock = document.getElementById('__cal' + this.arrayID + '__' + tallyNew.getFullYear() + '-' + tallyNew.getMonth() + '-' + tallyNew.getDate());

				remDayInList = false;
				for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++) {
					if (this.selectedDays[rdInd][0] == tallyNew.getFullYear() && 
						this.selectedDays[rdInd][1] == tallyNew.getMonth() && 
						this.selectedDays[rdInd][2] == tallyNew.getDate()) {
						remDayInListInd = rdInd;
						remDayInList = true;
					}
				}

				if (remDayInList) {
					this.selectedDays.splice(remDayInListInd, 1);
					
					this.selectedDays.sort(function(a, b) { return (new Date(a[0], a[1], a[2])).getTime() - (new Date(b[0], b[1], b[2])).getTime() })
					
					this.toggleDayHilight([tallyNew.getFullYear(), tallyNew.getMonth(), tallyNew.getDate()], newDivBlock, false);
				}

			}
		}

		if (dayInList != (-1)) {
			this.selectedDays.splice(dayInList, 1);

			this.selectedDays.sort(function(a, b) { return (new Date(a[0], a[1], a[2])).getTime() - (new Date(b[0], b[1], b[2])).getTime() })

			this.toggleDayHilight(day, divBlock, false);
		}

		this.startTally = new Array();
		
	}

}

Calendar.prototype.toggleDayHilight = function (day, divBlock, toggle) {
	// clear selection
	(document.selection) ? document.selection.empty() : window.getSelection().removeAllRanges();
	
	var userMsg = ' ';

	var dayInList = false;
	for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
		if (this.selectedDays[dInd][0] == day[0] && 
			this.selectedDays[dInd][1] == day[1] && 
			this.selectedDays[dInd][2] == day[2]) {
			dayInList = true;
		}
	}

	if (!dayInList && toggle) {
		if (this.startTally.length > 0) {

			// overflow highlighting 
			divBlock.style.background = '#E3E3E3';
			divBlock.style.color = '#FFFFFF';

		} else {
			divBlock.style.background = '#008FD6';
			divBlock.style.color = '#FFFFFF';
			userMsg = 'Add ' + __monthsshort[day[1]] + ' ' + day[2] + ', ' + day[0];
		}
	} else if (dayInList && this.startTally.length > 0) {
		userMsg = 'Click Again to add ' + __monthsshort[day[1]] + ' ' + day[2] + ', ' + day[0];
	} else if (dayInList && this.startTally.length == 0) {

		if (toggle) {

			divBlock.style.background = 'red';
			divBlock.style.color = '#FFFFFF';
			
			// if first day - remove whole block...
			var tallyNew = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
			var tallyPrev = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
			tallyPrev.setDate(tallyPrev.getDate() - 1);
			var prevDayInList = false;
			var remDayInList = true;
			var countDays = 1;

			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == tallyPrev.getFullYear() && 
					this.selectedDays[dInd][1] == tallyPrev.getMonth() && 
					this.selectedDays[dInd][2] == tallyPrev.getDate()) {
					prevDayInList = true;
				}
			}

			if (!prevDayInList) {
				while (remDayInList) {

					tallyNew.setDate(tallyNew.getDate() + 1);

					var newDivBlock = document.getElementById('__cal' + this.arrayID + '__' + tallyNew.getFullYear() + '-' + tallyNew.getMonth() + '-' + tallyNew.getDate());

					remDayInList = false;
					for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++) {
						if (this.selectedDays[rdInd][0] == tallyNew.getFullYear() && 
							this.selectedDays[rdInd][1] == tallyNew.getMonth() && 
							this.selectedDays[rdInd][2] == tallyNew.getDate()) {
							remDayInList = true;
						}
					}
					
					if (remDayInList) {
						newDivBlock.style.background = 'red';
						newDivBlock.style.color = '#FFFFFF';
						countDays++;
					}
				
				}
			}

			if (countDays > 1) {
				// nudge the tally check date back one since the iterrator will be on the day that failed to be in the cart
				tallyNew.setDate(tallyNew.getDate() - 1);
				var startDate = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
				userMsg = 'Click to remove ' + countDays + ' days from ' + __monthsshort[startDate.getMonth()] + ' ' + startDate.getDate() + ', ' + startDate.getFullYear() +
					' to ' + __monthsshort[tallyNew.getMonth()] + ' ' + tallyNew.getDate() + ', ' + tallyNew.getFullYear();
			} else {
				userMsg = 'Click to remove ' + __monthsshort[day[1]] + ' ' + day[2] + ', ' + day[0];
			}
		
		} else {
		
			divBlock.style.background = '#006699';
			divBlock.style.color = '#FFFFFF';

			// clear the sequence that was queued for deletion
			var tallyNew = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
			var tallyPrev = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
			tallyPrev.setDate(tallyPrev.getDate() - 1);
			var prevDayInList = false;
			var remDayInList = true;

			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == tallyPrev.getFullYear() && 
					this.selectedDays[dInd][1] == tallyPrev.getMonth() && 
					this.selectedDays[dInd][2] == tallyPrev.getDate()) {
					prevDayInList = true;
				}
			}

			if (!prevDayInList) {
				while (remDayInList) {

					tallyNew.setDate(tallyNew.getDate() + 1);

					var newDivBlock = document.getElementById('__cal' + this.arrayID + '__' + tallyNew.getFullYear() + '-' + tallyNew.getMonth() + '-' + tallyNew.getDate());

					remDayInList = false;
					for (var rdInd=0; rdInd < this.selectedDays.length; rdInd++) {
						if (this.selectedDays[rdInd][0] == tallyNew.getFullYear() && 
							this.selectedDays[rdInd][1] == tallyNew.getMonth() && 
							this.selectedDays[rdInd][2] == tallyNew.getDate()) {
							remDayInList = true;
						}
					}

					if (remDayInList) {
						newDivBlock.style.background = '#006699';
						newDivBlock.style.color = '#FFFFFF';
					}

				}
			}

			
		}

	} else if (!dayInList && !toggle) {
		divBlock.style.background = '#FFFFFF';
		divBlock.style.color = '#808080';
	}
	
	var countDays = 1;
	var maxWarning = false;
	if (this.startTally.length > 0) {

		var tallyDest = new Date(day[0], (parseInt(day[1]) + 0), day[2]);
		var tallyNew = new Date(this.startTally[0], (parseInt(this.startTally[1]) + 0), this.startTally[2]);
		var tallyPivot = new Date(this.startTally[0], (parseInt(this.startTally[1]) + 0), this.startTally[2]);

		while (tallyNew.getFullYear() != tallyDest.getFullYear() ||
			tallyNew.getMonth() != tallyDest.getMonth() ||
			tallyNew.getDate() != tallyDest.getDate()) {
			
			countDays++;

			if (tallyNew.getTime() < tallyDest.getTime()) {
				tallyNew.setDate(tallyNew.getDate() + 1);
			} else {
				tallyNew.setDate(tallyNew.getDate() - 1);
			}

			var newDivBlock = document.getElementById('__cal' + this.arrayID + '__' + tallyNew.getFullYear() + '-' + tallyNew.getMonth() + '-' + tallyNew.getDate());
				
			dayInList = false;
			for (var dInd=0; dInd < this.selectedDays.length; dInd++) {
				if (this.selectedDays[dInd][0] == tallyNew.getFullYear() && 
					this.selectedDays[dInd][1] == tallyNew.getMonth() && 
					this.selectedDays[dInd][2] == tallyNew.getDate()) {
					dayInList = true;
				}
			}

			if (countDays <= this.maxDays) {
				if (newDivBlock) {
					if (!dayInList && toggle) {
						newDivBlock.style.background='#008FD6';
						newDivBlock.style.color='#FFFFFF';
					} else if (!dayInList && !toggle) {
						newDivBlock.style.background='#FFFFFF';
						newDivBlock.style.color='#808080';
					}
				}

				if ((new Date(day[0], (parseInt(day[1]) + 0), day[2])).getTime() < tallyPivot.getTime()) {
					userMsg = 'Click to add ' + countDays + ' days from ' + __monthsshort[tallyNew.getMonth()] + ' ' + tallyNew.getDate() + ', ' + tallyNew.getFullYear() + ' to ' + __monthsshort[tallyPivot.getMonth()] + ' ' + tallyPivot.getDate() + ', ' + tallyPivot.getFullYear();
				} else {
					userMsg = 'Click to add ' + countDays + ' days from ' + __monthsshort[tallyPivot.getMonth()] + ' ' + tallyPivot.getDate() + ', ' + tallyPivot.getFullYear() + ' to ' + __monthsshort[tallyNew.getMonth()] + ' ' + tallyNew.getDate() + ', ' + tallyNew.getFullYear();
				}

			} else if (newDivBlock) {
				if (!dayInList && toggle) {
				
					// overflow highlighting 
					newDivBlock.style.background='#EEEEEE';
					newDivBlock.style.color='#808080';

					if (!maxWarning) {
						userMsg += '<BR>Cannot choose more than ' + this.maxDays + ' days.'
						maxWarning = true;
					}
				} else if (!dayInList && !toggle) {
					newDivBlock.style.background='#FFFFFF';
					newDivBlock.style.color='#808080';
				}
			}

		}
			
	}
	document.getElementById('__cal' + this.arrayID + '__message').innerHTML = userMsg;
}

Here are the images used in the source (SHIFT+CLICK to download):
comments
loading
new comment
NAME
EMAIL ME ON UPDATES
EMAIL (hidden)
URL
MESSAGE TAGS ALLOWED: <code> <a> <pre class="code [tab4|tabX|inline|bash]"> <br>
PREVIEW COMMENT
TURING TEST
gravatar