// Copyright (C) 2006 DKM Software. All rights reserved.
//
var SPCE = " ";
var UsePencil = false;
var pWin;
function SudCell() {
	this.Ans = 0;
	this.Value = 0;
	this.Fixed = false;
	this.Possible = "";
	this.Col = 0;
	this.Row = 0;
}
//-----------------------------

var Puzzle = new Array(10)
for (i = 0; i < 10; i++) Puzzle[i] = new Array(10)
var GeneratedPuzzle = false
var puzzleInProgress = false
var inputtingText = false
var busyInputtingPuzzle = false
var modified = false
var HTTP 
var activeCell = null
var refreshGrid = false
var startTime
var elapsedSeconds = 0
var hTimer = 0
var paused = false
var undoStack = new Array()
var levelNames = new Array("Beginner", "Intermediate", "Advanced", "Supreme")
var lastStart = new Array(4)
// Preferences
var hilightColor = "lightpink"
var inputColor = "steelblue"
var fixedColor = "black"
var gridColor = "white"
var thickColor = "steelblue"
var useTimer = false
var autoErrors = false
var numberFont = ""
var boldNumbers = false
var cellFontSize = "24pt"
//------
var isMozilla = !(document.all);
<!--
	if (isMozilla)	{
		Document.prototype.__defineGetter__("xml", function () {
			return (new XMLSerializer()).serializeToString(this);
		});
	}
//-->

//----------------------------------------------------
window.onload = function() {
	GetHTTPObject();
	SetPreferences("onload");
	GenerateGrid();
	PenClick();
	//	alert(window.navigator.userAgent);
	if (document.all && document.getElementById) {
		nodes = document.getElementsByTagName("UL");
		for (i=0; i< nodes.length; i++) {
			if (nodes[i].className == "MenuItem") {
				node = nodes[i].childNodes[0];
				node.onmouseover=function() {
					this.className+=" over";
				}
				node.onmouseout=function() {
					this.className=this.className.replace(" over", "");
				}
			}
		}
	}
}

//----------------------------------------------------
window.onbeforeunload = function() {
   if (modified)  window.event.returnValue = "Puzzle input will be lost.";
}

//----------------------------------------------------
function GetHTTPObject() {
	HTTP = false;
	/*@cc_on @*/
	/*@if (@_jscript_version >= 5)
	// JScript gives us Conditional compilation, we can cope with old IE versions.
	// and security blocked creation of the objects.
	try {
		HTTP = new ActiveXObject("Msxml2.XMLHTTP");
	} catch (e) {
		try {
			HTTP = new ActiveXObject("Microsoft.XMLHTTP");
		} catch (E) {
			HTTP = false;
		}
	}
	@end @*/
	if (!HTTP && typeof XMLHttpRequest != 'undefined') {
		HTTP = new XMLHttpRequest();
	}	
}


//----------------------------------------------------
// Main function to call Web Service on host
function CallWebMethod(method, data) {
	try {
		HTTP.open("POST", "SudokuServer.asmx/" + method, false);
		HTTP.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		HTTP.send(data);
		if (HTTP.readyState == 4) {
			rtnText = HTTP.responseXML.documentElement.firstChild.data;
			if (rtnText == "") rtnText = "Error: Null reply from server";
			if (rtnText.indexOf("Error:") >= 0) {
				alert(rtnText);
				return;
			}
		} else {
			alert("Timeout error - please try again.");
			return;
		}
		if (isMozilla){ 
			var parser = new DOMParser();
			// Hack for bug where xml is truncated
			if (rtnText.indexOf("</SudokuPuzzle>") < 0)  {
				rtnText = HTTP.responseText;  // get from text
				rtnText = rtnText.replace(/&lt;/g, "<").replace(/&gt;/g, ">");
				pos = rtnText.indexOf("<Sud");
				rtnText = rtnText.substr(pos).replace("</string>","");
			}
			PuzzleDoc = parser.parseFromString(rtnText, "text/xml");
		} else {
			if (PuzzleDoc.loadXML(rtnText) == false) {
				alert(rtnText);
				return;
			}
		}
		// Now process results of method call
		if (data == "create") return;  // just wanted to create xml doc;
		comment = GetNodeText(PuzzleDoc.documentElement, "Info/comment");
		if (method == "Hint" || method == "Clue" || method == "SolveOneCell") {
			if (comment == "") {
				StatusLine.innerHTML = "I cannot solve any more!";
				return;
			}
			if (comment.substr(0, 5) == "error") {
				StatusLine.innerHTML = comment.substr(6);
				return;
			}
			row = comment.substr(0,1);
			col = comment.substr(2,1);
			switch(method) {
				case "Hint" :
					elem = document.getElementById("cell" + row + col);
					HighlightCell(elem);
					StatusLine.innerHTML = "Look at cell in Row " + row + " Column " + col + ".";
					break;
				case "Clue" :
					if (activeCell != null) activeCell.style.backgroundColor = gridColor;
					activeCell = null;
					val = comment.substr(comment.length - 1, 1);
					if (comment.indexOf("Row") > 0) {
						HighlightRow(val);
					} else {
						if (comment.indexOf("Column") > 0) {
							HighlightCol(val);
						} else {
							HighlightBlock(val);
						}
					}		
					StatusLine.innerHTML = comment.substr(7) + " ?";
					refreshGrid = true;
					break;
				case "SolveOneCell" :
					cell = Puzzle[row][col];
					cell.Value = comment.substr(4, 1);
					cell.Possible = "";
					AddToStack(cell);
					UpdateOneCell(cell, true);
					StatusLine.innerHTML = "Cell in Row " + row + " Column " + col + " is " + comment.substr(4);
			}
		} else {
			PopulateGrid();  // do for all other methods
			if (method == "GenPuzzle" || method == "ValidatePuzzle")  NewPuzzleStarted();
			if (comment != "") StatusLine.innerHTML = comment;
			//DisplayAd();
		} 
	}	
	catch (ex) {
	  alert(ex.message);
   }
}

