﻿// Vlastnosti ulozene v cookie rozdeli na jednotlive slozky
function parseTQCookie (name)
{
	var tQArray = readCookie('testQuestions')[name].split(":");
	//alert(tQArray.join(":"));
	// Ukazkove cookie:
	// d41d8cd98f00b204e9800998ecf8427e:13548734:-7,15,-15:0,1,2{4,5[7,9,9[0]]},3{},4:3(7556/5),4(9500/1)
	// heslo:cas:body:otazky:nejhorsi-otazky
	// heslo				- heslo zakodovane pomoci md5
	// cas					- cas do konce testu v mikrosekundach
	// body					- dosazene,max,min
	//   dosazene			- cislo double
	//   min				- cislo double
	//   max				- cislo double
	// otazky				- otazka[,otazka]+
	//   otazka				- cislo-otazky{odpovedi}
	//     cislo-otazky		- cislo ve 32kovem formatu [0-9a-v]+
	//     odpovedi			- odpoved[,odpoved]
	// nejhorsi-otazky		- otazka[,otazka]
	//   otazka				- cislo-otazky(uspesnost/pocet-opakovani)
	//     uspesnost		- cislo s 4 ciframi udavajicimi desetiny promile
	//     pocet-opakovani	- kolikrat jiz byla otazka zobrazena uzivately
	tQ = [tQArray[0], tQArray[1], tQArray[2].split(","), Array(), Array()];
	tQ[3][-1] = Array();
	for (var i = 0, iQ = ""; i <= tQArray[3].length; i++)
	{
		if (i == tQArray[3].length) // Posledni cislo otazky, pokud za nim nejsou slozene zavorky
		{
			tQ[3][-1][tQ[3][-1].length] = iQ; // Pridam cislo otazky do seznamu otazek
		}
		else switch (tQArray[3].charAt(i))
		{
			case ",":
				tQ[3][-1][tQ[3][-1].length] = iQ; // Pridam cislo otazky do seznamu otazek
				iQ = ""; // Budu ocekavat dalsi cislo otazky
			break;
			case "{":
				tQ[3][iQ] = tQArray[3].substring(i + 1, i = tQArray[3].indexOf("}", i)); // Ulozim vnitrek slozenych zavorek a posunu ukazatel
			break;
			default:
				iQ += tQArray[3].charAt(i); // Cislo otazky tvori tento znak
		}
	}
	if (tQArray[4].length > 0) // Pokud je nejaky retezec zadan
	{
		tQArray[4] = tQArray[4].split(","); // Nejhorsi otazky rozdelim podle carek
		for (i = 0; i < tQArray[4].length; i++)
		{
			tQ[4][i] = Array();
			tQ[4][i][0] = tQArray[4][i].substring(0, tQArray[4][i].indexOf("(")); // Cislo otazky
			tQ[4][i][1] = tQArray[4][i].substring(1 + tQArray[4][i].indexOf("("), tQArray[4][i].indexOf("/")); // Uspesnost
			tQ[4][i][2] = tQArray[4][i].substring(1 + tQArray[4][i].indexOf("/"), tQArray[4][i].length - 1); // Pocet opakovani
		}
	}
	else // Neexistuji spatne odpovedi
	{
		tQ[4][0] = [-1, 10000, 1]; // Tak dobra nejhorsi otazka, aby ji kterakoliv jina nahradila. Ma vymysleny index -1, aby nemohla byt porovnana s nejakou existujici.
	}
	return tQ;
}

// Overi zda se heslo shoduje s heslem vyzadanym od uzivatele
function md5Check (md5)
{
	pass = _password("Zadejte prosím Vaše heslo.");
	if (!pass && pass.toString() != "")
	{
		return false;
	}
	if (hex_md5(pass) != md5)
	{
		alert("Heslo se neshoduje!");
		return false;
	}
	return true;
}

// Kontrola uzivatele podle hesla. Pokud se shoduje, je mozne cookie vymazat.
function userCheck (userName, md5)
{
	if (md5Check (md5))
	{
		setCookie ("actualUser", userName); // Nastavim aktualniho uzivatele do docasneho cookie
		setCookie ("testQuestions[" + userName + "]", readCookie("testQuestions")[userName], (new Date((new Date()).getTime() + cookieExpires)).toGMTString()); // Prodlouzim platnost cookie
		return true;
	}
	else
	{
		return false;
	}
}

