var TechnologyLeaders = new Object();
TechnologyLeaders.Calendar = new Object();


TechnologyLeaders.Calendar.setup = function(params) {
	var first = params.firstDay;
//	Zapatec.Calendar.prototype.create = TechnologyLeaders.Calendar.create;
	var cal = Zapatec.Calendar.setup(params);
	// TL Modifications
	cal.monthClicked = false;
	cal.curWeek = 0;
	cal.setFirstDayOfWeek(first);
	cal.yearClicked = false;
	cal.weekClicked = false;
	Zapatec.Calendar.cellClick = TechnologyLeaders.Calendar.cellClick;
//	Zapatec.Calendar.tableMouseUp = TechnologyLeaders.Calendar.tableMouseUp;
	return cal;

};
// New functions definitions overriding default Zapatec ones


// event handlers


/**
 * The generic "click" handler.  This function handles actions on any kind of
 * buttons that appear inside our calendar.  It determines the button type by
 * querying \em el.navtype.  The following types of objects are supported:
 *
 * - Date cells (navtype is undefined).  The function will select that date,
 *   add appropriate class names and remove them from the previously selected
 *   date.  If the date in the calendar \em has \em changed, it calls the
 *   calendar's onSelect handler (see the constructor).  If multiple dates is
 *   enabled, it will not unselect previously selected date but rather maintain
 *   an array of dates which will be avaliable to the onSelect or onClose
 *   handler.
 * - The Close button (navtype == 200).  If this is clicked, then the
 *   calendar's onClose handler is called immediately.
 * - The Today button (navtype == 0).  The calendar will jump to the "today"
 *   date and time, unless it's already there.
 * - The About button (navtype == 400).  It will display an alert with the
 *   "about message", as defined in the translation file.
 * - Previous year (navtype == -2)
 * - Previous month (navtype == -1)
 * - Next month (navtype == 1)
 * - Next year (navtype == 2)
 * - Day names (navtype == 100).  If any of them is clicked, the calendar will
 *   display that day as the first day of week.  It calls the "onFDOW" event
 *   handler if defined.
 * - Time parts (navtype == 50).  If any of them is clicked, this function will
 *   determine if it's a click or shift-click, and will take the appropriate
 *   action (simple click means add 1, shift-click means substract 1 from that
 *   time part).  Then it calls onUpdateTime() to refresh the display.
 * - Time scroll buttons (navtype == 201 or navtype == 202).  If such buttons
 *   are clicked, the time part involved is determined and it is incremented or
 *   decremented with the current step (default: 5).  201 is for "add", 202 for
 *   "substract".
 *
 * @param el [HTMLElement] the object being clicked on
 * @param ev [Event] the event object
 */
