﻿function CreateNode(class_name, inner_text, _styling_attribs_)
{ 
	var elm = document.createElement(class_name);
	// vyrobi element

	if(inner_text != null) {
		var txt = document.createTextNode(inner_text);
		elm.appendChild(txt);
	}
	// vyrobi textovy vnitrek (pokud nezadano null)

	for(var i = 2; i < arguments.length - 1; i += 2)
		elm.style[arguments[i]] = arguments[i + 1];
	// pouzije zbyvajici argumenty jako dvojice jmeno-hodnota pro styl

	return elm;
	// vrati hotovy element
}

function insertAfter(existing_elem, new_elem)
{
	var node_list = existing_elem.parentNode.childNodes;
	for(var i = 0; i < node_list.length; ++ i) {
		if(node_list[i] == existing_elem) {
			if(i + 1 < node_list.length)
				existing_elem.parentNode.insertBefore(new_elem, node_list[i + 1]);
			else
				existing_elem.parentNode.appendChild(new_elem);
			return;
		}
	}
	// najit existujici element v seznamu deti jeho rodice, pak vlozit new_elem budto pred dalsi,
	// nebo na konec pokud dalsi neni (tzn. existujici je posledni)

	existing_elem.parentNode.appendChild(new_elem);
	// existujici jsme nenasli (chyba?), pripojit nakonec
}

var calendar_count = 0;

/*
 *	function putCalendarIcon(elem_id, format_string, read_only, icon_dx, icon_dy)
 *		- use this to insert calendar controls into your page
 *		- elem_id is id of input field
 *		- format_string is formating string, such as "yyyy-mm-dd" or "dd.mm.yyyy"
 *		- read_only is boolean, if true, elem_id field is set to
 *		  read-only and can be only changed using calendar control
 *		- additional icon_dx, icon_dy parameters allow correction of icon position
 */
function putCalendarIcon(elem_id, format_string, read_only)
{
	var elem = document.getElementById(elem_id);
	// najde textbox

	elem.setAttribute("dateFormatString", format_string);
	// nastavime textboxu nas vlastni atribut, a sice formatovaci string pro datum

	if(read_only)
		elem.readOnly = true;
	// nastavi read-only

	var cal_id = calendar_count.toString();
	++ calendar_count;
	while(cal_id.length < 5)
		cal_id = '0' + cal_id;
	// vyrobi 5-mistne id pro kalendar, zarovnane nulami zleva

	var icon_dx = (arguments.length >= 4)? arguments[3] : 0;
	var icon_dy = (arguments.length >= 5)? arguments[4] : 0;
	var icon_node = CreateNode("div", null, "position", "absolute", "left", "" + icon_dx + "px", "top", "" + icon_dy + "px");
	icon_node.id = "calendar_button_" + cal_id;
	icon_node.className = "calendar-icon";
	icon_node.onclick = function() { showCalendar(elem_id, cal_id); };
	// div, obsahujici na pozadi ikonu kalendare; absolutni pozice -> plave mimo layout dokumentu

	var calendar_node = CreateNode("div", null, "position", "absolute",
		"left", "" + (icon_dx + 25) + "px", "top", "" + icon_dy + "px", "display", "none");
	calendar_node.id = "calendar_area_" + cal_id;
	// div, obsahujici vlastni kalendar; absolutni pozice -> plave mimo layout dokumentu; nyni skryte

	var new_node = CreateNode("div", " ", "display", "inline", "position", "relative", "margin", "5px");
	// inline div, ktery se zobrazi *na radku* za textboxem, bude obsahovat ikonu a vlastni kalendar

	new_node.appendChild(icon_node);
	new_node.appendChild(calendar_node);
	// vlozi ikonu a kalendar do inline divu

	insertAfter(elem, new_node);
}

function ceDayIndex(date) // central-european zero-based day in week index (monday is 0, sunday is 6)
{
	return (date.getDay() + 6) % 7;
}

function getISO8601Date(date) // returns date in ISO 8601 yyyy-mm-dd format
{
	return date.getFullYear() + '-' + ((date.getMonth() < 9)? '0' : '') +
		(date.getMonth() + 1) + '-' + ((date.getDate() <= 9)? '0' : '') + date.getDate();
}

function daysLink(date, textbox_id, partial_id_str) // returns HTML for link containing day number and onclicked handler to set the date
{
	return '<a href="#' + getISO8601Date(date) + '" onclick="clickedCalendar(' + date.getTime() + ", '" +
		textbox_id + "', '" + partial_id_str + '\'); return false;">' + date.getDate() + '</a>';
}

