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);