	/* ---- VARIABLES ---- */
	var http;
	var startValue;
	var currentValue;
	var maxValue;
	var result;
	var errorTimer;

	var numberOfTests = 1000;
	var numberOfTestsPerIteration = 100;
	var delayBetweenIterations = 1000;
	var delayBetweenTasks = 5000;
	var delayError = 120000;



	/* ---- HELPER FUNCTIONS ---- */
	function getHTTP() {
		// Get the XMLHTTP object

		var xmlhttp;

		/*@cc_on
		@if (@_jscript_version >= 5)
		try {
			xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		} catch (exp1) {
      			try {
        			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
      			} catch (exp2) {
        			xmlhttp = false;
      			}
    		}
  		@else
			xmlhttp = false;
		@end @*/
		if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
			try {
				xmlhttp = new XMLHttpRequest();
			} catch (exp3) {
				xmlhttp = false;
			}
		}

		return xmlhttp;
	}

	function increaseDelayBetweenTasks() {
		// In order not to turn this distributed calculation into a distributed DoS a (fairly)
		// long delay is used. It is incremented by 15 sec every time the server is called, up
		// to a maximum of 10 min.
		if(delayBetweenTasks < (10 * 60 * 1000)) {
			delayBetweenTasks = Math.round(delayBetweenTasks) + 15000;
		}
	}



	/* ---- UI FUNCTIONS ---- */

	function pageLoaded() {
		setStatus("Sidan laddas...");

		hideLoading();
		showIntro();
		positionObjects();

		startCalc();
	}

	function startAsFriend() {
		hideIntro();
		showSimpleExplanation();
		positionExtraInfo("friendinfo");
	}

	function startAsNerd() {
		hideIntro();
		showNerdInfo();
		positionExtraInfo("verboseinfo");
	}

	function becomeFriend() {
		hideNerdInfo();
		showSimpleExplanation();
		positionExtraInfo("friendinfo");
	}

	function becomeNerd() {
		hideSimpleExplanation();
		showNerdInfo();
		positionExtraInfo("verboseinfo");
	}

	function setStatus(statusText) {
		document.getElementById("dataStatus").innerHTML = "<nobr>" + statusText + "</nobr>";
	}



	/* ---- UI FUNCTIONS (SHOW/HIDE/POSITION OBJECTS) ---- */

	function hideLoading() {
		document.getElementById("loading").style.visibility = 'hidden';
	}

	function showIntro() {
		document.getElementById("intro").style.visibility = 'visible';
	}

	function hideIntro() {
		document.getElementById("intro").style.visibility = 'hidden';
	}

	function showSimpleExplanation() {
		document.getElementById("friendinfo").style.visibility = 'visible';
		showExtraInfo();
	}

	function hideSimpleExplanation() {
		document.getElementById("friendinfo").style.visibility = 'hidden';
	}

	function showNerdInfo() {
		document.getElementById("verboseinfo").style.visibility = 'visible';
		document.getElementById("stat").style.visibility = 'visible';
		showExtraInfo();
	}

	function hideNerdInfo() {
		document.getElementById("verboseinfo").style.visibility = 'hidden';
		document.getElementById("stat").style.visibility = 'hidden';
	}

	function showExtraInfo() {
		// No extra info is available right now
		// document.getElementById("extrainfo").style.visibility = 'visible';
	}

	function positionObjects() {
		positionFooter();
		var pos = positionStatSpans();
		positionStat(pos);
	}
	window.onresize=positionObjects;

	function positionFooter() {
		var pos = document.documentElement.clientHeight - 30;
		document.getElementById("footer").style.top = pos + 'px';
	}

	function positionStatSpans() {
		var pos = document.getElementById("dataStatHelpers").offsetLeft + 5;
		document.getElementById("dataStatCount").style.left = pos + 'px';
		document.getElementById("dataStatHighest").style.left = pos + 'px';
		document.getElementById("dataStatHelpers").style.left = pos + 'px';
		document.getElementById("dataStatHelpersNow").style.left = pos + 'px';
		document.getElementById("dataStatSimultanious").style.left = pos + 'px';
		return pos;
	}

	function positionStat(wpos) {
		var pos = document.documentElement.clientWidth - 100 - wpos;
		document.getElementById("stat").style.left = pos + 'px';
		document.getElementById("stat").style.top = '50px';
	}

	function positionExtraInfo(objectId) {
		var pos = document.getElementById(objectId).offsetTop + document.getElementById(objectId).offsetHeight + 30;
		document.getElementById("extrainfo").style.top = pos + "px";
	}



	/* ---- COMMUNICATION ---- */

	function startCalc() {
		// Reset answer variable and ask the server for the next task

		setStatus("Kontaktar servern...");
		result = "";
		http = getHTTP();
		http.open("get","interface.php?command=register&uid=" + uid);
		http.onreadystatechange = updateAndCalc;
		errorTimer = setTimeout(resetHTTP, delayError);
		http.send(null);
	}

	function sendResult() {
		setStatus("Sänder resultat...");
		increaseDelayBetweenTasks();

		// Send the old values and ask for a new task
		http = getHTTP();
		http.open("post","interface.php?command=report&uid=" + uid);
		http.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		http.onreadystatechange = updateAndCalc;
		errorTimer = setTimeout(resetHTTP, delayError);
		http.send("task=" + startValue + "&result=" + result);

		// Reset the result variable
		result = "";
	}

	function updateAndCalc() {
		// Handle the response from the server
		if (http.readyState == 4) {
			// The server replied so no "external" error handling is needed
			clearTimeout(errorTimer);
			if (http.status == 200) {
				setStatus("Tar emot information...");
				try {
					// Update stats (not so pretty)
					document.getElementById("dataStatCount").innerHTML = http.responseXML.getElementsByTagName("count")[0].firstChild.data;
					document.getElementById("dataStatHighest").innerHTML = http.responseXML.getElementsByTagName("highest")[0].firstChild.data;
					document.getElementById("dataStatHelpers").innerHTML = http.responseXML.getElementsByTagName("helpers")[0].firstChild.data;
					document.getElementById("dataStatHelpersNow").innerHTML = http.responseXML.getElementsByTagName("helpersnow")[0].firstChild.data;
					document.getElementById("dataStatSimultanious").innerHTML = http.responseXML.getElementsByTagName("simultanious")[0].firstChild.data;
	
					// Update our value (the selected item from the bag of tasks)
					startValue = http.responseXML.getElementsByTagName("task")[0].firstChild.data;
					currentValue = startValue;
					maxValue = Math.round(startValue) + Math.round(numberOfTests);
	
					// Do the actual calculation after a little rest
					setStatus("Väntar på att börja räkna (" + (delayBetweenTasks/1000) + " s)...");
					setTimeout(doCalc, delayBetweenTasks);
				} catch(exp) {
					restartCalc();
				}
			} else {
				restartCalc();
			}
		}
	}



	/* ---- BUSINESS LOGIC ---- */

	function doCalc() {
		// Do one iteration of prime computations

		setStatus("Beräknar (" + currentValue + ")...");
		var currentMax = Math.round(currentValue) + Math.round(numberOfTestsPerIteration);
		for(var i = currentValue; i <= currentMax; i++) {
			if (isPrime(i)) {
				result += i + ",";
			}
		}
		currentValue = currentMax;

		// Send the result if this is the last iteration, otherwise restart the function after a little while
		if(currentValue >= maxValue) {
			sendResult();
		} else {
			result += "\n";
			setTimeout(doCalc, delayBetweenIterations);
		}
	}

	function isPrime(number) {
		// Well, you can either do computations efficient or you can distribute them (I chose the latter) ;)

		if(number < 2)
			return false;

		var sqn = Math.sqrt(number);
		var primality = true;

		for(var i = 2; i <= sqn; i++) {
			var div = number/i;
			if (div == Math.round(div)) {
				primality = false;
				break;
			}
		}

		return primality;
	}



	/* ---- ERROR HANDLING ---- */

	function resetHTTP() {
		// Ignore any future state changes, and restart the calculations
		setStatus("Ett fel uppstod!");
		http.onreadystatechange = doNothing;
		restartCalc();
	}

	function doNothing() {
	}

	function restartCalc() {
		// Wait a while, and then try again (from the beginning)

		increaseDelayBetweenTasks();
		setStatus("Ett fel uppstod, försöker igen (" + (delayBetweenTasks/1000) + " s)...");
		setTimeout(startCalc, delayBetweenTasks);
	}
