
	var puzzleNum = "";
	var puzzle = "367925184819467532254318769486591273172843695935672418543186927628759341791234856";
	var presets = "06040716141322202330343147454351545065646073777185848625615227115876366670";
	var hints = new Array();

	if (!String.prototype.endsWith) {
		String.prototype.endsWith = function(suffix) {
			var startPos = this.length - suffix.length;
			if (startPos < 0) { return false; }
			return (this.lastIndexOf(suffix, startPos) == startPos);
		};
	}

	var zDown = false, xDown = false, cDown = false;

	function init()
	{
		writeBoard();
		setStyle(2);

		document.getElementById('puzznum').innerHTML = 'Puzzle # ' + addCommas(puzzleNum);

		document.onkeyup = function(e)
		{
			if (!e) e = window.event;

			if (e.keyCode == 90) zDown = false;
			else if (e.keyCode == 88) xDown = false;
			else if (e.keyCode == 67) cDown = false;
//window.status = 'Up ' + e.keyCode;

//			if (e.keyCode == 65 || e.keyCode == 83) rowColBoxHiLite();
			if (e.keyCode == 90 || e.keyCode == 88 || e.keyCode == 67)
			{
				rowColBoxHiLite();
				return;
			}

			if (lastEnteredEl == null) return;

			if (puzzleCorrect()) return;

			if (e.keyCode != 46) return; // Not DEL
			handleKey(46);
		};

		document.onkeydown = function(e)
		{
			if (lastAnyEnteredEl == null) return;

			if (!e) e = window.event;

			if (e.keyCode == 90) zDown = true;
			else if (e.keyCode == 88) xDown = true;
			else if (e.keyCode == 67) cDown = true;
//			if (e.keyCode == 65) zDown = true;
//			else if (e.keyCode == 83) xDown = true;

			rowColBoxHiLite();
		};

		document.onkeypress = function(e)
		{
			if (!lastEnteredEl) return;
			if (puzzleCorrect()) return;

			if (!e) e = window.event;
			var keychar = String.fromCharCode(window.event ? window.event.keyCode : e.which);
			handleKey(keychar);
		};

		document.getElementById('puzzfield').onkeypress = function(e)
		{
			if (!e) e = window.event;
			var keycode = window.event ? window.event.keyCode : e.which;
			if (keycode != 13) return;
			var f = document.getElementById('puzzfield');
			if (rtrim(f.value) == '') return;
			newPuzzle(f.value);
		};

		document.getElementById('timercb').onclick = function(e)
		{
			if (!puzztimerID) puzztimer(true);
		};

		newPuzzle();
	}

	function rowColBoxHiLite()
	{
		unhilite();
		if (!lastAnyEnteredEl) return;

		var r = getRowFromID(lastAnyEnteredEl.id);
		var c = getColFromID(lastAnyEnteredEl.id);

		if (zDown && xDown) hiliteBox(r, c);
		else if (zDown) hiliteRow(r);
		else if (xDown) hiliteCol(c);
		else if (cDown) hiliteNum(lastAnyEnteredEl.innerHTML);
		else unhilite();
	}

	function hiliteRow(r)
	{
		for (i=0; i < 9; ++i)
		{
			getCellEl(r, i).style.backgroundColor = 'PapayaWhip';
		}
	}

	function hiliteCol(c)
	{
		for (i=0; i < 9; ++i)
		{
			getCellEl(i, c).style.backgroundColor = 'PapayaWhip';
		}
	}

	function hiliteBox(r, c)
	{
		r = Math.floor(r/3) * 3;
		c = Math.floor(c/3) * 3;

		for (i=r; i < r+3; ++i)
		{
			for (j=c; j < c+3; ++j)
			{
				getCellEl(i, j).style.backgroundColor = 'PapayaWhip';
			}
		}
	}

	function hiliteNum(n)
	{
		var nn = parseInt(lastAnyEnteredEl.innerHTML);
		if (nn < 1 || nn > 9 || isEmpty(lastAnyEnteredEl.innerHTML)) return;

		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				var el = getCellEl(r, c);
				if (el.innerHTML == n)
					el.style.backgroundColor = 'PapayaWhip';
			}
		}
	}

	function unhilite()
	{
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				getCellEl(r, c).style.backgroundColor = 'white';
			}
		}
		if (lastEnteredEl) lastEnteredEl.style.backgroundColor='wheat';
	}

	function handleKey(keychar)
	{
		if (!checkInput(keychar))
		{
			return false;
		}

		if (keychar == 'h')
		{
			if (hints[lastEnteredEl.id])
			{
				hints[lastEnteredEl.id] = null;
				hideToolTip();
			}
			else
			{
				hints[lastEnteredEl.id] = getHint(lastEnteredEl.id);
				if (lastMouseEvent.x)
				{
					showToolTip(lastMouseEvent.x,lastMouseEvent.y, hints[lastEnteredEl.id]);
				}
				else
				{
					showToolTip(xy[0],xy[1], hints[lastEnteredEl.id]);
				}
			}
			return;
		}

		if (keychar == 'r')
		{
			revealCell(true);
		}
		else
		{
			if (keychar == 46)
			{
				lastEnteredEl.innerHTML = '&nbsp;';
			}
			else
			{
				var idx = lastEnteredEl.innerHTML.indexOf(keychar);
				if (idx >= 0)
				{
					var s = lastEnteredEl.innerHTML;
					s = s.substring(0,idx) + s.substring(idx+1);
					lastEnteredEl.innerHTML = s.length == 0 ? '&nbsp;' : s
				}
				else
				{
					if (parseInt(lastEnteredEl.innerHTML) >= 1000) return;
					if (isEmpty(lastEnteredEl.innerHTML)) lastEnteredEl.innerHTML = keychar;
					else lastEnteredEl.innerHTML += keychar;
				}
			}
		}

		var h = lastEnteredEl.innerHTML;
		if (h.length > 1 && !isEmpty(h))
		{
			if (!lastEnteredEl.className.endsWith(' smallnum'))
				lastEnteredEl.className += ' smallnum';	
		}
		else
		{
			if (lastEnteredEl.className.endsWith(' smallnum'))
				lastEnteredEl.className = lastEnteredEl.className.substring(0, lastEnteredEl.className.length-9);
		}

		showIncorrect();

		if (puzzleSolved())
		{
			if (puzzleCorrect()) solved();
			else notsolved();
		}
	}

	function writeBoard()
	{
		var table = document.getElementById('board');

		table.deleteRow(0);

		var alt = true;
		var row, cell;

		for (h=0; h < 3; ++h)
		{
			for (i=0; i < 3; ++i)
			{
				row = table.insertRow(-1);
				for (j=0; j < 3; ++j)
				{
					cell = newCell(row);
					cell.id = getID(h, i, j);
					if (alt) cell.className = 'board boardshaded';
					else cell.className = 'board';
				}
				for (j=0; j < 3; ++j)
				{
					cell = newCell(row);
					cell.id = getID(h, i, j+3);
					if (!alt) cell.className = 'board boardshaded';
					else cell.className = 'board';
				}
				for (j=0; j < 3; ++j)
				{
					cell = newCell(row);
					cell.id = getID(h, i, j+6);
					if (alt) cell.className = 'board boardshaded';
					else cell.className = 'board';
				}
			}
			alt = !alt;
		}
	}

	var lastEnteredEl, savedBGColor, lastMouseEvent, xy;
	var lastAnyEnteredEl;

	function newCell(row)
	{
		var cell = row.insertCell(-1);

		cell.onmouseover = function(e)
		{
			lastAnyEnteredEl = this;

			if (lastEnteredEl != null) lastEnteredEl.style.backgroundColor = savedBGColor;

			var r = getRowFromID(this.id);
			var c = getColFromID(this.id);

			if (isPreset(r, c)) return;

			savedBGColor = this.style.backgroundColor;
			this.style.backgroundColor='wheat';
			lastEnteredEl = this;
//			this.focus();

			var r = getRowFromID(this.id);
			var c = getColFromID(this.id);
//			window.status = '(' + r + ',' + c + ')';

			lastMouseEvent = e ? e : window.event;
			if (e) xy=mouseEvent(e);

			if (hints[lastEnteredEl.id])
			{
				if (e)
					showToolTip(xy[0], xy[1], hints[lastEnteredEl.id]);
				else
					showToolTip(lastMouseEvent.x, lastMouseEvent.y, hints[lastEnteredEl.id]);
			}
		};
		cell.onmouseout = function(e)
		{
			unhilite();
			lastAnyEnteredEl = null;

			if (lastEnteredEl) lastEnteredEl.style.backgroundColor = savedBGColor;
			lastEnteredEl = null;
			lastMouseEvent = null;
			xy = null;
			self.status = '';

			hideToolTip();
		};

		/* This doesn't work in Firefox for some reason -- it seems the div
		does not get the focus ...
		cell.onkeypress = function(e)
		{
			var r = getRowFromID(this.id);
			var c = getColFromID(this.id);
			var keynum;

			if (window.event) // IE
			{
				keynum = window.event.keyCode;
			}
			else if (event.which) // Netscape/Firefox/Opera
			{
				keynum = event.which;
			}
			keychar = String.fromCharCode(keynum);

			if (!checkInput(keychar))
			{
				return false;
			}

			this.innerHTML = keychar;
		};
		*/

		return cell;
	}

	function checkInput(keychar)
	{
		if (keychar == 'h' || keychar == 'r') return true;
		return (keychar >= '1' && keychar <= '9');
	}

	function getID(h, i, j)
	{
		return ((h*3)+i) + "-" + j;
	}

	function getRowFromID(s)
	{
		var idx = s.indexOf('-');
		return parseInt(s.substring(0,idx));
	}

	function getColFromID(s)
	{
		var idx = s.indexOf('-');
		return parseInt(s.substring(idx+1));
	}

	function writePuzzle(presetsonly)
	{
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				var el = getCellEl(r, c);
				if (presetsonly)
				{
					el.innerHTML = '&nbsp;';
					if (isPreset(r, c)) showNum(r, c, '#0e780e', false);
				}
				else
				{
					var offset = (r*9) + c;
					var n = parseInt(puzzle.substring(offset, offset+1));
					if (n != parseInt(el.innerHTML)) showNum(r, c, 'blue', false);
				}
			}
		}
	}

	function isEmpty(s)
	{
		return !s || s.length == 0 || s == '&nbsp;';
	}

	function showNum(r, c, clr, flash)
	{
		var el = getCellEl(r, c);
		el.innerHTML = getNum(r,c);
		el.style.color = clr;
		if (el.className.endsWith(' smallnum'))
				el.className = el.className.substring(0, el.className.length-9);

		if (flash) doFlash(el, clr);
	}

	function getNum(r, c)
	{
		var p = (r * 9) + c;
		return puzzle.substring(p, p+1);
	}

	function isPreset(row, col)
	{
		for (i=0; i < presets.length; i += 2)
		{
			var r = parseInt(presets.substring(i, i+1));
			var c = parseInt(presets.substring(i+1, i+2));

			if (row == r && col == c) return true;
		}

		return false;
	}

	function revealCell(currcell)
	{
		if (puzzleSolved()) return;

		var r, c;

		if (currcell)
		{
			if (!lastEnteredEl) return;

			r = getRowFromID(lastEnteredEl.id);
			c = getColFromID(lastEnteredEl.id);
		}
		else
		{
			while (true)
			{
				r = nextInt(8);
				if (hasEmptyCells(r)) break;
			}

			while (true)
			{
				c = nextInt(8);
				var el = getCellEl(r, c);
				if (isEmpty(el.innerHTML)) break;
			}
		}

		showNum(r,c,'blue',true);

		if (puzzleSolved())
		{
			if (puzzleCorrect()) solved();
			else notsolved();
		}
	}

	function hasEmptyCells(row)
	{
		for (c=0; c < 9; ++c)
		{
			var el = getCellEl(row, c);
			if (isEmpty(el.innerHTML)) return true;
		}

		return false;
	}

	function nextInt(n)
	{
		return Math.round(n*Math.random());
	}

	function getCellEl(r, c)
	{
		return document.getElementById(r + "-" + c);
	}

	function puzzleSolved()
	{
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				var el = getCellEl(r, c);
				if (isEmpty(el.innerHTML) || parseInt(el.innerHTML) > 9) return false;
			}
			}
		stopTimer();
		return true;
	}

	function puzzleCorrect()
	{
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				var offset = (r*9) + c;
				var n = parseInt(puzzle.substring(offset, offset+1));
				var el = getCellEl(r, c);
				if (isEmpty(el.innerHTML)) return false;
				if (n != parseInt(el.innerHTML)) return false;
			}
		}
		return true;
	}

	function solved()
	{
		doCorrectFlash();
		alert('CONGRATULATIONS!\nYou solved the Sudoku.');
	}

	function notsolved()
	{
		alert('The Sudoku is not correct. Keep trying.');
	}

	// Hint flash stuff
	var timerID;
	var currFlashText;
	var currFlashEl;
	var flashtimes = 6, currFlashTimes;
	
	function doFlash(el, clr)
	{
		if (timerID != null)
		{
			clearTimeout(timerID);
			currFlashEl.innerHTML = currFlashText;
			currFlashEl.style.color = 'black';
			timerID = null;
			currFlashEl = null;
		}

		if (el == null) return;

		currFlashTimes = flashtimes;
		currFlashEl = el;
		currFlashText = el.innerHTML;
		currFlashEl.style.color = clr;

		flasher();
	}

	function flasher()
	{
		if (currFlashTimes == 0) { doFlash(null, null); return; }

		if (currFlashTimes % 2 == 1) currFlashEl.innerHTML = '&nbsp;';
		else currFlashEl.innerHTML = currFlashText;

		--currFlashTimes;
		timerID = setTimeout("flasher()", 250)
	}

	var currCFlashTimes;

	function doCorrectFlash()
	{
		currCFlashTimes = flashtimes;
		flasherC();
	}

	function flasherC()
	{
		if (currCFlashTimes % 2 == 1)
			document.getElementById('board').style.borderColor="red";
		else
			document.getElementById('board').style.borderColor="black";

		if (--currCFlashTimes == 0) return;
		setTimeout("flasherC()", 750);
		}

