/*
 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
 emCalendar v3.2 - (c) 2004 - 2007 by Lukas "Emka" Zeman (www.proteus.cz)
 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 - feel free to use this code, but leave this header in it to help other users finding the actual version
 - find actual version at http://www.proteus.cz/tutorials/emCalendar/
 - find others at http://www.proteus.cz/tutorials/
 
 - you will need prototype.js library from http://www.prototypejs.org/ to run this code
 - you will need emDate.js library from http://www.proteus.cz/tutorials/emDate/
 - you will need appTools.js library http://www.proteus.cz/tutorials/appTools/

 - big thanks to Riki "Fczbkk" Fridrich (www.fczbkk.com)
*/

// emCalendar object constructor
function emCalendar(input, configArray) {
	this.input = input;
	if (typeof(configArray) != "undefined") {
		for (var i = 0; i < configArray.length; i++) {
			var configVariable = 'this.' + configArray[i][0];
			var configValue    = configArray[i][1];
			if (typeof(configValue) == 'string') {
				eval(configVariable + ' = "' + configValue + '"');
			} else {
				eval(configVariable + ' = ' + configValue);
			}
		}
	}

	// Default values 
	this.calClass     = typeof(this.calClass)     == "undefined" ? 'emCalendar' : this.calClass;
	this.pattern      = typeof(this.pattern)      == "undefined" ? 'd.m.Y'      : this.pattern;
	this.showDate     = typeof(this.showDate)     == "undefined" ? true         : this.showDate;
	this.showTime     = typeof(this.showTime)     == "undefined" ? false        : this.showTime;
	this.minuteStep   = typeof(this.minuteStep)   == "undefined" ? 1            : this.minuteStep;
	this.invClass     = typeof(this.invClass)     == "undefined" ? 'invalid'    : this.invClass;
	this.closeOnClick = typeof(this.closeOnClick) == "undefined" ? true         : this.closeOnClick;

	this.date         = new Date();
	this.timer        = null;

	this.lang             = new Array();
	this.lang['todayMsg'] = 'právě teď';
	this.lang['closeMsg'] = 'zavřít';
	this.lang['timeMsg']  = 'čas:';
	this.create(this.calClass);
}

