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.length; i++) { if (opts.events[i].date == null) continue; if (optDateEqual(opts.events[i].date, date)) { return true; } } return false; } function getDateAppts(date, baseClasses) { switch (opts.mode) { case $.fn.jqCal.modes.Big : var apptsHtml = ""; for (var i=0; i<opts.events.length; i++) { if (opts.events[i].date == null) continue; if (optDateEqual(opts.events[i].date, date)) { apptsHtml += "<a href='" + opts.events[i].url + "' class='AppointmentLink' target='_new" + opts.events[i].key + "'>" + "<div class='Appointment'>" + opts.events[i].desc + "</div></a>"; } } return apptsHtml; case $.fn.jqCal.modes.Print : var apptsHtml = "<table width='100%'>"; for (var i=0; i<opts.events.length; i++) { if (opts.events[i].date == null) continue; if (optDateEqual(opts.events[i].date, date)) { apptsHtml += "<tr><td><div class='" + this.CSS_ApptPrint + "'>" + opts.events[i].desc + "</div></td></tr>"; } } apptsHtml += "</table>"; return apptsHtml; case $.fn.jqCal.modes.Tiny : var appts = 0; var toolTipText = ""; var cssClass = baseClasses; for (var i=0; i<opts.events.length; i++) { if (opts.events[i].date == null) continue; if (optDateEqual(opts.events[i].date, date)) { appts++; if (appts > 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 = "<table width='100%'>"; for (var i=0; i<opts.events.length; i++) { if (opts.events[i].date == null) continue; if (optDateEqual(opts.events[i].date, date)) { apptsHtml += "<tr><td class='" + this.CSS_Appointment + "'>" + opts.events[i].desc + "</td></tr>"; } } apptsHtml += "</table>"; return apptsHtml; } } function dateIsHoliday(date) { for (var i=0; i<opts.holidays.length; i++) { if (opts.holidays[i].date == null) continue; if (optDateEqual(opts.holidays[i].date, date)) { return true; } } return false; } // iterate each matched element return this.each(function() { var $this = $(this); // build element specific options //var o = $.meta ? $.extend({}, opts, $this.data()) : opts; var o = opts; var date = o.date ? new Date(o.date.valueOf()) : new Date((new Date()).getFullYear(), (new Date()).getMonth(), 1); date.setDate(1); var currentDate = new Date(); var currentDate = [currentDate.getFullYear(), currentDate.getMonth() + 1, currentDate.getDate()]; var calHtml = ["<table class='" + o.cssClasses.CalClass + ((o.mode === $.fn.jqCal.modes.Tiny) ? " " + o.cssClasses.CalClass + "Tiny" : "") + "'>" + ((o.monthYear) ? "<tr><td class='" + o.cssClasses.MonthYear + ((o.mode === $.fn.jqCal.modes.Tiny) ? " " + o.cssClasses.MonthYear + "Tiny" : "") + "' colspan=7>" + o.months[date.getMonth()] + ", " + date.getFullYear() + "</td></tr><tr>" : "")]; for (var i=0; i<7; i++) { calHtml[calHtml.length] = "<td class='" + o.cssClasses.Weekday + "' align='center'>" + ((o.mode !== $.fn.jqCal.modes.Tiny) ? o.days[i] : o.daysTiny[i]) + "</td>"; } calHtml[calHtml.length] = "</tr>"; calHtml[calHtml.length] = "<tr>"; 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] = "<tr>"; } if (o.mode != $.fn.jqCal.modes.Tiny) { calHtml[calHtml.length] = "<td class='" + o.cssClasses.Day + " " + dayClass + "' id='" + iter.idStr + "' width='14%'>" + // ((isHoliday) ? "<table cellpadding='0' cellspacing='0' width='100%' height='100%'>" + // "<tr><td valign='top'>" : "") + iter.dayNum + // ((isHoliday) ? "</td></tr><tr><td valign='bottom'>" + this.getHolidays(iter.curDate) + // "</td></tr></table>" : "") + "" + ((hasAppts) ? getDateAppts(iter.optDate) : " ") + "</td>"; } else { calHtml[calHtml.length] = "<td" + getDateAppts(iter.optDate, o.cssClasses.Day + " " + dayClass) + " width='14%'>" + iter.dayNum + "</td>"; } if (iter.endRow === 6) { calHtml[calHtml.length] = "</tr>"; } }); calHtml[calHtml.length] = "</tr></table>"; $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);
The PHP and C# versions will be up here soon.