TechnologyLeaders.Calendar.cellClick = function(el, ev) {
	var cal = el.calendar;
	var closing = false;
	var newdate = false;
	var date = null;
	//BEGIN: fix for the extra information bug in IE
	while(!cal) {
		el = el.parentNode;
		cal = el.calendar;
	}
	//END
	if (el.className.indexOf("disabled") != -1 || el.className.indexOf("true") != -1) {
		return false;
	}
	if (typeof el.navtype == "undefined") {
		if (cal.currentDateEl) {
			Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
			Zapatec.Utils.addClass(el, "selected");
			closing = (cal.currentDateEl == el);
			if (!closing) {
				cal.currentDateEl = el;
			}
		}
		var tmpDate = new Date(el.caldate[0], el.caldate[1], el.caldate[2]);
		if (tmpDate.getDate() != el.caldate[2]) {
			tmpDate.setDate(el.caldate[2]);
		}
		cal.date.setDateOnly(tmpDate);
		cal.currentDate.setDateOnly(tmpDate);
		date = cal.date;
		var other_month = !(cal.dateClicked = !el.otherMonth);
		if (!other_month && cal.multiple)
			cal._toggleMultipleDate(new Date(date));
		newdate = true;
		// a date was clicked
		if (other_month)
			cal._init(cal.firstDayOfWeek, date);
		cal.onSetTime();
	} else {
		if (el.navtype == 200) {
			Zapatec.Utils.removeClass(el, "hilite");
			cal.callCloseHandler();
			return;
		}
         if(el.navtype == 420) {
		 		if (cal.currentDateEl) {
					Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
					Zapatec.Utils.addClass(el, "selected");
					closing = (cal.currentDateEl == el);
					if (!closing) {
						cal.currentDateEl = el;
					}
				}
				var thisDate = el.innerHTML.split(",");
				var month = 12;
				while (month-- >= 0) {
					if (Zapatec.Calendar._TT._MN[month] == thisDate[0]) {
					   break;
					}
				}
				var tmpDate = new Date(thisDate[1], month, 1);
				cal.date.setDateOnly(tmpDate);
				cal.currentDate.setDateOnly(tmpDate);
				date = cal.date;
				newdate = true;
                cal.monthClicked=true;
                cal.dateClicked=false;
                cal.weekClicked=false;
             }
            if(el.navtype == 430) {
		 		if (cal.currentDateEl) {
					Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
					Zapatec.Utils.addClass(el, "selected");
					closing = (cal.currentDateEl == el);
					if (!closing) {
						cal.currentDateEl = el;
					}
				}
				var firstDay = el.nextSibling;
				while (firstDay.className.indexOf("true")!= -1) {
					    firstDay = firstDay.nextSibling;  // Skip past the blank days at the beginning of the month
				}

				var tmpDate = new Date(firstDay.caldate[0], firstDay.caldate[1], firstDay.caldate[2]);
				if (tmpDate.getDate() != firstDay.caldate[2]) {
					tmpDate.setDate(firstDay.caldate[2]);
				}
				cal.date.setDateOnly(tmpDate);
				cal.currentDate.setDateOnly(tmpDate);
				date = cal.date;
				newdate = true;
                cal.weekClicked=true;
                cal.dateClicked=false;
                cal.monthClicked=false;

            }

		date = new Date(cal.date);
		if (el.navtype == 0 && !cal.bEventShowHistory)
			// Set date to Today if Today clicked AND History NOT shown
			date.setDateOnly(new Date()); // TODAY
		// unless "today" was clicked, we assume no date was clicked so
		// the selected handler will know not to close the calenar when
		// in single-click mode.
		// cal.dateClicked = (el.navtype == 0);
		cal.dateClicked = false;
		var year = date.getFullYear();
		var mon = date.getMonth();
		function setMonth(m) {
			var day = date.getDate();
			var max = date.getMonthDays(m);
			if (day > max) {
				date.setDate(max);
			}
			date.setMonth(m);
		};
		switch (el.navtype) {
		    case 400:
			Zapatec.Utils.removeClass(el, "hilite");
			var text = Zapatec.Calendar.i18n("ABOUT");
			if (typeof text != "undefined") {
				text += cal.showsTime ? Zapatec.Calendar.i18n("ABOUT_TIME") : "";
			} else {
				// FIXME: this should be removed as soon as lang files get updated!
				text = "Help and about box text is not translated into this language.\n" +
					"If you know this language and you feel generous please update\n" +
					"the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
					"and send it back to <support@zapatec.com> to get it into the distribution  ;-)\n\n" +
					"Thank you!\n" +
					"http://www.zapatec.com\n";
			}
			alert(text);
			return;
		    case -2:
			if (cal.currentDateEl) {
				Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
				Zapatec.Utils.addClass(el, "selected");
				closing = (cal.currentDateEl == el);
				if (!closing) {
					cal.currentDateEl = el;
				}
			}
			if (year > cal.minYear) {
				date.setFullYear(year - 1);
			}
			break;
		    case -1:
			if (cal.currentDateEl) {
				Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
				Zapatec.Utils.addClass(el, "selected");
				closing = (cal.currentDateEl == el);
				if (!closing) {
					cal.currentDateEl = el;
				}
			}
			if (mon > 0) {
				setMonth(mon - 1);
			} else if (year-- > cal.minYear) {
				date.setFullYear(year);
				setMonth(11);
			}
			break;
		    case 1:
			if (cal.currentDateEl) {
				Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
				Zapatec.Utils.addClass(el, "selected");
				closing = (cal.currentDateEl == el);
				if (!closing) {
					cal.currentDateEl = el;
				}
			}
			if (mon < 11) {
				setMonth(mon + 1);
			} else if (year < cal.maxYear) {
				date.setFullYear(year + 1);
				setMonth(0);
			}
			break;
		    case 2:
			if (cal.currentDateEl) {
				Zapatec.Utils.removeClass(cal.currentDateEl, "selected");
				Zapatec.Utils.addClass(el, "selected");
				closing = (cal.currentDateEl == el);
				if (!closing) {
					cal.currentDateEl = el;
				}
			}
			if (year < cal.maxYear) {
				date.setFullYear(year + 1);
			}
			break;
		    case 100:
			cal.setFirstDayOfWeek(el.fdow);
			Zapatec.Calendar.prefs.fdow = cal.firstDayOfWeek;
			Zapatec.Calendar.savePrefs();
			if (cal.onFDOW)
				cal.onFDOW(cal.firstDayOfWeek);
			return;
		    case 50:
			//turns off time changing if timeInterval is set with special value
			var date = cal.currentDate;
			if (el.className.indexOf("ampm", 0) >= 0)
				// always check ampm changes
				;
			else
			if (!((cal.timeInterval == null) || ((cal.timeInterval < 60) && (el.className.indexOf("hour", 0) != -1)))) {break;}
			var range = el._range;
			var current = el.firstChild.data;
			var pm = (date.getHours() >= 12);
			for (var i = range.length; --i >= 0;)
				if (range[i] == current)
					break;
			if (ev && ev.shiftKey) {
				if (--i < 0) {
					i = range.length - 1;
				}
			} else if ( ++i >= range.length ) {
					i = 0;
				}

		//ALLOWED TIME CHECK
			if (cal.getDateStatus) { //Current time is changing, check with the callback to see if it's in range
				// Fills "minute" and "hour" variables with the time that user wants to set, to pass them to the dateStatusHandler.
				// As the script passes hours in 24 format, we need to convert inputed values if they are not in the needed format
				var minute = null; // minutes to be passed
				var hour = null; // hours to be passed
				// as we pass date element to the handler, we need to create new one and fill it with new minutes or hours (depending on what had changed)
				var new_date = new Date(date);
				// if "ampm" was clicked
				if (el.className.indexOf("ampm", 0) != -1) {
					minute = date.getMinutes(); // minutes didn't change
					// if the "ampm" value has changed we need to correct hours (add 12 or exclude 12 or set it to zero)
					hour = (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) ? ((date.getHours() == 12) ? (date.getHours()) : (date.getHours() + 12)) : (date.getHours() - 12);
					// if the time is disabled we seek the first one disabled.
					// It fixes the bug when you can not change from 'am' to 'pm' or vice versa for the dates that have restrictions for time.
					// This part of code is very easy to understand, so it don't need much comments
					if ( cal.getDateStatus && cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10)) ) {
					   var dirrect;
					   if (range[i] == Zapatec.Calendar.i18n("pm", "ampm")) {
					      dirrect = -5;
					   } else {
					      dirrect = 5;
					   }
					   hours = hour;
					   minutes = minute;
					   do {
					      minutes += dirrect;
					      if (minutes >=60) {
						 minutes -= 60;
						 ++hours;
						 if (hours >= 24) hours -= 24;
						 new_date.setHours(hours);
					      }
					      if (minutes < 0) {
						 minutes += 60;
						 --hours;
					  	 if (hours < 0) hours += 24;
						 new_date.setHours(hours);
					      }
					      new_date.setMinutes(minutes);
					      if (!cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hours, 10), parseInt(minutes, 10))) {
						 hour = hours;
						 minute = minutes;
						 if (hour > 12) i = 1; else i = 0;
						 cal.date.setHours(hour);
						 cal.date.setMinutes(minute);
						 cal.onSetTime();
					      }
					   } while ((hour != hours) || (minute != minutes));
					}
					// updates our new Date object that will be passed to the handler
					new_date.setHours(hour);
				}
				// if hours were clicked
				if (el.className.indexOf("hour", 0) != -1) {
				   minute = date.getMinutes(); // minutes didn't change
				   hour = (!cal.time24) ? ((pm) ? ((range[i] != 12) ? (parseInt(range[i], 10) + 12) : (12)) : ((range[i] != 12) ? (range[i]) : (0))) : (range[i]);  // new value of hours
				   new_date.setHours(hour);
				}
				// if minutes were clicked
				if (el.className.indexOf("minute", 0) != -1) {
				   hour = date.getHours(); // hours didn't change
				   minute = range[i]; // new value of minutes
				   new_date.setMinutes(minute);
				}
			}
			var status = false;
			// if the handler is set, we pass new values and retreive result in "status" variable
			if (cal.getDateStatus) {
			   status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
			}
			if (!status) {
			   el.firstChild.data = range[i];
			}
			//END OF ALLOWED TIME CHECK

			cal.onUpdateTime();
			return;
		    case 201: // timepart, UP
		    case 202: // timepart, DOWN
			var cel = el.timePart;
			//turns off time changing if timeInterval is set with special value
			var date = cal.currentDate;
			if ((cel.className.indexOf("minute", 0) != -1) && (cal.timeInterval > 30)) {break;}
			var val = parseInt(cel.firstChild.data, 10);
			var pm = (date.getHours() >= 12);
			var range = cel._range;
			for (var i = range.length; --i >= 0;)
				if (val == range[i]) {
					val = i;
					break;
				}
			var step = cel._step;
			if (el.navtype == 201) {
				val = step*Math.floor(val/step);
				val += step;
				if (val >= range.length)
					val = 0;
			} else {
				val = step*Math.ceil(val/step);
				val -= step;
				if (val < 0)
					val = range.length-step;
			}

			//ALLOWED TIME CHECK
			if (cal.getDateStatus) { //Current time is changing, check with the callback to see if it's in range of allowed times
			   // Fills "minute" and "hour" variables with the time that user wants to set, to pass them to the dateStatusHandler.
			   // As the script passes hours in 24 format, we need to convert inputed values if they are not in the needed format
			   var minute = null; // minutes to be passed
			   var hour = null; // hours to be passed
			   // as we pass date element to the handler, we need to create new one and fill it with new minutes or hours (depending on what had changed)
			   var new_date = new Date(date);
			   // if hours were changed
			   if (cel.className == "hour") {
			      minute = date.getMinutes();
			      hour = (!cal.time24) ? ((pm) ? ((range[val] != 12) ? (parseInt(range[val], 10) + 12) : (12)) : ((range[val] != 12) ? (range[val]) : (0))) : (range[val]);
			      new_date.setHours(hour);
			   }
			   // if minutes were changed
			   if (cel.className == "minute") {
			      hour = date.getHours();
			      minute = val;
			      new_date.setMinutes(range[val]);
			   }
			}
			var status = false;
			// if the handler is set, we pass new values and retreive result in "status" variable
			if (cal.getDateStatus) {
			   status = cal.getDateStatus(new_date, date.getFullYear(), date.getMonth(), date.getDate(), parseInt(hour, 10), parseInt(minute, 10));
			}
			if (!status) {
			   cel.firstChild.data = range[val];
			}
			cal.onUpdateTime();
			//END OF ALLOWED TIME CHECK
			return;
		    case 0:
			// TODAY will bring us here
			//fix for the today bug for the special dates
			if (cal.getDateStatus && ((cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == true) || (cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate()) == "disabled"))) {
				// remember, "date" was previously set to new
				// Date() if TODAY was clicked; thus, it
				// contains today date.
				return false;
			}
			break;
		}
		if (!date.equalsTo(cal.date)) {
			if ((el.navtype >= -2 && el.navtype <=2) && (el.navtype != 0)) {
				cal._init(cal.firstDayOfWeek, date, true);
				return;
			}
			cal.setDate(date);
			newdate = !(el.navtype && (el.navtype >= -2 && el.navtype <=2));
		}
	}
	if (newdate) {
		cal.callHandler();
	}
	if (closing) {
		Zapatec.Utils.removeClass(el, "hilite");
		cal.callCloseHandler();
	}
};

