Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Cyril Hanquez and Hugo Sombreireiro and Reto Aeberli and Steven Peeters and Guust Nieuwenhuis and Aurélien Deleusière and Damien Bruyndonckx
Ben Nadel at Scotch On The Rocks (SOTR) 2011 (Edinburgh) with: Cyril Hanquez@Fitzchev ) , Hugo Sombreireiro@hsombreireiro ) , Reto Aeberli@aeberli ) , Steven Peeters@aikisteve ) , Guust Nieuwenhuis@Lagaffe ) , Aurélien Deleusière@adeleusiere ) , and Damien Bruyndonckx ( @damienbkx )

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:

http://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 ;
  • }
Tweet This Fascinating post by @BenNadel - A Small Calendar Utility For Reference Thanks my man — you rock the party that rocks the body!


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.