//----------------------------------------------------
// Generate a blank grid
function GenerateGrid() {
	txt = "<table class='puzzle'>";
	for(row = 1; row <= 9; row++) {
		txt = txt + "<tr>";
		for(col = 1; col <= 9; col++) {
			cell = new SudCell();
			cell.Col = col;
			cell.Row = row;
			cell.Value = 0;
			cell.Fixed = false;
			cell.Possible = "";
			Puzzle[row][col] = cell;
			txt = txt + "<td class='cell' id='cell" + row + col + "' style=''>";
			if(col == 3 || col == 6) txt = txt.substr(0, txt.length-2) + "border-right-width:3; border-right-color:" + thickColor + ";'>";
			if(row == 3 || row == 6) txt = txt.substr(0, txt.length-2) + "border-bottom-width:3; border-bottom-color:" + thickColor + ";'>";
			txt = txt + "&nbsp;</td>";
		} 
		txt = txt + "</tr>";
	}
	Grid.innerHTML = txt + "</table>";
}

//----------------------------------------------------
// Populate the grid with the puzzle information
function PopulateGrid() {
	Congrats.style.visibility = "hidden";
	GeneratedPuzzle = false;
//	nodeList = PuzzleDoc.documentElement.selectNodes("Grid/GridRow");
	var nodeList = EvaluateXPath(PuzzleDoc.documentElement, "Grid/GridRow");
	var nodecnt = 0
	if (nodeList != null) nodecnt = nodeList.length
	for(i = 0; i < nodecnt; i++ ) {
		rowNode = nodeList[i];	
		row = rowNode.getAttribute("row");
		col = 0;
		for(j = 0; j < rowNode.childNodes.length; j++ ) {
			node = rowNode.childNodes[j];
			col = col + 1;
			cell = Puzzle[row][col];
			cell.Value = node.getAttribute("value");
			cell.Ans = node.getAttribute("ans");
			attr = node.getAttribute("fixed");
			if (attr == "True") { cell.Fixed = true } else { cell.Fixed = false };
			cell.Possible = "";
			elem = document.getElementById("cell" + row + col);
			if (cell.Fixed) {
				elem.style.color = fixedColor;
				GeneratedPuzzle = true;
			} else {
				elem.style.color = inputColor;
			}
			if (elem.style.backgroundColor != gridColor) elem.style.backgroundColor = gridColor;
			if (cell.Value == "0") {
				cell.Possible = node.getAttribute("possible");
				if (cell.Possible == null) cell.Possible = "";
				if (cell.Possible.length > 0) {
					elem.style.verticalAlign = "top";
					elem.style.textAlign = "left";
					elem.style.fontSize = "10pt";
					elem.style.color = "Gray" ;
					elem.style.fontWeight = "normal";
					elem.innerHTML = cell.Possible;
					
				} else {  
					elem.innerHTML = "";
				}
			} else {
				if (elem.style.textAlign != "center") {
					elem.style.verticalAlign = "middle";
					elem.style.textAlign = "center";
					elem.style.fontSize = cellFontSize;
					if (boldNumbers)  elem.style.fontWeight = "bold";
				}
				elem.innerHTML = cell.Value;
			}
		}
	}
	var num = GetNodeText(PuzzleDoc.documentElement, "Info/number");
	if (num != "" && num > "0") {
		level = +num.substr(num.length - 1, 1);
		PuzzleNo.innerHTML = "Puzzle # " + num + "<br>" + levelNames[level];
	} else {
		PuzzleNo.innerHTML = ""
	}	
	StatusLine.innerHTML = "";
	puzzleInProgress = true;
}

//--------------------------------------------
// New puzzle just started
function NewPuzzleStarted() {
	modified = false;
	busyInputtingPuzzle = false;
	startTime = new Date();
	paused = false;
	if (undoStack.length)  undoStack.splice(0, undoStack.length);
	if (useTimer) hTimer = window.setInterval("UpdateTime()", 1000);
}	

