(function($) { var monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; function createDate(year, month, day) { var date = new Date(); date.setFullYear(year); date.setMonth(month || 0); date.setDate(day || 1); return date; } function getToday() { return new Date(); } function areDatesEqual(date1, date2) { return String(date1) == String(date2); } function daysInMonth(year, month) { if (year instanceof Date) return daysInMonth(year.getFullYear(), year.getMonth()); if (month == 1) { var leapYear = (year % 4 == 0) && (!(year % 100 == 0) || (year % 400 == 0)); return leapYear ? 29 : 28; } else if (month == 3 || month == 5 || month == 8 || month == 10) { return 30; } else { return 31; } } function dayAfter(date) { return createDate(date.getFullYear(), date.getMonth(), date.getDate()+1); } function dayBefore(date) { return createDate(date.getFullYear(), date.getMonth(), date.getDate()-1); } function monthAfter(year, month) { return createDate(year, month + 1); } function formatDate(date, options) { options = options || {}; options.separator = options.separator || '/'; var s; if (options.iso) { var m = date.getMonth() + 1; var d = date.getDate(); return date.getFullYear() + '-' + (m < 10 ? '0'+m : m) + '-' + (d < 10 ? '0'+d : d); } if (options.usa) { s = (date.getMonth() + 1) + options.separator + date.getDate() } else { s = date.getDate() + options.separator + (date.getMonth() + 1) } return s + options.separator + date.getFullYear(); } function parseDate(date, options) { options = options || {}; var a, day, month, year; a = date.split(/\D/); if (options.iso) { year = a.shift(); month = a.shift(); day = a.shift(); } else { if (options.usa) { month = a.shift(); day = a.shift(); } else { day = a.shift(); month = a.shift(); } year = a.shift(); } return new Date(month + '/' + day + '/' + year); } function formatTime(hour, minute, options) { var printMinute = minute; if (minute < 10) printMinute = '0' + minute; if (options.isoTime) { var printHour = hour if (printHour < 10) printHour = '0' + hour; return printHour + ':' + printMinute; } else { var printHour = hour % 12; if (printHour == 0) printHour = 12; if (options.meridiemUpperCase) { var half = (hour < 12) ? 'AM' : 'PM'; } else { var half = (hour < 12) ? 'am' : 'pm'; } return printHour + ':' + printMinute + half; } } function parseTime(text) { var match = match = /(\d+)\s*[:\-\.,]\s*(\d+)\s*(am|pm)?/i.exec(text); if (match && match.length >= 3) { var hour = Number(match[1]); var minute = Number(match[2]) if (hour == 12 && match[3]) hour -= 12; if (match[3] && match[3].toLowerCase() == 'pm') hour += 12; return { hour: hour, minute: minute }; } else { return null; } } function timeToMinutes(time) { return time && (time.hour * 60 + time.minute); } /** * Generates calendar header, with month name, << and >> controls, and * initials for days of the week. */ function renderCalendarHeader(element, year, month, options) { options = options || {}; //Prepare thead element var thead = $(''); var titleRow = $('').appendTo(thead); //Generate << (back a month) link $('').addClass('monthCell').append( $('«') .addClass('prevMonth') .mousedown(function(e) { renderCalendarPage(element, month == 0 ? (year - 1) : year, month == 0 ? 11 : (month - 1), options ); e.preventDefault(); }) ).appendTo(titleRow); //Generate month title var month_label; if (options.iso) { month_label = year + '-' + (month < 9 ? '0': '') + (month + 1); } else { month_label = monthNames[month] + ' ' + year; } $('').addClass('monthCell').attr('colSpan', 5).append( $('' + month_label + '').addClass('monthName') ).appendTo(titleRow); //Generate >> (forward a month) link $('').addClass('monthCell').append( $('»') .addClass('nextMonth') .mousedown(function() { renderCalendarPage(element, month == 11 ? (year + 1) : year, month == 11 ? 0 : (month + 1), options ); }) ).appendTo(titleRow); //Generate weekday initials row var dayNames = $('').appendTo(thead); $.each(String('SMTWTFS').split(''), function(k, v) { $('').addClass('dayName').append(v).appendTo(dayNames); }); return thead; } function renderCalendarPage(element, year, month, options) { options = options || {}; var today = getToday(); var date = createDate(year, month, 1); //Wind end date forward to saturday week after month var endDate = monthAfter(year, month); var ff = 6 - endDate.getDay(); if (ff < 6) ff += 7; for (var i = 0; i < ff; i++) endDate = dayAfter(endDate); var table = $(''); renderCalendarHeader(element, year, month, options).appendTo(table); var tbody = $('').appendTo(table); var row = $(''); //Rewind date to monday week before month var rewind = date.getDay() + 7; for (var i = 0; i < rewind; i++) date = dayBefore(date); while (date <= endDate) { var td = $(''); } date = dayAfter(date); } if (row.children().length) { tbody.append(row); } else { row.remove(); } element.empty().append(table); } function renderTimeSelect(element, options) { var minTime = timeToMinutes(options.minTime); var maxTime = timeToMinutes(options.maxTime); var defaultTime = timeToMinutes(options.defaultTime); var selection = options.selection && timeToMinutes(parseTime(options.selection)); //Round selection to nearest time interval so that it matches a list item selection = selection && ( ( Math.floor((selection - minTime) / options.timeInterval) * options.timeInterval ) + minTime ); var scrollTo; //Element to scroll the dropdown box to when shown var ul = $('
') .addClass('day') .append( $('' + date.getDate() + '' ).click((function() { var thisDate = date; return function() { if (options && options.selectDate) { options.selectDate(thisDate); } } }())) ) .appendTo(row); var isToday = areDatesEqual(date, today); var isSelected = options.selected && areDatesEqual(options.selected, date); if (isToday) td.addClass('today'); if (isSelected) td.addClass('selected'); if (isToday && isSelected) td.addClass('today_selected'); if (date.getMonth() != month) td.addClass('nonMonth'); dow = date.getDay(); if (dow == 6) { tbody.append(row); row = $('