function decoratedDayCell(date, marked_date, textbox_id, partial_id_str) // returns HTML for table cell (td), containing styled link with day number and onclicked handler to set the date
{
	if(date.getDate() == marked_date.getDate() &&
	   date.getMonth() == marked_date.getMonth() &&
	   date.getYear() == marked_date.getYear())
		return '<td><span class="calend-today">' + daysLink(date, textbox_id, partial_id_str) + '</span></td>';
	else if(ceDayIndex(date) >= 5)
		return '<td><span class="calend-weekend">' + daysLink(date, textbox_id, partial_id_str) + '</span></td>';
	else
		return '<td><span class="calend-ordinary">' + daysLink(date, textbox_id, partial_id_str) + '</span></td>';
}

function shiftMonth(some_date, sign) // returns date some_date with month shifted by 1 * sign (handles crossing year boundary)
{
	var date = new Date();
	date.setTime(some_date.getTime());
	// zkopiruje some_date do noveho objektu datum

	if(some_date.getMonth() + sign > 11) {
		date.setMonth(0); // leden
		date.setFullYear(date.getFullYear() + 1); // dalsi rok
	} else if(some_date.getMonth() + sign < 0) {
		date.setMonth(11); // prosinec
		date.setFullYear(date.getFullYear() - 1); // predchozi rok
	} else {
		var expectedMonth = date.getMonth() + sign;
		date.setMonth(date.getMonth() + sign); // predchozi / dalsi mesic
		while(date.getMonth() > expectedMonth)
			date.setDate(date.getDate() - 1); // fixup preteceni datumu (je 31.10. a ja chci 11., ten ale nema 31 dni, tak to skoci do prosince ...)
		while(date.getMonth() < expectedMonth)
			date.setDate(date.getDate() + 1); // does this ever happen?
	}

	return date;
}

function generateCalendarHTML(marked_date, orig_date, textbox_id, partial_id_str) // returns HTML for calendar, displaying monh / year from marked_date (day is ignored), while orig_date is date, written inside the associated textbox (and is highlighted when displayed)
{
	var prevFun = "regenCalendar(" + shiftMonth(marked_date, -1).getTime() + ", '" +
		textbox_id + "', '" + partial_id_str + "');";
	var todayFun = "clickedCalendar(" + (new Date()).getTime() + ", '" +
		textbox_id + "', '" + partial_id_str + "');";
	var nextFun = "regenCalendar(" + shiftMonth(marked_date, +1).getTime() + ", '" +
		textbox_id + "', '" + partial_id_str + "');";
	var closeFun = "hideCalendar('" + textbox_id + "', '" + partial_id_str + "');";
	// vyrobi funkce pro odkazy kliknuti na predchozi / dalsi mesic

	var year = marked_date.getFullYear();

	var dayLabels = new Array('Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne');
	var monthLabels = new Array('Leden', 'Únor', 'Březen', 'Duben', 'Květen',
		'Červen', 'Červenec', 'Srpen', 'Září', 'Říjen', 'Listopad', 'Prosinec');
	var firstDay = new Date();
	firstDay.setTime(marked_date.getTime());
	firstDay.setDate(1);

	var calHtml = '<table class="calendar">';
	// begin calendar table ...

	calHtml += '<caption>';
	calHtml += '<a class="calend-left-spin" href="#predchozi-mesic" onclick="' + prevFun + '; return false;">«</a>';
	calHtml += '<span>' + monthLabels[marked_date.getMonth()] + ' ' + year + '</span>';
	calHtml += '<a class="calend-right-spin" href="#dalsi-mesic" onclick="' + nextFun + '; return false;">»</a>';
	calHtml += '<a class="calend-close-btn" href="#zavrit" onClick="' + closeFun + '; return false;">x</a>';
	calHtml += '</caption>';
	// add caption with current year / date / next / prev / close controls

	calHtml += '<thead><tr>';
	for(var i = 0; i < 7; ++ i)
		calHtml += '<th><span>' + dayLabels[i] + '</span></th>';
	calHtml += '</tr></thead>';
	// first line containing header with day names

	calHtml += '<tbody><tr>';
	for(var i = 0, n = ceDayIndex(firstDay); i < n; ++ i)
		calHtml += '<td class="calend-empty"><span>&nbsp;</span></td>';
	// second line, empty space - occupied by days in previous month

	for(;; firstDay.setDate(firstDay.getDate() + 1)) {
		calHtml += decoratedDayCell(firstDay, orig_date, textbox_id, partial_id_str);
		if(ceDayIndex(firstDay) == 6)
			break;
    }
	calHtml += '</tr><tr>';
	// second line containing first week

	for(firstDay.setDate(firstDay.getDate() + 1);
	   firstDay.getMonth() == marked_date.getMonth();
	   firstDay.setDate(firstDay.getDate() + 1)) {
		calHtml += decoratedDayCell(firstDay, orig_date, textbox_id, partial_id_str);
		if(ceDayIndex(firstDay) == 6)
			calHtml += '</tr><tr>';
	}
	// the rest of calendar

	for(var i = ceDayIndex(firstDay); i > 0 && i < 7; ++ i)
		calHtml += '<td class="calend-empty"><span>&nbsp;</span></td>';
	calHtml += '</tr></tbody>';
	// last table columns

	calHtml += '<tfoot><tr>';
	calHtml += '<td colspan="7"><a href="#dnesni-datum" onclick="' + todayFun + '; return false;">dnes</a></td>';
	calHtml += '</tr></tfoot>';
	// link back to today

	return calHtml + '</table>';
}