//--------------------------------------------
// Display advert (if one)
function DisplayAd() {

	//txt = GetNodeText(PuzzleDoc.documentElement, "Ad");
	// Instead of ad text from server, hard code here for now
	txt = "Like fast paced word games? Try the new <a href='../WordFerret.htm'>Word Ferret</a>.<br>Play against others or by yourself.";
	if (txt.length == 0)  return;
	AdOverlay.style.borderWidth = 1;
	AdOverlay.style.borderColor = "lightsteelblue";
	AdOverlay.style.borderStyle = "solid";
	AdOverlay.style.textAlign = "center";
	AdOverlay.style.verticalAlign = "middle";
	//if (txt.indexOf("<div") >= 0) header = "Sponsor Links"; else header = "Advertisement";
	//AdOverlay.innerHTML = "<font color=#D3D3D3>" + header + "</font><p><p>" + txt;
	AdOverlay.innerHTML = txt;
	return;
}

//--------------------------------------------
// Check each cell to see if input is correct
function CheckGridInput() {
	if (GeneratedPuzzle == false) {
		alert("Can only check input for validated puzzles");
		return;
	}
	error = false;
	incomplete = false;
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			if (Puzzle[row][col].Value != Puzzle[row][col].Ans) {
				if (Puzzle[row][col].Value > 0) {  // display in red
					error = true;
					elem = document.getElementById("cell" + row + col);
					elem.style.color = "Red";
				} else { 
					incomplete = true ;
				}
			}
		}
	}
	if (error == false && incomplete == false) {
		PuzzleCompleted();
		return;
	}
	if (error == false && incomplete == true) {
		StatusLine.innerHTML = "Solution correct so far.";
		return;
	}
	// May be duplicate solution - check
	error = false;
	for(row = 1; row <= 9; row++) {
		cnt = 0;
		for(col = 1; col <= 9; col++) {
			if (Puzzle[row][col].Value == 0) incomplete = true;
			cnt = cnt + Puzzle[row][col].Value;
		}
		if (cnt != 45) error = true;
	}
	for(col = 1; col <= 9; col++) {
		cnt = 0;
		for(row = 1; row <= 9; row++) {
			cnt = cnt + Puzzle[row][col].Value;
		}
		if (cnt != 45) error = true;
	}
	if (error) {
		StatusLine.innerHTML = "Solution not correct!";
	} else {
		if (incomplete) {
			StatusLine.innerHTML = "Solution correct so far.";
		} else { 
			PuzzleCompleted();
		}
	}
}

//--------------------------------------------
function CheckIfDone() {
	if (GeneratedPuzzle == false) return;
	error = false;
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			if (Puzzle[row][col].Value != Puzzle[row][col].Ans) {
				error = true;
				break;
			}
		}
		if (error) break;
	}
	if (error == false) PuzzleCompleted();
}  

//--------------------------------------------
// Set the value of a cell - either pen or pencil data
function SetCellValue(val, altKey) {

	if (!busyInputtingPuzzle) StatusLine.innerHTML = "";
	if (refreshGrid) ClearHighlights();
	if (activeCell == null) return;
	row = +activeCell.id.substr(4, 1);
	col = +activeCell.id.substr(5, 1);
	cell = Puzzle[row][col];
	AddToStack(cell);
//	var evtobj = window.event? event : e;
	if (altKey == true || UsePencil) {
		activeCell.style.verticalAlign = "top";
		activeCell.style.textAlign = "left";
		if (!isNaN(val) && val > 0) {
			AddPossible(cell, val);
		} else {
			cell.Possible = "";
		}
		cell.Value = 0;
		activeCell.style.fontSize = "10pt";
		activeCell.style.color = "Gray" // "LightSteelBlue"
		activeCell.style.fontWeight = "normal";
		activeCell.innerHTML = cell.Possible ;
	} else {
		activeCell.style.color = inputColor;
		if (activeCell.style.textAlign != "center") {
			activeCell.style.verticalAlign = "middle";
			activeCell.style.textAlign = "center";
			activeCell.style.fontSize = cellFontSize;
			if (boldNumbers) activeCell.style.fontWeight = "bold";
		}
		if (!isNaN(val) == false) val = 0;
		cell.Value = val;
		cell.Possible = "";
		modified = true;
		if (busyInputtingPuzzle) {
			cell.Ans = val
         cell.Fixed = (val > 0)
         activeCell.style.color = fixedColor;
      }   
		if (cell.Value == 0) {
			activeCell.innerHTML = "";
		} else {
			activeCell.innerHTML = val;
			if (autoErrors)  {
				if (cell.Value != cell.Ans)  activeCell.style.color = "Red";
			}
			CheckIfDone();
		}
	}
}

