web/wp-content/plugins/event-calendar/js/calendar.js
changeset 136 bde1974c263b
equal deleted inserted replaced
135:53cff4b4a802 136:bde1974c263b
       
     1 /*  Copyright Mihai Bazon, 2002-2005  |  www.bazon.net/mishoo
       
     2  * -----------------------------------------------------------
       
     3  *
       
     4  * The DHTML Calendar, version 1.0 "It is happening again"
       
     5  *
       
     6  * Details and latest version at:
       
     7  * www.dynarch.com/projects/calendar
       
     8  *
       
     9  * This script is developed by Dynarch.com.  Visit us at www.dynarch.com.
       
    10  *
       
    11  * This script is distributed under the GNU Lesser General Public License.
       
    12  * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
       
    13  */
       
    14 
       
    15 // $Id: calendar.js,v 1.51 2005/03/07 16:44:31 mishoo Exp $
       
    16 
       
    17 /** The Calendar object constructor. */
       
    18 Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
       
    19 	// member variables
       
    20 	this.activeDiv = null;
       
    21 	this.currentDateEl = null;
       
    22 	this.getDateStatus = null;
       
    23 	this.getDateToolTip = null;
       
    24 	this.getDateText = null;
       
    25 	this.timeout = null;
       
    26 	this.onSelected = onSelected || null;
       
    27 	this.onClose = onClose || null;
       
    28 	this.dragging = false;
       
    29 	this.hidden = false;
       
    30 	this.minYear = 1970;
       
    31 	this.maxYear = 2050;
       
    32 	this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
       
    33 	this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
       
    34 	this.isPopup = true;
       
    35 	this.weekNumbers = true;
       
    36 	this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
       
    37 	this.showsOtherMonths = false;
       
    38 	this.dateStr = dateStr;
       
    39 	this.ar_days = null;
       
    40 	this.showsTime = false;
       
    41 	this.time24 = true;
       
    42 	this.yearStep = 2;
       
    43 	this.hiliteToday = true;
       
    44 	this.multiple = null;
       
    45 	// HTML elements
       
    46 	this.table = null;
       
    47 	this.element = null;
       
    48 	this.tbody = null;
       
    49 	this.firstdayname = null;
       
    50 	// Combo boxes
       
    51 	this.monthsCombo = null;
       
    52 	this.yearsCombo = null;
       
    53 	this.hilitedMonth = null;
       
    54 	this.activeMonth = null;
       
    55 	this.hilitedYear = null;
       
    56 	this.activeYear = null;
       
    57 	// Information
       
    58 	this.dateClicked = false;
       
    59 
       
    60 	// one-time initializations
       
    61 	if (typeof Calendar._SDN == "undefined") {
       
    62 		// table of short day names
       
    63 		if (typeof Calendar._SDN_len == "undefined")
       
    64 			Calendar._SDN_len = 3;
       
    65 		var ar = new Array();
       
    66 		for (var i = 8; i > 0;) {
       
    67 			ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
       
    68 		}
       
    69 		Calendar._SDN = ar;
       
    70 		// table of short month names
       
    71 		if (typeof Calendar._SMN_len == "undefined")
       
    72 			Calendar._SMN_len = 3;
       
    73 		ar = new Array();
       
    74 		for (var i = 12; i > 0;) {
       
    75 			ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
       
    76 		}
       
    77 		Calendar._SMN = ar;
       
    78 	}
       
    79 };
       
    80 
       
    81 // ** constants
       
    82 
       
    83 /// "static", needed for event handlers.
       
    84 Calendar._C = null;
       
    85 
       
    86 /// detect a special case of "web browser"
       
    87 Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
       
    88 		   !/opera/i.test(navigator.userAgent) );
       
    89 
       
    90 Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
       
    91 
       
    92 /// detect Opera browser
       
    93 Calendar.is_opera = /opera/i.test(navigator.userAgent);
       
    94 
       
    95 /// detect KHTML-based browsers
       
    96 Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
       
    97 
       
    98 // BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
       
    99 //        library, at some point.
       
   100 
       
   101 Calendar.getAbsolutePos = function(el) {
       
   102 	var SL = 0, ST = 0;
       
   103 	var is_div = /^div$/i.test(el.tagName);
       
   104 	if (is_div && el.scrollLeft)
       
   105 		SL = el.scrollLeft;
       
   106 	if (is_div && el.scrollTop)
       
   107 		ST = el.scrollTop;
       
   108 	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
       
   109 	if (el.offsetParent) {
       
   110 		var tmp = this.getAbsolutePos(el.offsetParent);
       
   111 		r.x += tmp.x;
       
   112 		r.y += tmp.y;
       
   113 	}
       
   114 	return r;
       
   115 };
       
   116 
       
   117 Calendar.isRelated = function (el, evt) {
       
   118 	var related = evt.relatedTarget;
       
   119 	if (!related) {
       
   120 		var type = evt.type;
       
   121 		if (type == "mouseover") {
       
   122 			related = evt.fromElement;
       
   123 		} else if (type == "mouseout") {
       
   124 			related = evt.toElement;
       
   125 		}
       
   126 	}
       
   127 	while (related) {
       
   128 		if (related == el) {
       
   129 			return true;
       
   130 		}
       
   131 		related = related.parentNode;
       
   132 	}
       
   133 	return false;
       
   134 };
       
   135 
       
   136 Calendar.removeClass = function(el, className) {
       
   137 	if (!(el && el.className)) {
       
   138 		return;
       
   139 	}
       
   140 	var cls = el.className.split(" ");
       
   141 	var ar = new Array();
       
   142 	for (var i = cls.length; i > 0;) {
       
   143 		if (cls[--i] != className) {
       
   144 			ar[ar.length] = cls[i];
       
   145 		}
       
   146 	}
       
   147 	el.className = ar.join(" ");
       
   148 };
       
   149 
       
   150 Calendar.addClass = function(el, className) {
       
   151 	Calendar.removeClass(el, className);
       
   152 	el.className += " " + className;
       
   153 };
       
   154 
       
   155 // FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
       
   156 Calendar.getElement = function(ev) {
       
   157 	var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
       
   158 	while (f.nodeType != 1 || /^div$/i.test(f.tagName))
       
   159 		f = f.parentNode;
       
   160 	return f;
       
   161 };
       
   162 
       
   163 Calendar.getTargetElement = function(ev) {
       
   164 	var f = Calendar.is_ie ? window.event.srcElement : ev.target;
       
   165 	while (f.nodeType != 1)
       
   166 		f = f.parentNode;
       
   167 	return f;
       
   168 };
       
   169 
       
   170 Calendar.stopEvent = function(ev) {
       
   171 	ev || (ev = window.event);
       
   172 	if (Calendar.is_ie) {
       
   173 		ev.cancelBubble = true;
       
   174 		ev.returnValue = false;
       
   175 	} else {
       
   176 		ev.preventDefault();
       
   177 		ev.stopPropagation();
       
   178 	}
       
   179 	return false;
       
   180 };
       
   181 
       
   182 Calendar.addEvent = function(el, evname, func) {
       
   183 	if (el.attachEvent) { // IE
       
   184 		el.attachEvent("on" + evname, func);
       
   185 	} else if (el.addEventListener) { // Gecko / W3C
       
   186 		el.addEventListener(evname, func, true);
       
   187 	} else {
       
   188 		el["on" + evname] = func;
       
   189 	}
       
   190 };
       
   191 
       
   192 Calendar.removeEvent = function(el, evname, func) {
       
   193 	if (el.detachEvent) { // IE
       
   194 		el.detachEvent("on" + evname, func);
       
   195 	} else if (el.removeEventListener) { // Gecko / W3C
       
   196 		el.removeEventListener(evname, func, true);
       
   197 	} else {
       
   198 		el["on" + evname] = null;
       
   199 	}
       
   200 };
       
   201 
       
   202 Calendar.createElement = function(type, parent) {
       
   203 	var el = null;
       
   204 	if (document.createElementNS) {
       
   205 		// use the XHTML namespace; IE won't normally get here unless
       
   206 		// _they_ "fix" the DOM2 implementation.
       
   207 		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
       
   208 	} else {
       
   209 		el = document.createElement(type);
       
   210 	}
       
   211 	if (typeof parent != "undefined") {
       
   212 		parent.appendChild(el);
       
   213 	}
       
   214 	return el;
       
   215 };
       
   216 
       
   217 // END: UTILITY FUNCTIONS
       
   218 
       
   219 // BEGIN: CALENDAR STATIC FUNCTIONS
       
   220 
       
   221 /** Internal -- adds a set of events to make some element behave like a button. */
       
   222 Calendar._add_evs = function(el) {
       
   223 	with (Calendar) {
       
   224 		addEvent(el, "mouseover", dayMouseOver);
       
   225 		addEvent(el, "mousedown", dayMouseDown);
       
   226 		addEvent(el, "mouseout", dayMouseOut);
       
   227 		if (is_ie) {
       
   228 			addEvent(el, "dblclick", dayMouseDblClick);
       
   229 			el.setAttribute("unselectable", true);
       
   230 		}
       
   231 	}
       
   232 };
       
   233 
       
   234 Calendar.findMonth = function(el) {
       
   235 	if (typeof el.month != "undefined") {
       
   236 		return el;
       
   237 	} else if (typeof el.parentNode.month != "undefined") {
       
   238 		return el.parentNode;
       
   239 	}
       
   240 	return null;
       
   241 };
       
   242 
       
   243 Calendar.findYear = function(el) {
       
   244 	if (typeof el.year != "undefined") {
       
   245 		return el;
       
   246 	} else if (typeof el.parentNode.year != "undefined") {
       
   247 		return el.parentNode;
       
   248 	}
       
   249 	return null;
       
   250 };
       
   251 
       
   252 Calendar.showMonthsCombo = function () {
       
   253 	var cal = Calendar._C;
       
   254 	if (!cal) {
       
   255 		return false;
       
   256 	}
       
   257 	var cal = cal;
       
   258 	var cd = cal.activeDiv;
       
   259 	var mc = cal.monthsCombo;
       
   260 	if (cal.hilitedMonth) {
       
   261 		Calendar.removeClass(cal.hilitedMonth, "hilite");
       
   262 	}
       
   263 	if (cal.activeMonth) {
       
   264 		Calendar.removeClass(cal.activeMonth, "active");
       
   265 	}
       
   266 	var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
       
   267 	Calendar.addClass(mon, "active");
       
   268 	cal.activeMonth = mon;
       
   269 	var s = mc.style;
       
   270 	s.display = "block";
       
   271 	if (cd.navtype < 0)
       
   272 		s.left = cd.offsetLeft + "px";
       
   273 	else {
       
   274 		var mcw = mc.offsetWidth;
       
   275 		if (typeof mcw == "undefined")
       
   276 			// Konqueror brain-dead techniques
       
   277 			mcw = 50;
       
   278 		s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
       
   279 	}
       
   280 	s.top = (cd.offsetTop + cd.offsetHeight) + "px";
       
   281 };
       
   282 
       
   283 Calendar.showYearsCombo = function (fwd) {
       
   284 	var cal = Calendar._C;
       
   285 	if (!cal) {
       
   286 		return false;
       
   287 	}
       
   288 	var cal = cal;
       
   289 	var cd = cal.activeDiv;
       
   290 	var yc = cal.yearsCombo;
       
   291 	if (cal.hilitedYear) {
       
   292 		Calendar.removeClass(cal.hilitedYear, "hilite");
       
   293 	}
       
   294 	if (cal.activeYear) {
       
   295 		Calendar.removeClass(cal.activeYear, "active");
       
   296 	}
       
   297 	cal.activeYear = null;
       
   298 	var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
       
   299 	var yr = yc.firstChild;
       
   300 	var show = false;
       
   301 	for (var i = 12; i > 0; --i) {
       
   302 		if (Y >= cal.minYear && Y <= cal.maxYear) {
       
   303 			yr.innerHTML = Y;
       
   304 			yr.year = Y;
       
   305 			yr.style.display = "block";
       
   306 			show = true;
       
   307 		} else {
       
   308 			yr.style.display = "none";
       
   309 		}
       
   310 		yr = yr.nextSibling;
       
   311 		Y += fwd ? cal.yearStep : -cal.yearStep;
       
   312 	}
       
   313 	if (show) {
       
   314 		var s = yc.style;
       
   315 		s.display = "block";
       
   316 		if (cd.navtype < 0)
       
   317 			s.left = cd.offsetLeft + "px";
       
   318 		else {
       
   319 			var ycw = yc.offsetWidth;
       
   320 			if (typeof ycw == "undefined")
       
   321 				// Konqueror brain-dead techniques
       
   322 				ycw = 50;
       
   323 			s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
       
   324 		}
       
   325 		s.top = (cd.offsetTop + cd.offsetHeight) + "px";
       
   326 	}
       
   327 };
       
   328 
       
   329 // event handlers
       
   330 
       
   331 Calendar.tableMouseUp = function(ev) {
       
   332 	var cal = Calendar._C;
       
   333 	if (!cal) {
       
   334 		return false;
       
   335 	}
       
   336 	if (cal.timeout) {
       
   337 		clearTimeout(cal.timeout);
       
   338 	}
       
   339 	var el = cal.activeDiv;
       
   340 	if (!el) {
       
   341 		return false;
       
   342 	}
       
   343 	var target = Calendar.getTargetElement(ev);
       
   344 	ev || (ev = window.event);
       
   345 	Calendar.removeClass(el, "active");
       
   346 	if (target == el || target.parentNode == el) {
       
   347 		Calendar.cellClick(el, ev);
       
   348 	}
       
   349 	var mon = Calendar.findMonth(target);
       
   350 	var date = null;
       
   351 	if (mon) {
       
   352 		date = new Date(cal.date);
       
   353 		if (mon.month != date.getMonth()) {
       
   354 			date.setMonth(mon.month);
       
   355 			cal.setDate(date);
       
   356 			cal.dateClicked = false;
       
   357 			cal.callHandler();
       
   358 		}
       
   359 	} else {
       
   360 		var year = Calendar.findYear(target);
       
   361 		if (year) {
       
   362 			date = new Date(cal.date);
       
   363 			if (year.year != date.getFullYear()) {
       
   364 				date.setFullYear(year.year);
       
   365 				cal.setDate(date);
       
   366 				cal.dateClicked = false;
       
   367 				cal.callHandler();
       
   368 			}
       
   369 		}
       
   370 	}
       
   371 	with (Calendar) {
       
   372 		removeEvent(document, "mouseup", tableMouseUp);
       
   373 		removeEvent(document, "mouseover", tableMouseOver);
       
   374 		removeEvent(document, "mousemove", tableMouseOver);
       
   375 		cal._hideCombos();
       
   376 		_C = null;
       
   377 		return stopEvent(ev);
       
   378 	}
       
   379 };
       
   380 
       
   381 Calendar.tableMouseOver = function (ev) {
       
   382 	var cal = Calendar._C;
       
   383 	if (!cal) {
       
   384 		return;
       
   385 	}
       
   386 	var el = cal.activeDiv;
       
   387 	var target = Calendar.getTargetElement(ev);
       
   388 	if (target == el || target.parentNode == el) {
       
   389 		Calendar.addClass(el, "hilite active");
       
   390 		Calendar.addClass(el.parentNode, "rowhilite");
       
   391 	} else {
       
   392 		if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
       
   393 			Calendar.removeClass(el, "active");
       
   394 		Calendar.removeClass(el, "hilite");
       
   395 		Calendar.removeClass(el.parentNode, "rowhilite");
       
   396 	}
       
   397 	ev || (ev = window.event);
       
   398 	if (el.navtype == 50 && target != el) {
       
   399 		var pos = Calendar.getAbsolutePos(el);
       
   400 		var w = el.offsetWidth;
       
   401 		var x = ev.clientX;
       
   402 		var dx;
       
   403 		var decrease = true;
       
   404 		if (x > pos.x + w) {
       
   405 			dx = x - pos.x - w;
       
   406 			decrease = false;
       
   407 		} else
       
   408 			dx = pos.x - x;
       
   409 
       
   410 		if (dx < 0) dx = 0;
       
   411 		var range = el._range;
       
   412 		var current = el._current;
       
   413 		var count = Math.floor(dx / 10) % range.length;
       
   414 		for (var i = range.length; --i >= 0;)
       
   415 			if (range[i] == current)
       
   416 				break;
       
   417 		while (count-- > 0)
       
   418 			if (decrease) {
       
   419 				if (--i < 0)
       
   420 					i = range.length - 1;
       
   421 			} else if ( ++i >= range.length )
       
   422 				i = 0;
       
   423 		var newval = range[i];
       
   424 		el.innerHTML = newval;
       
   425 
       
   426 		cal.onUpdateTime();
       
   427 	}
       
   428 	var mon = Calendar.findMonth(target);
       
   429 	if (mon) {
       
   430 		if (mon.month != cal.date.getMonth()) {
       
   431 			if (cal.hilitedMonth) {
       
   432 				Calendar.removeClass(cal.hilitedMonth, "hilite");
       
   433 			}
       
   434 			Calendar.addClass(mon, "hilite");
       
   435 			cal.hilitedMonth = mon;
       
   436 		} else if (cal.hilitedMonth) {
       
   437 			Calendar.removeClass(cal.hilitedMonth, "hilite");
       
   438 		}
       
   439 	} else {
       
   440 		if (cal.hilitedMonth) {
       
   441 			Calendar.removeClass(cal.hilitedMonth, "hilite");
       
   442 		}
       
   443 		var year = Calendar.findYear(target);
       
   444 		if (year) {
       
   445 			if (year.year != cal.date.getFullYear()) {
       
   446 				if (cal.hilitedYear) {
       
   447 					Calendar.removeClass(cal.hilitedYear, "hilite");
       
   448 				}
       
   449 				Calendar.addClass(year, "hilite");
       
   450 				cal.hilitedYear = year;
       
   451 			} else if (cal.hilitedYear) {
       
   452 				Calendar.removeClass(cal.hilitedYear, "hilite");
       
   453 			}
       
   454 		} else if (cal.hilitedYear) {
       
   455 			Calendar.removeClass(cal.hilitedYear, "hilite");
       
   456 		}
       
   457 	}
       
   458 	return Calendar.stopEvent(ev);
       
   459 };
       
   460 
       
   461 Calendar.tableMouseDown = function (ev) {
       
   462 	if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
       
   463 		return Calendar.stopEvent(ev);
       
   464 	}
       
   465 };
       
   466 
       
   467 Calendar.calDragIt = function (ev) {
       
   468 	var cal = Calendar._C;
       
   469 	if (!(cal && cal.dragging)) {
       
   470 		return false;
       
   471 	}
       
   472 	var posX;
       
   473 	var posY;
       
   474 	if (Calendar.is_ie) {
       
   475 		posY = window.event.clientY + document.body.scrollTop;
       
   476 		posX = window.event.clientX + document.body.scrollLeft;
       
   477 	} else {
       
   478 		posX = ev.pageX;
       
   479 		posY = ev.pageY;
       
   480 	}
       
   481 	cal.hideShowCovered();
       
   482 	var st = cal.element.style;
       
   483 	st.left = (posX - cal.xOffs) + "px";
       
   484 	st.top = (posY - cal.yOffs) + "px";
       
   485 	return Calendar.stopEvent(ev);
       
   486 };
       
   487 
       
   488 Calendar.calDragEnd = function (ev) {
       
   489 	var cal = Calendar._C;
       
   490 	if (!cal) {
       
   491 		return false;
       
   492 	}
       
   493 	cal.dragging = false;
       
   494 	with (Calendar) {
       
   495 		removeEvent(document, "mousemove", calDragIt);
       
   496 		removeEvent(document, "mouseup", calDragEnd);
       
   497 		tableMouseUp(ev);
       
   498 	}
       
   499 	cal.hideShowCovered();
       
   500 };
       
   501 
       
   502 Calendar.dayMouseDown = function(ev) {
       
   503 	var el = Calendar.getElement(ev);
       
   504 	if (el.disabled) {
       
   505 		return false;
       
   506 	}
       
   507 	var cal = el.calendar;
       
   508 	cal.activeDiv = el;
       
   509 	Calendar._C = cal;
       
   510 	if (el.navtype != 300) with (Calendar) {
       
   511 		if (el.navtype == 50) {
       
   512 			el._current = el.innerHTML;
       
   513 			addEvent(document, "mousemove", tableMouseOver);
       
   514 		} else
       
   515 			addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
       
   516 		addClass(el, "hilite active");
       
   517 		addEvent(document, "mouseup", tableMouseUp);
       
   518 	} else if (cal.isPopup) {
       
   519 		cal._dragStart(ev);
       
   520 	}
       
   521 	if (el.navtype == -1 || el.navtype == 1) {
       
   522 		if (cal.timeout) clearTimeout(cal.timeout);
       
   523 		cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
       
   524 	} else if (el.navtype == -2 || el.navtype == 2) {
       
   525 		if (cal.timeout) clearTimeout(cal.timeout);
       
   526 		cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
       
   527 	} else {
       
   528 		cal.timeout = null;
       
   529 	}
       
   530 	return Calendar.stopEvent(ev);
       
   531 };
       
   532 
       
   533 Calendar.dayMouseDblClick = function(ev) {
       
   534 	Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
       
   535 	if (Calendar.is_ie) {
       
   536 		document.selection.empty();
       
   537 	}
       
   538 };
       
   539 
       
   540 Calendar.dayMouseOver = function(ev) {
       
   541 	var el = Calendar.getElement(ev);
       
   542 	if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
       
   543 		return false;
       
   544 	}
       
   545 	if (el.ttip) {
       
   546 		if (el.ttip.substr(0, 1) == "_") {
       
   547 			el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
       
   548 		}
       
   549 		el.calendar.tooltips.innerHTML = el.ttip;
       
   550 	}
       
   551 	if (el.navtype != 300) {
       
   552 		Calendar.addClass(el, "hilite");
       
   553 		if (el.caldate) {
       
   554 			Calendar.addClass(el.parentNode, "rowhilite");
       
   555 		}
       
   556 	}
       
   557 	return Calendar.stopEvent(ev);
       
   558 };
       
   559 
       
   560 Calendar.dayMouseOut = function(ev) {
       
   561 	with (Calendar) {
       
   562 		var el = getElement(ev);
       
   563 		if (isRelated(el, ev) || _C || el.disabled)
       
   564 			return false;
       
   565 		removeClass(el, "hilite");
       
   566 		if (el.caldate)
       
   567 			removeClass(el.parentNode, "rowhilite");
       
   568 		if (el.calendar)
       
   569 			el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
       
   570 		return stopEvent(ev);
       
   571 	}
       
   572 };
       
   573 
       
   574 /**
       
   575  *  A generic "click" handler :) handles all types of buttons defined in this
       
   576  *  calendar.
       
   577  */
       
   578 Calendar.cellClick = function(el, ev) {
       
   579 	var cal = el.calendar;
       
   580 	var closing = false;
       
   581 	var newdate = false;
       
   582 	var date = null;
       
   583 	if (typeof el.navtype == "undefined") {
       
   584 		if (cal.currentDateEl) {
       
   585 			Calendar.removeClass(cal.currentDateEl, "selected");
       
   586 			Calendar.addClass(el, "selected");
       
   587 			closing = (cal.currentDateEl == el);
       
   588 			if (!closing) {
       
   589 				cal.currentDateEl = el;
       
   590 			}
       
   591 		}
       
   592 		cal.date.setDateOnly(el.caldate);
       
   593 		date = cal.date;
       
   594 		var other_month = !(cal.dateClicked = !el.otherMonth);
       
   595 		if (!other_month && !cal.currentDateEl)
       
   596 			cal._toggleMultipleDate(new Date(date));
       
   597 		else
       
   598 			newdate = !el.disabled;
       
   599 		// a date was clicked
       
   600 		if (other_month)
       
   601 			cal._init(cal.firstDayOfWeek, date);
       
   602 	} else {
       
   603 		if (el.navtype == 200) {
       
   604 			Calendar.removeClass(el, "hilite");
       
   605 			cal.callCloseHandler();
       
   606 			return;
       
   607 		}
       
   608 		date = new Date(cal.date);
       
   609 		if (el.navtype == 0)
       
   610 			date.setDateOnly(new Date()); // TODAY
       
   611 		// unless "today" was clicked, we assume no date was clicked so
       
   612 		// the selected handler will know not to close the calenar when
       
   613 		// in single-click mode.
       
   614 		// cal.dateClicked = (el.navtype == 0);
       
   615 		cal.dateClicked = false;
       
   616 		var year = date.getFullYear();
       
   617 		var mon = date.getMonth();
       
   618 		function setMonth(m) {
       
   619 			var day = date.getDate();
       
   620 			var max = date.getMonthDays(m);
       
   621 			if (day > max) {
       
   622 				date.setDate(max);
       
   623 			}
       
   624 			date.setMonth(m);
       
   625 		};
       
   626 		switch (el.navtype) {
       
   627 		    case 400:
       
   628 			Calendar.removeClass(el, "hilite");
       
   629 			var text = Calendar._TT["ABOUT"];
       
   630 			if (typeof text != "undefined") {
       
   631 				text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
       
   632 			} else {
       
   633 				// FIXME: this should be removed as soon as lang files get updated!
       
   634 				text = "Help and about box text is not translated into this language.\n" +
       
   635 					"If you know this language and you feel generous please update\n" +
       
   636 					"the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
       
   637 					"and send it back to <mihai_bazon@yahoo.com> to get it into the distribution  ;-)\n\n" +
       
   638 					"Thank you!\n" +
       
   639 					"http://dynarch.com/mishoo/calendar.epl\n";
       
   640 			}
       
   641 			alert(text);
       
   642 			return;
       
   643 		    case -2:
       
   644 			if (year > cal.minYear) {
       
   645 				date.setFullYear(year - 1);
       
   646 			}
       
   647 			break;
       
   648 		    case -1:
       
   649 			if (mon > 0) {
       
   650 				setMonth(mon - 1);
       
   651 			} else if (year-- > cal.minYear) {
       
   652 				date.setFullYear(year);
       
   653 				setMonth(11);
       
   654 			}
       
   655 			break;
       
   656 		    case 1:
       
   657 			if (mon < 11) {
       
   658 				setMonth(mon + 1);
       
   659 			} else if (year < cal.maxYear) {
       
   660 				date.setFullYear(year + 1);
       
   661 				setMonth(0);
       
   662 			}
       
   663 			break;
       
   664 		    case 2:
       
   665 			if (year < cal.maxYear) {
       
   666 				date.setFullYear(year + 1);
       
   667 			}
       
   668 			break;
       
   669 		    case 100:
       
   670 			cal.setFirstDayOfWeek(el.fdow);
       
   671 			return;
       
   672 		    case 50:
       
   673 			var range = el._range;
       
   674 			var current = el.innerHTML;
       
   675 			for (var i = range.length; --i >= 0;)
       
   676 				if (range[i] == current)
       
   677 					break;
       
   678 			if (ev && ev.shiftKey) {
       
   679 				if (--i < 0)
       
   680 					i = range.length - 1;
       
   681 			} else if ( ++i >= range.length )
       
   682 				i = 0;
       
   683 			var newval = range[i];
       
   684 			el.innerHTML = newval;
       
   685 			cal.onUpdateTime();
       
   686 			return;
       
   687 		    case 0:
       
   688 			// TODAY will bring us here
       
   689 			if ((typeof cal.getDateStatus == "function") &&
       
   690 			    cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
       
   691 				return false;
       
   692 			}
       
   693 			break;
       
   694 		}
       
   695 		if (!date.equalsTo(cal.date)) {
       
   696 			cal.setDate(date);
       
   697 			newdate = true;
       
   698 		} else if (el.navtype == 0)
       
   699 			newdate = closing = true;
       
   700 	}
       
   701 	if (newdate) {
       
   702 		ev && cal.callHandler();
       
   703 	}
       
   704 	if (closing) {
       
   705 		Calendar.removeClass(el, "hilite");
       
   706 		ev && cal.callCloseHandler();
       
   707 	}
       
   708 };
       
   709 
       
   710 // END: CALENDAR STATIC FUNCTIONS
       
   711 
       
   712 // BEGIN: CALENDAR OBJECT FUNCTIONS
       
   713 
       
   714 /**
       
   715  *  This function creates the calendar inside the given parent.  If _par is
       
   716  *  null than it creates a popup calendar inside the BODY element.  If _par is
       
   717  *  an element, be it BODY, then it creates a non-popup calendar (still
       
   718  *  hidden).  Some properties need to be set before calling this function.
       
   719  */
       
   720 Calendar.prototype.create = function (_par) {
       
   721 	var parent = null;
       
   722 	if (! _par) {
       
   723 		// default parent is the document body, in which case we create
       
   724 		// a popup calendar.
       
   725 		parent = document.getElementsByTagName("body")[0];
       
   726 		this.isPopup = true;
       
   727 	} else {
       
   728 		parent = _par;
       
   729 		this.isPopup = false;
       
   730 	}
       
   731 	this.date = this.dateStr ? new Date(this.dateStr) : new Date();
       
   732 
       
   733 	var table = Calendar.createElement("table");
       
   734 	this.table = table;
       
   735 	table.cellSpacing = 0;
       
   736 	table.cellPadding = 0;
       
   737 	table.calendar = this;
       
   738 	Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
       
   739 
       
   740 	var div = Calendar.createElement("div");
       
   741 	this.element = div;
       
   742 	div.className = "calendar";
       
   743 	if (this.isPopup) {
       
   744 		div.style.position = "absolute";
       
   745 		div.style.display = "none";
       
   746 	}
       
   747 	div.appendChild(table);
       
   748 
       
   749 	var thead = Calendar.createElement("thead", table);
       
   750 	var cell = null;
       
   751 	var row = null;
       
   752 
       
   753 	var cal = this;
       
   754 	var hh = function (text, cs, navtype) {
       
   755 		cell = Calendar.createElement("td", row);
       
   756 		cell.colSpan = cs;
       
   757 		cell.className = "button";
       
   758 		if (navtype != 0 && Math.abs(navtype) <= 2)
       
   759 			cell.className += " nav";
       
   760 		Calendar._add_evs(cell);
       
   761 		cell.calendar = cal;
       
   762 		cell.navtype = navtype;
       
   763 		cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
       
   764 		return cell;
       
   765 	};
       
   766 
       
   767 	row = Calendar.createElement("tr", thead);
       
   768 	var title_length = 6;
       
   769 	(this.isPopup) && --title_length;
       
   770 	(this.weekNumbers) && ++title_length;
       
   771 
       
   772 	hh("?", 1, 400).ttip = Calendar._TT["INFO"];
       
   773 	this.title = hh("", title_length, 300);
       
   774 	this.title.className = "title";
       
   775 	if (this.isPopup) {
       
   776 		this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
       
   777 		this.title.style.cursor = "move";
       
   778 		hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
       
   779 	}
       
   780 
       
   781 	row = Calendar.createElement("tr", thead);
       
   782 	row.className = "headrow";
       
   783 
       
   784 	this._nav_py = hh("&#x00ab;", 1, -2);
       
   785 	this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
       
   786 
       
   787 	this._nav_pm = hh("&#x2039;", 1, -1);
       
   788 	this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
       
   789 
       
   790 	this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
       
   791 	this._nav_now.ttip = Calendar._TT["GO_TODAY"];
       
   792 
       
   793 	this._nav_nm = hh("&#x203a;", 1, 1);
       
   794 	this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
       
   795 
       
   796 	this._nav_ny = hh("&#x00bb;", 1, 2);
       
   797 	this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
       
   798 
       
   799 	// day names
       
   800 	row = Calendar.createElement("tr", thead);
       
   801 	row.className = "daynames";
       
   802 	if (this.weekNumbers) {
       
   803 		cell = Calendar.createElement("td", row);
       
   804 		cell.className = "name wn";
       
   805 		cell.innerHTML = Calendar._TT["WK"];
       
   806 	}
       
   807 	for (var i = 7; i > 0; --i) {
       
   808 		cell = Calendar.createElement("td", row);
       
   809 		if (!i) {
       
   810 			cell.navtype = 100;
       
   811 			cell.calendar = this;
       
   812 			Calendar._add_evs(cell);
       
   813 		}
       
   814 	}
       
   815 	this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
       
   816 	this._displayWeekdays();
       
   817 
       
   818 	var tbody = Calendar.createElement("tbody", table);
       
   819 	this.tbody = tbody;
       
   820 
       
   821 	for (i = 6; i > 0; --i) {
       
   822 		row = Calendar.createElement("tr", tbody);
       
   823 		if (this.weekNumbers) {
       
   824 			cell = Calendar.createElement("td", row);
       
   825 		}
       
   826 		for (var j = 7; j > 0; --j) {
       
   827 			cell = Calendar.createElement("td", row);
       
   828 			cell.calendar = this;
       
   829 			Calendar._add_evs(cell);
       
   830 		}
       
   831 	}
       
   832 
       
   833 	if (this.showsTime) {
       
   834 		row = Calendar.createElement("tr", tbody);
       
   835 		row.className = "time";
       
   836 
       
   837 		cell = Calendar.createElement("td", row);
       
   838 		cell.className = "time";
       
   839 		cell.colSpan = 2;
       
   840 		cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";
       
   841 
       
   842 		cell = Calendar.createElement("td", row);
       
   843 		cell.className = "time";
       
   844 		cell.colSpan = this.weekNumbers ? 4 : 3;
       
   845 
       
   846 		(function(){
       
   847 			function makeTimePart(className, init, range_start, range_end) {
       
   848 				var part = Calendar.createElement("span", cell);
       
   849 				part.className = className;
       
   850 				part.innerHTML = init;
       
   851 				part.calendar = cal;
       
   852 				part.ttip = Calendar._TT["TIME_PART"];
       
   853 				part.navtype = 50;
       
   854 				part._range = [];
       
   855 				if (typeof range_start != "number")
       
   856 					part._range = range_start;
       
   857 				else {
       
   858 					for (var i = range_start; i <= range_end; ++i) {
       
   859 						var txt;
       
   860 						if (i < 10 && range_end >= 10) txt = '0' + i;
       
   861 						else txt = '' + i;
       
   862 						part._range[part._range.length] = txt;
       
   863 					}
       
   864 				}
       
   865 				Calendar._add_evs(part);
       
   866 				return part;
       
   867 			};
       
   868 			var hrs = cal.date.getHours();
       
   869 			var mins = cal.date.getMinutes();
       
   870 			var t12 = !cal.time24;
       
   871 			var pm = (hrs > 12);
       
   872 			if (t12 && pm) hrs -= 12;
       
   873 			var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
       
   874 			var span = Calendar.createElement("span", cell);
       
   875 			span.innerHTML = ":";
       
   876 			span.className = "colon";
       
   877 			var M = makeTimePart("minute", mins, 0, 59);
       
   878 			var AP = null;
       
   879 			cell = Calendar.createElement("td", row);
       
   880 			cell.className = "time";
       
   881 			cell.colSpan = 2;
       
   882 			if (t12)
       
   883 				AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
       
   884 			else
       
   885 				cell.innerHTML = "&nbsp;";
       
   886 
       
   887 			cal.onSetTime = function() {
       
   888 				var pm, hrs = this.date.getHours(),
       
   889 					mins = this.date.getMinutes();
       
   890 				if (t12) {
       
   891 					pm = (hrs >= 12);
       
   892 					if (pm) hrs -= 12;
       
   893 					if (hrs == 0) hrs = 12;
       
   894 					AP.innerHTML = pm ? "pm" : "am";
       
   895 				}
       
   896 				H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
       
   897 				M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
       
   898 			};
       
   899 
       
   900 			cal.onUpdateTime = function() {
       
   901 				var date = this.date;
       
   902 				var h = parseInt(H.innerHTML, 10);
       
   903 				if (t12) {
       
   904 					if (/pm/i.test(AP.innerHTML) && h < 12)
       
   905 						h += 12;
       
   906 					else if (/am/i.test(AP.innerHTML) && h == 12)
       
   907 						h = 0;
       
   908 				}
       
   909 				var d = date.getDate();
       
   910 				var m = date.getMonth();
       
   911 				var y = date.getFullYear();
       
   912 				date.setHours(h);
       
   913 				date.setMinutes(parseInt(M.innerHTML, 10));
       
   914 				date.setFullYear(y);
       
   915 				date.setMonth(m);
       
   916 				date.setDate(d);
       
   917 				this.dateClicked = false;
       
   918 				this.callHandler();
       
   919 			};
       
   920 		})();
       
   921 	} else {
       
   922 		this.onSetTime = this.onUpdateTime = function() {};
       
   923 	}
       
   924 
       
   925 	var tfoot = Calendar.createElement("tfoot", table);
       
   926 
       
   927 	row = Calendar.createElement("tr", tfoot);
       
   928 	row.className = "footrow";
       
   929 
       
   930 	cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
       
   931 	cell.className = "ttip";
       
   932 	if (this.isPopup) {
       
   933 		cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
       
   934 		cell.style.cursor = "move";
       
   935 	}
       
   936 	this.tooltips = cell;
       
   937 
       
   938 	div = Calendar.createElement("div", this.element);
       
   939 	this.monthsCombo = div;
       
   940 	div.className = "combo";
       
   941 	for (i = 0; i < Calendar._MN.length; ++i) {
       
   942 		var mn = Calendar.createElement("div");
       
   943 		mn.className = Calendar.is_ie ? "label-IEfix" : "label";
       
   944 		mn.month = i;
       
   945 		mn.innerHTML = Calendar._SMN[i];
       
   946 		div.appendChild(mn);
       
   947 	}
       
   948 
       
   949 	div = Calendar.createElement("div", this.element);
       
   950 	this.yearsCombo = div;
       
   951 	div.className = "combo";
       
   952 	for (i = 12; i > 0; --i) {
       
   953 		var yr = Calendar.createElement("div");
       
   954 		yr.className = Calendar.is_ie ? "label-IEfix" : "label";
       
   955 		div.appendChild(yr);
       
   956 	}
       
   957 
       
   958 	this._init(this.firstDayOfWeek, this.date);
       
   959 	parent.appendChild(this.element);
       
   960 };
       
   961 
       
   962 /** keyboard navigation, only for popup calendars */
       
   963 Calendar._keyEvent = function(ev) {
       
   964 	var cal = window._dynarch_popupCalendar;
       
   965 	if (!cal || cal.multiple)
       
   966 		return false;
       
   967 	(Calendar.is_ie) && (ev = window.event);
       
   968 	var act = (Calendar.is_ie || ev.type == "keypress"),
       
   969 		K = ev.keyCode;
       
   970 	if (ev.ctrlKey) {
       
   971 		switch (K) {
       
   972 		    case 37: // KEY left
       
   973 			act && Calendar.cellClick(cal._nav_pm);
       
   974 			break;
       
   975 		    case 38: // KEY up
       
   976 			act && Calendar.cellClick(cal._nav_py);
       
   977 			break;
       
   978 		    case 39: // KEY right
       
   979 			act && Calendar.cellClick(cal._nav_nm);
       
   980 			break;
       
   981 		    case 40: // KEY down
       
   982 			act && Calendar.cellClick(cal._nav_ny);
       
   983 			break;
       
   984 		    default:
       
   985 			return false;
       
   986 		}
       
   987 	} else switch (K) {
       
   988 	    case 32: // KEY space (now)
       
   989 		Calendar.cellClick(cal._nav_now);
       
   990 		break;
       
   991 	    case 27: // KEY esc
       
   992 		act && cal.callCloseHandler();
       
   993 		break;
       
   994 	    case 37: // KEY left
       
   995 	    case 38: // KEY up
       
   996 	    case 39: // KEY right
       
   997 	    case 40: // KEY down
       
   998 		if (act) {
       
   999 			var prev, x, y, ne, el, step;
       
  1000 			prev = K == 37 || K == 38;
       
  1001 			step = (K == 37 || K == 39) ? 1 : 7;
       
  1002 			function setVars() {
       
  1003 				el = cal.currentDateEl;
       
  1004 				var p = el.pos;
       
  1005 				x = p & 15;
       
  1006 				y = p >> 4;
       
  1007 				ne = cal.ar_days[y][x];
       
  1008 			};setVars();
       
  1009 			function prevMonth() {
       
  1010 				var date = new Date(cal.date);
       
  1011 				date.setDate(date.getDate() - step);
       
  1012 				cal.setDate(date);
       
  1013 			};
       
  1014 			function nextMonth() {
       
  1015 				var date = new Date(cal.date);
       
  1016 				date.setDate(date.getDate() + step);
       
  1017 				cal.setDate(date);
       
  1018 			};
       
  1019 			while (1) {
       
  1020 				switch (K) {
       
  1021 				    case 37: // KEY left
       
  1022 					if (--x >= 0)
       
  1023 						ne = cal.ar_days[y][x];
       
  1024 					else {
       
  1025 						x = 6;
       
  1026 						K = 38;
       
  1027 						continue;
       
  1028 					}
       
  1029 					break;
       
  1030 				    case 38: // KEY up
       
  1031 					if (--y >= 0)
       
  1032 						ne = cal.ar_days[y][x];
       
  1033 					else {
       
  1034 						prevMonth();
       
  1035 						setVars();
       
  1036 					}
       
  1037 					break;
       
  1038 				    case 39: // KEY right
       
  1039 					if (++x < 7)
       
  1040 						ne = cal.ar_days[y][x];
       
  1041 					else {
       
  1042 						x = 0;
       
  1043 						K = 40;
       
  1044 						continue;
       
  1045 					}
       
  1046 					break;
       
  1047 				    case 40: // KEY down
       
  1048 					if (++y < cal.ar_days.length)
       
  1049 						ne = cal.ar_days[y][x];
       
  1050 					else {
       
  1051 						nextMonth();
       
  1052 						setVars();
       
  1053 					}
       
  1054 					break;
       
  1055 				}
       
  1056 				break;
       
  1057 			}
       
  1058 			if (ne) {
       
  1059 				if (!ne.disabled)
       
  1060 					Calendar.cellClick(ne);
       
  1061 				else if (prev)
       
  1062 					prevMonth();
       
  1063 				else
       
  1064 					nextMonth();
       
  1065 			}
       
  1066 		}
       
  1067 		break;
       
  1068 	    case 13: // KEY enter
       
  1069 		if (act)
       
  1070 			Calendar.cellClick(cal.currentDateEl, ev);
       
  1071 		break;
       
  1072 	    default:
       
  1073 		return false;
       
  1074 	}
       
  1075 	return Calendar.stopEvent(ev);
       
  1076 };
       
  1077 
       
  1078 /**
       
  1079  *  (RE)Initializes the calendar to the given date and firstDayOfWeek
       
  1080  */
       
  1081 Calendar.prototype._init = function (firstDayOfWeek, date) {
       
  1082 	var today = new Date(),
       
  1083 		TY = today.getFullYear(),
       
  1084 		TM = today.getMonth(),
       
  1085 		TD = today.getDate();
       
  1086 	this.table.style.visibility = "hidden";
       
  1087 	var year = date.getFullYear();
       
  1088 	if (year < this.minYear) {
       
  1089 		year = this.minYear;
       
  1090 		date.setFullYear(year);
       
  1091 	} else if (year > this.maxYear) {
       
  1092 		year = this.maxYear;
       
  1093 		date.setFullYear(year);
       
  1094 	}
       
  1095 	this.firstDayOfWeek = firstDayOfWeek;
       
  1096 	this.date = new Date(date);
       
  1097 	var month = date.getMonth();
       
  1098 	var mday = date.getDate();
       
  1099 	var no_days = date.getMonthDays();
       
  1100 
       
  1101 	// calendar voodoo for computing the first day that would actually be
       
  1102 	// displayed in the calendar, even if it's from the previous month.
       
  1103 	// WARNING: this is magic. ;-)
       
  1104 	date.setDate(1);
       
  1105 	var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
       
  1106 	if (day1 < 0)
       
  1107 		day1 += 7;
       
  1108 	date.setDate(-day1);
       
  1109 	date.setDate(date.getDate() + 1);
       
  1110 
       
  1111 	var row = this.tbody.firstChild;
       
  1112 	var MN = Calendar._SMN[month];
       
  1113 	var ar_days = this.ar_days = new Array();
       
  1114 	var weekend = Calendar._TT["WEEKEND"];
       
  1115 	var dates = this.multiple ? (this.datesCells = {}) : null;
       
  1116 	for (var i = 0; i < 6; ++i, row = row.nextSibling) {
       
  1117 		var cell = row.firstChild;
       
  1118 		if (this.weekNumbers) {
       
  1119 			cell.className = "day wn";
       
  1120 			cell.innerHTML = date.getWeekNumber();
       
  1121 			cell = cell.nextSibling;
       
  1122 		}
       
  1123 		row.className = "daysrow";
       
  1124 		var hasdays = false, iday, dpos = ar_days[i] = [];
       
  1125 		for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
       
  1126 			iday = date.getDate();
       
  1127 			var wday = date.getDay();
       
  1128 			cell.className = "day";
       
  1129 			cell.pos = i << 4 | j;
       
  1130 			dpos[j] = cell;
       
  1131 			var current_month = (date.getMonth() == month);
       
  1132 			if (!current_month) {
       
  1133 				if (this.showsOtherMonths) {
       
  1134 					cell.className += " othermonth";
       
  1135 					cell.otherMonth = true;
       
  1136 				} else {
       
  1137 					cell.className = "emptycell";
       
  1138 					cell.innerHTML = "&nbsp;";
       
  1139 					cell.disabled = true;
       
  1140 					continue;
       
  1141 				}
       
  1142 			} else {
       
  1143 				cell.otherMonth = false;
       
  1144 				hasdays = true;
       
  1145 			}
       
  1146 			cell.disabled = false;
       
  1147 			cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
       
  1148 			if (dates)
       
  1149 				dates[date.print("%Y%m%d")] = cell;
       
  1150 			if (this.getDateStatus) {
       
  1151 				var status = this.getDateStatus(date, year, month, iday);
       
  1152 				if (this.getDateToolTip) {
       
  1153 					var toolTip = this.getDateToolTip(date, year, month, iday);
       
  1154 					if (toolTip)
       
  1155 						cell.title = toolTip;
       
  1156 				}
       
  1157 				if (status === true) {
       
  1158 					cell.className += " disabled";
       
  1159 					cell.disabled = true;
       
  1160 				} else {
       
  1161 					if (/disabled/i.test(status))
       
  1162 						cell.disabled = true;
       
  1163 					cell.className += " " + status;
       
  1164 				}
       
  1165 			}
       
  1166 			if (!cell.disabled) {
       
  1167 				cell.caldate = new Date(date);
       
  1168 				cell.ttip = "_";
       
  1169 				if (!this.multiple && current_month
       
  1170 				    && iday == mday && this.hiliteToday) {
       
  1171 					cell.className += " selected";
       
  1172 					this.currentDateEl = cell;
       
  1173 				}
       
  1174 				if (date.getFullYear() == TY &&
       
  1175 				    date.getMonth() == TM &&
       
  1176 				    iday == TD) {
       
  1177 					cell.className += " today";
       
  1178 					cell.ttip += Calendar._TT["PART_TODAY"];
       
  1179 				}
       
  1180 				if (weekend.indexOf(wday.toString()) != -1)
       
  1181 					cell.className += cell.otherMonth ? " oweekend" : " weekend";
       
  1182 			}
       
  1183 		}
       
  1184 		if (!(hasdays || this.showsOtherMonths))
       
  1185 			row.className = "emptyrow";
       
  1186 	}
       
  1187 	this.title.innerHTML = Calendar._MN[month] + ", " + year;
       
  1188 	this.onSetTime();
       
  1189 	this.table.style.visibility = "visible";
       
  1190 	this._initMultipleDates();
       
  1191 	// PROFILE
       
  1192 	// this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
       
  1193 };
       
  1194 
       
  1195 Calendar.prototype._initMultipleDates = function() {
       
  1196 	if (this.multiple) {
       
  1197 		for (var i in this.multiple) {
       
  1198 			var cell = this.datesCells[i];
       
  1199 			var d = this.multiple[i];
       
  1200 			if (!d)
       
  1201 				continue;
       
  1202 			if (cell)
       
  1203 				cell.className += " selected";
       
  1204 		}
       
  1205 	}
       
  1206 };
       
  1207 
       
  1208 Calendar.prototype._toggleMultipleDate = function(date) {
       
  1209 	if (this.multiple) {
       
  1210 		var ds = date.print("%Y%m%d");
       
  1211 		var cell = this.datesCells[ds];
       
  1212 		if (cell) {
       
  1213 			var d = this.multiple[ds];
       
  1214 			if (!d) {
       
  1215 				Calendar.addClass(cell, "selected");
       
  1216 				this.multiple[ds] = date;
       
  1217 			} else {
       
  1218 				Calendar.removeClass(cell, "selected");
       
  1219 				delete this.multiple[ds];
       
  1220 			}
       
  1221 		}
       
  1222 	}
       
  1223 };
       
  1224 
       
  1225 Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
       
  1226 	this.getDateToolTip = unaryFunction;
       
  1227 };
       
  1228 
       
  1229 /**
       
  1230  *  Calls _init function above for going to a certain date (but only if the
       
  1231  *  date is different than the currently selected one).
       
  1232  */
       
  1233 Calendar.prototype.setDate = function (date) {
       
  1234 	if (!date.equalsTo(this.date)) {
       
  1235 		this._init(this.firstDayOfWeek, date);
       
  1236 	}
       
  1237 };
       
  1238 
       
  1239 /**
       
  1240  *  Refreshes the calendar.  Useful if the "disabledHandler" function is
       
  1241  *  dynamic, meaning that the list of disabled date can change at runtime.
       
  1242  *  Just * call this function if you think that the list of disabled dates
       
  1243  *  should * change.
       
  1244  */
       
  1245 Calendar.prototype.refresh = function () {
       
  1246 	this._init(this.firstDayOfWeek, this.date);
       
  1247 };
       
  1248 
       
  1249 /** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
       
  1250 Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
       
  1251 	this._init(firstDayOfWeek, this.date);
       
  1252 	this._displayWeekdays();
       
  1253 };
       
  1254 
       
  1255 /**
       
  1256  *  Allows customization of what dates are enabled.  The "unaryFunction"
       
  1257  *  parameter must be a function object that receives the date (as a JS Date
       
  1258  *  object) and returns a boolean value.  If the returned value is true then
       
  1259  *  the passed date will be marked as disabled.
       
  1260  */
       
  1261 Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
       
  1262 	this.getDateStatus = unaryFunction;
       
  1263 };
       
  1264 
       
  1265 /** Customization of allowed year range for the calendar. */
       
  1266 Calendar.prototype.setRange = function (a, z) {
       
  1267 	this.minYear = a;
       
  1268 	this.maxYear = z;
       
  1269 };
       
  1270 
       
  1271 /** Calls the first user handler (selectedHandler). */
       
  1272 Calendar.prototype.callHandler = function () {
       
  1273 	if (this.onSelected) {
       
  1274 		this.onSelected(this, this.date.print(this.dateFormat));
       
  1275 	}
       
  1276 };
       
  1277 
       
  1278 /** Calls the second user handler (closeHandler). */
       
  1279 Calendar.prototype.callCloseHandler = function () {
       
  1280 	if (this.onClose) {
       
  1281 		this.onClose(this);
       
  1282 	}
       
  1283 	this.hideShowCovered();
       
  1284 };
       
  1285 
       
  1286 /** Removes the calendar object from the DOM tree and destroys it. */
       
  1287 Calendar.prototype.destroy = function () {
       
  1288 	var el = this.element.parentNode;
       
  1289 	el.removeChild(this.element);
       
  1290 	Calendar._C = null;
       
  1291 	window._dynarch_popupCalendar = null;
       
  1292 };
       
  1293 
       
  1294 /**
       
  1295  *  Moves the calendar element to a different section in the DOM tree (changes
       
  1296  *  its parent).
       
  1297  */
       
  1298 Calendar.prototype.reparent = function (new_parent) {
       
  1299 	var el = this.element;
       
  1300 	el.parentNode.removeChild(el);
       
  1301 	new_parent.appendChild(el);
       
  1302 };
       
  1303 
       
  1304 // This gets called when the user presses a mouse button anywhere in the
       
  1305 // document, if the calendar is shown.  If the click was outside the open
       
  1306 // calendar this function closes it.
       
  1307 Calendar._checkCalendar = function(ev) {
       
  1308 	var calendar = window._dynarch_popupCalendar;
       
  1309 	if (!calendar) {
       
  1310 		return false;
       
  1311 	}
       
  1312 	var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
       
  1313 	for (; el != null && el != calendar.element; el = el.parentNode);
       
  1314 	if (el == null) {
       
  1315 		// calls closeHandler which should hide the calendar.
       
  1316 		window._dynarch_popupCalendar.callCloseHandler();
       
  1317 		return Calendar.stopEvent(ev);
       
  1318 	}
       
  1319 };
       
  1320 
       
  1321 /** Shows the calendar. */
       
  1322 Calendar.prototype.show = function () {
       
  1323 	var rows = this.table.getElementsByTagName("tr");
       
  1324 	for (var i = rows.length; i > 0;) {
       
  1325 		var row = rows[--i];
       
  1326 		Calendar.removeClass(row, "rowhilite");
       
  1327 		var cells = row.getElementsByTagName("td");
       
  1328 		for (var j = cells.length; j > 0;) {
       
  1329 			var cell = cells[--j];
       
  1330 			Calendar.removeClass(cell, "hilite");
       
  1331 			Calendar.removeClass(cell, "active");
       
  1332 		}
       
  1333 	}
       
  1334 	this.element.style.display = "block";
       
  1335 	this.hidden = false;
       
  1336 	if (this.isPopup) {
       
  1337 		window._dynarch_popupCalendar = this;
       
  1338 		Calendar.addEvent(document, "keydown", Calendar._keyEvent);
       
  1339 		Calendar.addEvent(document, "keypress", Calendar._keyEvent);
       
  1340 		Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
       
  1341 	}
       
  1342 	this.hideShowCovered();
       
  1343 };
       
  1344 
       
  1345 /**
       
  1346  *  Hides the calendar.  Also removes any "hilite" from the class of any TD
       
  1347  *  element.
       
  1348  */
       
  1349 Calendar.prototype.hide = function () {
       
  1350 	if (this.isPopup) {
       
  1351 		Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
       
  1352 		Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
       
  1353 		Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
       
  1354 	}
       
  1355 	this.element.style.display = "none";
       
  1356 	this.hidden = true;
       
  1357 	this.hideShowCovered();
       
  1358 };
       
  1359 
       
  1360 /**
       
  1361  *  Shows the calendar at a given absolute position (beware that, depending on
       
  1362  *  the calendar element style -- position property -- this might be relative
       
  1363  *  to the parent's containing rectangle).
       
  1364  */
       
  1365 Calendar.prototype.showAt = function (x, y) {
       
  1366 	var s = this.element.style;
       
  1367 	s.left = x + "px";
       
  1368 	s.top = y + "px";
       
  1369 	this.show();
       
  1370 };
       
  1371 
       
  1372 /** Shows the calendar near a given element. */
       
  1373 Calendar.prototype.showAtElement = function (el, opts) {
       
  1374 	var self = this;
       
  1375 	var p = Calendar.getAbsolutePos(el);
       
  1376 	if (!opts || typeof opts != "string") {
       
  1377 		this.showAt(p.x, p.y + el.offsetHeight);
       
  1378 		return true;
       
  1379 	}
       
  1380 	function fixPosition(box) {
       
  1381 		if (box.x < 0)
       
  1382 			box.x = 0;
       
  1383 		if (box.y < 0)
       
  1384 			box.y = 0;
       
  1385 		var cp = document.createElement("div");
       
  1386 		var s = cp.style;
       
  1387 		s.position = "absolute";
       
  1388 		s.right = s.bottom = s.width = s.height = "0px";
       
  1389 		document.body.appendChild(cp);
       
  1390 		var br = Calendar.getAbsolutePos(cp);
       
  1391 		document.body.removeChild(cp);
       
  1392 		if (Calendar.is_ie) {
       
  1393 			br.y += document.documentElement.scrollTop || document.body.scrollTop;
       
  1394 			br.x += document.documentElement.scrollLeft || document.body.scrollLeft;
       
  1395 		} else {
       
  1396 			br.y += window.scrollY;
       
  1397 			br.x += window.scrollX;
       
  1398 		}
       
  1399 		var tmp = box.x + box.width - br.x;
       
  1400 		if (tmp > 0) box.x -= tmp;
       
  1401 		tmp = box.y + box.height - br.y;
       
  1402 		if (tmp > 0) box.y -= tmp;
       
  1403 	};
       
  1404 	this.element.style.display = "block";
       
  1405 	Calendar.continuation_for_the_fucking_khtml_browser = function() {
       
  1406 		var w = self.element.offsetWidth;
       
  1407 		var h = self.element.offsetHeight;
       
  1408 		self.element.style.display = "none";
       
  1409 		var valign = opts.substr(0, 1);
       
  1410 		var halign = "l";
       
  1411 		if (opts.length > 1) {
       
  1412 			halign = opts.substr(1, 1);
       
  1413 		}
       
  1414 		// vertical alignment
       
  1415 		switch (valign) {
       
  1416 		    case "T": p.y -= h; break;
       
  1417 		    case "B": p.y += el.offsetHeight; break;
       
  1418 		    case "C": p.y += (el.offsetHeight - h) / 2; break;
       
  1419 		    case "t": p.y += el.offsetHeight - h; break;
       
  1420 		    case "b": break; // already there
       
  1421 		}
       
  1422 		// horizontal alignment
       
  1423 		switch (halign) {
       
  1424 		    case "L": p.x -= w; break;
       
  1425 		    case "R": p.x += el.offsetWidth; break;
       
  1426 		    case "C": p.x += (el.offsetWidth - w) / 2; break;
       
  1427 		    case "l": p.x += el.offsetWidth - w; break;
       
  1428 		    case "r": break; // already there
       
  1429 		}
       
  1430 		p.width = w;
       
  1431 		p.height = h + 40;
       
  1432 		self.monthsCombo.style.display = "none";
       
  1433 		fixPosition(p);
       
  1434 		self.showAt(p.x, p.y);
       
  1435 	};
       
  1436 	if (Calendar.is_khtml)
       
  1437 		setTimeout("Calendar.continuation_for_the_fucking_khtml_browser()", 10);
       
  1438 	else
       
  1439 		Calendar.continuation_for_the_fucking_khtml_browser();
       
  1440 };
       
  1441 
       
  1442 /** Customizes the date format. */
       
  1443 Calendar.prototype.setDateFormat = function (str) {
       
  1444 	this.dateFormat = str;
       
  1445 };
       
  1446 
       
  1447 /** Customizes the tooltip date format. */
       
  1448 Calendar.prototype.setTtDateFormat = function (str) {
       
  1449 	this.ttDateFormat = str;
       
  1450 };
       
  1451 
       
  1452 /**
       
  1453  *  Tries to identify the date represented in a string.  If successful it also
       
  1454  *  calls this.setDate which moves the calendar to the given date.
       
  1455  */
       
  1456 Calendar.prototype.parseDate = function(str, fmt) {
       
  1457 	if (!fmt)
       
  1458 		fmt = this.dateFormat;
       
  1459 	this.setDate(Date.parseDate(str, fmt));
       
  1460 };
       
  1461 
       
  1462 Calendar.prototype.hideShowCovered = function () {
       
  1463 	if (!Calendar.is_ie && !Calendar.is_opera)
       
  1464 		return;
       
  1465 	function getVisib(obj){
       
  1466 		var value = obj.style.visibility;
       
  1467 		if (!value) {
       
  1468 			if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
       
  1469 				if (!Calendar.is_khtml)
       
  1470 					value = document.defaultView.
       
  1471 						getComputedStyle(obj, "").getPropertyValue("visibility");
       
  1472 				else
       
  1473 					value = '';
       
  1474 			} else if (obj.currentStyle) { // IE
       
  1475 				value = obj.currentStyle.visibility;
       
  1476 			} else
       
  1477 				value = '';
       
  1478 		}
       
  1479 		return value;
       
  1480 	};
       
  1481 
       
  1482 	var tags = new Array("applet", "iframe", "select");
       
  1483 	var el = this.element;
       
  1484 
       
  1485 	var p = Calendar.getAbsolutePos(el);
       
  1486 	var EX1 = p.x;
       
  1487 	var EX2 = el.offsetWidth + EX1;
       
  1488 	var EY1 = p.y;
       
  1489 	var EY2 = el.offsetHeight + EY1;
       
  1490 
       
  1491 	for (var k = tags.length; k > 0; ) {
       
  1492 		var ar = document.getElementsByTagName(tags[--k]);
       
  1493 		var cc = null;
       
  1494 
       
  1495 		for (var i = ar.length; i > 0;) {
       
  1496 			cc = ar[--i];
       
  1497 
       
  1498 			p = Calendar.getAbsolutePos(cc);
       
  1499 			var CX1 = p.x;
       
  1500 			var CX2 = cc.offsetWidth + CX1;
       
  1501 			var CY1 = p.y;
       
  1502 			var CY2 = cc.offsetHeight + CY1;
       
  1503 
       
  1504 			if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
       
  1505 				if (!cc.__msh_save_visibility) {
       
  1506 					cc.__msh_save_visibility = getVisib(cc);
       
  1507 				}
       
  1508 				cc.style.visibility = cc.__msh_save_visibility;
       
  1509 			} else {
       
  1510 				if (!cc.__msh_save_visibility) {
       
  1511 					cc.__msh_save_visibility = getVisib(cc);
       
  1512 				}
       
  1513 				cc.style.visibility = "hidden";
       
  1514 			}
       
  1515 		}
       
  1516 	}
       
  1517 };
       
  1518 
       
  1519 /** Internal function; it displays the bar with the names of the weekday. */
       
  1520 Calendar.prototype._displayWeekdays = function () {
       
  1521 	var fdow = this.firstDayOfWeek;
       
  1522 	var cell = this.firstdayname;
       
  1523 	var weekend = Calendar._TT["WEEKEND"];
       
  1524 	for (var i = 0; i < 7; ++i) {
       
  1525 		cell.className = "day name";
       
  1526 		var realday = (i + fdow) % 7;
       
  1527 		if (i) {
       
  1528 			cell.ttip = Calendar._TT["DAY_FIRST"].replace("%s", Calendar._DN[realday]);
       
  1529 			cell.navtype = 100;
       
  1530 			cell.calendar = this;
       
  1531 			cell.fdow = realday;
       
  1532 			Calendar._add_evs(cell);
       
  1533 		}
       
  1534 		if (weekend.indexOf(realday.toString()) != -1) {
       
  1535 			Calendar.addClass(cell, "weekend");
       
  1536 		}
       
  1537 		cell.innerHTML = Calendar._SDN[(i + fdow) % 7];
       
  1538 		cell = cell.nextSibling;
       
  1539 	}
       
  1540 };
       
  1541 
       
  1542 /** Internal function.  Hides all combo boxes that might be displayed. */
       
  1543 Calendar.prototype._hideCombos = function () {
       
  1544 	this.monthsCombo.style.display = "none";
       
  1545 	this.yearsCombo.style.display = "none";
       
  1546 };
       
  1547 
       
  1548 /** Internal function.  Starts dragging the element. */
       
  1549 Calendar.prototype._dragStart = function (ev) {
       
  1550 	if (this.dragging) {
       
  1551 		return;
       
  1552 	}
       
  1553 	this.dragging = true;
       
  1554 	var posX;
       
  1555 	var posY;
       
  1556 	if (Calendar.is_ie) {
       
  1557 		posY = window.event.clientY + document.body.scrollTop;
       
  1558 		posX = window.event.clientX + document.body.scrollLeft;
       
  1559 	} else {
       
  1560 		posY = ev.clientY + window.scrollY;
       
  1561 		posX = ev.clientX + window.scrollX;
       
  1562 	}
       
  1563 	var st = this.element.style;
       
  1564 	this.xOffs = posX - parseInt(st.left);
       
  1565 	this.yOffs = posY - parseInt(st.top);
       
  1566 	with (Calendar) {
       
  1567 		addEvent(document, "mousemove", calDragIt);
       
  1568 		addEvent(document, "mouseup", calDragEnd);
       
  1569 	}
       
  1570 };
       
  1571 
       
  1572 // BEGIN: DATE OBJECT PATCHES
       
  1573 
       
  1574 /** Adds the number of days array to the Date object. */
       
  1575 Date._MD = new Array(31,28,31,30,31,30,31,31,30,31,30,31);
       
  1576 
       
  1577 /** Constants used for time computations */
       
  1578 Date.SECOND = 1000 /* milliseconds */;
       
  1579 Date.MINUTE = 60 * Date.SECOND;
       
  1580 Date.HOUR   = 60 * Date.MINUTE;
       
  1581 Date.DAY    = 24 * Date.HOUR;
       
  1582 Date.WEEK   =  7 * Date.DAY;
       
  1583 
       
  1584 Date.parseDate = function(str, fmt) {
       
  1585 	var today = new Date();
       
  1586 	var y = 0;
       
  1587 	var m = -1;
       
  1588 	var d = 0;
       
  1589 	var a = str.split(/\W+/);
       
  1590 	var b = fmt.match(/%./g);
       
  1591 	var i = 0, j = 0;
       
  1592 	var hr = 0;
       
  1593 	var min = 0;
       
  1594 	for (i = 0; i < a.length; ++i) {
       
  1595 		if (!a[i])
       
  1596 			continue;
       
  1597 		switch (b[i]) {
       
  1598 		    case "%d":
       
  1599 		    case "%e":
       
  1600 			d = parseInt(a[i], 10);
       
  1601 			break;
       
  1602 
       
  1603 		    case "%m":
       
  1604 			m = parseInt(a[i], 10) - 1;
       
  1605 			break;
       
  1606 
       
  1607 		    case "%Y":
       
  1608 		    case "%y":
       
  1609 			y = parseInt(a[i], 10);
       
  1610 			(y < 100) && (y += (y > 29) ? 1900 : 2000);
       
  1611 			break;
       
  1612 
       
  1613 		    case "%b":
       
  1614 		    case "%B":
       
  1615 			for (j = 0; j < 12; ++j) {
       
  1616 				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { m = j; break; }
       
  1617 			}
       
  1618 			break;
       
  1619 
       
  1620 		    case "%H":
       
  1621 		    case "%I":
       
  1622 		    case "%k":
       
  1623 		    case "%l":
       
  1624 			hr = parseInt(a[i], 10);
       
  1625 			break;
       
  1626 
       
  1627 		    case "%P":
       
  1628 		    case "%p":
       
  1629 			if (/pm/i.test(a[i]) && hr < 12)
       
  1630 				hr += 12;
       
  1631 			else if (/am/i.test(a[i]) && hr >= 12)
       
  1632 				hr -= 12;
       
  1633 			break;
       
  1634 
       
  1635 		    case "%M":
       
  1636 			min = parseInt(a[i], 10);
       
  1637 			break;
       
  1638 		}
       
  1639 	}
       
  1640 	if (isNaN(y)) y = today.getFullYear();
       
  1641 	if (isNaN(m)) m = today.getMonth();
       
  1642 	if (isNaN(d)) d = today.getDate();
       
  1643 	if (isNaN(hr)) hr = today.getHours();
       
  1644 	if (isNaN(min)) min = today.getMinutes();
       
  1645 	if (y != 0 && m != -1 && d != 0)
       
  1646 		return new Date(y, m, d, hr, min, 0);
       
  1647 	y = 0; m = -1; d = 0;
       
  1648 	for (i = 0; i < a.length; ++i) {
       
  1649 		if (a[i].search(/[a-zA-Z]+/) != -1) {
       
  1650 			var t = -1;
       
  1651 			for (j = 0; j < 12; ++j) {
       
  1652 				if (Calendar._MN[j].substr(0, a[i].length).toLowerCase() == a[i].toLowerCase()) { t = j; break; }
       
  1653 			}
       
  1654 			if (t != -1) {
       
  1655 				if (m != -1) {
       
  1656 					d = m+1;
       
  1657 				}
       
  1658 				m = t;
       
  1659 			}
       
  1660 		} else if (parseInt(a[i], 10) <= 12 && m == -1) {
       
  1661 			m = a[i]-1;
       
  1662 		} else if (parseInt(a[i], 10) > 31 && y == 0) {
       
  1663 			y = parseInt(a[i], 10);
       
  1664 			(y < 100) && (y += (y > 29) ? 1900 : 2000);
       
  1665 		} else if (d == 0) {
       
  1666 			d = a[i];
       
  1667 		}
       
  1668 	}
       
  1669 	if (y == 0)
       
  1670 		y = today.getFullYear();
       
  1671 	if (m != -1 && d != 0)
       
  1672 		return new Date(y, m, d, hr, min, 0);
       
  1673 	return today;
       
  1674 };
       
  1675 
       
  1676 /** Returns the number of days in the current month */
       
  1677 Date.prototype.getMonthDays = function(month) {
       
  1678 	var year = this.getFullYear();
       
  1679 	if (typeof month == "undefined") {
       
  1680 		month = this.getMonth();
       
  1681 	}
       
  1682 	if (((0 == (year%4)) && ( (0 != (year%100)) || (0 == (year%400)))) && month == 1) {
       
  1683 		return 29;
       
  1684 	} else {
       
  1685 		return Date._MD[month];
       
  1686 	}
       
  1687 };
       
  1688 
       
  1689 /** Returns the number of day in the year. */
       
  1690 Date.prototype.getDayOfYear = function() {
       
  1691 	var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
       
  1692 	var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
       
  1693 	var time = now - then;
       
  1694 	return Math.floor(time / Date.DAY);
       
  1695 };
       
  1696 
       
  1697 /** Returns the number of the week in year, as defined in ISO 8601. */
       
  1698 Date.prototype.getWeekNumber = function() {
       
  1699 	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
       
  1700 	var DoW = d.getDay();
       
  1701 	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
       
  1702 	var ms = d.valueOf(); // GMT
       
  1703 	d.setMonth(0);
       
  1704 	d.setDate(4); // Thu in Week 1
       
  1705 	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
       
  1706 };
       
  1707 
       
  1708 /** Checks date and time equality */
       
  1709 Date.prototype.equalsTo = function(date) {
       
  1710 	return ((this.getFullYear() == date.getFullYear()) &&
       
  1711 		(this.getMonth() == date.getMonth()) &&
       
  1712 		(this.getDate() == date.getDate()) &&
       
  1713 		(this.getHours() == date.getHours()) &&
       
  1714 		(this.getMinutes() == date.getMinutes()));
       
  1715 };
       
  1716 
       
  1717 /** Set only the year, month, date parts (keep existing time) */
       
  1718 Date.prototype.setDateOnly = function(date) {
       
  1719 	var tmp = new Date(date);
       
  1720 	this.setDate(1);
       
  1721 	this.setFullYear(tmp.getFullYear());
       
  1722 	this.setMonth(tmp.getMonth());
       
  1723 	this.setDate(tmp.getDate());
       
  1724 };
       
  1725 
       
  1726 /** Prints the date in a string according to the given format. */
       
  1727 Date.prototype.print = function (str) {
       
  1728 	var m = this.getMonth();
       
  1729 	var d = this.getDate();
       
  1730 	var y = this.getFullYear();
       
  1731 	var wn = this.getWeekNumber();
       
  1732 	var w = this.getDay();
       
  1733 	var s = {};
       
  1734 	var hr = this.getHours();
       
  1735 	var pm = (hr >= 12);
       
  1736 	var ir = (pm) ? (hr - 12) : hr;
       
  1737 	var dy = this.getDayOfYear();
       
  1738 	if (ir == 0)
       
  1739 		ir = 12;
       
  1740 	var min = this.getMinutes();
       
  1741 	var sec = this.getSeconds();
       
  1742 	s["%a"] = Calendar._SDN[w]; // abbreviated weekday name [FIXME: I18N]
       
  1743 	s["%A"] = Calendar._DN[w]; // full weekday name
       
  1744 	s["%b"] = Calendar._SMN[m]; // abbreviated month name [FIXME: I18N]
       
  1745 	s["%B"] = Calendar._MN[m]; // full month name
       
  1746 	// FIXME: %c : preferred date and time representation for the current locale
       
  1747 	s["%C"] = 1 + Math.floor(y / 100); // the century number
       
  1748 	s["%d"] = (d < 10) ? ("0" + d) : d; // the day of the month (range 01 to 31)
       
  1749 	s["%e"] = d; // the day of the month (range 1 to 31)
       
  1750 	// FIXME: %D : american date style: %m/%d/%y
       
  1751 	// FIXME: %E, %F, %G, %g, %h (man strftime)
       
  1752 	s["%H"] = (hr < 10) ? ("0" + hr) : hr; // hour, range 00 to 23 (24h format)
       
  1753 	s["%I"] = (ir < 10) ? ("0" + ir) : ir; // hour, range 01 to 12 (12h format)
       
  1754 	s["%j"] = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy; // day of the year (range 001 to 366)
       
  1755 	s["%k"] = hr;		// hour, range 0 to 23 (24h format)
       
  1756 	s["%l"] = ir;		// hour, range 1 to 12 (12h format)
       
  1757 	s["%m"] = (m < 9) ? ("0" + (1+m)) : (1+m); // month, range 01 to 12
       
  1758 	s["%M"] = (min < 10) ? ("0" + min) : min; // minute, range 00 to 59
       
  1759 	s["%n"] = "\n";		// a newline character
       
  1760 	s["%p"] = pm ? "PM" : "AM";
       
  1761 	s["%P"] = pm ? "pm" : "am";
       
  1762 	// FIXME: %r : the time in am/pm notation %I:%M:%S %p
       
  1763 	// FIXME: %R : the time in 24-hour notation %H:%M
       
  1764 	s["%s"] = Math.floor(this.getTime() / 1000);
       
  1765 	s["%S"] = (sec < 10) ? ("0" + sec) : sec; // seconds, range 00 to 59
       
  1766 	s["%t"] = "\t";		// a tab character
       
  1767 	// FIXME: %T : the time in 24-hour notation (%H:%M:%S)
       
  1768 	s["%U"] = s["%W"] = s["%V"] = (wn < 10) ? ("0" + wn) : wn;
       
  1769 	s["%u"] = w + 1;	// the day of the week (range 1 to 7, 1 = MON)
       
  1770 	s["%w"] = w;		// the day of the week (range 0 to 6, 0 = SUN)
       
  1771 	// FIXME: %x : preferred date representation for the current locale without the time
       
  1772 	// FIXME: %X : preferred time representation for the current locale without the date
       
  1773 	s["%y"] = ('' + y).substr(2, 2); // year without the century (range 00 to 99)
       
  1774 	s["%Y"] = y;		// year with the century
       
  1775 	s["%%"] = "%";		// a literal '%' character
       
  1776 
       
  1777 	var re = /%./g;
       
  1778 	if (!Calendar.is_ie5 && !Calendar.is_khtml)
       
  1779 		return str.replace(re, function (par) { return s[par] || par; });
       
  1780 
       
  1781 	var a = str.match(re);
       
  1782 	for (var i = 0; i < a.length; i++) {
       
  1783 		var tmp = s[a[i]];
       
  1784 		if (tmp) {
       
  1785 			re = new RegExp(a[i], 'g');
       
  1786 			str = str.replace(re, tmp);
       
  1787 		}
       
  1788 	}
       
  1789 
       
  1790 	return str;
       
  1791 };
       
  1792 
       
  1793 Date.prototype.__msh_oldSetFullYear = Date.prototype.setFullYear;
       
  1794 Date.prototype.setFullYear = function(y) {
       
  1795 	var d = new Date(this);
       
  1796 	d.__msh_oldSetFullYear(y);
       
  1797 	if (d.getMonth() != this.getMonth())
       
  1798 		this.setDate(28);
       
  1799 	this.__msh_oldSetFullYear(y);
       
  1800 };
       
  1801 
       
  1802 // END: DATE OBJECT PATCHES
       
  1803 
       
  1804 
       
  1805 // global object that remembers the calendar
       
  1806 window._dynarch_popupCalendar = null;