// Zkontroluje zda se intervaly obtiznosti otazek neprekryvaji a vygeneruje seznam otazek
function randomQuestions ()
{
	var i, j, k, l; // Promenne pro prochazeni cykly
	var iQ; // Nahodne cislo otazky pridavane do seznamu
	var qDNum = new Array(); // Pocet otazek na kazdou obtiznost
	var qList = new Array(); // Vysledny seznam otazek
	var qString = ""; // Vysledny seznam otazek v retezcove podobe
	
	for (i = 0; i < iQuestions.length; i++) // Projdu pres pocet obtiznosti otazek
	{
		if (iQuestions[i] != null && iQuestions[i] > 0) // Pocet otazek pro tuto obtiznost je zadany
		{
			if (iQDifficulty[i] == null) // Otazky pro tuto obtiznost nejsou zadany
			{
				alert ("Interval otázek pro obtížnost iQuestions[" + i + "] není definován!");
				break;
			}
			else if (iQDifficulty[i].length % 2 == 1)
			{
				alert ("Interval otázek pro obtížnost iQuestions[" + i + "] není správně definován!\n(Lichý počet prvků v poli.)");
				break;
			}
			
			qDNum[i] = 0; // Pocet otazek dane obtiznosti
			for (j = 0; j < iQDifficulty[i].length; j+=2) // Zjistim intervaly otazek dane obtiznosti
			{
				if (base32_decode(iQDifficulty[i][j]) > base32_decode(iQDifficulty[i][j + 1]))
				{
					alert ("Interval otázek pro obtížnost iQuestions[" + i + "] není správně definován!\n(Prvek " + iQDifficulty[i][j] + " je větší než prvek " + iQDifficulty[i][j + 1] + ".)");
					err = 2;
					break;
				}
				
				for (k = 0; k <= i; k++) // Projdu pres jiz existujici urovne obtiznosti
				{
					for (l = 0; l < iQDifficulty[k].length; l+=2) // V kazde urovni projdu pres jiz existujici intervaly otazek
					{
						if (k == i && l >= j) // Prochazim jen predchozi intervaly otazek v posledni urovni
						{
							break;
						}
						if (base32_decode(iQDifficulty[i][j]) <= base32_decode(iQDifficulty[k][l + 1]) && base32_decode(iQDifficulty[i][j + 1]) >= base32_decode(iQDifficulty[k][l]))
						{
							alert ("Chyba v zadání intervalů pro otázky iQDifficulty[" + k + "] a iQDifficulty[" + i + "]. Obsahují překrývající se interval " + (base32_decode(iQDifficulty[i][j]) < base32_decode(iQDifficulty[k][l]) ? iQDifficulty[k][l] : iQDifficulty[i][j]) + " - " + (base32_decode(iQDifficulty[i][j + 1]) > base32_decode(iQDifficulty[k][l + 1]) ? iQDifficulty[k][l + 1] : iQDifficulty[i][j + 1]) + "!");
							err = 2;
							break;
						}
					}
					if (err == 2)
					{
						break;
					}
				}
				qDNum[i] += 1 + base32_decode(iQDifficulty[i][j + 1]) - base32_decode(iQDifficulty[i][j]); // Navyseni poctu otazek dle delky intervalu
				if (err == 2)
				{
					break;
				}
			}
			if (err == 2)
			{
				break;
			}
			
			if (iQuestions[i] > qDNum[i]) // Zkontrolovat zda je pocet otazek teto obtiznosti nizsi nebo roven celkovemu poctu otazek teto obtiznosti
			{
				alert ("Interval otázek pro obtížnost iQuestions[" + i + "] je nižší než požadovaný počet otázek dané obtížnosti!");
				break;
			}
			
			qList[i] = new Array(); // Vysledny seznam otazek dane obtiznosti
			for (j = 0; j < iQuestions[i]; j++) // Vygeneruju zadany pocet nahodnych cisel otazek z dane obtiznosti
			{
				iQ = Math.floor(Math.random() * (qDNum[i] - j)); // Nahodne cislo v rozsahu jeste nevybranych otazek dane obtiznosti
				for (k = 0; k < qList[i].length; k++) // Projdu pres vysledny seznam otazek
				{
					if (qList[i][k] > iQ) // Pokud pred danym cislem lezi jine cislo (funguje jen na setridenem seznamu vyslednych otazek) 
					{
						break;
					}
					iQ++; // Posunu se na nasledujici
				}
				qList[i][qList[i].length] = iQ; // Pridani otazky do seznamu
				qList[i].sort(numsort); // Setrideni podle cisel
			}
			
			k = 0; // Poradove cislo prvku v ramci sjednocenych intervalu dane obtiznosti
			l = 0; // Cislo intervalu
			for (j = 0; j < qList[i].length; j++) // Projdu pres vysledny seznam otazek
			{
				if (qList[i][j] >= k && qList[i][j] <= k + base32_decode(iQDifficulty[i][l + 1]) - base32_decode(iQDifficulty[i][l])) // Pokud je nalezen interval ve kterem se nachazi hledane cislo otazky
				{
					qList[i][j] = (base32_decode(iQDifficulty[i][l]) + qList[i][j] - k); // Ulozeni zakodovaneho prvku
					continue; // Pokracovani bez posunu (dalsi prvek muze stale lezet v danem intervalu)
				}
				k += 1 + base32_decode(iQDifficulty[i][l + 1]) - base32_decode(iQDifficulty[i][l]); // Zvetseni o pocet prvku intervalu
				l += 2; // Zdvihnuti intervalu
			}
		}
	}
	
	for (i = 0; i < qList.length; i++) // Projdu pres obtiznosti vysledneho seznamu otazek
	{
		for (j = 0; j < qList[i].length; j++) // Projdu pres vysledny seznam otazek dane obtiznosti
		{
			qString += base32_encode(qList[i][j]);
			if (j + 1 < qList[i].length) // Dokud nejsem na posledni otazce
			{
				qString += ","; // Pridavam carku jako oddelovac otazek
			}
		}
		if (i + 1 < qList.length) // Dokud nejsem na posledni obtiznosti
		{
			qString += ","; // Pridavam carku jako oddelovac otazek
		}
	}
	return qString;
}