//--------------------------------------------
function AddPossible(cell, val) {
	if (cell.Possible == "") {
		cell.Possible = val + "";
		return;
	}
	if (cell.Possible.indexOf(val) >= 0) {
		cell.Possible = cell.Possible.replace(val, "");
		return;
	}
	for (pos = 0; pos < cell.Possible.length; pos++) {
		if (val < cell.Possible.substr(pos,1)) break;
	}
	if (pos < cell.Possible.length) cell.Possible = cell.Possible.substr(0,pos) + val + cell.Possible.substr(pos);
	else cell.Possible = cell.Possible + val;
}

//--------------------------------------------
function UpdateOneCell(cell, hilite) {
	if (refreshGrid) ClearHighlights();
	var elem = document.getElementById("cell" + cell.Row + cell.Col) ;
	if (cell.Possible.length)  {
		elem.style.verticalAlign = "top";
		elem.style.textAlign = "left";
		elem.style.fontSize = "10pt";
		elem.style.color = "Gray";
		elem.style.fontWeight = "normal";
		elem.innerHTML = cell.Possible;
	} else {
		elem.style.color = inputColor;
		if (elem.style.textAlign != "center") {
			elem.style.verticalAlign = "middle";
			elem.style.textAlign = "center";
			elem.style.fontSize = cellFontSize;
			if (boldNumbers) elem.style.fontWeight = "bold";
		}
		if (cell.Value > 0) elem.innerHTML = cell.Value; else elem.innerHTML = "";
	}	
	if (hilite) HighlightCell(elem);
}

//--------------------------------------------
// Clicked on an element in the document
document.onclick = function(e) {
	if(!e) e = window.event;
	var elem = e.srcElement? e.srcElement : e.target; 
	//elem = window.event.srcElement;
	if (elem.className == "cell") {
		if (refreshGrid) ClearHighlights();
		HighlightCell(elem);
		if (!busyInputtingPuzzle) StatusLine.innerHTML = "";
	} else {
		if(elem.className == "InputVal") {
			SetCellValue(elem.innerHTML, e.altKey);
		} else {
			return ;
		}
	}
	window.event.returnValue = false;
}

//--------------------------------------------
// Key event in the document
document.onkeyup = function(e) {
	if (inputtingText) return;
	if (activeCell == null) return;
	if(!e) e = window.event;
	keycode = e.keyCode;
	//keycode = window.event.keyCode;
	row = +(activeCell.id.substr(4, 1));
	col = +(activeCell.id.substr(5, 1));
	switch(keycode) {
		case 37 : // left
			do {
				col = col - 1;
				if (col < 1) col = 9;
			} while(Puzzle[row][col].Fixed && !busyInputtingPuzzle);
			elem = document.getElementById("cell" + row + col);
			HighlightCell(elem);
			break;
		case 38 :  // up
			do {
				row = row - 1;
				if (row < 1) row = 9;
			} while(Puzzle[row][col].Fixed && !busyInputtingPuzzle);
			elem = document.getElementById("cell" + row + col);
			HighlightCell(elem);
			break;
		case 39 : // right
			do {
				col = col + 1;
				if (col > 9) col = 1;
			} while(Puzzle[row][col].Fixed && !busyInputtingPuzzle);
			elem = document.getElementById("cell" + row + col);
			HighlightCell(elem);
			break;
		case 40 : // down
			do {
				row = row + 1;
				if (row > 9) row = 1;
			} while(Puzzle[row][col].Fixed && !busyInputtingPuzzle);
			elem = document.getElementById("cell" + row + col);
			HighlightCell(elem);
			break;
		case 8 :
		case 32 :
		case 46 : // del
			SetCellValue(0, e.altKey);
			break;
		case 80 : // P
			if (UsePencil) PenClick(); else PencilClick();
			break;	
		case 85 : // U  undo	
			UndoLastInput();
			break;
		case 49 :
		case 50:
		case 51:
		case 52:
		case 53:
		case 54:
		case 55:
		case 56:
		case 57: 
			SetCellValue(keycode - 48, e.altKey);
			break;
		case 97:
		case 98:
		case 99:
		case 100:
		case 101:
		case 102:
		case 103:
		case 104:
		case 105:
			SetCellValue(keycode - 96, e.altKey);
			break;
	}
	window.event.returnValue = false ;
}

