Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

A Small Calendar Utility For Reference

By Ben Nadel on

I created a rather small calendar utility for use during project management meetings. We usually brain storm a project and use the Windows date/time calendar (in the icon tray) to think out our time estimates. The problem is that calendar sucks and only has one month at a time (and moving from month to month is a pain). My small calendar utility, written in ColdFusion with some Javascript, displays a year's worth of calendars and allows the user to click and highlight a single time span.

Feel free to use it if you like:

www.bennadel.com/util/calendar

If anyone is interested in the code that goes behind this, it's fairly small.

Action Script

<cfscript>

	// Get the current month.
	REQUEST.ThisMonth = CreateDate(
		Year( REQUEST.Environment.DateTime.Now ),
		Month( REQUEST.Environment.DateTime.Now ),
		1
		);


	// Create an array of months.
	REQUEST.Months = ArrayNew( 1 );

	// Add a date for each month to the array. Right now, we are going
	// to be using one month previous to this one, plus 11 months going
	// forward. This should give us just over a year going forward.
	for (intI = -1 ; intI LTE 12 ; intI = (intI + 1)){

		ArrayAppend(
			REQUEST.Months,
			DateAdd( "m", intI, REQUEST.ThisMonth )
			);

	}


</cfscript>

Java Script Code

<script type="text/javascript">

	// These are the global variables neede to keep track of
	// the calendar and to provide faster lookup via indexing.
	var objStartDay = null;
	var objEndDay = null;
	var arrDays = new Array();
	var objToday = null;


	// This loads the global properties based on the calendar.
	function InitCalendars(){
		var objHolder = document.getElementById( "calendars" );
		var arrTBody = objHolder.getElementsByTagName( "tbody" );
		var arrTD = null;
		var intI, intJ;

		// Loop over tbodys and get their tds.
		for (intI = 0 ; intI < arrTBody.length ; intI++){

			// Get all the TDs.
			arrTD = arrTBody[ intI ].getElementsByTagName( "td" );

			// Loop over all the TDs to init.
			for (intJ = 0 ; intJ < arrTD.length ; intJ ++){

				InitTD( arrTD[ intJ ] );

			}

		}

	}


	// This initialized each TD within the calendar days.
	function InitTD( objTD ){
		// Set the TD collection index.
		objTD.dayIndex = arrDays.length;

		// Add this TD to the collection.
		arrDays[ arrDays.length ] = objTD;

		// Store the original class name.
		objTD.baseClassName = objTD.className;

		// Check to see if this is today.
		if (objTD.className == "today"){
			objToday = objTD;
		}

		// Set onclick handler.
		objTD.onclick = function(){
			SetDay( this );
		}
	}


	// This is the onClick handler for each calendar day. It is
	// used to set the start and end days of the selected day span,
	// then it renders the calendar.
	function SetDay( objTD ){

		// We can only set a day that is NOT an other month. If this
		// an other day, just return out.
		if (objTD.className == "othermonth"){
			return;
		}

		// Check to see if we if this is the day one.
		if (objTD == objStartDay){

			// Set the start day to be the end day.
			objStartDay = objEndDay;

			// Turn off end day.
			objEndDay = null;

		} else if (objTD == objEndDay){

			// Turn off the end day.
			objEndDay = null;

		} else {

			// We are turning ON a day. Check to see if there is an
			// existing start and end day.
			if (objStartDay && objEndDay){

				// Both days already exist. We need to alter the
				// way the selection exists. Check to see the
				// indexes.
				if (objTD.dayIndex < objStartDay.dayIndex){

					// Expanding back.
					objStartDay = objTD;

				} else {

					// We are going beyong the end day OR we are
					// going somewhere in the middle of the
					// existing range. Either way, when we do that,
					// we are always going to change the END day,
					// not the Start day.
					objEndDay = objTD;

				}

			} else if (objStartDay){

				// Check to see if the new TD is less than or
				// greater than the current start day.
				if (objStartDay.dayIndex < objTD.dayIndex){

					objEndDay = objTD;

				} else {

					objEndDay = objStartDay;
					objStartDay = objTD;
				}

			} else {

				// We will never have JUST an end day. Set this to
				// the start day.
				objStartDay = objTD;

			}

		}


		// Render the calendar with the new time span.
		RenderCalendar();

	}


	// This renders the calendar and turns the days on or off
	// depending on where they fall in any selected time span.
	function RenderCalendar(){
		var intDay = null;

		// Loop over all the days in the collection.
		for (intDay = 0 ; intDay < arrDays.length ; intDay++){

			// We cannot do anything to other month days. If this
			// is an other month day, just continue the loop.
			if (arrDays[ intDay ].className == "othermonth"){
				continue;
			}

			// Check to see if we have both start and end day.
			if (
				objStartDay &&
				objEndDay &&
				(objStartDay.dayIndex <= arrDays[ intDay ].dayIndex) &&
				(objEndDay.dayIndex >= arrDays[ intDay ].dayIndex)
				){

				// This is a selected day.
				arrDays[ intDay ].className = (
					( (arrDays[ intDay ] == objToday) ? "today" : "" ) +
					"selected"
					);

			} else if (
				(arrDays[ intDay ] == objStartDay) ||
				(arrDays[ intDay ] == objEndDay)
				){

				// This is a selected day.
				arrDays[ intDay ].className = (
					( (arrDays[ intDay ] == objToday) ? "today" : "" ) +
					"selected"
					);

			} else {

				// This is NOT a selected day. Turn off.
				arrDays[ intDay ].className = arrDays[ intDay ].baseClassName;

			}

		}

	}