function tQEnd (score)
{
	score = score[0] == 0 ? 0 : (score[0] - score[1]) / (score[2] - score[1]) * 100;
	alert ("Úspěšnost: " + score.toString().substring(0, 5) + "%");
	var qList = "";
	var bList = "";
	for (var i = 0; i < tQ[3][-1].length; i++) // Projdu pres zodpovezene otazky
	{
		if (i > 0)
		{
			qList += ","; // Otazky oddeluju carkou
		}
		qList += tQ[3][-1][i] + "{"; // Oteviraci zavorka pro odpoved
		if (tQ[3][i] != null) // Pokud je odpoved vyplnena
		{
			qList += tQ[3][i]; // Pridam odpoved do seznamu
		}
		qList += "}";
	}
	for (var i = 0; i < tQ[4].length; i++) // Projdu pres spatne odpovedi
	{
		if (i > 0)
		{
			bList += ","; // Odpovedi oddeluji carkou
		}
		bList += tQ[4][i][0] + "(" + str_pad(tQ[4][i][1], 4, 0) + "/" + tQ[4][i][2] + ")"; // Cislo-otazky(uspesnost/pocet-opakovani)
	}
	delCookie("actualUser"); // Smazu cookie se jmenem aktualniho uzivatele
	setCookie("testQuestions[" + actualUser + "]", tQ[0] + ":0:" + tQ[2].join() + ":" + randomQuestions () + ":" + bList, (new Date((new Date()).getTime() + cookieExpires)).toGMTString()); // Ulozim zmeneny cas a novy seznam otazek
	location.href("index.html");
}