//--------------------------------------------
function DoAction(opt) {
	switch(opt) {
		case "Gen0":
		case "Gen1":
		case "Gen2":
		case "Gen3":
			if (modified)  {
				var ans = confirm("Click OK to abort existing puzzle else Cancel");
				if (ans == false) break;
			}
			var lastTime = lastStart[Number(opt.substr(3,1))];
			if (lastTime != undefined) {
			    if (new Date() - lastTime < 60000) {
			        alert("You must wait 60 seconds for a new puzzle at this level");
			        break;
			    }
			}
			CallWebMethod("GenPuzzle", "difficulty=" + opt.substr(3,1));
			lastStart[Number(opt.substr(3,1))] = new Date();
			break;
		case "InputManual":
			CallWebMethod("InitializeNew", "");
			busyInputtingPuzzle = true;
			StatusLine.innerHTML = "Enter fixed numbers then click here when done."
			break;
		case "Input#":
			if (modified)  {
				var ans = confirm("Click OK to abort existing puzzle else Cancel");
				if (ans == false) break;
			}
			var seed = prompt("Enter puzzle #", "") 
			if (seed != null && seed != "") {
				if (!isNaN(seed)) { 
					CallWebMethod("GenPuzzle", "difficulty=0&seed=" + seed);
				} else {
					alert("Number must be numeric");
				}
			}
			break;
		case "Check": 
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
			CheckGridInput();
			break;
		case "Hint":
	 		if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
		   UpdatePuzzleDoc(false);
			CallWebMethod("Hint", PuzzleDoc.xml);
			break;
		case "Clue":
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
		   UpdatePuzzleDoc(false);
			CallWebMethod("Clue", PuzzleDoc.xml);
			break;	
		case "SolveOne":
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
			UpdatePuzzleDoc(false);
			CallWebMethod("SolveOneCell", PuzzleDoc.xml);
			break;
		case "Possib1":
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
			UpdatePuzzleDoc(false);
			CallWebMethod("FindPossibles", PuzzleDoc.xml);
			break;
		case "Possib2":
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
			UpdatePuzzleDoc(false);
			CallWebMethod("FindPossiblesLevel2", PuzzleDoc.xml);
			break;
		case "Solve":
			if (puzzleInProgress == false) { alert("First generate a puzzle"); break; }
	 		if (busyInputtingPuzzle) { alert("Click message box to validate puzzle before using this option"); break; }
			SolvePuzzle();
			break;
		case "Clear": 
			ClearPossibles();
			break;
		case "ClearAll":
			ClearAll();
			break;
		case "ConvertPencilPen":
			ConvertPencilPen();
			break;			
		case "Print":
			if (isMozilla) {
				alert("This option only available in Internet Explorer");
				return;
			}
			window.clipboardData.setData("Text", PuzzleNo.innerHTML + Grid.innerHTML);
			window.open("SudokuPrint.htm", null, "height=560,width=450,left=50,top=50,status=no,toolbar=no,menubar=yes,location=no");
			break;
		case "Save":
			SavePuzzle();
			break;
		case "Load": 
			LoadPuzzle();
			break;
	}
}

//--------------------------------------------
function HighlightCell(elem) {
	row = elem.id.substr(4, 1);
	col = elem.id.substr(5, 1);
	if (Puzzle[row][col].Fixed == true && !busyInputtingPuzzle) return;
	if (activeCell != null) activeCell.style.backgroundColor = gridColor;
	activeCell = elem;
	elem.style.backgroundColor = hilightColor //"LightSteelBlue" ' "AliceBlue";
}

//--------------------------------------------
function HighlightRow(row) {
	for(col = 1; col <= 9; col++) {
		elem = document.getElementById("cell" + row + col);
		elem.style.backgroundColor = hilightColor;
	}	
}
//--------------------------------------------
function HighlightCol(col) {
	for(row = 1; row <= 9; row++) {
		elem = document.getElementById("cell" + row + col);
		elem.style.backgroundColor = hilightColor;
	}	
}
//--------------------------------------------
function HighlightBlock(block) {
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			if (GetBlock(row, col) == block) {
				elem = document.getElementById("cell" + row + col);
				elem.style.backgroundColor = hilightColor;
			}	
		}
	}	
}

//--------------------------------------------
function ClearHighlights() {
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			elem = document.getElementById("cell" + row + col);
			elem.style.backgroundColor = gridColor;
		}
	}	
	refreshGrid = false;
}

//--------------------------------------------
function HiLite(elem) {
	elem.style.backgroundColor = "MidnightBlue";
	elem.style.color = "white";
}

function FindPosX(obj) {
	var curleft = 0;
   if(obj.offsetParent)
      while(1) {
        curleft += obj.offsetLeft;
        if(!obj.offsetParent) break;
        obj = obj.offsetParent;
      }
    else if(obj.x)
        curleft += obj.x;
    return curleft;
}

function FindPosY(obj) {
    var curtop = 0;
    if(obj.offsetParent)
        while(1)
        {
          curtop += obj.offsetTop;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.y) curtop += obj.y;
    if (isMozilla) curtop -= 2;
    return curtop;
}


//--------------------------------------------
function UnHiLite(elem) {
	elem.style.backgroundColor = "LightSteelBlue";
	if (elem.innerHTML != "C") elem.style.color = "black";
 //elem.style.cursor = "default";
}

//-------------------------------------
function PencilClick() {
	Pencil.style.borderColor = "silver"; 
	Pen.style.borderColor = "white";
	UsePencil = true;
	InputButtons.style.cursor = PencilCursor.style.cursor;
	if (isMozilla) InputButtons.style.cursor = "pointer";
	Grid.style.cursor = InputButtons.style.cursor;
}