</script>

Calendar Display Code

<div id="calendars">

	<!--- Loop over the months in the array. --->
	<cfloop
		index="intI"
		from="1"
		to="#ArrayLen( REQUEST.Months )#"
		step="1">

		<cfsilent>

			<!--- Get the pointer to the current date. --->
			<cfset dtThisMonth = REQUEST.Months[ intI ] />

			<!--- Get the starting day. --->
			<cfset dtStartDay = DateAdd(
				"d",
				-(DayOfWeek( dtThisMonth ) - 1),
				dtThisMonth
				) />

			<!--- Get the ending day. --->
			<cfset dtEndDay = (DateAdd( "m", 1, dtThisMonth ) - 1) />

			<!--- Stretch it to the end of the week. --->
			<cfset dtEndDay = DateAdd(
				"d",
				(7 - DayOfWeek( dtEndDay )),
				dtEndDay
				) />


			<!--- Get today's date. --->
			<cfset dtToday = Fix( REQUEST.Environment.DateTime.Now ) />

		</cfsilent>

		<table width="100%" border="0" cellspacing="2" cellpadding="0" class="calendarmonth">
		<thead>
			<tr>
				<td colspan="7">
					#MonthAsString( Month( dtThisMonth ) )# #Year( dtThisMonth )#
				</td>
			</tr>
		</thead>
		<tbody>
			<cfsilent>
				<cfsavecontent variable="strMonthCode">

					<tr>

						<!--- Loop over the days of the month. --->
						<cfloop index="dtDay" from="#dtStartDay#" to="#dtEndDay#" step="1">

							<td class="<cfif (dtDay EQ dtToday)>today<cfelseif (Month( dtDay ) EQ Month( dtThisMonth ))>thismonth<cfelse>othermonth</cfif>">
								#Day( dtDay )#
							</td>

							<cfif (
								(DayOfWeek( dtDay ) EQ 7) AND
								(dtDay NEQ dtEndDay)
								)>
								</tr>
								<tr>
							</cfif>

						</cfloop>

					</tr>

				</cfsavecontent>
			</cfsilent>

			<!--- Output code with as little white space as possible. --->
			#strMonthCode.ReplaceAll( ">\s+", ">" ).ReplaceAll( "\s+<", "<" )#
		</tbody>
		</table>

		<!--- Flush for user feedback. --->
		<cfflush />

	</cfloop>

</div>

<!--- Initialize the Calendar javascript. --->
<script type="text/javascript">
	InitCalendars();
</script>

CSS Code

table.calendarmonth {
	margin-bottom: 20px ;
	}

table.calendarmonth td {
	border: 1px solid #999999 ;
	cursor: default ;
	padding: 5px 10px 5px 10px ;
	}

table.calendarmonth thead td {
	background-color: #F0F0F0 ;
	border-bottom-width: 2px ;
	font-size: 12px ;
	font-weight: bold ;
	text-align: center ;
	}

table.calendarmonth td.today {
	background-color: #FEEAB7 ;
	border-color: #333333 ; /* #E4A705 ; */
	font-weight: bold ;
	}

table.calendarmonth td.thismonth {}

table.calendarmonth td.othermonth {
	background-color: #EAEAEA ;
	border-color: #999999 ;
	color: #AAAAAA ;
	}

table.calendarmonth td.selected {
	background-color: #FFE8E1 ;
	border-color: #FF3333 ;
	/* color: #FA3E0A ; */
	}

table.calendarmonth td.todayselected {
	background-color: #FFB8A4 ;
	border-color: #FF3333 ;
	font-weight: bold ;
	}

Reader Comments

Awesome :) Glad you like it. Please feel free to suggest any changes you want to it. Someone already suggested dragging for creating time span.