JQuery Calendar Code

Of all the code floating around the web calendar drawing code is usually messy, hard to maintain, and inflexible. A while back I got permission to open-source calendar drawing code that I wrote. It’s client-side JavaScript code as a JQuery plug-in but I plan to port it to both PHP and C# (the first version used Prototype’s psuedo object oriented framework so porting to classical OO languages will be a cinch).

/*global jQuery: false */
/*jslint undef: true, nomen: true, bitwise: true, regexp: true, strict: true, newcap: true */
"use strict";
(function ($) {

//
// jqCal
//
$.fn.jqCal = function(options) {
	// build main options before element iteration
	var opts = $.extend({}, $.fn.jqCal.defaults, options);

	function optDateEqual(d1, d2) {
		return d1[0] == d2[0] &&
				d1[1] == d2[1] &&
				d1[2] == d2[2];
	}

	function dateHasAppts(date) {
		for (var i=0; i" +
						"
" +
						opts.events[i].desc + "
";
				}
			}
			return apptsHtml;
		case $.fn.jqCal.modes.Print :
			var apptsHtml = "";
			for (var i=0; i" + opts.events[i].desc + "";
				}
			}
			apptsHtml += "
";
			return apptsHtml;
		case $.fn.jqCal.modes.Tiny :
			var appts = 0;
			var toolTipText = "";
			var cssClass = baseClasses;
			for (var i=0; i 1) toolTipText += " | ";
					toolTipText += opts.events[i].desc.replace(/<[^>]*>/g, " ").replace(/'/g, "'");
					cssClass += " TinyAppointment";
				}
			}
			if (appts > 1) cssClass += this.CSS_MultiA;
			return " class='" + cssClass + "' title='" + toolTipText + "' ";
		case $.fn.jqCal.modes.Collective :
			var apptsHtml = "";
			for (var i=0; i" + opts.events[i].desc + "";
				}
			}
			apptsHtml += "
";
			return apptsHtml;
		}
	}


	function dateIsHoliday(date) {
		for (var i=0; i" +
			((o.monthYear) ? "" + 
			o.months[date.getMonth()] + ", " + date.getFullYear() + "" : "")];
		for (var i=0; i<7; i++) {
			calHtml[calHtml.length] = "" + 
				((o.mode !== $.fn.jqCal.modes.Tiny) ? o.days[i] : o.daysTiny[i]) + "";
		}
		calHtml[calHtml.length] = "";
		calHtml[calHtml.length] = "";
		var dayClass, numClass, isHoliday, hasAppts;
		$.fn.jqCal.calDays(date, function(iter) {
			dayClass = iter.during ? o.cssClasses.DayReg : o.cssClasses.DayEmpty;
			dayClass += ((o.mode === $.fn.jqCal.modes.Tiny) ? " " + o.cssClasses.Day + "Tiny" : "");
			dayClass += optDateEqual(currentDate, iter.optDate) ? " today" : "";
			numClass = iter.during ? o.cssClasses.DayRegNum : o.cssClasses.DayEmptyNum;
			hasAppts = dateHasAppts(iter.optDate);
			isHoliday = dateIsHoliday(iter.optDate);
			if (iter.newRow) {
				calHtml[calHtml.length] = "";
			}
			if (o.mode != $.fn.jqCal.modes.Tiny) {
				calHtml[calHtml.length] = 
					"" +
//					((isHoliday) ? "" +
//					"
" : "") +
					iter.dayNum +
//					((isHoliday) ? "
" + this.getHolidays(iter.curDate) + 
//					"
" : "") +
					"" + ((hasAppts) ? getDateAppts(iter.optDate) : " ") + 
					"";
			} else {
				calHtml[calHtml.length] = 
					"" + iter.dayNum + "";
			}
			if (iter.endRow === 6) {
				calHtml[calHtml.length] = "";
			}
		});
		calHtml[calHtml.length] = "";

		$this.html(calHtml.join(""));
	});
};