function mouseEvent(e){
	if(!e){
		e = window.event;
	}
	
	var xcoord = 0; 
	var ycoord = 0;

	if( ( typeof( e.pageX ) != 'number' && 
      		typeof( e.clientX ) != 'number' ) ) { 
		xcoord = 0; 
		ycoord = 0;
	} else if( typeof( e.pageX ) == 'number' ) { 
		xcoord = e.pageX; 
		ycoord = e.pageY; 
	} else {
		xcoord = e.clientX; 
		ycoord = e.clientY;
		if(!((window.navigator.userAgent.indexOf('Opera') + 1 ) || 
		 	(window.ScriptEngine && 
		 	 ScriptEngine().indexOf('InScript')+1) || 
		 	window.navigator.vendor == 'KDE' ) ) {
			if(document.documentElement && 
			 (document.documentElement.scrollTop || 
			  document.documentElement.scrollLeft)){
				xcoord += document.documentElement.scrollLeft; 
				ycoord += document.documentElement.scrollTop;
			} else if(document.body && 
				(document.body.scrollTop || 
				 document.body.scrollLeft)){
				xcoord += document.body.scrollLeft; 
				ycoord += document.body.scrollTop; 
			} 
		} 
	} 
	return [xcoord,ycoord];
}

	function showToolTip(x,y,text)
	{
		showHintTip(x,y,text);
	}

	function showHintTip(x,y,text)
	{
		var tt = document.getElementById('tooltip');
		tt.innerHTML="<table><tr><td class='ToolTipTD'>"+text+"</td></tr></table>"; 
		if (xy)
		{
			tt.style.left=(x+15+document.body.scrollLeft) + "px";
			tt.style.top=(y+document.body.scrollTop) + "px"; 
		}
		else
		{
			tt.style.pixelLeft=(x+15+document.body.scrollLeft); 
			tt.style.pixelTop=(y+document.body.scrollTop); 
		}
		tt.style.visibility="visible"; 
	}

	function hideToolTip()
	{
		var tt = document.getElementById('tooltip');
		tt.style.visibility="hidden"; 
	}

	function startOver()
	{
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				var el = getCellEl(r, c);
				el.innerHTML = '&nbsp;';
				el.style.color = 'black';
			}
		}
		hints = new Array();
		writePuzzle(true);
		doFlash(null,null);
		lastEnteredEl = null;
		currCFlashTimes = 0;
	}

	function getHint(id)
	{
		var r = getRowFromID(id);
		var c = getColFromID(id);
		var n = parseInt( getNum(r,c) );

		var r = nextInt(1);

		if (r == 0)
		{
			return (n % 2 == 0) ? "Is even" : "Is odd";
		}
		else
		{
			return (n >= 5) ? "Is >= 5" : "Is < 5";
		}
	}

	function printPuzzle()
	{
		var win1 = window.open('', 'PuzzlePrint', 'height=600,width=400,status=no,toolbar=yes,menubar=no,location=no');
		win1.document.open();

		win1.document.write('<head><title>Sudoku Print</title>');
		win1.document.write(
'<style>\n' +
'TD.board { font:italic normal bold 12pt Verdana;color:black;text-align:center;width:30px;height:30px;border:1px solid black;padding:0px;margin:0px;background-color:white}' +
'TD.board2 { font:italic normal bold 12pt Verdana;color:black;text-align:center;width:30px;height:30px;padding:0px;margin:0px;background-color:white}' +
'TD.boardshaded { background-color:#e0e0e0}' +
'TD.smallnum { font:italic normal bold 6pt Verdana;color:black}' +
'.table1 {border-collapse:collapse;border:1px solid black;background-color:white}' +
'.table2 {border-collapse:collapse;border:1px solid black;background-color:white}' +
'</style>');

		win1.document.write('</head><body><br><br><br>');
		win1.document.write('<center><img src=\'5star.gif\'><br><br>');
		win1.document.write('<ul>fivestarsudoku.com</ul></center><br><br><br>');
		win1.document.write('<center><span style="font:normal bold 8pt Tahoma">')
		win1.document.write(document.getElementById('puzznum').innerHTML);
		win1.document.write('</span><br><br>');

		win1.document.write(document.getElementById('boardtd').innerHTML);

		win1.document.write('</center></body>');

		win1.document.close();
		win1.print();
	}

	function showIncorrect()
	{
		var cb = document.getElementById('showinc');
		for (r=0; r < 9; ++r)
		{
			for (c=0; c < 9; ++c)
			{
				if (isPreset(r, c)) continue;
				var el = getCellEl(r, c);
				var n=parseInt(el.innerHTML);
				var inc = n > 0 && n != getNum(r,c);
				el.style.color = cb.checked && inc ? 'red' : 'black';
			}
		}
	}

	function setStyle(s)
	{
		var table = document.getElementById('board');

		table.className = 'table' + s;

		var alt = true;
		var row, cell;

		for (h=0; h < 3; ++h)
		{
			for (i=0; i < 3; ++i)
			{
				var rnum = (h*3)+i;
				row = table.rows[rnum];
				for (j=0; j < 3; ++j)
				{
					cell = row.cells[j];
					if (s == 1)
					{
						if (alt) cell.className = 'board boardshaded';
						else cell.className = 'board';
					}
					else
					{
						cell.className = 'board2';
						cell.style.borderRight = j==2 ? '2px solid #9797ad' : '1px solid #c3c3e0';
						if (rnum < 8) cell.style.borderBottom = i==2 ? '2px solid #9797ad' : '1px solid #c3c3e0';
					}
				}
				for (j=0; j < 3; ++j)
				{
					cell = row.cells[j+3];
					if (s == 1)
					{
						if (!alt) cell.className = 'board boardshaded';
						else cell.className = 'board';
					}
					else
					{
						cell.className = 'board2';
						cell.style.borderRight = j==2 ? '2px solid #9797ad' : '1px solid #c3c3e0';
						if (rnum < 8) cell.style.borderBottom = i==2 ? '2px solid #9797ad' : '1px solid #c3c3e0';
					}
				}
				for (j=0; j < 3; ++j)
				{
					cell = row.cells[j+6];
					if (s == 1)
					{
						if (alt) cell.className = 'board boardshaded';
						else cell.className = 'board';
					}
					else
					{
						cell.className = 'board2';
						if (j < 2) cell.style.borderRight = '1px solid #c3c3e0';
						if (rnum < 8) cell.style.borderBottom = i==2 ? '2px solid #9797ad' : '1px solid #c3c3e0';
					}
				}
			}
			alt = !alt;
		}

//		table.style.border='1px solid black'; //Firefox bug
	}

	function newPuzzle(pnum)
	{
		if (pnum) pnum = pnum.replace(/,/g, '');
		var n;
		if (pnum) n = parseInt(pnum);
		loadXMLDoc("sudoku.pl" + (n ? '?num=' + n : ''));
		document.getElementById('board').style.borderColor="black";
		document.getElementById('puzzfield').value = '';
		stopTimer();
		startTimer();
	}