//-------------------------------------
function PenClick() {
	Pen.style.borderColor = "silver";
	Pencil.style.borderColor = "white";
	UsePencil = false;
	InputButtons.style.cursor = PenCursor.style.cursor;
	if (isMozilla) InputButtons.style.cursor = "pointer";
	Grid.style.cursor = InputButtons.style.cursor;
}

//-------------------------------------
function SolvePuzzle() {
	try {
		var nodeList = EvaluateXPath(PuzzleDoc.documentElement, "Grid/GridRow");
		if (nodeList == null) return;
		for(i = 0; i < nodeList.length; i++ ) {
			rowNode = nodeList[i];
			for(j = 0; j < rowNode.childNodes.length; j++ ) {
				node = rowNode.childNodes[j];
				node.setAttribute("value", node.getAttribute("ans"));
			}
		}
		PopulateGrid();
	}
	catch (ex)  { alert(ex.message) }
}

//-------------------------------------
function SavePuzzle() {
	if (isMozilla) {
		alert("This option only available in Internet Explorer");
		return;
	}
   try {
		UpdatePuzzleDoc(true);
		PuzzleDefn.load("oXMLStore");
		PuzzleDefn.XMLDocument.documentElement = PuzzleDoc.documentElement;
		PuzzleDefn.save("oXMLStore");
		StatusLine.innerHTML = "Puzzle Saved";
		// Load it again else PuzzleDoc null for some reason?
		LoadPuzzle();
	}
	catch(ex) {
		alert(ex.message)
	}
}

//-------------------------------------
function LoadPuzzle() {
	if (isMozilla) {
		alert("This option only available in Internet Explorer");
		return;
	}
	try {
		PuzzleDefn.load("oXMLStore");
		PuzzleDoc.documentElement = PuzzleDefn.XMLDocument.documentElement;
		if (PuzzleDoc.documentElement == null) alert("null");
		if (PuzzleDoc.documentElement.nodeName != "SudokuPuzzle") {
			alert("Unable to load puzzle");
			return;
		}
		modified = false;
	}
	catch(ex) {
		alert(ex.message)
	}	
	PopulateGrid();
}

//-------------------------------------
function ClearPossibles() {
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			if (Puzzle[row][col].Possible.length > 0) {
				Puzzle[row][col].Possible = "";
				elem = document.getElementById("cell" + row + col);
				elem.innerHTML = "";
			}
		}
	}
}

//-------------------------------------
function ClearAll() {
	if (modified) {
		var ans = confirm("Click OK to clear all input else Cancel");
		if (ans == false) return;
	}
	for (row = 1; row <= 9; row++) {
		for (col = 1; col <= 9; col++) {
			if (Puzzle[row][col].Fixed == false) {
				Puzzle[row][col].Value = 0;
				Puzzle[row][col].Possible = "";
				elem = document.getElementById("cell" + row + col);
				elem.innerHTML = "";
			}
		}
	}
	if (undoStack.length)  undoStack.splice(0, undoStack.length);
}

//-------------------------------------
function ConvertPencilPen() {
	for(row = 1; row <= 9; row++) {
		for(col = 1; col <= 9; col++) {
			cell = Puzzle[row][col];
			if (cell.Possible.length == 1) {
				cell.Value = +cell.Possible;
				cell.Possible = "";
				UpdateOneCell(cell, false);
				if (autoErrors && cell.Value != cell.Ans) {
					elem = document.getElementById("cell" + row + col);
					elem.style.color = "Red";
				}
			}
		}
	}
	CheckIfDone();
}

//-------------------------------------
function UpdatePuzzleDoc(includePossib) {
	try {
		var nodeList = EvaluateXPath(PuzzleDoc.documentElement, "Grid/GridRow");
		if (nodeList.length == 0) {
			CallWebMethod("InitializeNew", "create")  // this will create xml doc
			nodeList = EvaluateXPath(PuzzleDoc.documentElement, "Grid/GridRow");
		}
		for(i = 0; i < nodeList.length; i++ ) {
			rowNode = nodeList[i];
			row = rowNode.getAttribute("row");
			col = 0;
			for(j = 0; j < rowNode.childNodes.length; j++ ) {
				node = rowNode.childNodes[j];
				col++;
				cell = Puzzle[row][col];
				node.setAttribute("value", cell.Value);
				if (includePossib) node.setAttribute("possible", cell.Possible);
				if (busyInputtingPuzzle) {
					if (cell.Fixed) node.setAttribute("fixed", "True"); 
				}	
			}
		}
	}
	catch (ex)  { alert(ex.message) }
}	

pad = function(numb) {
	return numb = (numb < 10)?"0" + numb:numb;
}