// END: CALENDAR STATIC FUNCTIONS

// BEGIN: CALENDAR OBJECT FUNCTIONS

/**
 * This function creates the calendar HTML elements inside the given parent.
 * If _par is null than it creates a popup calendar inside the BODY element.
 * If _par is an element, be it BODY, then it creates a non-popup calendar
 * (still hidden).
 *
 * The function looks rather complicated, but what it does is quite simple.
 * The basic calendar elements will be created, that is, a containing DIV, a
 * TABLE that contains a headers (titles, navigation bar and day names bars), a
 * body containing up to 12 months, each has 6 rows with 7 or 8 cells (this depends on whether week
 * numbers are on or off) and a footer containing the status bar.  Appropriate
 * event handlers are assigned to all buttons or to the titles and status bar
 * (for drag'n'drop).
 *
 * This function also builds the time selector if the calendar is configured
 * so, and it also creates the elements required for combo boxes (years,
 * months, history).
 *
 * This function does not display day names or dates.  This is done in
 * Zapatec.Calendar.prototype._init().  Therefore, by separating these 2
 * actions we can make date switching happen much faster because the _init
 * function will already have the elements in place (so we don't need to create
 * them again and again).  This was a major improvement which got in
 * the calendar v0.9.1.
 *
 * @param _par
 */