function isDigit(character_str) // returns true if first character of character_str is digit (assumes character_str is not empty)
{
	return character_str.charCodeAt(0) >= '0'.charCodeAt(0) &&
		character_str.charCodeAt(0) <= '9'.charCodeAt(0);
}

function digitNo(character_str) // returns value of digit at first character of character_str (assumes character_str is not empty)
{
	return character_str.charCodeAt(0) - '0'.charCodeAt(0);
}

function getTextboxDate(textbox_elem) // parses date from textbox textbox_elem (object, not string with id) and returns it. in case error(s) occured, displays erro message. and returns current date.
{
	var format_string = textbox_elem.getAttribute("dateFormatString").toLowerCase();
	// vezme si formatovaci string, mala pismena
	// ocekava vyskyty yyyy, mm, dd

	var date_str = textbox_elem.value;
	// nazere obsah textboxu

	var year = 0, month = 0, day = 0;
	var j = 0;
	for(var i = 0; i < format_string.length; ++ i) {
		var f_ch = format_string.charAt(i);
		if(f_ch == 'y' || f_ch == 'm' || f_ch == 'd') {
			while(i + 1 < format_string.length && format_string.charAt(i + 1) == f_ch)
				++ i;
			// preskoci vicenasobny vyskyt pismene
		}

		if(f_ch == 'y') {
			if(j >= date_str.length) {
				alert('chyba: datum je příliš krátké');
				return new Date();
			}
			// chyba - datum prilis kratke, vrat dnesni datum

			if(!isDigit(date_str.charAt(j))) {
				alert('chyba: datum neobsahuje znaky, odpovídající formátovacímu řetězci');
				return new Date();
			}
			// chyba - tady maji byt cisla

			while(j < date_str.length && isDigit(date_str.charAt(j))) {
				year *= 10;
				year += digitNo(date_str.charAt(j));
				++ j;
			}
			// precti integer, parsuj "rucne"
		} else if(f_ch == 'm') {
			if(j >= date_str.length) {
				alert('chyba: datum je příliš krátké');
				return new Date();
			}
			// chyba - datum prilis kratke, vrat dnesni datum

			if(!isDigit(date_str.charAt(j))) {
				alert('chyba: datum neobsahuje znaky, odpovídající formátovacímu řetězci');
				return new Date();
			}
			// chyba - tady maji byt cisla

			while(j < date_str.length && isDigit(date_str.charAt(j))) {
				month *= 10;
				month += digitNo(date_str.charAt(j));
				++ j;
			}
			// precti integer, parsuj "rucne"
		} else if(f_ch == 'd') {
			if(j >= date_str.length) {
				alert('chyba: datum je příliš krátké');
				return new Date();
			}
			// chyba - datum prilis kratke, vrat dnesni datum

			if(!isDigit(date_str.charAt(j))) {
				alert('chyba: datum neobsahuje znaky, odpovídající formátovacímu řetězci');
				return new Date();
			}
			// chyba - tady maji byt cisla

			while(j < date_str.length && isDigit(date_str.charAt(j))) {
				day *= 10;
				day += digitNo(date_str.charAt(j));
				++ j;
			}
			// precti integer, parsuj "rucne"
		} else {
			if(j >= date_str.length) {
				alert('chyba: datum je příliš krátké');
				return new Date();
			}
			// chyba - datum prilis kratke, vrat dnesni datum

			if(date_str.charAt(j) != format_string.charAt(i)) {
				alert('chyba: datum neobsahuje znaky, odpovídající formátovacímu řetězci');
				return new Date();
			}
			// chyba - ostatni znaky nesouhlasi, vrat dnesni datum

			++ j;
		}
	}
	if(j != date_str.length) {
		alert('chyba: datum je příliš dlouhé - znaky na konci navíc');
		return new Date();
	}

	if(month > 12 || day > 31) {
		alert('chyba: datum je nesmyslné - špatné číslo měsíce, či dne');
		return new Date();
	}

	var result = new Date();
	result.setFullYear(year, month - 1, day);
	//result.setMonth(month - 1);
	//result.setDate(day);
	// vyrobi novy objekt datum

	return result;
}