//-------------------------------------
function UpdateTime() {
	if (useTimer == false || puzzleInProgress == false) return;
	elapsedSeconds = Math.round((new Date() - startTime) / 1000);
	var mins = Math.floor(elapsedSeconds / 60);
	var secs = elapsedSeconds % 60;
	//if (mins < 60) {
	//	TimeDisplay.innerHTML = pad(mins) + ":" + pad(secs);
	//} else {
		var hrs = Math.floor(mins / 60);
		mins = mins % 60;
		TimeDisplay.innerHTML = hrs + ":" + pad(mins) + ":" + pad(secs);
	//}	
}

//-------------------------------------
function PauseToggle() {
	if (!puzzleInProgress) return;
	paused = !paused;
	if (paused)  {
		PauseButton.style.color = "red";
		elapsedSeconds = Math.round((new Date() - startTime) / 1000);
		if (hTimer > 0) {
			window.clearInterval(hTimer);
			hTimer = 0;
		}	
		Grid.style.visibility = "hidden";
		StatusLine.innerHTML = "Puzzle paused - press Pause button to resume."
	} else {
		PauseButton.style.color = "black";
		startTime.setTime(new Date() - (elapsedSeconds * 1000));
		Grid.style.visibility = "visible";
		StatusLine.innerHTML = "";
		hTimer = window.setInterval("UpdateTime()", 1000);
	}
}

//-------------------------------------
// Add to input stack
function AddToStack(cell) {
	var stackCell = new SudCell();
   stackCell.Value = cell.Value;
   stackCell.Possible = cell.Possible;
   stackCell.Fixed = cell.Fixed;   // need this for when inputting puzzle manually
	stackCell.Col = cell.Col;
	stackCell.Row = cell.Row;
   undoStack.push(stackCell);
}

//-------------------------------------
// Undo last input
function UndoLastInput(cell) {
	if (undoStack.length <= 0 || puzzleInProgress == false)  return;
	var cell = undoStack.pop();
	Puzzle[cell.Row][cell.Col].Value = cell.Value;
   Puzzle[cell.Row][cell.Col].Possible = cell.Possible;
   Puzzle[cell.Row][cell.Col].Fixed = cell.Fixed;
	UpdateOneCell(Puzzle[cell.Row][cell.Col], true); 
}

//-------------------------------------
function ShowPrefs() {
	if (isMozilla) {
		alert("Currently only available in Internet Explorer");
		return;
	}
	var status = window.showModalDialog("SudokuPrefs.htm");
	if (status == "ok") SetPreferences("");
}

//-------------------------------------
function SetPreferences(flag) {
	if (isMozilla) {
	//	TimeDisplay.style.display = "inline";
	//	PauseButton.style.display = "inline";
		return;
	}	
	try {
		PrefsData.load("SudokuPrefs");
		var root = PrefsData.XMLDocument.documentElement;
		if (root != null) useTimer = (GetNodeText(root, "Timer") == "true");
		if (useTimer) val = "inline-block"; else val = "none";
		TimeDisplay.style.display = val;
		PauseButton.style.display = val;
		if (root == null) return;
		autoErrors = (GetNodeText(root, "AutoError") == "true");
		numberFont = GetNodeText(root, "FontFamily");
		boldNumbers = (GetNodeText(root, "FontBold") == "true");
		if (numberFont.length)  ChangeClassElement(".cell", "fontFamily", numberFont);
		if (boldNumbers) val = "bold"; else val = "normal";
		ChangeClassElement(".cell", "fontWeight", val);
		if (GetNodeText(root, "SmallGrid") == "true") {
			cellFontSize = "20pt";
			ChangeClassElement(".cell", "fontSize", cellFontSize);
			ChangeClassElement(".cell", "width", 37);	
			ChangeClassElement(".cell", "height", 37);	
			LeftPanel.style.width = "368px";	
			LeftPanel.style.height = "515px";	
			StatusLine.style.width = InputButtons.offsetWidth;
			//RightText.style.height = "475px";
			//HomeLink.style.height = "100px";
		}	
		val = GetNodeText(root, "GridColor");
		if (val.length)  {
			gridColor = val;
			ChangeClassElement(".puzzle", "backgroundColor", gridColor);
			ChangeClassElement(".cell", "backgroundColor", gridColor);
		}	
		val = GetNodeText(root, "InputColor");
		if (val.length) inputColor = val;
		val = GetNodeText(root, "FixedColor");
		if (val.length) fixedColor = val;
		val = GetNodeText(root, "HilightColor");
		if (val.length) hilightColor = val;
		val = GetNodeText(root, "ThickColor");
		if (val.length) {
			thickColor = val;
			ChangeClassElement(".puzzle", "borderColor", thickColor);
		}	
		val = GetNodeText(root, "ThinColor");
		if (val.length) ChangeClassElement(".cell", "borderColor", val);
		if (flag == "onload")  return;
		
		// Prefs have changed in middle of puzzle	
		for(row = 1; row <= 9; row++) {
			for(col = 1; col <= 9; col++) {
				elem = document.getElementById("cell" + row + col);
				elem.style.backgroundColor = gridColor;
				if (col == 3 || col == 6) elem.style.borderRightColor = thickColor;
				if (row == 3 || row == 6) elem.style.borderBottomColor = thickColor;
				if (elem.style.textAlign == "center") {    // only input cells
					if (boldNumbers) val = "bold"; else val = "normal";
					elem.style.fontWeight = val;
					elem.style.fontSize = cellFontSize;
					if (elem.style.color != "red")  {
						if (Puzzle[row][col].Fixed == true) elem.style.color = fixedColor; else elem.style.color = inputColor;
					}	
				}
			}
		}
		
	}
	catch(ex) {
		alert(ex.message)
	}	
}