var xmlhttp;

function loadXMLDoc(url)
{
if (window.XMLHttpRequest)
  {
  xmlhttp=new XMLHttpRequest();
  xmlhttp.onreadystatechange=xmlhttpChange;
  xmlhttp.open("GET",url,true);
  xmlhttp.send(null);
  }
else if (window.ActiveXObject)
  {
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    if (xmlhttp)
    {
    xmlhttp.onreadystatechange=xmlhttpChange;
    xmlhttp.open("GET",url,true);
    xmlhttp.send();
    }
  }
}

function xmlhttpChange()
{
if (xmlhttp.readyState==4)
  {
  if (xmlhttp.status==200)
  {
  var root = xmlhttp.responseXML.documentElement;
	puzzleNum = root.getElementsByTagName('n')[0].firstChild.data;
	document.getElementById('puzznum').innerHTML = 'Puzzle # ' + addCommas(puzzleNum);
	puzzle = root.getElementsByTagName('p')[0].firstChild.data;
	presets = root.getElementsByTagName('b')[0].firstChild.data;
	startOver();
    }
  else
    {
	alert("Problem retrieving XML " + xmlhttp.status)
    }
  }
}

var puzztimerID, puzzseconds;

function startTimer()
{
	puzzseconds = 0;

	puzztimer();
}

function stopTimer()
{
	if (puzztimerID) clearTimeout(puzztimerID);
	puzztimerID = null;
}

function puzztimer(showonly)
{
	var el = document.getElementById('puzztimer');
	if (!document.getElementById('timercb').checked)
	{
		el.innerHTML = '--:--';
	}
	else
	{
		var m = Math.floor(puzzseconds / 60);
		var s = puzzseconds % 60;
		if (m < 10) m = '0' + m;
		if (s < 10) s = '0' + s;
		el.innerHTML = m + ':' + s;
	}

	if (showonly) return;

	if (++puzzseconds > 7200) return;

	puzztimerID = setTimeout("puzztimer()", 1000);
}

function rtrim(VALUE){
var w_space = String.fromCharCode(32);
var v_length = VALUE.length;
var strTemp = "";
if(v_length < 0){
return"";
}
var iTemp = v_length -1;

while(iTemp > -1){
if(VALUE.charAt(iTemp) == w_space){
}
else{
strTemp = VALUE.substring(0,iTemp +1);
break;
}
iTemp = iTemp-1;

} //End While
return strTemp;

} //End Function

function addCommas(nStr)
{
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}


