    $(function() {
	function getSQLtable(TableName, fnCallback) {
		$.ajax({
		    type: "POST",
// 17 Dec 09
// OK so I know this works in principle but mono seems to have broken authentication recently
// and the Sheevplug doesn't really support it, so to get the demo working again
// I've switched to pure LAMP - i.e. using PHP to convert a MySQL DataSet to JSON
		    url: "../PHP/MYSQL2JSON.php",
		    data: "SQLparm=* FROM " + TableName,
//		    url: "/mono/SQLconnect/MySQLService.asmx/SQLselect",
//		    url: "/mono/SQLconnect/WebService1.asmx/SQLselect",
//		    data: "{SQLparm: '* FROM " + TableName + "'}",
//		    contentType: "application/json; charset=utf-8",
//		    dataType: "json",
		    error: function(msg){
			    $("#" + TableName).text("Sorry - can't fetch " + TableName + "  from the database")
			    }, 
		    success: function(msg) {
//			var strJSON = eval(msg.d);
			var strJSON = eval(msg);
			TableName = TableName.split(" ")[0];
			var JSONtable = "<table><caption>" + TableName + "</caption>";
			JSONtable += "<thead><tr>";
			for (var field in strJSON[0]) {
			    JSONtable += "<th>" + field + "</th>";
			}
			JSONtable += "</tr></thead>";
			for (var i = 0, l = strJSON.length; i < l; i++) {
			    JSONtable += "<tr>";
			    var thisRecord = strJSON[i];
			    for (var field in thisRecord) {
				JSONtable += "<td>" + thisRecord[field] + "</td>";
			    }
			    JSONtable += "</tr>";
			}
			JSONtable += "</table>";
			$("#" + TableName).html(JSONtable);
			if (fnCallback){
			    fnCallback();
			}
		    }
		});
	    };
	$("#btnSQL").click(function() {
	    $("#lblSQL").hide();
	    $("#frmSQL").addClass("loading");
	    $.ajax({//delete new TestRun and TestResults records
		type: "POST",
		url: "../PHP/MYSQLexec.php",
		data: "SQLparm=CleanSimulatedTestRun",
//		url: "/mono/SQLconnect/MySQLService.asmx/SQLexec",
//		url: "/mono/SQLconnect/WebService1.asmx/SQLexec",
//		data: "{SQLparm: 'CleanSimulatedTestRun'}",
//		contentType: "application/json; charset=utf-8",
		success: function() {
		    getSQLtable("TestBed");
		    getSQLtable("TestConditions");
		    getSQLtable("TestRun ORDER BY RunID", function(){
			$("#TestRun caption").append("<form><input id='btnSTR' type='button' value='Simulate Test Run' /></form>");
		    });
		    getSQLtable("TestResults ORDER BY TestResultsID", function(){
			$("html,body").animate({ scrollTop: $("#btnSQL").offset().top }, 500);
			$("#lblSQL").fadeIn("slow");
			$("#frmSQL").removeClass("loading");
		    });
		}
	    });
	    return false;
	});
	function describeSQLtable(TableName, fnCallback) {
		$.ajax({
		    type: "POST",
		    url: "../PHP/MYSQLDESC.php",
		    data: "SQLparm=" + TableName,
		    error: function(msg){
			    $("#" + TableName).text("Sorry - can't fetch " + TableName + "  from the database")
			    }, 
		    success: function(msg) {
			var strJSON = eval(msg);
			TableName = TableName.split(" ")[0];
			var JSONtable = "<table><caption>" + TableName + "</caption>";
			JSONtable += "<thead><tr>";
			for (var field in strJSON[0]) {
			    JSONtable += "<th>" + field + "</th>";
			}
			JSONtable += "</tr></thead>";
			for (var i = 0, l = strJSON.length; i < l; i++) {
			    JSONtable += "<tr>";
			    var thisRecord = strJSON[i];
			    for (var field in thisRecord) {
				JSONtable += "<td>" + thisRecord[field] + "</td>";
			    }
			    JSONtable += "</tr>";
			}
			JSONtable += "</table>";
			$("#" + TableName).html(JSONtable);
			if (fnCallback){
			    fnCallback();
			}
		    }
		});
	    };
	$("#btnDESC").click(function() {
	    $("#frmSQL").addClass("loading");
	    describeSQLtable("TestBed");
	    describeSQLtable("TestConditions");
	    describeSQLtable("TestRun");
	    describeSQLtable("TestResults");
	    $("#frmSQL").removeClass("loading");
	    return false;
	});
	//tables are chained as:
	//TB[0]-[2]TN[0]-[2]TR[1]-[0]TC
	function highlightMatchingCells(thisTR, thisCell){
	    var thisIndex = thisTR.childNodes[thisCell].innerHTML;
	    $("td:contains('" + thisIndex + "')").each(function(){
		if ($(this).context.innerHTML == thisIndex){
		    $(this).addClass("highlight");
		}
	    })
	};
	$("table").live("click", function(e){
	    $(".highlight").removeClass("highlight");
	    var thisTR = e.target.parentNode;
	    if (thisTR.tagName === "TR"){
		var thisTable = this.caption.innerHTML;
		switch (thisTable){
		    case "TestBed":
			highlightMatchingCells(thisTR, 0);
			$("#TestRun td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 0);
			    });
			$("#TestResults td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 1);
			    });
			break;
		    case "TestResults":
			highlightMatchingCells(thisTR, 1);
			highlightMatchingCells(thisTR, 2);
			$("#TestRun td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 2);
			    });
			break;		    
		    case "TestConditions":
			highlightMatchingCells(thisTR, 0);
			$("#TestResults td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 2);
			    });
			$("#TestRun td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 2);
			    });
			break;
		    default: //		    case "TestRun":
			highlightMatchingCells(thisTR, 2);
			highlightMatchingCells(thisTR, 0);
			$("#TestResults td.highlight").each(function(){
			    var thisTR = this.parentNode;
			    highlightMatchingCells(thisTR, 1);
			    });
			break;
		}
		//TestResults and TestRun need to have matched columns [2][1] and [2][0]
		function removeUnmatchedColumns(rUCtable, rUC1, rUC2){
		    $("#" + rUCtable + " tr").each(function(r){
			var rUCtd1 = $("#" + rUCtable + " tr:eq(" + r + ") td:eq(" + rUC1 + ")");
			var rUCtd2 = $("#" + rUCtable + " tr:eq(" + r + ") td:eq(" + rUC2 + ")");
			if (!(rUCtd1.hasClass("highlight")&&rUCtd2.hasClass("highlight"))){
			    rUCtd1.removeClass("highlight");
			    rUCtd2.removeClass("highlight");
			}
		    });
		}
		removeUnmatchedColumns("TestResults", 1, 2);
		removeUnmatchedColumns("TestRun", 0, 2);
	    }
	});
	function generateTestRun(rtRunStarted,rtTestPC,rtTestBuild,rtTestCode,rtOutputMatch) {
	    $.ajax({//create new TestRun and TestResults records
		type: "POST",
		url: "../PHP/MYSQLexec.php",
		data: "SQLparm=SimulatedTestRun('" + rtRunStarted + "','" + rtTestPC + "','" + rtTestBuild + "'," + rtTestCode + "," + rtOutputMatch + ")",
//		url: "/mono/SQLconnect/MySQLService.asmx/SQLexec",
//		url: "/mono/SQLconnect/WebService1.asmx/SQLexec",
//		data: "{SQLparm: 'SimulatedTestRun (#" + rtRunStarted + "#,#" + rtTestPC + "#,#" + rtTestBuild + "#,#" + rtTestCode + "#,#" + rtOutputMatch + "#)'}",
//		contentType: "application/json; charset=utf-8",
		success: function() {
		    getSQLtable("TestRun ORDER BY RunID", function(){
			$("#TestRun caption").append("<form><input id='btnSTR' type='button' value='Simulate Test Run' /></form>");
		    });
		    getSQLtable("TestResults ORDER BY TestResultsID", function(){
			$("#TestRun tr:last td:eq(0)").addClass("highlight");
			$("#TestResults tr:last td:eq(0)").addClass("highlight");
			$("html,body").animate({ scrollTop: $("#btnSQL").offset().top }, 500);
		    });
		}
	    });
	};
	$("#btnSTR").live("click",function(){
	    generateTestRun("2009-10-19 6:02:00","TestVista","1.1.0.1",100000,0);    
	    return false;
	});
    });