// Objekt, stromove usporadane otazky
var tQTree = {
	form: document.forms[0], // Ukazatel na formular se kterym se pracuje
	branch: new Array (), // Pole jednotlivych vetvi
	leaf: function leaf (anObject) // Prida uzel nebo list na vetev (list pokud je zadan objekt)
	{
		var l = new Object ();
		if (anObject != null)
		{
			l.reference = anObject; // Ukazatel na puvodni objekt
			l.score = [0, 0]; // Body za vynechanou nebo zvolenou odpoved
			l.checked = false; // Tato odpoved neni uzivatelem oznacena za spravnou
		}
		l.branch = new Array (); // Nove vytvoreny list nema zadnou podvetev, ale muze se mu vytvorit pozdeji
		return l;
	},
	setTree: function setTree () // Z nazvu otazek v HTML zdroji vypestuje strom :-)
	{
		for (var i = 0; i < this.form.elements.length; i++) // Projdu pres vsechny prvky ve formulari
		{
			if (this.form.elements[i].name && this.form.elements[i].name.substring(0, 3) == "tq_") // Prvky s testovymi otazkami, ktere muze uzivatel oznacit, jsou pojmenovane a jejich jmeno zacina na tq_
			{
				var branch = this.branch; // Ukazatel na soucasnou vetev stromu nastavim na kmen
				var key; // Klic listu ve vetvi
				for (var offset = 0; this.form.elements[i].name.indexOf("[", offset) > -1; offset = 1 + this.form.elements[i].name.indexOf("]", offset)) // Dokud existuji v nazvu hranate zavorky
				{
					key = (this.form.elements[i].name.substring(1 + this.form.elements[i].name.indexOf("[", offset), this.form.elements[i].name.indexOf("]", offset)));
					if (this.form.elements[i].name.indexOf("[", offset) + key.length + 4 < this.form.elements[i].name.length) // Pokud nejsem na konci jmena (na konci muzou byt prazdne zavorky, ktere se preskakuji)
					{
						if (branch[key] == null) // Pokud uzel na vetvi neexistuje
						{
							branch[key] = this.leaf(); // Novy uzel
						}
						branch = branch[key].branch; // Presun ukazatele na podvetev
					}
					else // Narazil jsem na konec jmena
					{
						break; // Ukoncit prubeh cyklem, aby se nezpracovali prazdne zavorky, pokud jsou jeste soucasti jmena
					}
				}
				if (this.form.elements[i].nodeName.toLowerCase() == "input" && this.form.elements[i].type == "radio") // Pokud se jedna o prvek input type="radio"
				{
					if (branch[key] == null) // Pokud uzel na vetvi neexistuje
					{
						branch[key] = this.leaf(); // Novy uzel
					}
					branch[key].branch[this.form.elements[i].value] = this.leaf(this.form.elements[i]); // Posledni list ma nastaven odkaz na puvodni objekt
				}
				else if (this.form.elements[i].nodeName.toLowerCase() == "select") // Pokud se jedna o prvek select
				{
					if (branch[key] == null) // Pokud uzel na vetvi neexistuje
					{
						branch[key] = this.leaf(); // Novy uzel
					}
					for (var j = 0; j < this.form.elements[i].options.length; j++)
					{
						branch[key].branch[this.form.elements[i].options[j].value] = this.leaf(this.form.elements[i].options[j]); // Posledni list ma nastaven odkaz na puvodni objekt
					}
				}
				else // Jakykoliv jiny prvek
				{
					branch[key] = this.leaf(this.form.elements[i]); // Poslednimu listu nastavim odkaz na puvodni objekt
				}
			}
		}
	},
	setScores: function setScores () // Nastavi jednotlivym vetvim pocet bodu pri spravne a pri spatne odpovedi
	{
		var tree = [this.branch]; // Aktualni vetev stromu
		tree[-1] = 0; // Ukazatel
		var name = ""; // Jmeno vetve
		for (var i = 0; i < this.form.fit.value.length; i++)
		{
			switch (this.form.fit.value.charAt(i)) // Podle aktualniho znaku se rozhoduju co delat
			{
				case "(": // Je-li znak otevirajici kulata zavorka
					score = this.form.fit.value.substring(i + 1, this.form.fit.value.indexOf(")", i)); // Vse uvnitr zavorek je bodove ohodnoceni otazky
					i += 1 + score.length; // Posunu ukazatel na znak az pred uzaviraci zavorku (pozor, potom se jeste pricita 1 za prubeh cyklem)
					tree[tree[-1]][name].score = score.split(",");
					name = "";
				break;
				case "]":
					tree[-1]--;
					name = "";
				break;
				case "[":
					tree[tree[-1] + 1] = tree[tree[-1]][name].branch; // Posun na dalsi podvetev
					tree[-1]++;
				case ",":
					name = "";
				break;
				default:
					name += this.form.fit.value.charAt(i);
			}
		}
	},
	getScore: function getScore (aBranch) // Zjisti aktualni pocet bodu v dane vetvi
	{
		if (aBranch == null) // Pokud neni zadana zadna vetev (ocekava se zpracovani celeho stromu)
		{
			aBranch = this.branch; // Vezme se nejvyssi vetev
			this.check (); // Nastavi vysledky odpovedi
		}
		var score = [0, 0, 0]; // Vynuluje pocet dosazenych bodu
		var branchScore = [0, 0, 0]; // Dosazene skore v podvetvi
		
		if (aBranch.branch && isArray (aBranch.branch)) // Pokud existuje podvetev tvorena polem, vim ze jsem na nejakem uzlu
		{
			if (aBranch.checked != null && aBranch.score != null) // Ma-li uzel vlastnosti checked a score
			{
				if (aBranch.checked) // Je-li dana odpoved uzivatelem oznacena za spravnou
				{
					score[0] += parseInt(aBranch.score[1]); // Prictu body za dobre zodpovezenou otazku
				}
				else // Neni-li dana odpoved uzivatelem oznacena za spravnou
				{
					score[0] += parseInt(aBranch.score[0]); // Prictu body za spatne zodpovezenou otazku (body sou zaporne, tak je pricitam)
				}
				score[1] += Math.min(parseInt(aBranch.score[0]), parseInt(aBranch.score[1])); // Minimum bodu
				score[2] += Math.max(parseInt(aBranch.score[0]), parseInt(aBranch.score[1])); // Maximum bodu
			}
			branchScore = this.getScore(aBranch.branch); // Postoupim o pul urovne niz
			score[0] += branchScore[0];
			score[1] += branchScore[1];
			score[2] += branchScore[2];
		}
		else // Jsem na nejake vetvi a musim zjistit jmeno uzlu
		{
			for (var name in aBranch) // Projdu pres vsechny prvky ve vetvi
			{
				branchScore = this.getScore(aBranch[name]); // Postoupim o pul urovne niz
				score[0] += branchScore[0];
				score[1] += branchScore[1];
				score[2] += branchScore[2];
			}
		}
		return score; // Vratim dosazeny pocet bodu
	},
	check: function check (aBranch) // Projde stromem a nastavi zvolene odpovedi
	{
		if (aBranch == null) // Pokud neni zadana zadna vetev (ocekava se zpracovani celeho stromu)
		{
			aBranch = this.branch; // Vezme se nejvyssi vetev
		}
		
		if (aBranch.branch && isArray (aBranch.branch)) // Pokud existuje podvetev tvorena polem, vim ze jsem na nejakem uzlu
		{
			if (aBranch.reference != null) // Ma-li uzel vlastnost reference
			{
				switch (aBranch.reference.nodeName.toLowerCase())
				{
					case "input": // Je-li dana vetev prvek input
						switch (aBranch.reference.type) // Prepinani podle typu prvku input
						{
							case "checkbox": // Jedna-li se o checkbox
							case "radio": // Nebo radio
								if (aBranch.reference.checked) // A je zaskrtnuty
								{
									aBranch.checked = true; // Oznacim ze je odpoved zvolena
								}
								else
								{
									aBranch.checked = false; // Odpoved neni zvolena
								}
							break;
						}
					break;
					case "option": // Je-li dana vetev prvek select
						if (aBranch.reference.selected) // A je zaskrtnuty
						{
							aBranch.checked = true; // Oznacim ze je odpoved zvolena
						}
						else
						{
							aBranch.checked = false; // Odpoved neni zvolena
						}
					break;
				}
			}
			this.check(aBranch.branch); // Postoupim o pul urovne niz
		}
		else // Jsem na nejake vetvi a musim zjistit jmeno uzlu
		{
			for (var name in aBranch) // Projdu pres vsechny prvky ve vetvi
			{
				this.check(aBranch[name]); // Postoupim o pul urovne niz
			}
		}
	},
	toString: function toString (aBranch, level) // Vraci ASCII art strom
	{
		var output = "";
		if (aBranch == null) // Pokud neni zadana zadna vetev (ocekava se zpracovani celeho stromu)
		{
			aBranch = this.branch; // Vezme se nejvyssi vetev
		}
		if (level == null)
		{
			level = 0;
		}
		
		for (var name in aBranch) // Projdu pres vsechny prvky ve vetvi
		{
			for (i = 0; i + 1 < level; i++)
			{
				output += "| ";
			}
			if (i < level)
			{
				output += "+-";
			}
			output += name + "\n";
			
			properties = {
				//reference: null,
				score: null,
				checked: null
			}
			for (var prop in properties)
			{
				if (aBranch[name][prop])
				{
					for (i = 0; i < level; i++)
					{
						output += "| ";
					}
					if (i < level + 1)
					{
						output += "+-";
					}
					
					output += prop + " = ";
					if (prop == "score")
					{
						output += "[";
					}
					output += aBranch[name][prop];
					if (prop == "score")
					{
						output += "]";
					}
					output += "\n";
				}
			}
			
			if (aBranch[name].branch.length > 0) // Existuje-li podvetev
			{
				output += this.toString(aBranch[name].branch, level + 1); // Zavolam tuto funkci rekurzivne
			}
		}
		return output; // Vratim dosazeny pocet bodu
	},
	filled: function filled (aBranch, aBranchName) // Textova reprezentace uzivatelem vyplnenych moznosti
	{
		if (aBranch == null) // Pokud neni zadana zadna vetev (ocekava se zpracovani celeho stromu)
		{
			aBranch = this.branch; // Vezme se nejvyssi vetev
		}
		if (aBranchName == null) // Pokud neni zadano jmeno
		{
			aBranchName = ""; // Pouzije se prazdny retezec
		}
		var inner = "", output = "";
		
		if (aBranch.branch && isArray (aBranch.branch)) // Pokud existuje podvetev tvorena polem, vim ze jsem na nejakem uzlu
		{
			if (aBranch.reference != null) // Ma-li uzel vlastnost reference
			{
				if (aBranch.checked) // Je-li prvek zaskrtnuty
				{
					output += aBranchName;
				}
			}
			if (inner = this.filled(aBranch.branch)) // Pokud postoup o pul urovne niz vraci neprazdny retezec
			{
				if (output)
				{
					output += ",";
				}
				output += aBranchName + "[" + inner + "]";
			}
		}
		else // Jsem na nejake vetvi a musim zjistit jmeno uzlu
		{
			for (var name in aBranch) // Projdu pres vsechny prvky ve vetvi
			{
				if (inner = this.filled(aBranch[name], name)) // Postoupim o pul urovne niz
				{
					if (output)
					{
						output += ",";
					}
					output += inner;
				}
			}
		}
		return output;
	},
	fillIn: function fillIn (aString) // Z retezce vyplni prvky formulare (opacny postup nez filled)
	{
		if (aString == null || aString == "")
		{
			return false;
		}
		
		var tree = [this.branch]; // Aktualni vetev stromu
		tree[-1] = 0; // Ukazatel
		var name = ""; // Jmeno vetve
		var levelUp; // Zdali se ma vratit na predchozi uroven stromu
		var nextName; // Zdali byl zaznamenan oddelovac jmen
		for (var i = 0; i < aString.length; i++)
		{
			levelUp = false; // Nevracet se na predchozi vetev
			nextName = false; // Dalsi jmeno nebude hned zpracovavano
			switch (aString.toString().charAt(i)) // Podle aktualniho znaku se rozhoduju co delat
			{
				case "[": // Otevreni hranate zavorky
					tree[tree[-1] + 1] = tree[tree[-1]][name].branch; // Nastaveni podvetve
					tree[-1]++; // Posun na dalsi uroven ve stromu
					name = ""; // Jenom vynuluju jmeno
				break;
				case "]": // Uzavreni hranate zavorky
					levelUp = true; // Posunu se na predchozi uroven stromu az pote co zpracuju nazev prvku
				case ",":
					nextName = true; // Toto jmeno bude zpracovano a pote vynulovano
				break;
				default:
					name += aString.charAt(i);
			}
			
			if (name != "" && (nextName || i + 1 == aString.length)) // V retezci jsme narazili na oddelovac jmen, nebo na konec retezce
			{
				switch (tree[tree[-1]][name].reference.nodeName.toLowerCase()) // Prepinam na zaklade HTML prvku ve vetvi
				{
					case "input": // Je-li dana vetev prvek input
						switch (tree[tree[-1]][name].reference.type) // Prepinani podle typu prvku input
						{
							case "checkbox": // Jedna-li se o checkbox
							case "radio": // Nebo radio
								tree[tree[-1]][name].reference.checked = true; // Oznacim ze je odpoved zvolena
							break;
						}
					break;
					case "option": // Je-li dana vetev prvek select
						tree[tree[-1]][name].reference.selected = true; // Oznacim ze je odpoved zvolena
					break;
				}
				
				name = ""; // Vynulovani jmena
			}
			if (levelUp)
			{
				tree[-1]--; // Posun na predchozi uroven ve stromu
			}
		}
		return true;
	}
};

