/* * Calendar v2.6 * * Copyright 2001-2004, Mackley F. Pexton. All rights reserved. */ /** These routines display calendars and acts on the date selected by the user. The displayCalendar() is the main interface routine. It can be used to display a pop-up calendar, or it can be used as a front-end to a server-side application program. The callback argument is a string which can be a function name call (like "func(arg)") or a URL (like "http://www.company.com") or a form field name (like "sample_date" or "myform.sample_date" or "document.myform.sample_date"). -If the callback is a function, the function gets called with the added arguments of the calendar object, the selected date (as a string), and a boolean which is true if the selected date is an active date. -If the callback is an empty string, it is interpreted as the URL of the current page. -If the callback is a URL, it is called with the extra parameter 'date=mm/dd/yyyy' appended. -If the callback is the name of a form field, its value is set to the selected date, and if the field has an onChange event handler defined, it is executed. -If the callback is undefined, nothing is done. There are a multitude of options to displayCalendar(), all are optional. Usually, only the first couple of arguments are used in pop-up calendars. For example: This displays a one month calendar, filling in the 'fld' field with the selected date, the selected date displayed on the calendar being the date currently in the 'fld' field, and only the dates on or after today can be selected as a return date by the user. Options to displayCalendar() can be specified with key/value pairs if the first argument is an Object. For example, the above call to displayCalendar could be written like: displayCalendar( { 'n' : 1, 'callback' : this.form.fld, 'select_date' : this.form.fld.value, 'active_date' : 'today' } ) Calendars are named to keep them separate. Calendar names correspond to browser window names. Null or missing names refer the last calendar made. Although several calendars can be rendered at once, only one calendar per window frame is allowed because each calendar's JavaScript that controls the date buttons uses global variables. Calendars have a navigation button bar. On it is a menu of options which allows the calendar to be resized and allows the calendar to be printed. If the calendar is in a lookup window it is simply printed without the navigation button bar. If the calendar is in the primary window, the page is reloaded with the '&print=yes' parameter added to the url. The navigation bar can be overwritten by specifying a 'toolbar' argument or additional menu items can be added to the menu by using the 'menu' function argument. There are different types of calendars: 1, 3, 6, or 12. The calendar_report.js library adds the types: 0, 'full', 'list', and 'day'. The calendar types 1, 3, 6, or 12 show little calendars with pop-up menus listing the day's contents. They are shown with 1, 3, 6, or 12 calendars on a page. The calendar type 0 is the same as 'full' which stands for "fullscreen". It displays one month's calendar on a page and the contents of the dates can be seen without the pop-up menus. The 'list' type calender is really not a calendar at all. Only the navigation bar is displayed. The server is responsible for printing a list of items. The 'full', 'list', and 'day' calendar types need a cooperating server side program. They are not available on lookup windows. History: 8/15/2001 v2.0 - rewrite to include database info in pop-up panels. 2/9/2003 v2.1 - hacked to work on Netscape 6+. 3/28/2004 v2.2 - added DayMenu.list(). 4/1/2004 v2.3 - rewrite, optimize, remove CalendarDisplays collection. 4/10/2004 v2.4 - simplified CSS by assigning multiple class names to TD tags. 4/18/2004 v2.5 - split library into this for pop-ups and calendar_report.js for applications. 7/26/2004 v2.6 - added tear-off and resizable day menus. **/ // Uncomment following to display the calendar's html. //Calendar.Debug = true; function displayCalendar(n_months, // number of months to display callback, // form field name or function name select_date, // date to be highlighted on calendar active_date, // before which buttons are inactive start_date, // beginning month/year of calendar start_day, // start weeks with day of week (0-6) win, // window to use or null cal_name, // name of calendar autoclick, // seconds before hover becomes click unclickfunc, // function invoked to un-autoclick stylesheet, // url of css stylesheet for calendar hilites, // array or object of date strings cal_type, // 'full', 'list', or 'day' daymenu, // the DayMenu object for day menus printing, // true if calendar is for print menu, // menu html added to toolbar toolbar, // substitute navigation toolbar button_help, // day button pop-up help text on full vertical // months are displayed out vertically ) { // Arguments can be key/value pairs if the first argument is an object. if (typeof arguments[0] == 'object') { var args = arguments[0]; // -- Program -- -- Alternate -- // -- Names -- -- Names -- n_months = args['n_months'] || args['n']; callback = args['callback'] || args['click']; select_date = args['select_date']; active_date = args['active_date']; start_date = args['start_date']; start_day = args['start_day']; win = args['win'] || args['window']; cal_name = args['cal_name'] || args['name']; autoclick = args['autoclick']; unclickfunc = args['unclick']; stylesheet = args['stylesheet']; hilites = args['hilites']; cal_type = args['cal_type'] || args['type']; daymenu = args['daymenu'] || args['day_menu']; printing = args['print'] || args['printing']; menu = args['menu']; toolbar = args['toolbar']; button_help = args['button_help'] || args['button_title']; vertical = args['vertical']; } // Set defaults for missing parameters. if (n_months == null) n_months = 1; // number of months if (n_months == 0) { cal_type = 'full'; n_months = 1; } if (cal_type == 'day') { n_months = 3; vertical = true; } if (!select_date && typeof callback == 'object' && callback.value) { select_date = callback.value; } // Note: intolerant Netscape 6.2 needs dates in 01/01/2002 format, not 01-01-2002 (!!) if (!start_date) start_date = select_date; if (!start_date) start_date = new Date(); else start_date = new Date(start_date.replace(/\W/g,"/")); if (cal_name) { // Calendar callback functions uses the window name as the calendar name. self.name = cal_name; } else { if (win == self) { if (self.name) { cal_name = self.name; } else { // assign name to this window cal_name = 'self' + n_months; self.name = cal_name; } } else { // Pop-up window // Uncomment next to have different pop-up window for each field. cal_name = 'cal' + Math.floor(Math.random() * 10000); // Uncomment next to have all fields use same pop-up window. //cal_name = 'cal' + n_months; } } if (!win) { var settings = "resizable"; var w,h; // Following dimensions are for a lookup window without // browser buttons, menu, scrollbar, or status line. if (cal_type == 'day') { w=840; h=520; } else if (n_months > 6) { w=840; h=840; } else if (n_months > 3) { w=840; h=520; } else if (n_months > 1) { w=840; h=300; } else if (n_months == 1) { w=280; h=285; } settings += ",width="+w+",height="+h; // Center new window var w_width = window.innerWidth ? window.innerWidth : window.document.body.offsetWidth; var w_height = window.innerHeight ? window.innerHeight : window.document.body.offsetHeight; var left = window.screenX ? window.screenX : window.screenLeft ? window.screenLeft : 0; var top = window.screenY ? window.screenY : window.screenTop ? window.screenTop : 0; left += (w_width/2 - w/2); top += (w_height/2 - h/2); settings += ',top='+top+',left='+left; var win = window.open("", cal_name, settings); win.focus(); } // Determine starting month/year of calendar start_month = start_date.getMonth(); if (cal_type == 'day') { start_month = start_month; //###((start_month-1+12) % 12); // previous month } else if (n_months > 6) { start_month = 0; // beginning of year n_months = 12 * Math.floor((n_months+11) / 12); } else if (n_months > 3) { start_month -= start_month % 6; // beginning of half n_months = 6 * Math.floor((n_months+5) / 6); } else if (n_months > 1) { start_month -= start_month % 3; // beginning of quarter n_months = 3 * Math.floor((n_months+2) / 3); } start_month++; // between 1 and 12 start_year = start_date.getFullYear(); // Setup calendar. var c = new Calendar(cal_name, n_months, start_month, start_year, win, daymenu); if (hilites) c.setHilites(hilites); if (autoclick) c.setAutoClick(autoclick); if (unclickfunc) c.setAutoUnClick(unclickfunc); if (stylesheet) c.setAuxiliaryStylesheet(stylesheet); if (active_date) c.setActiveDate(active_date); if (cal_type) c.setType(cal_type); if (button_help) c.setDayButtonHelp(button_help); if (toolbar) c.setNavigationHTML(toolbar); if (menu) c.setMenuHTML(menu); if (printing) c.setPrinting(printing); if (vertical) c.setVerticalCalendar(true); if (callback) c.setCallbackFunction(callback); if (select_date) c.setSelectedDate(select_date); if (start_day != null) c.setStartDay(start_day); if (Calendar.Debug) Calendar.debug_on(win); c.display(); // Display the calendar if (Calendar.Debug) Calendar.debug_off(win); // Check if a date is to be highlighted // Highlight selected date after calendar has been rendered. if (select_date) setTimeout("with(Calendar.calendars['"+cal_name+"']){hiliteDate('select','"+select_date+"');}",300); } //----------------------------------------------------------------------------- // Calendar object is to display a calendar Calendar.calendars = {}; // collection of defined calendars by name function Calendar(cal_name, n_months, month, year, win, daymenu) { // Calendar object constructor. // month is between 1 and 12 // Check type of calendar (fullscreen, list, or normal) if (n_months == null) n_months = 1; if (n_months == 0 || n_months == 'full') { this.setType('full'); n_months = 1; } else if (n_months == 'list') { this.setType('list'); n_months = 1; } else if (n_months == 'day') { this.setType('day'); n_months = 3; } else { this.setType(n_months); } // Properties this.n_months = n_months; this.win = win || self; this.cal_name = cal_name; // Instance variables // Set today to midnight today. this.today = new Date(); this.today.setHours(0); this.today.setMinutes(0); this.today.setSeconds(0); this.today.setMilliseconds(0); // Set default arguments if (!year) year = this.today.getFullYear(); if (!month) month = this.today.getMonth() + 1; this.start_date = new Date(year,month-1); this.selected_date = null; this.date = new Date(this.start_date); // current date being processed this.activated = new Object; // list of reactivated buttons this.deactivated = new Object; // list of deactivated buttons this.callback = null; // callback function or field name this.uncallback = null; // function called when date unselected this.active_date = null; // buttons before this date are inactive this.hilites = new Object; // collection of date, highlight pairs // Object for displaying pop-up day menus. this.daymenu = daymenu ? daymenu : (new Object); this.buf = ''; // initialize output buffer this.month_index = 0; // number of months output this.week_head_index = 0; // number of week headers output (flag) // Register this calendar object Calendar.calendars[cal_name] = this; Calendar.last_calendar = this; } // // Class constants // Calendar.MonthDays = [31,28,31,30,31,30,31,31,30,31,30,31]; Calendar.MonthNames = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]; Calendar.WeekNames = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; Calendar.Columns = 3; // number of columns in calendar Calendar.WeekColumns = 7; // number of columns in week // // Default values // // Define the default stylesheet. // Note: stylesheet settings are used only on pop-up windows, not primary windows, // they are responsible for specifying their own CSS stylesheet(s). Calendar.prototype.Stylesheet = 'styles/calendar.css'; Calendar.prototype.AuxiliaryStylesheet = null; // ancillary stylesheet // Define number of seconds before a mouse click is simulated when the user // hovers over a date button. Set to 0 to disable. Calendar.prototype.AutoClick = 0; // Define whether callback should be executed if date becomes out of focus. Calendar.prototype.AutoUnClick = false; // Define the date before which buttons are not highlighted. The buttons are // still active if clicked by the user allowing the callback function to // determine if they should be executed. Calendar.prototype.ActiveDate = null; // Setting NavigationHTML overrides the default HTML generated programically // for the button bar of calendar navigation buttons. Calendar.prototype.NavigationHTML = null; // Each calendar day's button has an ID attribute of the DayButtonPrefix with // the button's date appended to it, like: cal_2004-01-30. Calendar.prototype.DayButtonPrefix = 'cal_'; //----------------------------------------------------------------------------- // // Calendar class methods // Calendar.dateToString = function(date) { // Return date as a string like mm-dd-yyyy. // This function is used to make the calendar buttons' ids. var mm = date.getMonth() + 1; var dd = date.getDate(); var yyyy = date.getFullYear(); if (mm < 10) mm = '0' + mm; if (dd < 10) dd = '0' + dd; return mm+'-'+dd+'-'+yyyy; } Calendar.returnCalendar = function(cal_name, command, arg, date) { // This is the standard callback routine executed when a // user clicks a date or toolbar button. // Note: arg is often a date. var c = Calendar.calendars[cal_name]; switch (command.toLowerCase()) { case 'select': c.doSelect(arg); break; case 'unselect': c.doUnSelect(arg); break; case 'next': c.nextCalendar(); break; case 'nextyear': c.nextYearCalendar(); break; case 'prev': c.prevCalendar(); break; case 'prevyear': c.prevYearCalendar(); break; case 'today': c.todayCalendar(); break; case 'setmonth': c.setMonthCalendar(arg); c.redisplay(); break; case 'setquarter': c.setQuarterCalendar(arg); c.redisplay(); break; case 'sethalf': c.setHalfCalendar(arg); c.redisplay(); break; case 'setyear': c.setYearCalendar(arg); c.redisplay(); break; case 'setdayofweek': c.setStartDay(arg); c.redisplay(); break; case 'setstyle': c.setStyle(arg,date); break; case 'print': c.print(arg); break; case 'go': c.go(arg); break; } } Calendar.debug_on = function(win) { // Debug non-pop-up calendars win.document.write('
');
	self.document.orig_write = win.document.write;
	win.document.write = function(s) {
					s = s.replace(/');
}

//-----------------------------------------------------------------------------

//
// Calendar application methods
//

// These are hooks to methods in calendar_report.js

Calendar.prototype.redisplay = function(cmd) {

	if (cmd != null && cmd != this.Type) this.resize(cmd);
	this.display(cmd);
}

Calendar.prototype.print = function() {
	// Hide navigation button bar
	var e = this.win.document.getElementById("navigation_toolbar");
	e.style.display = 'none';
	e.style.visibility = 'hidden';

	// Print calendar window
	if (this.win.print) { this.win.print(); }
	else {
		alert("Your browser does not support this shortcut.\n" +
		      "Please select Print from the File menu.");
	}

	// Show navigation button bar
	e.style.display = 'block';
	e.style.visibility = 'visible';
}

//-----------------------------------------------------------------------------

//
// Calendar navigation methods
//

Calendar.prototype.nextCalendar = function() {
	this.addStartMonth(this.n_months);
	this.redisplay();
}

Calendar.prototype.nextYearCalendar = function() {
	this.addStartYear(1);
	this.redisplay();
}

Calendar.prototype.prevCalendar = function() {
	this.addStartMonth(-this.n_months);
	this.redisplay();
}

Calendar.prototype.prevYearCalendar = function() {
	this.addStartYear(-1);
	this.redisplay();
}

Calendar.prototype.todayCalendar = function() {
	var id =this.DayButtonPrefix + Calendar.dateToString(this.today);
	var e = document.getElementById(id);
	if (e) {
		do_select(e);
	}
	else {
		this.setSelectedDate(Calendar.dateToString(this.today));
		this.setStyle(this.Type,Calendar.dateToString(this.today));
	}
}

Calendar.prototype.go = function() {
	var frm = document.navigation;
	this.setYearCalendar(frm.year[frm.year.selectedIndex].value);
	if (frm.half) this.setHalfCalendar(frm.half[frm.half.selectedIndex].value);
	if (frm.quarter) this.setQuarterCalendar(frm.quarter[frm.quarter.selectedIndex].value);
	if (frm.month) this.setMonthCalendar(frm.month[frm.month.selectedIndex].value);
	this.redisplay();
}

//-----------------------------------------------------------------------------

//
// Calendar methods to change type of display.
//

Calendar.prototype.setMonthCalendar = function(month) {
	if (month < 1 || month > 12) month = 1;
	this.setStartMonth(month-1);
}

Calendar.prototype.setQuarterCalendar = function(quarter) {
	var month = (quarter < 2) ? 1
		  : (quarter < 3) ? 4
		  : (quarter < 4) ? 7
		  : 10;
	this.setStartMonth(month-1);
}

Calendar.prototype.setHalfCalendar = function(half) {
	var month = (half < 2) ? 1 : 7;
	this.setStartMonth(month-1);
}

Calendar.prototype.setYearCalendar = function(year) {
	this.setStartYear(year);
}

//-----------------------------------------------------------------------------

//
// Calendar methods to change settings.
//

Calendar.prototype.setToday = function(s) {
	// Set today's date (which has its own display class)
	if (!s) return;
	this.today.setTime(Date.parse(s));
	// Set to midnight for date comparisons
	this.today.setHours(0);
	this.today.setMinutes(0);
	this.today.setSeconds(0);
	this.today.setMilliseconds(0);
}

Calendar.prototype.setSelectedDate = function(s) {
	// Record the date that is selected on the calendar
	this.selected_date = s;
}

Calendar.prototype.getSelectedDate = function() {
	// Record the date that is selected on the calendar
	return this.selected_date;
}

Calendar.prototype.setStartDay = function(n) {
	// Set the day of week calendar starts (0-6)
	if (!n) n = 0;
	this.StartDay = n % 7;
}

Calendar.prototype.getStartDay = function() {
	if (this.StartDay) return this.StartDay;
	return 0;	// start on Sunday
}

Calendar.prototype.getStartDate = function() {
	return Calendar.dateToString(this.start_date);
}

Calendar.prototype.getEndDate = function() {
	// Return last date of calendar.
	var mm   = this.start_date.getMonth(); 
	var yyyy = this.start_date.getFullYear();
	var n = this.n_months - 1;

	mm += (n % 12);
	yyyy += Math.floor(n / 12);
	if (mm >= 12) { mm -= 12; yyyy++ }
	var dd = this.getMonthDays(mm,yyyy);
	mm++;
	if (mm < 10) mm = '0' + mm;

	return mm+'-'+dd+'-'+yyyy;
}

Calendar.prototype.getNMonths = function() {
	return this.n_months;
}

Calendar.prototype.setNMonths = function(n) {
	var p = this.n_months;
	this.n_months = n;
	return p;
}

Calendar.prototype.setType = function(s) {
	// Set calendar type. Currently: 'day', 'list', 'full' or 0, 1, 3, 6, or 12.
	var p = this.Type;
	this.Type = s.toString();
	return p;
}

Calendar.prototype.setStyle = function(style,date) {
	var d, m, y;
	if (date) { d = new Date(date.replace(/\W/g,"/")) }
	else      { d = this.start_date }
	m = d.getMonth();
	y = d.getFullYear();

	switch (style) {
	case '1':
		this.setStartMonth(m);
		this.setStartYear(y);
		this.setNMonths(1);
		break;
	case '3':
		this.setStartMonth(m - (m % 3));
		this.setStartYear(y);
		this.setNMonths(3);
		break;
	case '6':
		this.setStartMonth(m - (m % 6));
		this.setStartYear(y);
		this.setNMonths(6);
		break;
	case '12':
		this.setStartMonth(m - (m % 12));
		this.setStartYear(y);
		this.setNMonths(12);
		break;
	case '0':
	case 'full':
		this.setStartMonth(m);
		this.setStartYear(y);
		this.setNMonths(1);	// display only on full screen month
		break;
	case 'list':
		this.setStartMonth(m);
		this.setStartYear(y);
		this.setNMonths(1);	// list only one month at a time
		break;
	case 'day':
		this.setStartMonth( ((m - 1 + 12) % 12) ); // previous month
		this.setStartYear( ((m == 0) ? y - 1 : y) );
		this.setNMonths(3);	// list only three months
		this.setVerticalCalendar(true);
		this.daymenu.FixedMenu = true;
		break;
	}
	this.redisplay(style);
	this.setType(style);
}

Calendar.prototype.setStylesheet = function(s) {
	// Set CSS style sheet used by calendars.
	var p = this.Stylesheet;
	this.Stylesheet = s;
	return p;
}

Calendar.prototype.setAuxiliaryStylesheet = function(s) {
	// Set auxiliary CSS style sheet used by calendars.
	var p = this.AuxiliaryStylesheet;
	this.AuxiliaryStylesheet = s;
	return p;
}

Calendar.prototype.setCallbackFunction = function(s) {
	// Set function used to accept calendar requests and results.
	var p = this.callback;
	s = s.replace(/\(.*/,'');	// remove possible ()
	this.callback = s;
	return p;
}

Calendar.prototype.setUnCallbackFunction = function(s) {
	// Set function alled when a date is unselected.
	var p = this.uncallback;
	s = s.replace(/\(.*/,'');	// remove possible ()
	this.uncallback = s;
	return p;
}

Calendar.prototype.setAutoClick = function(n) {
	// Set number of seconds before a date button is automatically clicked.
	var p = this.AutoClick;
	this.AutoClick = n;
	return p;
}

Calendar.prototype.setAutoUnClick = function(unclickfunc) {
	if (eval('typeof('+unclickfunc.replace(/\(.*\)/,'')+')') == 'function') {
		// Register unclickfunc function
		this.uncallback = unclickfunc;
		this.AutoUnClick = true;
	}
}

Calendar.prototype.setVerticalCalendar = function(b) {
	// Set menu id to use as a pop-up menu for each day.
	var p = this.VerticalCalendar;
	this.VerticalCalendar = b;
	return p;
}

Calendar.prototype.isVerticalCalendar = function() {
	if (this.VerticalCalendar) return true;
	return false;
}

Calendar.prototype.setMenuHTML = function(s) {
	// Set menu to display on default navigation toolbar
	var p = this.MenuHTML;

	// Set flag if the menus include an option for Full calendar
	if (s.match(/\bfull\b/)) this.can_do_full_calendar = true;

	this.MenuHTML = s;
	return p;
}

Calendar.prototype.setDayButtonPrefix = function(s) {
	var p = this.DayButtonPrefix;
	this.DayButtonPrefix = s;
	return p;
}

Calendar.prototype.setDayButtonHelp = function(s) {
	// Set pop-up help text when hovering over a button.
	var p = this.DayButtonHelp;
	this.DayButtonHelp = s;
	return p;
}

Calendar.prototype.setHilites = function(hilites) {
	var date;
	if (typeof hilites == 'array') {
		for (date in hilites) {
			this.hilites[date] = 'select';	// set to default CSS highlight
		}
	} else if(typeof hilites == 'object') {
		for (date in hilites) {
			this.hilites[date] = hilites[date];// set to specified highlight
		}
	}
}

Calendar.prototype.setNavigationHTML = function(s) {
	// Set navigation toolbar
	var p = this.NavigationHTML;
	this.NavigationHTML = s;
	return p;
}

Calendar.prototype.setPrinting = function(printing) {
	var p = this.Printing;
	if (printing) { this.Printing = true; }
	else          { this.Printing = false;}
	return p;
}

Calendar.prototype.isPrinting = function() {
	if (this.Printing) return true;
	return false;
}

//-----------------------------------------------------------------------------

//
// Date activation/deactivation methods.
//

Calendar.prototype.setActiveDate = function(s) {
	// Set date before which date buttons are not highlighted.

	var p = this.ActiveDate;
	if (s && s == 'today') {
		var d = new Date();
		s = (d.getMonth()+1) + '-' + d.getDate() + '-' + d.getFullYear();
	}

	if (s) this.ActiveDate = new Date(s.replace(/-/g,"/"));	// enable
	else   this.ActiveDate = null;				// disable
	return p;
}

Calendar.prototype.isActive = function(date) {
	if (this.deactivated[date] && this.deactivated[date] != 'undefined') return false;
	if (this.activated[date] && this.activated[date] != 'undefined') return true;

	if (!this.ActiveDate) return true;	// default is yes

	var testdate = new Date(date.replace(/\W/g,"/"));
	return (testdate.valueOf() >= this.ActiveDate.valueOf());
}

Calendar.prototype.activateDate = function(date) {
	// Re-activate date by entering it in the activation object.
	if (this.deactivated[date]
	&& this.deactivated[date] != 'undefined')
		delete this.deactivated[date];
	this.activated[date] = true;

	// Adjust global lists in calendar window
	if (this.win.deactivated_dates[date]
	&& this.win.deactivated_dates[date] != 'undefined')
		delete this.win.deactivated_dates[date];
	this.win.activated_dates[date] = true;
}

Calendar.prototype.deactivateDate = function(date) {
	// De-activate date by entering it in the deactivation object.
	if (this.activated[date]
	&& this.activated[date] != 'undefined')
		delete this.activated[date];
	this.deactivated[date] = true;

	// Adjust global lists in calendar window
	if (this.win.activated_dates[date]
	&& this.win.activated_dates[date] != 'undefined')
		delete this.win.activated_dates[date];
	this.win.deactivated_dates[date] = true;
}

//-----------------------------------------------------------------------------

//
// Callback methods when user selects a date cell.
//

Calendar.prototype.doSelect = function(date) {
	
	// Select date cell and do callback function.

	this.setSelectedDate(date);

	var callback  = this.callback;
	var active = this.isActive();

	// Null callbacks are no-ops
	if (callback == null) {
		if (this.win != self) this.win.close();	// close pop-up calendar
		return true;
	}

	// Blank strings (not null) re-executes current url with date
	if (callback == '' && active) {
		var sep = (self.location.toString().search(/\?/) >= 0) ? ';' : '?';

		// Append date specification to url
		callback = self.location + sep + 'date=' + date;

		// Goto new url
		this.win.location.href = callback;
		return;
	}

	if (eval('typeof self.'+callback.replace(/\(.*\)/,'')) == 'function') {

		// Callback is a function name.
		// Three arguments are appended to the callback function
		// arguments: the calendar object, selected date as a string,
		// and a boolian flag which is true unless the active date
		// was specified and the selected date is less than the 
		// active date.

		// Note: The function must close the calendar window when done.
		// It is also responsible for making buttons inactive if before
		// the active date.

		callback = callback.replace(/\((.*)\)/,'');
		var callargs = (RegExp.$1.length > 0) ? ($RegExp.$1 + ',') : '';
		return eval(callback + '(' + callargs
			+ 'this'
			+ ', "' + date + '"'
			+ ', active'
			+ ')'
			);
	}
	
	if (!active) return;	// button is inactive

	if (typeof callback == 'string') {
		// Check if callback is a url.
		if (callback.search(/^\w+:/) >= 0) {
			var sep = (callback.search(/\?/) >= 0) ? ';' : '?';

			// Append date specification to url
			callback += sep + 'date=' + date;

			// Goto new url
			window.open(callback,'');
			self.close();
		}

		// Look for a form field with the name of the callback.
		var f,t,frm;
		if (callback.search(/(.*)\.(.*)/) >= 0) {
			// Parse form name from string.
			frm = RegExp.$1;
			fld = RegExp.$2;
			frm = frm.replace(/document\./,'');
		} else {
			// Search for field name in all forms.
			fld = callback;
			for (var i = 0; i < self.document.forms.length; i++) {
				frm = self.document.forms[i].name;
				if (document[frm][fld] != null) break;
			}
		}
		if (document[frm] == null || document[frm][fld] == null) {
			alert("Cannot find form element: "+fld+" (form="+frm+")");
		}
		f = self.document[frm][fld];
		if (f) {
			t = f.type;
			if (t == 'text' || t == 'textarea' || t == 'hidden') {
				f.value = date;

				// Do field's onChange event.
				if (! this.doOnChange(f)) {
					this.win.close();
					return false;
				}

				// Close calendar window
				this.win.close();

				return true;
			}
		}
	}
	else if (typeof callback == 'object') {
		// object is assumed to be a form field object
		callback['value'] = date;

		// Do field's onChange event.
		if (! this.doOnChange(callback)) {
			this.win.close();
			return false;
		}

		// Close calendar window
		this.win.close();

		return true;
	}
}

Calendar.prototype.doUnSelect = function(date) {
	
	this.setSelectedDate(void 0);	// unset

	if (this.uncallback != null && eval('typeof(self.'+this.uncallback.replace(/\(.*\)/,'')+')') == 'function') {
		// Callback is a function name
		var uncallback = this.uncallback.replace(/\((.*)\)/,'');
		var callargs = (RegExp.$1.length > 0) ? ($RegExp.$1 + ',') : '';
		return eval(uncallback + '(' + callargs
			+ 'this'
			+ ', "' + date + '"'
			+ ', this.isActive(date)'
			+ ')'
			);
	}
}

Calendar.prototype.doOnChange = function(obj) {

	// Utility function to execute the onChange event on form fields.

	if (obj.onchange) {

		// Note: this should be called like Javascript calls
		// an event handler. But I haven't figured that out yet
		// so I just substitute 'obj' for any reference
		// to 'this'. IE uses the function name 'anonymous()'
		// but Netscape uses 'onchange()'. Netscape needs a
		// event parameter passed to the handler, according
		// to its syntax.

		var func = obj.onchange.toString();
		func = func.replace(/\bthis\b/g,"obj");
		func.match(/(\w+) (\w+)/);
		var func_call = RegExp.$2;

		if ( eval(func + ";" + func_call + "()") == false) return false;
	}
	return true;
}

//-----------------------------------------------------------------------------

//
// Calendar methods for years
//

Calendar.prototype.getStartYear = function() {
	return this.start_date.getFullYear();
}

Calendar.prototype.setStartYear = function(y) {
	return this.start_date.setFullYear(y);
}

Calendar.prototype.addStartYear = function(n) {
	return this.setStartYear(this.start_date.getFullYear() + n);
}

//-----------------------------------------------------------------------------

//
// Calendar methods for months
//

Calendar.prototype.getStartMonth = function() {
	// Return start month (0-11)
	return this.start_date.getMonth();
}

Calendar.prototype.setStartMonth = function(m) {
	// Set current month (0-11), return previous date
	return this.start_date.setMonth(m);
}

Calendar.prototype.addStartMonth = function(n) {
	return this.setStartMonth(this.start_date.getMonth() + n);
}

Calendar.prototype.getMonthName = function(d) {
	if (!d) d = this.date;
	return Calendar.MonthNames[d.getMonth()];
}

Calendar.prototype.addMonths = function(n) {
	// Add number of months to start_date (not current date month).
	this.date.setTime(this.start_date.getTime());
	return this.date.setMonth(this.date.getMonth() + n);
}

Calendar.prototype.getMonthTitle = function() {
	var title = this.getMonthName();
	if (this.n_months == 1 || this.Type == 'day') title += ' '+this.date.getFullYear();
	if (this.n_months >= 1 && this.can_do_full_calendar && this.Type != 'full') {

		var d = (this.date.getMonth() + 1) + "/1/" + this.date.getFullYear();
		title = ''+title+'';
	}
	return ''+title+'\n';
}

//-----------------------------------------------------------------------------

//
// Calendar methods for dates
//

Calendar.prototype.addDate = function(n) {
	if (!n) n = 1;
	return this.date.setDate(this.date.getDate() + n);
}

Calendar.prototype.isWeekday = function(wday) {
	if (!wday) wday = this.date.getDay();
	if (wday == 0 || wday == 6) return false;	// not on Sun or Sat
	return true;
}

Calendar.prototype.isWeekend = function(wday) {
	return ! this.isWeekday(wday);
}

Calendar.prototype.isLeapYear = function(y) {
	if (!y) y = this.date.getFullYear();
	return ((y % 4) == 0) && ( ((y % 100) != 0) || ((y % 400) == 0) );
}

Calendar.prototype.getMonthDays = function(m, y) {
	// m between 0-11
	if (!m) m = this.date.getMonth();
	if (!y) y = this.date.getFullYear();
	if (m != 1 || ! this.isLeapYear(y)) return Calendar.MonthDays[m];
	return 29;
}

//-----------------------------------------------------------------------------

//
// Calendar display methods
//

Calendar.prototype.display = function() {
	
	this.month_index = 0;
	this.week_head_index = 0;

	this.date.setTime(this.start_date.getTime());

	if (this.win != self) {
		// This is a lookup window, write HTML header.
		this.open();

		this.write(''
		  + '\n'
		  + '\n'
		  + '\n'
		  + 'Calendar\n'
		  + '\n'
		  + '\n'
		  );

		if (this.Stylesheet) {
			this.write('\n');
		}
		if (this.AuxiliaryStylesheet) {
			this.write('\n');
		}
		this.write(this.getJavaScript());
		this.write(''
		  + '\n'
		  + '\n'
		  );
	} else {
		// This is the primary window, HTML header has already been output.
		this.write(this.getJavaScript());
		if (! document.body.parentNode.onclick && typeof do_unselect == 'function')
			document.body.parentNode.onclick = do_unselect;
	}

	var y = this.date.getFullYear();
	this.write('
\n'); this.write(this.getNavigationHTML()); if (this.n_months > 1 && this.Type != 'day') { this.write('Year '+y+'\n'); } if (this.Type == 'list') { this.write('
\n'); this.flush(); return; } if (! this.isVerticalCalendar()) this.write('
'); this.write('\n'); // Start row of calendar this.write('\n'); var m; if (this.n_months > Calendar.Columns) { // Beginning blank months to keep display even. var beginning_m = this.start_date.getMonth() % Calendar.Columns; for (m = 0; m < beginning_m; m++) { this.write('\n'); } } // Get the complete calendar code for each month. for (m = 0; m < this.n_months; m++) { this.addMonths(m); this.write('\n'); if ((m % Calendar.Columns) == (Calendar.Columns - 1) || this.isVerticalCalendar()) { this.write('\n'); this.write('\n'); } } // Finish out row of calendar with blank months. if (this.n_months > 1) { var remaining_m = (this.start_date.getMonth() + this.n_months) % Calendar.Columns; if (remaining_m > 0) { for (m = 0; m < remaining_m; m++) { this.write('\n'); } } } this.write('\n'); this.write('
\n'); this.write(this.getMonthHTML()); this.write('
'); if (! this.isVerticalCalendar()) this.write('
'); this.write('\n'); if (this.win != self) { this.write('\n'); this.write('\n'); this.close(); } else { this.flush(); // output calendar } // Redo highlights if (!this.hilites) return; var date, hilite_code; for (date in this.hilites) { hilite_code = this.hilites[date]; this.hiliteDate(hilite_code,date); } } Calendar.prototype.resize = function(cmd) { if (cmd.toString().match(/^\d+$/)) { var n_months = cmd; // Following dimensions are for a lookup window without // browser buttons, menu, scrollbar, or status line. // Note: resizeTo() uses different coordinates than // window.open(). if (n_months > 6) { w=844; h=920; } else if (n_months > 3) { w=844, h=530; } else if (n_months > 1) { w=844, h=340; } else if (n_months == 1) { w=292, h=318; } this.win.resizeTo(w,h); } } // hiliteDate() can be used after the calendar is displayed to change // a date's appearance. The hilite_code can be a background color like // '#33AA99' or it can be a CSS class name like 'holiday'. Color codes // are applied to the date's button, class names are assigned to the // TD tag enclosing the date's button. Calendar.prototype.hiliteDate = function(hilite_code,date) { var w = this.win; if (!w) w = self; if (!this.isActive(date)) this.activateDate(date); var d = new Date(date.replace(/\W/g,"/")); if (isNaN(d)) return; var id =this.DayButtonPrefix + Calendar.dateToString(d); var e = w.document.getElementById(id) this.hiliteElement(e, hilite_code); // save highlight for calendar redisplay this.hilites[date] = hilite_code; } Calendar.prototype.hiliteElement = function(e, hilite_code) { if (!e) return; if (hilite_code.match(/^#[0-9aAbBcCdDeEfF]{6}/)) { // hilite_code is a background color. e.style.backgroundColor = hilite_code; } else { // hilite_code is a css class suffix. // Highlight day button by appending hilite_code argument to // existing class name. e.className += " " + hilite_code; if (this.Type == 'full') e.parentNode.parentNode.className += " " + hilite_code; else e.parentNode.className += " " + hilite_code; } } // // Buffered ouput routines // Calendar.prototype.open = function() { return this.win.document.open(); } Calendar.prototype.write = function(s) { this.buf += s; // buffer the output } Calendar.prototype.flush = function() { this.win.document.write(this.buf); this.buf = ''; // reinitialize, free memory } Calendar.prototype.close = function() { this.flush(); return this.win.document.close(); } // // Calendar construction methods // Calendar.prototype.getJavaScript = function() { // Return the JavaScript functions needed for the calendar /* * The following Javascript supports elements which have the event * handlers defined as: * * onmouseover="hilite_date(this)" * onmouseout="unhilite_date(this)" * onclick="do_callback(this)" */ // Note: the list of dates that have been deactivated/reactivated must // reside in global variables in the calendar window. var d; var activate_dates_cmd = ''; var deactivate_dates_cmd = ''; for (d in this.activated) activate_dates_cmd += 'activated_dates["'+d+'"] = true;\n'; for (d in this.deactivated) deactivate_dates_cmd += 'deactivated_dates["'+d+'"] = true;\n'; return '' + '\n'; } Calendar.prototype.getNavigationHTML = function() { // Don't render navigation buttons on print jobs. if (this.isPrinting()) return ''; // Check if navigation bar previously defined by caller. if (this.NavigationHTML) return this.NavigationHTML; var html = ''; html += '\n'; return html; } Calendar.prototype.getMenuHTML = function() { if (this.MenuHTML) return this.MenuHTML; return ''; } Calendar.prototype.getMonthHTML = function() { // Note: on calendars with more than one month displayed, // the
tag has the standard class "month" assigned to it // and the class "month1", "month2" ... or "month12" assigned to it. var m = this.n_months <= 0 ? '' : ' month' + (++this.month_index); return '
' + this.getMonthTitle() + '
\n' + this.getWeeksHeaderHTML() + this.getWeeksHTML() + '
' + '
'; } Calendar.prototype.getWeeksHeaderHTML = function() { if (this.week_head_index++ == 0) return this.getWeeksHeaderLinksHTML(); var start_day = this.getStartDay(); var html = '\n' for (i = 0; i < 7; i++) { html += '' + Calendar.WeekNames[((start_day+i)%7)].substr(0,3) + '\n' } html += '\n'; return html; } Calendar.prototype.getWeeksHeaderLinksHTML = function() { var start_day = this.getStartDay(); var html = '\n' var i,d; for (i = 0; i < 7; i++) { d = (start_day + i) % 7; html += '' + '' + '' + Calendar.WeekNames[d].substr(0,3) + '\n' } html += '\n'; return html; } Calendar.prototype.getWeeksHTML = function() { // Return HTML for all weeks in month. this.date.setDate(1); // safety var i; var first_day=((this.date.getDay() - this.getStartDay()) + 7) % 7; var last_date=this.getMonthDays(); var html = ''; // Write first week up to first day of month html += '\n'; if (first_day > 0) { // Write first week up to first day. this.addDate(-first_day); // set calendar back for display for (i=0; i < first_day; i++) { if (this.n_months > 1) { html += '\n'; } else { html += '' + this.getBlankDateHTML() + '\n'; } this.addDate(1); } } // Write calendar var tdclass; for (i = 0; i < last_date; i++) { // Note: 4/2004: The new schema of CSS classes now assigns multiple classes // names to the day's TD tag, so add "today" class name if needed. tdclass = "day" + ((this.date.getTime() == this.today.getTime()) ? " today" : ""); html += '' + this.getDateHTML() + '\n'; this.addDate(1); if ( ((first_day+i) % Calendar.WeekColumns) == (Calendar.WeekColumns-1) ) { html += "\n"; html += '\n'; } } // Finish out week with blank days var remaining_days = Calendar.WeekColumns - ((first_day+last_date) % Calendar.WeekColumns); if (remaining_days > 0 && remaining_days < Calendar.WeekColumns) { for (i = 0; i < remaining_days; i++) { if (this.n_months > 1) { html += ''; } else { html += '' + this.getBlankDateHTML() + '\n'; } this.addDate(1); } this.addDate(-remaining_days); // reset date to 1st of next mo. } html += "\n"; return html; } Calendar.prototype.getDateHTML = function() { var date_str = Calendar.dateToString(this.date); var id =this.DayButtonPrefix + date_str; var d = this.date.getDate(); if (this.Type == 'full') { // Surround date button with info box return '
' + '' + '
'; } else { return ''; } } Calendar.prototype.getBlankDateHTML = function() { if (this.n_months > 1) return ' '; if (this.Type == 'full') { return '
' + '
' + this.date.getDate() + '
' + '
'; } else { return '
' + this.date.getDate() + '
'; } } Calendar.prototype.getDayClass = function() { // Return class for days in calendar var s = this.isWeekday() ? "week-day" : "weekend-day"; if (this.date.getTime() == this.today.getTime()) s += " today"; return s; } Calendar.prototype.getBlankDayClass = function() { // Return class for blank days in calendar return this.isWeekday() ? "week-day-blank" : "weekend-day-blank"; }