//-------------------------------------
function GetBlock(row, col) {
	if (row < 4) {
		if (col < 4) return 1;
		if (col < 7) return 2;
		return 3;
	} else {
		if (row < 7) {
			if (col < 4) return 4;
			if (col < 7) return 5;
			return 6;
		} else { 
			if (col < 4) return 7;
			if (col < 7) return 8;
			return 9;
		}
	}      
}

//-------------------------------------
function ShowHelp(url) {
	window.open(url, null, "height=570,width=470,left=150,top=50,scrollbars=yes,resizable=yes,status=no,toolbar=no,menubar=no,location=no");
}
//-------------------------------------
function ShowWindow(url) {
	window.open(url, null, "height=280,width=400,left=200,top=150,scrollbars=no,resizable=yes,status=no,toolbar=no,menubar=no,location=no");
}
//-------------------------------------
function PuzzleCompleted() {
	modified = false;
	puzzleInProgress = false;
	if (hTimer > 0) {
		window.clearInterval(hTimer);
		hTimer = 0;
	}	
	Congrats.style.width = Grid.offsetWidth - 6;
	Congrats.style.height = (Grid.offsetHeight * .32);
	if (cellFontSize == "24pt")  Congrats.style.height = Congrats.offsetHeight + 1;
	Congrats.style.left = FindPosX(Grid) + 3;
	Congrats.style.top = FindPosY(Grid) + Congrats.offsetHeight + 8;
//alert("congat height=" + Congrats.offsetHeight + "  grid height=" + Grid.offsetHeight);	
	try {
		Congrats.filters(0).Apply();
		Congrats.style.visibility = "visible";
		Congrats.filters(0).Play();
	}	
	catch (ex) { 
		Congrats.style.visibility = "visible";
	}
//	DisplayAd();
}

//-------------------------------------
function ValidatePuzzle() {
	if (!busyInputtingPuzzle) return;
	var ans = confirm("Click OK if you have finished entering puzzle else Cancel");
	if (ans == false) return;
	UpdatePuzzleDoc(false);
	CallWebMethod("ValidatePuzzle", PuzzleDoc.xml);
}

//-------------------------------------
function CongratsClick() {
	Congrats.style.visibility = "hidden";
}
//-------------------------------------
document.oncontextmenu = function() {
	window.event.returnValue = false  // stop right click to see source
}

//-------------------------------------
function EvaluateXPath(baseNode, aExpr) {
	if (!isMozilla) return baseNode.selectNodes(aExpr);
	var xpe = new XPathEvaluator();
	var nsResolver = xpe.createNSResolver(baseNode.ownerDocument == null ?  baseNode.documentElement : baseNode.ownerDocument.documentElement);
	var result = xpe.evaluate(aExpr, baseNode, nsResolver, 0, null);
	var found = [];
	var res;
	while (res = result.iterateNext())	found.push(res);
	return found;
}

//-------------------------------------
function GetNodeText(baseNode, aExpr) {
	if (!isMozilla) {
		var node = baseNode.selectSingleNode(aExpr);
		if (node == null) return "";
		return node.text;
	}
	var nodelist = EvaluateXPath(baseNode, aExpr);
	if (nodelist.length == 0)  return "";
	return nodelist[0].textContent;
}

//-------------------------------------
function ChangeClassElement(className, elem, newval) {
	 var cssRules;
	 if (document.all) cssRules = 'rules'; else cssRules = 'cssRules';
	 for (sheet = 0; sheet < document.styleSheets.length; sheet++) {
		for (rule = 0; rule < document.styleSheets[sheet][cssRules].length; rule++) {
			if (document.styleSheets[sheet][cssRules][rule].selectorText == className) {
				document.styleSheets[sheet][cssRules][rule].style[elem] = newval;
			}
		}
	}	
}

//-------------------------------------
function SendComments() {
	if (Comments.value == "")  {
		alert("Please enter a comment first");
		return;
	}
	try {
		HTTP.open("POST", "SudokuServer.asmx/LogComments", false);
		HTTP.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
		HTTP.send("comments=" + Comments.value);
		alert("Thank you for your feedback");
		Comments.value = "";
	}	
	catch (ex) {}
	return;
}