// Odpocitavani casu do konce testu
timeOutIteration.repeat; // Casovac teto funkce
function timeOutIteration (type)
{
	var date = new Date(); // Ziskam aktualni cas
	var timeOut = parseInt(tQ[1]) - date.getTime(); // Cas v milisekundach do konce testu
	if (type == null) // Neni-li zadan typ
	{
		return timeOut; // Vratim cas do konce testu v milisekundach
	}
	
	var scale = [0x000000, 0xFF6600, 0xFF0000]; // Plynula zmena barvy z cerne na oranzovou a pak cervenou
	var percentage = 1 - (parseInt(tQ[1]) - date.getTime()) / (iMinutes * 60000); // Zbyvajici cas deleny casem celkovym
	var portions = [5, 1]; // Pomer delky useku mezi zmenami barev
	color = getColorFromScale (scale, percentage, portions); // Ziskam barvu
	if (type == 'c') // Je-li zadan typ c(olor)
	{
		return color; // Vracim barvu aktualniho casoveho useku
	}
	
	var timeStamp = Math.floor(timeOut / 60000) + ":" + str_pad(Math.floor(timeOut % 60000 / 1000), 2, 0, -1); // Cas vyjadreny v minutach:sekundach
	if (type == 'ts') // Je-li zadan typ ts(timestamp)
	{
		return timeStamp; // Vracim cas v minutach:sekundach
	}
	
	// Je-li zadan typ dhtml
	if (document.getElementById("timestamp")) // Jde-li ziskat ukazatel na objekt
	{
		document.getElementById("timestamp").innerText = timeStamp; // Zmenim casovy udaj
		document.getElementById("timestamp").style.color = "#" + color; // Nastavim barvu
		timeOutIteration.repeat = setTimeout ('timeOutIteration ("dhtml");', 1000); // Opakuj tuto funkci kazdou sekundu
	}
	else if (type != 'tic')
	{
		timeOutIteration.repeat = setTimeout ('timeOutIteration ("tic");', 1000); // Zopakuj tuto funkci za sekundu
	}
	return timeStamp; // Stejne vracim cas v minutach:sekundach
}

