	function cal_day(parent, day, month, year, flag) {
		var self = this;
		
		this.parent = parent;
		this.container = null;
		this.span = null;
	
		this.day = day;
		this.month = month;
		this.year = year;
		this.flag = flag;
		this.classes = false;
		
		this.construct = function() {
			this.container = document.createElement("TD");
			
			if (this.day == this.parent.parent.open_day && this.month == this.parent.parent.open_month && this.year == this.parent.parent.open_year) this.flag = "middle";
			
			if (this.month == this.parent.month && this.year == this.parent.year) {
				this.container.className = this.classes = "normal normal_" + this.flag;
			} else {
				this.container.className = this.classes = "leaf leaf_" + this.flag;
			}
			this.container.onmouseover = function(trgEvent) { self.evt_over(trgEvent == null ? event : trgEvent); }
			this.container.onmouseout = function(trgEvent) { self.evt_out(trgEvent == null ? event : trgEvent); }
			this.container.onclick = function(trgEvent) { self.evt_click(trgEvent == null ? event : trgEvent); }
		
			this.span = document.createElement("SPAN");
			this.span.innerHTML = this.day;
			
			this.container.appendChild(this.span);
			
			return this.container;
		}
		
		this.destroy = function() {
			this.container.removeChild(this.span);
			this.span = null;
			
			this.container.parentNode.removeChild(this.container);
			this.container = null;
			
			this.parent = null;
			self = null;
		}
		
		this.evt_over = function(trgEvent) {
			this.container.className = this.classes + " hover";
		}
		
		this.evt_out = function(trgEvent) {
			this.container.className = this.classes;
		}
		
		this.evt_click = function(trgEvent) {
			this.parent.parent.pick(this)
		}
	}

	function cal_month(parent, side, month, month_name, year) {
		var self = this;
		
		this.parent = parent;
		this.side = side;
		this.month = month;
		this.month_name = month_name;
		this.year = year;
		
		this.container = null;
		this.info_container = null;
		this.table = null;
		this.tbody = null;
		
		this.motion = 0;
		this.motion_flag = 0;
		
		this.nodes = new Array();
		
		this.construct = function() {
			var c = this.container = document.createElement("DIV");
			c.className = "cal_month";
			c.style.position = "absolute";
			
			var i = this.info_container = document.createElement("DIV");
			i.className = "cal_info";
			i.innerHTML = this.month_name + " " + this.year;
			
			c.appendChild(i);
			
			var t = this.table = document.createElement("TABLE");
			var b = this.tbody = document.createElement("TBODY");
			
			t.cellSpacing = 0;
			t.cellPadding = 0;
			
			t.appendChild(b);
			
			var f, row;
			
			for (f = 0; f < this.nodes.length; f++) {
				if (f % 7 == 0) {
					row = document.createElement("TR");
					b.appendChild(row);
				}
				
				row.appendChild(this.nodes[f].construct());
			}
			
			c.appendChild(t);
			this.loop();
			
			this.parent.container_inner.appendChild(c);
		}
		
		this.destroy = function() {
			var f;
			for (f = 0; f < this.nodes.length; f++) {
				this.nodes[f].destroy();
			}
			
			for (f = this.tbody.rows.length - 1; f >= 0; f += -1) {
				this.tbody.removeChild(this.tbody.rows[f]);
				this.tbody.rows[f] = null;
			}
			
			this.table.removeChild(this.tbody);
			this.tbody = null;
		
			this.container.removeChild(this.info_container);
			this.container.removeChild(this.table);
			
			this.info_container = null;
			this.table = null;
		
			this.parent.container_inner.removeChild(this.container);
			this.container = null;
			
			this.parent = null;
			self = null;
		}
		
		this.add_node = function(day, month, year, flag) {
			this.nodes[this.nodes.length] = new cal_day(this, day, month, year, flag);
		}
		
		this.loop = function() {
			if (this.motion_flag <= 1) {
				this.container.style.top = "30px";
				switch (this.side) {
					case 1 : // z prave strany
						this.container.style.left = (this.parent.max - this.motion) + "px";
						break;
						
					case -1 : // z leve strany
						this.container.style.left = (- this.parent.max + this.motion) + "px";
						break;
						
					case 0 : // staticke
						this.container.style.left = "0px";
						break;
				}
				
				if (this.motion_flag == 0) {
					this.motion += 25;
					if (this.motion >= this.parent.max) {
						this.motion = this.parent.max;
						this.motion_flag = 1;
					}
				} else this.motion_flag = 2;
			}
		}
	}

	function cal_class(ident, element) {
		var self = this;
	
		this.parent = null;
		this.element = element;
		this.next = null;
		this.last = null;
		
		this.container = null;
		this.container_inner = null;
		this.shade = null;
		
		this.c_close_butt = null;
		this.c_next_butt = null;
		this.c_last_butt = null;
		
		this.ident = ident;
		this.input = document.getElementById(this.ident);
		this.max = 200;
		
		this.data_day = false;
		this.data_month = false;
		this.data_month_name = false;
		this.data_year = false;
		
		this.open_day = false;
		this.open_month = false;
		this.open_year = false;
		
		this.cur_month = null;
		this.last_month = null;
		
		this.request = null;
		
		this.construct = function() {
			// explode input hodnoty
			var inputs = this.get_inputs();
			
			if (inputs.length == 3) {
				this.data_day = inputs[0].options[inputs[0].selectedIndex].value;
				this.data_month = inputs[1].options[inputs[1].selectedIndex].value;
				this.data_year = inputs[2].options[inputs[2].selectedIndex].value;
				
				this.open_day = this.data_day;
				this.open_month = this.data_month;
				this.open_year = this.data_year;
			
			} else {
				var tmp_val = inputs[0].value;
				var parts = new Array();
				var last = "";
				var f, ch;
				
				for (f = 0; f <= tmp_val.length; f++) {
					ch = f == tmp_val.length ? "." : tmp_val.substr(f, 1);
					
					switch (ch) {
						case "." :
							last = parseInt(last);
							if (!isNaN(last) && last > 0) parts[parts.length] = last;
							last = "";
							break;
							
						default :
							last += ch;
							break;
					}
				}
				
				if (parts.length == 3) {
					this.data_day = parts[0];
					this.data_month = parts[1];
					this.data_year = parts[2];
					
					this.open_day = parts[0];
					this.open_month = parts[1];
					this.open_year = parts[2];
				}
			}
			
			// hlavni kontejner
			this.container = document.createElement("DIV");
			this.container.className = "cal_container";
			
			//  kontejner stinu
			this.shade = document.createElement("DIV");
			this.shade.className = "cal_shade";

			// pozice
			if (this.element != null) {
				var posX = 0;
				var posY = 0;
				
				if (this.element.clientX != null && this.element.clientY != null) {
					posX = this.element.clientX + document.body.left;
					posY = this.element.clientY + document.body.top;
				
				} else {
					var current = this.element;
					while (current != null) {
						posX += current.offsetLeft;
						posY += current.offsetTop;
						current = current.offsetParent;
					}
				}
				
				posY += - 90;
				
				this.container.style.left = posX + "px";
				this.container.style.top = posY + "px";
				
				this.shade.style.left = (posX - 36) + "px";
				this.shade.style.top = (posY - 38) + "px";
			}
			
			// vnitrni relativni kontejner
			this.container_inner = document.createElement("DIV");
			this.container_inner.className = "cal_inner";
			
			var cl = this.c_close_butt = document.createElement("INPUT"); // button pro zavreni
			var nx = this.c_next_butt = document.createElement("INPUT"); // button dalsi
			var ls = this.c_last_butt = document.createElement("INPUT"); // button predchozi
			
			cl.type = "button";
			cl.value = "zavřít";
			cl.className = "close_butt";
			cl.onclick = function(trgEvent) { self.evt_close(trgEvent == null ? event : trgEvent); }
			
			nx.type = "button";
			nx.value = "další >>";
			nx.className = "next_butt";
			nx.onclick = function(trgEvent) { self.evt_next(trgEvent == null ? event : trgEvent); }
			
			ls.type = "button";
			ls.value = "<< předchozí";
			ls.className = "last_butt";
			ls.onclick = function(trgEvent) { self.evt_last(trgEvent == null ? event : trgEvent); }
			
			this.container_inner.appendChild(cl);
			this.container_inner.appendChild(nx);
			this.container_inner.appendChild(ls);
			
			this.container.appendChild(this.container_inner);
			
			this.parent.container.appendChild(this.container);
			this.parent.container.appendChild(this.shade);
			
			// refresh
			this.make(0);
		}
		
		this.destroy = function() {
			// destrukce
			if (this.request != null) this.request = null;
			
			if (this.last_month != null) this.last_month.destroy();
			if (this.cur_month != null) this.cur_month.destroy();
			
			this.container_inner.removeChild(this.c_close_butt);
			this.container_inner.removeChild(this.c_next_butt);
			this.container_inner.removeChild(this.c_last_butt);
			
			this.c_close_butt = null;
			this.c_next_butt = null;
			this.c_last_butt = null;
			
			this.container.removeChild(this.container_inner);
			this.container_inner = null;
			
			this.parent.container.removeChild(this.shade);
			this.parent.container.removeChild(this.container);
			this.shade = null;
			this.container = null;
			
			this.parent = null;
			self = null;
		}
		
		this.state_changed = function() {
			switch (this.request.readyState) {
				case 4 : // complete
					this.evt_request_done();
					break;
			}
		}
		
		this.make = function(flag) {
			// request
			if (this.request != null) this.request = null;
			
			if (window.ActiveXObject) {
				this.request = new ActiveXObject("Microsoft.XMLHTTP");
			} else {
				this.request = new XMLHttpRequest();
			}
			
			this.request.onreadystatechange = function() { self.state_changed(); }
		
			// input
			var input_str = "";
			
			if (this.data_month != false) input_str += "month=" + this.data_month + "&";
			if (this.data_year != false) input_str += "year=" + this.data_year + "&";
			
			input_str += "flag=" + flag;
		
			this.request.open("POST", this.parent.interface, true);
			this.request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
			this.request.send(input_str);
		}
		
		this.get_response = function() {
			xml = this.request.responseXML;
			
			if (xml != null) {
				var head = xml.getElementsByTagName("head")[0];
				var body = xml.getElementsByTagName("body")[0];

				if (head != null && body != null) {
					return new Array(head, body);
						
				} else {
					alert("Document does not contain base elements! output: " + this.request.responseText);
				}
			} else {
				alert("Invalid response document! output: " + this.request.responseText);
			}
			
			return null;
		}
	
		this.lock_flag = false;
		this.locked = function() { return this.lock_flag; }
		this.lock = function() { this.lock_flag = true; }
		this.unlock = function() { this.lock_flag = false; }
		
		this.select_value = function(input, to_select) {
			var founded = false;
			var pos = 0;
			
			while (!founded && pos < input.options.length)
				if (input.options[pos].value == to_select) founded = true;
				else pos++;
			
			if (founded) input.selectedIndex = pos;
		}
		
		this.get_inputs = function() {
			var inputs = new Array();
			
			switch (this.input.tagName.toUpperCase()) {
				case "SELECT" : // 3 selecty vedle sebe
					var parent = this.input.parentNode;
					var pos = 0;
					var input_start = false;
					
					while (pos < parent.childNodes.length && inputs.length < 3) {
						if (parent.childNodes[pos] == this.input) {
							inputs[inputs.length] = parent.childNodes[pos];
							input_start = true;
						} else if (input_start && parent.childNodes[pos].tagName.toUpperCase() == "SELECT") {
							inputs[inputs.length] = parent.childNodes[pos];
						}
						
						pos++;
					}
					break;
					
				case "INPUT" : // nastaveni hodnoty
					inputs[inputs.length] = this.input;
					break;
			}
			
			return inputs;
		}
		
		this.pick = function(ref) {
			var inputs = this.get_inputs();
			
			if (inputs.length == 3) {
				this.select_value(inputs[0], ref.day);
				this.select_value(inputs[1], ref.month);
				this.select_value(inputs[2], ref.year);
				
			} else {
				inputs[0].value = ref.day + "." + ref.month + "." + ref.year;
			}
			
			this.destroy();
		}
		
		this.add_month = function(ref) {
			if (this.last_month != null) this.last_month.destroy();
			
			this.last_month = this.cur_month;
			
			this.cur_month = ref;
			this.cur_month.construct();
		}
		
		this.go_next = function() {
			// next request
			this.lock();
			this.make(1);
		}
		
		this.go_last = function() {
			// last request
			this.lock();
			this.make(-1);
		}
		
		this.loop = function() {
			if (this.cur_month != null) this.cur_month.loop();
			if (this.last_month != null) this.last_month.loop();
		}
		
		this.evt_request_done = function() {
			this.unlock();
			var res = this.get_response();
			
			if (res != null) {
				var det_flag = 0;
				var before_month = this.data_month;
				var before_year = this.data_year;
			
				this.data_month = parseInt(res[0].getElementsByTagName("month")[0].firstChild.nodeValue);
				this.data_month_name = res[0].getElementsByTagName("month_name")[0].firstChild.nodeValue;
				this.data_year = parseInt(res[0].getElementsByTagName("year")[0].firstChild.nodeValue);
				
				if (before_month == false || before_year == false) det_flag = 1;
				else det_flag = (this.data_year > before_year || (this.data_year == before_year && this.data_month > before_month)) ? 1 : -1;
				
				var nMonth = new cal_month(this, det_flag, this.data_month, this.data_month_name, this.data_year);
				
				var el_nodes = res[1].getElementsByTagName("node");
				if (el_nodes.length > 0) {
				
					var f;
					for (f = 0; f < el_nodes.length; f++) {
						nMonth.add_node(
							parseInt(el_nodes[f].getElementsByTagName("day")[0].firstChild.nodeValue),
							parseInt(el_nodes[f].getElementsByTagName("month")[0].firstChild.nodeValue),
							parseInt(el_nodes[f].getElementsByTagName("year")[0].firstChild.nodeValue),
							el_nodes[f].getElementsByTagName("flag")[0].firstChild.nodeValue
						);
					}
				
					this.add_month(nMonth);
				}
			}
		}
		
		this.evt_close = function(trgEvent) {
			if (!this.locked()) {
				this.parent.rem(this);
				this.destroy();
			}
		}
		
		this.evt_next = function(trgEvent) {
			if (!this.locked()) {
				this.go_next();
			}
		}
		
		this.evt_last = function(trgEvent) {
			if (!this.locked()) {
				this.go_last();
			}
		}
	}

	function cal_collector(container) {
		this.interface = "http://www.budget.cz/cal_ifc.php";
		
		this.container = container;
		this.first = null;
		this.end = null;
		
		this.add = function(ref) {
			if (this.first == null) {
				this.first = ref;
				this.end = ref;
				ref.parent = this;
			} else {
				this.end.next = ref;
				ref.last = this.end;
				this.end = ref;
				ref.parent = this;
			}
		}
		
		this.rem = function(ref) {
			if (ref.next == null && ref.last == null) {
				this.first = null;
				this.end = null;
			} else if (ref.next == null) {
				this.end = this.end.last;
				this.end.next = null;
			} else if (ref.last == null) {
				this.first = this.first.next;
				this.first.last = null;
			} else {
				ref.last.next = ref.next;
				ref.next.last = ref.last;
			}
			ref = null;
		}
		
		this.loop = function() {
			var current = this.first;
			
			while (current != null) {
				current.loop();
				current = current.next;
			}
		}
		
		this.open = function(ident, element) {
			var ref = new cal_class(ident, element);
			this.add(ref);
			ref.construct();
		}
	}

	var callendar = null;
	
	function callendar_init(ident) {
		callendar = new cal_collector(document.getElementById(ident));
		callendar_loop();
	}
	
	function callendar_loop() {
		callendar.loop();
		setTimeout("callendar_loop()", 50);
	}