function setTextboxDate(textbox_elem, set_date) // sets date set_date to textbox textbox_elem (object, not string with id) based on format string (specified in putCalendarIcon)
{
	var format_string = textbox_elem.getAttribute("dateFormatString").toLowerCase();
	// vezme si formatovaci string, mala pismena
	// ocekava vyskyty yyyy, mm, dd

	var date_str = '';
	// zacneme s prazdnym retezcem

	if(format_string == 'yyyy-mm-dd') {
		date_str = getISO8601Date(set_date);
		// pretty common format
	} else {
		var year = set_date.getFullYear().toString(), month = (set_date.getMonth() + 1).toString(),
			day = set_date.getDate().toString();
		// pripravi si rok, mesic, den jako retezce

		for(var i = 0; i < format_string.length; ++ i) {
			var f_ch = format_string.charAt(i);
			if(f_ch == 'y' || f_ch == 'm' || f_ch == 'd') {
				while(i + 1 < format_string.length && format_string.charAt(i + 1) == f_ch)
					++ i;
				// preskoci vicenasobny vyskyt pismene
			}

			if(f_ch == 'y') {
				date_str += year;
				// pripoji rok
			} else if(f_ch == 'm') {
				date_str += month;
				// pripoji mesic
			} else if(f_ch == 'd') {
				date_str += day;
				// pripoji den
			} else {
				date_str += format_string.charAt(i);
				// pripoji "jiny" znak (napr. tecky, ...)
			}
		}
	}

	textbox_elem.value = date_str;
	// nastavi text
}

function hideCalendar(textbox_id, partial_id_str) // hides calendar control, changes are ignored
{
	var icon_div = document.getElementById("calendar_button_" + partial_id_str);
	// najde div s ikonou

	var area_div = document.getElementById("calendar_area_" + partial_id_str);
	// najde div, co bude obsahovat kalendar

	icon_div.onclick = function() { showCalendar(textbox_id, partial_id_str); };
	// treti kliknuti = zase ukazat kalendar

	area_div.innerHTML = "";
	area_div.style.display = "none";
	// schova div s kalendarem a smaze jeho obsah
}

function showCalendar(textbox_id, partial_id_str) // shows calendar control, re-reads date from the associated textbox
{
	var elem = document.getElementById(textbox_id);
	// najde textbox

	var icon_div = document.getElementById("calendar_button_" + partial_id_str);
	// najde div s ikonou

	var area_div = document.getElementById("calendar_area_" + partial_id_str);
	// najde div, co bude obsahovat kalendar

	icon_div.onclick = function() { hideCalendar(textbox_id, partial_id_str); };
	// druhe kliknuti = schovat kalendar

	var regen_date = getTextboxDate(elem);
	// pouzijeme datum z textboxu

	elem.setAttribute("currentDate", regen_date.getTime());
	// zapamatujeme si nastavene datum

	area_div.innerHTML = generateCalendarHTML(regen_date, regen_date, textbox_id, partial_id_str);
	area_div.style.display = "block";
	// ukaze div s kalendarem
}

function regenCalendar(milliseconds, textbox_id, partial_id_str) // regenerates calendar to contain month / year specified by milliseconds (unix time), used to skip calendar pages when clicking < / >
{
	var elem = document.getElementById(textbox_id);
	// najde textbox

	var area_div = document.getElementById("calendar_area_" + partial_id_str);
	// najde div, co bude obsahovat kalendar

	var regen_date = new Date();
	regen_date.setTime(milliseconds);
	// ted pouzijeme urcene datum

	var orig_date = new Date();
	orig_date.setTime(elem.getAttribute("currentDate"));
	// zjistime puvodni nastavene datum

	area_div.innerHTML = generateCalendarHTML(regen_date, orig_date, textbox_id, partial_id_str);
	// vygeneruje znova zdrojovy kod pro div s kalendarem
}

function clickedCalendar(milliseconds, textbox_id, partial_id_str) // date milliseconds (unix time) was clicked on the calendar, hide calendar control and update associated textbox
{
	var elem = document.getElementById(textbox_id);
	// najde textbox

	var icon_div = document.getElementById("calendar_button_" + partial_id_str);
	// najde div s ikonou

	var area_div = document.getElementById("calendar_area_" + partial_id_str);
	// najde div, co bude obsahovat kalendar

	icon_div.onclick = function() { showCalendar(textbox_id, partial_id_str); };
	// treti kliknuti = zase ukazat kalendar

	area_div.innerHTML = "";
	area_div.style.display = "none";
	// schova div s kalendarem a smaze jeho obsah

	var set_date = new Date();
	set_date.setTime(milliseconds);
	setTextboxDate(elem, set_date);
	// nastavi oznacene datum do textboxu
}