// Po nacteni stranky provede vse potrebne
function tQLoad ()
{
	if (actualUser = readCookie("actualUser")) // Pokud je zadano jmeno aktualniho uzivatele
	{
		tQ = parseTQCookie (actualUser);
		var bList = "";
		if (tQ[1] == 0) // Jeste nebyl zadan cas
		{
			var date = new Date(); // Ziskam aktualni cas
			tQ[1] = iMinutes * 60000 + date.getTime(); // Ulozim cas konce testu
			tQ[2] = [0,0,0]; // Vynuluju pocet bodu
			for (var i = 0; i < tQ[4].length && tQ[4][i][1] < 10000; i++) // Uplne spravne odpovedi preskocim
			{
				if (i > 0)
				{
					bList += ","; // Odpovedi oddeluji carkou
				}
				bList += tQ[4][i][0] + "(" + str_pad(tQ[4][i][1], 4, 0) + "/" + tQ[4][i][2] + ")"; // Cislo-otazky(uspesnost/pocet-opakovani)
			}
			setCookie("testQuestions[" + actualUser + "]", tQ[0] + ":" + tQ[1] + ":" + tQ[2].join() + ":" + tQ[3][-1].join() + ":" + bList, (new Date((new Date()).getTime() + cookieExpires)).toGMTString()); // Ulozim zmeneny cas do cookie
		}
		
		if (timeOutIteration () < 0) // Rozdil casu je zaporny (test uz zkoncil)
		{
			return tQEnd(tQ[2]); // Zavolam funkci na vysledek a ukoncim zpracovani
		}
		
		setTimeout ('tQEnd ([' + tQ[2].join() + ']);', timeOutIteration ()); // Nastaveni casovace konce testu
		
		document.writeln("<!-- Question list -->");  // Zobrazi seznam nahodne zvolenych otazek, aby se k nim mohl uzivatel vracet
		document.writeln("<div id=\"question-list\">");
		document.writeln("	<p class=\"float-right\">Čas do konce testu: <span id=\"timestamp\" style=\"color: " + timeOutIteration ('c') + "\">" + timeOutIteration ('dhtml') + "</span></p>");
		document.writeln("	<p>");
		for (var i = 0; i < tQ[3][-1].length; i++) // Projdu pres vsechna cisla otazek
		{
			document.writeln("		<a href=\"" + str_pad (tQ[3][-1][i], 8, 0, -1) + ".html\">" + tQ[3][-1][i] + "</a>"); // Kazde cislo ulozim jako odkaz na stranku
		}
		document.writeln("	</p>");
		document.writeln("</div>");
		
		tQTree.setTree(); // Ze zadaneho formulare vypestuje strom
		tQTree.setScores(); // Nastavi bodove ohodnoceni za jednotlive odpovedi
		
		var slashOffset = location.pathname.lastIndexOf("/") + 1; // Najdu posledni lomitko v adrese souboru
		if (location.protocol.substring(0,4) == "file") // Ctu-li soubor napriklad z disku
		{
			slashOffset = location.pathname.lastIndexOf("\\") + 1; // Najdu posledni zpetne lomitko v adrese souboru
		}
		iQ = base32_encode(base32_decode(location.pathname.substring(slashOffset, location.pathname.indexOf(".", slashOffset)))); // Dekoduju cislo otazky z nazvu souboru (od lomitka, po tecku)
		for (var i = 0; i < tQ[3][-1].length; i++) // Projdu pres vygenerovany seznam otazek
		{
			if (tQ[3][-1][i] == iQ) // Nalezeno cislo otazky
			{
				tQLoad.prevScore = [0, 0, 0]; // Kdyz je otazka nevyplnena je skore predchozi odpovedi nulove
				nQ = i; // Ulozim poradi otazky v seznamu
				if (tQ[3][base32_encode(i)] != null) // Pokud jiz byla odpoved vyplnena
				{
					tQTree.fillIn(tQ[3][base32_encode(i)]); // Nastavi predchozi uzivatelem zvolene odpovedi
					tQLoad.prevScore = tQTree.getScore(); // Zjisti skore dosazene na dane otazce pri predchozich odpovedich
				}
				break; // Uz nemusim dal hledat
			}
		}
	}
}