$.fn.jqCal.modes = {
Tiny : 0,
Big : 1,
Print : 2,
Collective : 3
};

//
// plugin defaults
//
$.fn.jqCal.defaults = {
mode : $.fn.jqCal.modes.Tiny,
events : [],
holidays : [],
monthYear : true,
months : ["January", "February", "March", "April", "May", "June", "July", "August",
	"September", "October", "November", "December"],
monthsTiny : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
	"Sep", "Oct", "Nov", "Dec"],
days : ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"],
daysTiny : ["S", "M", "T", "W", "T", "F", "S"],
cssClasses : {
	CalClass : "CalClass",
	MonthYear : "MonthYear",
	Weekday : "Weekday",
	Day : "CalDay",
	DayEmpty : "DayEmpty",
	DayEmptyNum : "DayEmptyNum",
	Appointment : "Appointment",
	ApptDay : "ApptDay",
	ApptDesc : "ApptDesc",
	MultiA : "MultiAppt",
	DayReg : "DayReg",
	DayRegNum : "DayRegNum",
	Holiday : "Holiday",
	ApptPrint : "ApptPrint",
	Hover : "CalDateHover"
		}
};

$.fn.jqCal.calDays = function(date, callback) {
	var calendarDate = new Date(date.valueOf());
	var calendarDateDay = calendarDate.getDay();
	var curDate = new Date(date.valueOf());
	curDate.setDate(curDate.getDate() - (curDate.getDay() + 1));

	var calendarMonth = calendarDate.getMonth();
	var calendarYear = calendarDate.getFullYear();

	var beforeMonth = curDate.getMonth() !== calendarMonth;
	var duringMonth = curDate.getMonth() === calendarMonth;
	var afterMonth = false;

	var idStrBase = "cal-" + calendarMonth + "-" + calendarYear + "-";
	
	var dayMs = 1000 * 60 * 60 * 24;

	var lastDayMs = new Date(calendarYear, calendarMonth + 1, 1).valueOf() - dayMs;
	var numDays = (new Date(lastDayMs)).getDate();

	var before=0, day=0, after=0;

	function dayNum() {
		if (duringMonth) {
			return day;
		} else {
			return curDate.getDate();
		}
	}

	function month() {
		var ret;
		if (duringMonth) {
			ret = calendarMonth;
		} else if (beforeMonth) {
			ret = calendarMonth - 1;
			if (ret === -1) {
				ret = 11;
			}
		} else {
			ret = calendarMonth + 1;
			if (ret === 12) {
				ret = 0;
			}
		}
		return ret;
	}

	function year() {
		if (duringMonth) {
			return calendarYear;
		} else {
			return curDate.getFullYear();
		}
	}

	function doDay() {
		var showDay = dayNum();
		var weekDay = curDate.getDay();
		callback({
			day : curDate, 
			during : duringMonth,
			before : beforeMonth, 
			after : afterMonth,
			dayNum : showDay,
			idStr : idStrBase + month() + "-" + showDay + "-" + year(),
			newRow : weekDay === 0,
			endRow : weekDay === 6,
			optDate : [year(), month(), showDay]
				});
	}

	while (true) {
		if (beforeMonth) {
			if (before < calendarDateDay) {
				curDate.setDate(curDate.getDate() + 1);
				before++;
				doDay();
				continue;
			} else {
				beforeMonth = false;
				duringMonth = true;
			}
		}
		if (duringMonth) {
			if (day < numDays) {
				curDate.setDate(curDate.getDate() + 1);
				day++;
				doDay();
				continue;
			}
			after = curDate.getDay();
			if (after === 6) {
				return;
			} else {
				duringMonth = false;
				afterMonth = true;
			}
		}
		if (afterMonth) {
			if (after < 6) {
				curDate.setDate(curDate.getDate() + 1);
				after++;
				doDay();
				continue;
			} else {
				return;
			}
		}
	}

};


})(jQuery);