emCalendar.prototype = {

	create: function(className) {
		var self    = this;
		this.block  = new BlockHolder(className);
		Event.observe(document,   'keyup', function(e) {self.hide(Event.element(e));});
		Event.observe(document,   'click', function(e) {self.hide(Event.element(e));});
		Event.observe(this.input, 'keyup', function(e)  {self.validate(Event.element(e));});
		document.body.appendChild(this.block.div);
		if (this.date.validate(this.input.value, this.pattern)) {
			this.date = this.date.validate(this.input.value, this.pattern);
			this.date.roundMinutes(this.minuteStep);
		}
		this.redraw();
	},

	show: function() {
		var xCorrection = 0;
		var yCorrection = 0;
		if (this.scrollParentId) {
			if (document.getElementById(this.scrollParentId)) {
				var scroller = document.getElementById(this.scrollParentId);
				yCorrection = - scroller.scrollTop;
			}
		}
		this.block.show(fixOffsetTop(this.input) + parseInt(this.input.offsetHeight) + yCorrection + 'px', fixOffsetLeft(this.input) + xCorrection + 'px');
	},

	hide: function(e) {
		if (!e || ((e != this.input)&&(!this.isParent(this.block.div, e))&&(!Element.hasClassName(e, 'emCalendarStarter')))) {
			this.block.hide();
		}
	},

	insertElement : function(parentObj, tagName, className, textInserted) {
		var tag = document.createElement(tagName);
		if (className) tag.className = className;
		if (textInserted != null) {
			var text = document.createTextNode(textInserted);
			tag.appendChild(text);
		}
		parentObj.appendChild(tag);
		return tag;
	},
	
	isParent : function(objParent, objTarget) {
		var parent = false;
		do {
			if (objParent == objTarget) {
				parent = true;
				break;
			}
			objTarget = objTarget.parentNode;
  }
  while (objTarget != document);
		return parent;
	},

	redraw: function() {
		var self = this;

		// destroy all children
		while (this.block.div.childNodes.length > 0) {
			this.block.div.removeChild(this.block.div.firstChild);
		}
		
		if (this.showDate) {
			var divNav      = this.insertElement(this.block.div, 'DIV', 'emCalNavigation', null);
	
			var aLeftYear   = this.insertElement(divNav, 'A', 'emCalLeftYear',   null);
			var span        = this.insertElement(aLeftYear, 'SPAN', null,   '...');
			Event.observe(aLeftYear, 'click', function() {self.date.setYear(self.date.getYearFixed()-1); self.redraw();})
	
			var aLeftMonth  = this.insertElement(divNav, 'A', 'emCalLeftMonth',  null);
			var span        = this.insertElement(aLeftMonth, 'SPAN', null,   '...');
			Event.observe(aLeftMonth, 'click', function() {self.date.setMonth(self.date.getMonth()-1); self.redraw();})
			
			var aRightYear  = this.insertElement(divNav, 'A', 'emCalRightYear',  null);
			var span        = this.insertElement(aRightYear, 'SPAN', null,   '...');
			Event.observe(aRightYear, 'click', function() {self.date.setYear(self.date.getYearFixed()+1); self.redraw();})
	
			var aRightMonth = this.insertElement(divNav, 'A', 'emCalRightMonth', null);
			var span        = this.insertElement(aRightMonth, 'SPAN', null,   '...');
			Event.observe(aRightMonth, 'click', function() {self.date.setMonth(self.date.getMonth()+1); self.redraw();})
	
			var navString   = this.insertElement(divNav, 'DIV', null, this.date.format('F Y'));
	
			// table creation
			var table = this.insertElement(this.block.div, 'TABLE', null, null);
			var thead = this.insertElement(table, 'THEAD', null, null);
			var tbody = this.insertElement(table, 'TBODY', null, null);
	
			var tr    = this.insertElement(thead, 'TR', null, null);
			
			// table header (day names)
			for (i = 1; i < 8; i++) {
				var pom  = (i < 7) ? i : 0;
				var th   = this.insertElement(tr, 'TH', null, this.date.dayNamesShort[pom]);
			}
			
			var firstDay = new Date(this.date.getYearFixed(), this.date.getMonth(), 1).getDay();
			firstDay = (firstDay == 0) ? 7 : firstDay;
			var lastDay  = new Date(this.date.getYearFixed(), this.date.getMonth()+1, 0).getDate();
	
			var lastRow = 1; // let's make the constant height of the table
			
			var tr    = this.insertElement(tbody, 'TR', null, null);
			// ...and insert empty cells in the beginning
			dayInWeek = 0;
			for (i = 1; i < firstDay; i++) {
				var td = this.insertElement(tr, 'TD', null, '\u00a0');
				dayInWeek++;
			}
			// let's draw all days
			if (this.date.validate(this.input.value, this.pattern)) {
				var testDate = this.date.validate(this.input.value, this.pattern);
			}
			for (i = 1; i <= lastDay; i++) {
				if (dayInWeek == 7) {
					var tr = this.insertElement(tbody, 'TR', null, null);
					lastRow++;
					dayInWeek = 0;
				}
				var td = this.insertElement(tr, 'TD', null, i);
				td.day = i;
				Element.addClassName(td, 'day');
				Event.observe(td, 'click', function(e) {self.pasteValue(Event.element(e)); self.redraw(); if (self.closeOnClick) {self.hide();}});
				Event.observe(td, 'mouseover',  function(e) {self.highlightCell(Event.element(e));});
				Event.observe(td, 'mouseout',  function(e) {self.unHighlightCell(Event.element(e));});
				if (testDate && (testDate.getDate() == i) && (testDate.getMonth() == this.date.getMonth()) && (testDate.getYear() == this.date.getYear())) {
					Element.addClassName(td, 'emCalSelected');
				}
				
				dayInWeek++;
			}
			// let's insert empty cells in the end
			for (i = dayInWeek; i < 7; i++) {
				var td = this.insertElement(tr, 'TD', null, '\u00a0');
			}
			// zobrazi vzdy 6 radku, aby kalendar neposkakoval
			for (j = lastRow;j < 6; j++) {  
				var tr = this.insertElement(tbody, 'TR', null, null);
				for (i = 0; i < 7; i++) {
					var td = this.insertElement(tr, 'TD', null, '\u00a0');
				}
			}
		}

		// create time edit navigation
		if (this.showTime) {
			var timeSelect = this.insertElement(this.block.div,   'DIV', 'emCalTimeSelect', null);
			var timeMsg    = this.insertElement(timeSelect, 'DIV', 'emCalTimeMsg', this.lang["timeMsg"]);
			
			var hours      = this.insertElement(timeSelect, 'DIV', 'emCalHours', this.date.getHours());
			this.hours     = hours;
			var aHUp       = this.insertElement(hours, 'A', 'emCalUp', null);
			Event.observe(aHUp, 'mousedown', function () {self.addHour(); if (!self.timer) {self.timer = setInterval(function() {self.addHour();}, 100);}});
			var aHDown     = this.insertElement(hours, 'A', 'emCalDown', null);
			Event.observe(aHDown, 'mousedown', function () {self.subtractHour(); if (!self.timer) {self.timer = setInterval(function() {self.subtractHour();}, 100);}});

			var minutes    = this.insertElement(timeSelect, 'DIV', 'emCalMinutes', this.date.getMinutes() < 10 ? '0' + this.date.getMinutes() : this.date.getMinutes());
			this.minutes   = minutes;
			var aMUp       = this.insertElement(minutes, 'A', 'emCalUp', null);
			Event.observe(aMUp, 'mousedown', function () {self.addMinute(); if (!self.timer) {self.timer = setInterval(function() {self.addMinute();}, parseInt(100 * ((self.minuteStep+1) * 0.5)));}});
			var aMDown     = this.insertElement(minutes, 'A', 'emCalDown', null);
			Event.observe(aMDown, 'mousedown', function () {self.subtractMinute(); if (!self.timer) {self.timer = setInterval(function() {self.subtractMinute();}, parseInt(100 * ((self.minuteStep+1) * 0.5)));}});

			Event.observe(document, 'mouseup', function () {clearInterval(self.timer); self.timer = null;});
		}

		var bottomNav = this.insertElement(this.block.div, 'DIV', 'emCalNavigation', null);
		var todayLink = this.insertElement(bottomNav, 'DIV', 'emCaltodayLink', this.lang['todayMsg']);
		Event.observe(todayLink, 'click', function() {self.date = new Date(); self.date.roundMinutes(self.minuteStep); self.pasteValue(); self.redraw();})

		var closeMsg  = this.insertElement(bottomNav, 'DIV', 'emCalNavClose',  this.lang['closeMsg']);
		Event.observe(closeMsg, 'click', function() {self.hide(null);})
		var reset     = this.insertElement(bottomNav, 'DIV', 'reset', '\u00a0');
	},

	pasteValue: function(e) {
		var pasteDate    = this.date;
		if (e) {
			pasteDate.setDate(e.day);
			this.unHighlightCell(e);
		}
		this.input.value = pasteDate.format(this.pattern);
		this.validate();
	},

	highlightCell: function(e) {
		Element.addClassName(e, 'emCalDayHover');
	},

	unHighlightCell: function(e) {
		Element.removeClassName(e, 'emCalDayHover');
	},

	addHour: function() {
		this.date.setHours((this.date.getHours() == 23) ? 0 : this.date.getHours() + 1);
		this.redrawTime();
		this.pasteValue();
	},

	addMinute: function() {
		this.date.setMinutes(((parseInt(this.date.getMinutes() + this.minuteStep) < 60) ? (this.date.getMinutes() + this.minuteStep) : 0));
		this.redrawTime();
		this.pasteValue();
	},

	subtractHour: function() {
		this.date.setHours((this.date.getHours() == 0) ? 23 : this.date.getHours() - 1);
		this.redrawTime();
		this.pasteValue();
	},

	subtractMinute: function() {
		var minute = this.date.getMinutes();
		minute     = ((minute - this.minuteStep) >= 0 ) ? (minute - this.minuteStep) : (60 + (minute - this.minuteStep));
		this.date.setMinutes(minute);
		this.redrawTime();
		this.pasteValue();
	},
	
	redrawTime: function() {
		var newHours   = document.createTextNode(this.date.getHours());
		this.hours.replaceChild(newHours, this.hours.firstChild);
		var newMinutes = document.createTextNode((this.date.getMinutes() < 10) ? '0' + this.date.getMinutes() : this.date.getMinutes());
		this.minutes.replaceChild(newMinutes, this.minutes.firstChild);
	},

	validate: function(e) {
		if (this.date.validate(this.input.value, this.pattern)) {
			this.date = this.date.validate(this.input.value, this.pattern);
			this.date.roundMinutes(this.minuteStep);
			Element.removeClassName(this.input, this.invClass);
			if (e && e == this.input) {this.redraw();}
		} else {
			Element.addClassName(this.input, this.invClass);
		}
	}
}

function initEmCalendars() {
	if (document.createElement) {
		var inputs = document.getElementsByTagName("INPUT");
		for (var i = 0; i < inputs.length; i++) {
			if (Element.hasClassName(inputs[i], 'emCalendarInput')) {
				var calInput      = inputs[i];
				configArrayName   = calInput.className.match(/emCalendarConfig\-([a-zA-z0-9\-]+)/);
				if (configArrayName) {
					try {
						var configArray   = eval(configArrayName[1]);
						calInput.calendar = new emCalendar(calInput, configArray);
					}
					catch (e) {
						calInput.calendar = new emCalendar(calInput);
						alert('Config array "' + configArrayName[1] + '" for emCalendar script is not defined!');
					} 
				} else {
					calInput.calendar = new emCalendar(calInput);
				}
				Event.observe(calInput, 'focus', function(e) {e = Event.element(e); e.calendar.show();});
			}
		}
	}
}

Event.observe(window, 'load', initEmCalendars);