// Ulozi odpoved na otazku a zobrazi nasledujici otazku
function tQSave ()
{
	if (actualUser)
	{
		//alert(tQTree); // Zobrazi strom v textove reprezentaci
		//alert(tQTree.filled()); // Zobrazi uzivatelem vyplnene odpovedi
		
		var score = tQTree.getScore(); // Zjisti skore dosazene na dane otazce
		var mustBeAnsvered = null; // Cislo otazky, kterou je nutno zodpovedet aby bylo mozno ukoncit test predcasne
		var qList = "";
		var iQBad = false; // Nalezena mezi spatnymi odpovedmi?
		var bList = "";
		
		tQ[3][tQ[3][-1][nQ]] = tQTree.filled(); // Nastavi uzivatelem zvolene odpovedi
		for (var i = 0; i < tQ[3][-1].length; i++)
		{
			if (i > 0)
			{
				qList += ","; // Otazky oddeluju carkou
			}
			qList += tQ[3][-1][i]; // Oteviraci zavorka pro odpoved
			if (tQ[3][tQ[3][-1][i]] != null) // Pokud je odpoved vyplnena
			{
				qList += "{" + tQ[3][tQ[3][-1][i]] + "}"; // Pridam odpoved do seznamu
			}
			else if (mustBeAnsvered == null) // Prvni nezodpovezena otazka na kterou jsme narazili
			{
				mustBeAnsvered = str_pad(tQ[3][-1][i], 8, 0, -1); // Ulozim cislo otazky, ktera neni zodpovezena a neni diky ni mozno ukoncit test predcasne
			}
		}
		tQ[2][0] = parseInt(tQ[2][0]) + score[0] - tQLoad.prevScore[0]; // Pricte k dosazenemu skore ze vsech otazek
		tQ[2][1] = parseInt(tQ[2][1]) + score[1] - tQLoad.prevScore[1]; // Minimum
		tQ[2][2] = parseInt(tQ[2][2]) + score[2] - tQLoad.prevScore[2]; // Maximum
		
		for (i = 0; i < tQ[4].length; i++) // Projdu pres seznam spatnych odpovedi
		{
			if (tQ[4][i][0] == iQ) // Pokud je soucasna otazka zarazena mezi nejhorsi odpovedi
			{
				if (tQ[3][nQ] != null) // Otazka jiz byla v tomto testu zodpovezena, ale uzivatel zmenil svou odpoved
				{
					tQ[4][i][1] = Math.ceil((tQ[4][i][1] * tQ[4][i][2] + 10000 * (score[0] - tQLoad.prevScore[0]) / (score[2] - score[1])) / tQ[4][i][2]); // Zmenim uspesnost
				}
				else
				{
					tQ[4][i][1] = Math.ceil((tQ[4][i][1] * tQ[4][i][2] + 10000 * (score[0] - score[1]) / (score[2] - score[1])) / ++tQ[4][i][2]); // Zmenim uspesnost a zvisim citac otazek
				}
				iQBad = true;
			}
		}
		if (!iQBad) // Odpoved nenalezena mezi spatnymi
		{
			if (tQ[4].length < iBadQuestions) // Seznam spatnych odpovedi jeste neni zaplnen
			{
				tQ[4][tQ[4].length] = [iQ, Math.round(10000 * (score[0] - score[1]) / (score[2] - score[1])), 1]; // Pridam novou odpoved mezi spatne
			}
			else if(10000 * (score[0] - score[1]) / (score[2] - score[1]) < tQ[4][tQ[4].length - 1][1]) // Seznam je zaplnen, ale odpoved je horsi nez posledni (nejlepsi) ze spatnejch
			{
				tQ[4][tQ[4].length -1] = [iQ, Math.round(10000 * (score[0] - score[1]) / (score[2] - score[1])), 1]; // Posledni (nejlepsi z nejhorsich) odpoved nahradim novou
			}
		}
		tQ[4].sort(new Function ("a", "b", "return a[1] > b[1] ? 1 : a[1] == b[1] ? 0 : -1;")); // Setridim pole od nejhorsi po nejlepsi
		
		for (var i = 0; i < tQ[4].length && tQ[4][i][1] < 10000; i++) // Uplne spravne odpovedi (z dlouhodobeho hlediska) preskocim
		{
			if (i > 0)
			{
				bList += ","; // Odpovedi oddeluji carkou
			}
			bList += tQ[4][i][0] + "(" + str_pad(tQ[4][i][1], 4, 0) + "/" + tQ[4][i][2] + ")"; // Cislo-otazky(uspesnost/pocet-opakovani)
		}
		
		setCookie("testQuestions[" + actualUser + "]", tQ[0] + ":" + tQ[1] + ":" + tQ[2].join() + ":" + qList + ":" + bList, (new Date((new Date()).getTime() + cookieExpires)).toGMTString());
		if (nQ + 1 < tQ[3][-1].length) // Pokud je poradi aktualni otazky nizsi nez pocet otazek
		{
			location.href(str_pad(tQ[3][-1][nQ + 1], 8, 0, -1) + ".html"); // Presmeruju na nasledujici otazku
		}
		else if (mustBeAnsvered == null)
		{
			if (_confirm("Přejete si test ukončit před vypršením časového limitu?")) // Zobrazim dotaz zda uzivatel chce jiz skoncit
			{
				tQEnd (tQ[2]); // Ukonceni testu
			}
			else
			{
				tQLoad.prevScore = tQTree.getScore(); // Zjisti skore dosazene na posledni otazce pri ulozeni odpovedi
			}
		}
		else
		{
			_alert("Test nelze předčasně ukončit, pokud nebyly zodpovězeny všechny otázky!");
			location.href(mustBeAnsvered + ".html"); // Presmerovani na prvni nezodpovezenou otazku
		}
		return false; // Neodesle se formular
	}
}