Zapatec.Calendar.prototype.create = function (_par) {
	var parent = null;
	if (! _par) {
		// default parent is the document body, in which case we create
		// a popup calendar.
		parent = window.document.getElementsByTagName("body")[0];
		this.isPopup = true;
		this.WCH = Zapatec.Utils.createWCH();
	} else {
		parent = _par;
		this.isPopup = false;
	}
	this.currentDate = this.date = this.dateStr ? new Date(this.dateStr) : new Date();

	var table = Zapatec.Utils.createElement("table");
	this.table = table;
	table.cellSpacing = 0;
	table.cellPadding = 0;
	table.calendar = this;
	Zapatec.Utils.addEvent(table, "mousedown", Zapatec.Calendar.tableMouseDown);

	var div = Zapatec.Utils.createElement("div");
	this.element = div;
	div.className = "calendar";
	//FIX for Opera's bug with row highlighting
	if (Zapatec.is_opera) {
		table.style.width = (this.monthsInRow * ((this.weekNumbers) ? (8) : (7)) * 2 + 4.4 * this.monthsInRow) + "em";
	}
	if (this.isPopup) {
		div.style.position = "absolute";
		div.style.display = "none";
	}
	div.appendChild(table);

	var cell = null;
	var row = null;

	var cal = this;
	var hh = function (text, cs, navtype) {
		cell = Zapatec.Utils.createElement("td", row);
		cell.colSpan = cs;
		cell.className = "button";
		if (Math.abs(navtype) <= 2)
			cell.className += " nav";
		Zapatec.Calendar._add_evs(cell);
		cell.calendar = cal;
		cell.navtype = navtype;
		if (text.substr(0, 1) != "&") {
			cell.appendChild(document.createTextNode(text));
		}
		else {
			// FIXME: dirty hack for entities
			cell.innerHTML = text;
		}
		return cell;
	};
	//Creating all the controls on the top
	var title_length = ((this.weekNumbers) ? (8) : (7)) * this.monthsInRow - 2;
	var thead = Zapatec.Utils.createElement("thead", table);
	if (this.numberMonths == 1) {
		this.title = thead;
	}
	row = Zapatec.Utils.createElement("tr", thead);
	if (this.helpButton) {
		hh("?", 1, 400).ttip = Zapatec.Calendar.i18n("INFO");
	} else {
		cell = Zapatec.Utils.createElement("td", row);
		cell.colSpan = 1;
		cell.className = "button";
		cell.innerHTML = "<p>&nbsp</p>";
	}

	this.title = hh("", title_length, 420);
	this.title.className = "title";
	if (this.isPopup) {
		if (!this.disableDrag) {
			this.title.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
			this.title.style.cursor = "move";
		}
		hh("&#x00d7;", 1, 200).ttip = Zapatec.Calendar.i18n("CLOSE");
	} else {
		cell = Zapatec.Utils.createElement("td", row);
		cell.colSpan = 1;
		cell.className = "button";
		cell.innerHTML = "<p>&nbsp</p>";
	}

	row = Zapatec.Utils.createElement("tr", thead);
	this._nav_py = hh("&#x00ab;", 1, -2);
	this._nav_py.ttip = Zapatec.Calendar.i18n("PREV_YEAR");
	this._nav_pm = hh("&#x2039;", 1, -1);
	this._nav_pm.ttip = Zapatec.Calendar.i18n("PREV_MONTH");
	this._nav_now = hh(Zapatec.Calendar.i18n("TODAY"), title_length - 2, 0);
	this._nav_now.ttip = Zapatec.Calendar.i18n("GO_TODAY");
	this._nav_nm = hh("&#x203a;", 1, 1);
	this._nav_nm.ttip = Zapatec.Calendar.i18n("NEXT_MONTH");
	this._nav_ny = hh("&#x00bb;", 1, 2);
	this._nav_ny.ttip = Zapatec.Calendar.i18n("NEXT_YEAR");

	//Here we calculate the number of rows for multimonth calendar
	var rowsOfMonths = Math.floor(this.numberMonths / this.monthsInRow);
	if (this.numberMonths % this.monthsInRow > 0) {
		++rowsOfMonths;
	}
	//Every iteration of this cycle creates a row of months in the calendar
	for (var l = 1; l <= rowsOfMonths; ++l) {
		var thead = Zapatec.Utils.createElement("thead", table);
		//Fix for the Operas bug, this is a workaround which makes Opera display THEAD elements as TBODY el.
		//The problem is that Opera displays all the THEAD elements in the table first, and only then TBODY elements (an ugly look!).
		if (Zapatec.is_opera) {thead.style.display = "table-row-group";}
		if (this.numberMonths != 1) {
			row = Zapatec.Utils.createElement("tr", thead);
			var title_length = 5;
			this.weekNumbers && ++title_length;
			//creating the titles for the months
			this.titles[l] = new Array();
			for (var k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
				cell = Zapatec.Utils.createElement("td", row);
				cell.colSpan = 1;
				cell.className = "button";
				cell.innerHTML = "<p>&nbsp</p>";
				this.titles[l][k] = hh("", title_length, 300);
				this.titles[l][k].className = "title";
				cell = Zapatec.Utils.createElement("td", row);
				cell.colSpan = 1;
				cell.className = "button";
				cell.innerHTML = "<p>&nbsp</p>";
			}
		}
	// day names
		row = Zapatec.Utils.createElement("tr", thead);
		row.className = "daynames";
		for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
			if (this.weekNumbers) {
				cell = Zapatec.Utils.createElement("td", row);
				cell.className = "name wn";
				cell.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n("WK")));
				if (k > 1) {
					Zapatec.Utils.addClass(cell, "month-left-border");
				}
				var cal_wk = Zapatec.Calendar.i18n("WK")
					if (cal_wk == null) {
						//if it's not defined in the language file, leave it blank
						cal_wk = "";
					}

			}
			for (var i = 7; i > 0; --i) {
				cell = Zapatec.Utils.createElement("td", row);
				cell.appendChild(window.document.createTextNode(""));
			}
		}
		this.firstdayname = row.childNodes[this.weekNumbers?1:0];
		this.rowsOfDayNames[l] = this.firstdayname;
		this._displayWeekdays();

		var tbody = Zapatec.Utils.createElement("tbody", table);
		this.tbody[l] = tbody;

		for (i = 6; i > 0; --i) {
			//creating a row of days for all the months in the row
			row = Zapatec.Utils.createElement("tr", tbody);
			for (k = 1; (k <= this.monthsInRow) && ((l - 1) * this.monthsInRow + k <= this.numberMonths); ++k) {
				if (this.weekNumbers) {
					cell = Zapatec.Utils.createElement("td", row);
					cell.className="button";
					cell.calendar=this;
					cell.navtype = 430;
					cell.appendChild(document.createTextNode(""));
					Zapatec.Calendar._add_evs(cell);
				}
				for (var j = 7; j > 0; --j) {
					cell = Zapatec.Utils.createElement("td", row);
					cell.appendChild(document.createTextNode(""));
					cell.calendar = this;
					Zapatec.Calendar._add_evs(cell);
				}
			}
		}
	}

	var tfoot = Zapatec.Utils.createElement("tfoot", table);

	if (this.showsTime) {
		row = Zapatec.Utils.createElement("tr", tfoot);
		row.className = "time";
		//empty area for positioning the time controls under the control month
		var emptyColspan;
		if (this.monthsInRow != 1) {
			cell = Zapatec.Utils.createElement("td", row);
			emptyColspan = cell.colSpan = Math.ceil((((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1)) / 2);
			cell.className = "timetext";
			cell.innerHTML = "&nbsp";
		}

		cell = Zapatec.Utils.createElement("td", row);
		cell.className = "timetext";
		cell.colSpan = this.weekNumbers ? 2 : 1;
		cell.innerHTML = Zapatec.Calendar.i18n("TIME") || "&nbsp;";

		(function() {
			function makeTimePart(className, init, range_start, range_end) {
				var table, tbody, tr, tr2, part;
				if (range_end) {
					cell = Zapatec.Utils.createElement("td", row);
					cell.colSpan = 1;
					if (cal.showsTime != "seconds") {
						++cell.colSpan;
					}
					cell.className = "parent-" + className;
					table = Zapatec.Utils.createElement("table", cell);
					table.cellSpacing = table.cellPadding = 0;
					if (className == "hour")
						table.align = "right";
					table.className = "calendar-time-scroller";
					tbody = Zapatec.Utils.createElement("tbody", table);
					tr    = Zapatec.Utils.createElement("tr", tbody);
					tr2   = Zapatec.Utils.createElement("tr", tbody);
				} else
					tr = row;
				part = Zapatec.Utils.createElement("td", tr);
				part.className = className;
				part.appendChild(window.document.createTextNode(init));
				part.calendar = cal;
				part.ttip = Zapatec.Calendar.i18n("TIME_PART");
				part.navtype = 50;
				part._range = [];
				if (!range_end)
					part._range = range_start;
				else {
					part.rowSpan = 2;
					for (var i = range_start; i <= range_end; ++i) {
						var txt;
						if (i < 10 && range_end >= 10) txt = '0' + i;
						else txt = '' + i;
						part._range[part._range.length] = txt;
					}
					var up = Zapatec.Utils.createElement("td", tr);
					up.className = "up";
					up.navtype = 201;
					up.calendar = cal;
					up.timePart = part;
					if (Zapatec.is_khtml)
						up.innerHTML = "&nbsp;";
					Zapatec.Calendar._add_evs(up);

					var down = Zapatec.Utils.createElement("td", tr2);
					down.className = "down";
					down.navtype = 202;
					down.calendar = cal;
					down.timePart = part;
					if (Zapatec.is_khtml)
						down.innerHTML = "&nbsp;";
					Zapatec.Calendar._add_evs(down);
				}
				Zapatec.Calendar._add_evs(part);
				return part;
			};
			var hrs = cal.currentDate.getHours();
			var mins = cal.currentDate.getMinutes();
			if (cal.showsTime == "seconds") {
				var secs = cal.currentDate.getSeconds();
			}
			var t12 = !cal.time24;
			var pm = (hrs > 12);
			if (t12 && pm) hrs -= 12;
			var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
			//calculating of the step for hours
			H._step = (cal.timeInterval > 30) ? (cal.timeInterval / 60) : 1;
			cell = Zapatec.Utils.createElement("td", row);
			cell.innerHTML = ":";
			cell.className = "colon";
			var M = makeTimePart("minute", mins, 0, 59);
			//calculating of the step for minutes
			M._step = ((cal.timeInterval) && (cal.timeInterval < 60)) ? (cal.timeInterval) : 5; // FIXME: make this part configurable
			if (cal.showsTime == "seconds") {
				cell = Zapatec.Utils.createElement("td", row);
				cell.innerHTML = ":";
				cell.className = "colon";
				var S = makeTimePart("minute", secs, 0, 59);
				S._step = 5;
			}
			var AP = null;
			if (t12) {
				AP = makeTimePart("ampm", pm ? Zapatec.Calendar.i18n("pm", "ampm") : Zapatec.Calendar.i18n("am", "ampm"), [Zapatec.Calendar.i18n("am", "ampm"), Zapatec.Calendar.i18n("pm", "ampm")]);
				AP.className += " button";
			} else
				Zapatec.Utils.createElement("td", row).innerHTML = "&nbsp;";

			cal.onSetTime = function() {
				var hrs = this.currentDate.getHours();
				var mins = this.currentDate.getMinutes();
				if (this.showsTime == "seconds") {
					var secs = cal.currentDate.getSeconds();
				}
				if (this.timeInterval) {
					mins += this.timeInterval - ((mins - 1 + this.timeInterval) % this.timeInterval) - 1;
				}
				while (mins >= 60) {
					mins -= 60;
					++hrs;
				}
				if (this.timeInterval > 60) {
					var interval = this.timeInterval / 60;
					if (hrs % interval != 0) {
						hrs += interval - ((hrs - 1 + interval) % interval) - 1;
					}
					if (hrs >= 24) {hrs -= 24;}
				}
			//ALLOWED TIME CHECK
				// This part of code seeks for the first enabled time value for this date.
				// It is written for the cases when you change day, month or year and the time value is disabled for the new date.
				// So if you only allow 8:00 - 17:00 on Mondays and you change the date to a Monday but the time is 7:00 it will
				// automatically move forward to 8:00.
				var new_date = new Date(this.currentDate);
				if (this.getDateStatus && this.getDateStatus(this.currentDate, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hrs, mins)) {
				   hours = hrs;
				   minutes = mins;
				   do {
				     if (this.timeInterval) {
					 	if (this.timeInterval < 60) {
							minutes += this.timeInterval;
						} else {
							hrs += this.timeInterval / 60;
						}
					 } else {
					 	minutes += 5;
					 }
				     if (minutes >=60) {
						minutes -= 60;
						hours += 1;
				     }
				     if (hours >= 24) {hours -= 24;}
					 new_date.setMinutes(minutes);
				     new_date.setHours(hours);
				     if (!this.getDateStatus(new_date, this.currentDate.getFullYear(), this.currentDate.getMonth(), this.currentDate.getDate(), hours, minutes)) {
					 	hrs = hours;
				 	 	mins = minutes;
				     }
				   } while ((hrs != hours) || (mins != minutes));
				}
			//END OF ALLOWED TIME CHECK
				this.currentDate.setMinutes(mins);
				this.currentDate.setHours(hrs);
				var pm = (hrs >= 12);
				if (pm && t12 && hrs != 12) hrs -= 12;
				if (!pm && t12 && hrs == 0) hrs = 12;
				H.firstChild.data = (hrs < 10) ? ("0" + hrs) : hrs;
				M.firstChild.data = (mins < 10) ? ("0" + mins) : mins;
				if (this.showsTime == "seconds") {
					S.firstChild.data = (secs < 10) ? ("0" + secs) : secs;
				}
				if (t12)
				   AP.firstChild.data = pm ? Zapatec.Calendar.i18n("pm", "ampm") : Zapatec.Calendar.i18n("am", "ampm");
			};

			cal.onUpdateTime = function() {
				var date = this.currentDate;
				var h = parseInt(H.firstChild.data, 10);
				if (t12) {
					if (/pm/i.test(AP.firstChild.data) && h < 12)
						h += 12;
					else if (/am/i.test(AP.firstChild.data) && h == 12)
						h = 0;
				}
				var d = date.getDate();
				var m = date.getMonth();
				var y = date.getFullYear();
				date.setHours(h);
				date.setMinutes(parseInt(M.firstChild.data, 10));
				if (this.showsTime == "seconds") {
					date.setSeconds(parseInt(S.firstChild.data, 10));
				}
				date.setFullYear(y);
				date.setMonth(m);
				date.setDate(d);
				this.dateClicked = false;
				this.callHandler();
			};
		})();
		//empty area after the time controls
		if (this.monthsInRow != 1) {
			cell = Zapatec.Utils.createElement("td", row);
			cell.colSpan = ((this.weekNumbers) ? 8 : 7) * (this.monthsInRow - 1) - Math.ceil(emptyColspan);
			cell.className = "timetext";
			cell.innerHTML = "&nbsp";
		}
	} else {
		this.onSetTime = this.onUpdateTime = function() {};
	}

	row = Zapatec.Utils.createElement("tr", tfoot);
	row.className = "footrow";

	cell = hh(Zapatec.Calendar.i18n("SEL_DATE"), this.weekNumbers ? (8 * this.numberMonths) : (7 * this.numberMonths), 300);
	cell.className = "ttip";
	if (this.isPopup && !this.disableDrag) {
		cell.ttip = Zapatec.Calendar.i18n("DRAG_TO_MOVE");
		cell.style.cursor = "move";
	}
	this.tooltips = cell;

	div = this.monthsCombo = Zapatec.Utils.createElement("div", this.element);
	div.className = "combo";
	for (i = 0; i < 12; ++i) {
		var mn = Zapatec.Utils.createElement("div");
		mn.className = Zapatec.is_ie ? "label-IEfix" : "label";
		mn.month = i;
		mn.appendChild(window.document.createTextNode(Zapatec.Calendar.i18n(i, "smn")));
		div.appendChild(mn);
	}

	div = this.yearsCombo = Zapatec.Utils.createElement("div", this.element);
	div.className = "combo";
	for (i = 12; i > 0; --i) {
		var yr = Zapatec.Utils.createElement("div");
		yr.className = Zapatec.is_ie ? "label-IEfix" : "label";
		yr.appendChild(window.document.createTextNode(""));
		div.appendChild(yr);
	}

	div = this.histCombo = Zapatec.Utils.createElement("div", this.element);
	div.className = "combo history";

	this._init(this.firstDayOfWeek, this.date);
	parent.appendChild(this.element);
};

//  Override week generation function for our dating algorithm.
/** Original returns the number of the week in year, as defined in ISO 8601. */
/** We return the first week whenever any days occurred, not the first week of Sun-Wed */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(0); // First day of year
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};
