/*
 -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
 emCalendar v3.0 - (2005) 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 dom.js library from http://www.fczbkk.com/js/dom/ 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.pattern      = typeof(this.pattern)      == "undefined" ? 'j.n.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" ? false     : 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('emCalendar');
}

emCalendar.prototype = {

	create: function(className) {
		var self    = this;
		this.block  = new BlockHolder(className);
		evt.add(document,   'keyup', function(e) {self.hide(evt.fix(e));});
		evt.add(document,   'click', function(e) {self.hide(evt.fix(e));});
		evt.add(this.input, 'keyup', function(e)  {self.validate(evt.fix(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() {
		this.block.show(fixOffsetTop(this.input) + parseInt(this.input.offsetHeight) + 'px', fixOffsetLeft(this.input) + 'px');
	},

	hide: function(e) {
		if (!e || ((e.target != this.input)&&(!this.isParent(this.block.div, e.target))&&(!cls.has(e.target, '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,   '...');
			evt.add(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,   '...');
			evt.add(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,   '...');
			evt.add(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,   '...');
			evt.add(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;
				cls.add(td, 'day');
				evt.add(td, 'click', function(e) {self.pasteValue(evt.fix(e)); self.redraw(); if (self.closeOnClick) {self.hide();}});
				evt.add(td, 'mouseover',  function(e) {self.highlightCell(evt.fix(e));});
				evt.add(td, 'mouseout',  function(e) {self.unHighlightCell(evt.fix(e));});
				if (testDate && (testDate.getDate() == i) && (testDate.getMonth() == this.date.getMonth()) && (testDate.getYear() == this.date.getYear())) {
					cls.add(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);
			evt.add(aHUp, 'mousedown', function () {self.addHour(); if (!self.timer) {self.timer = setInterval(function() {self.addHour();}, 100);}});
			var aHDown     = this.insertElement(hours, 'A', 'emCalDown', null);
			evt.add(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);
			evt.add(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);
			evt.add(aMDown, 'mousedown', function () {self.subtractMinute(); if (!self.timer) {self.timer = setInterval(function() {self.subtractMinute();}, parseInt(100 * ((self.minuteStep+1) * 0.5)));}});

			evt.add(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']);
		evt.add(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']);
		evt.add(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.target.day);
			this.unHighlightCell(e);
		}
		this.input.value = pasteDate.format(this.pattern);
		this.validate();
	},

	highlightCell: function(e) {
		cls.add(e.target, 'emCalDayHover');
	},

	unHighlightCell: function(e) {
		cls.remove(e.target, '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);
			cls.remove(this.input, this.invClass);
			if (e && e.target == this.input) {this.redraw();}
		} else {
			cls.add(this.input, this.invClass);
		}
	}
}