// JavaScript Document

function init() {
	xmlhttp = new XMLHttpRequest();
	xmlhttp.onreadystatechange = loaded;
	xmlhttp.open("GET", "test.xml", true);
	xmlhttp.send("");
}

function loaded() {
	if (xmlhttp.readyState != 4) return;
	
	xmldoc = xmlhttp.responseXML;
	var textnode = document.createTextNode(xmlhttp.responseText);
	document.getElementById('thexml').appendChild(textnode);
	
	alltestsuites = [ testsuite ];
	alltestsuites = alltestsuites.concat(previoustestresults);
	
	runTests();
	outputTestResults(compactOutput);
	var matches = matchTestResults();
	serialiseTestResults(!matches);
}

function previoustestresultsloaded(results) {
	previoustestresults = results;
}

function runTests() {
	for (var i in testsuite.tests) {
		try {
			var result = testsuite.tests[i].test();
			if (result == null) result = '(null)';
			if (result == '') result = "('')";
			result = result.toString();
		} catch(e) {
			var result = 'Error (' + e.name + ')';
			testsuite.tests[i].error = e.name;
			testsuite.tests[i].errormessage = e.message;
		}
		testsuite.tests[i].result = result;
	}
}

/**
 * Match the test results with previous test results
 * @param {boolean} compact If true, output compact table.
 */
function outputTestResults(compact) {
	//Output test results to table...
	var table = document.createElement('table');
//	table.border = '1';
	
	var thead = document.createElement('thead');
	var tr = document.createElement('tr');
	var th = document.createElement('th');
	if (compact) {
		th.appendChild(document.createTextNode('Test ID'));
	} else {
		th.appendChild(document.createTextNode('DOM method'));
	}
	tr.appendChild(th);
	var th = document.createElement('th');
	th.appendChild(document.createTextNode('DOM spec'));
	tr.appendChild(th);
	for (var j in alltestsuites) {
		var th = document.createElement('th');
		th.appendChild(document.createTextNode(alltestsuites[j].name));
		tr.appendChild(th);
	}
	thead.appendChild(tr);
	table.appendChild(thead);
	
	var tbody = document.createElement('tbody');
	for (var i in testsuite.tests) {
		var tr = document.createElement('tr');
		if (testsuite.tests[i].id) {
			tr.setAttribute('id',testsuite.tests[i].id);
		}
		var td = document.createElement('td');
		var testfunction = testsuite.tests[i].test.toString();
		testfunction = testfunction.replace(/^[\s]*?function[\s]*?\(\)[\s]*?\{[\s]*?/,'');
		testfunction = testfunction.replace(/[\s]*?\}[\s]*?$/,'');
		if (compact) {
			td.appendChild(document.createTextNode(testsuite.tests[i].id));
			if (testsuite.tests[i].id) td.setAttribute('title',testfunction);
		} else {
			var testfunctionarray = testfunction.split('\n');
			for (var j in testfunctionarray) {
				if (!testfunctionarray[j].match(/^\s*$/)) {
					td.appendChild(document.createTextNode(testfunctionarray[j]));
					td.appendChild(document.createElement('br'));
				}
			}
			if (testsuite.tests[i].id) td.setAttribute('title',testsuite.tests[i].id);
		}
		tr.appendChild(td);
		var td = document.createElement('td');
		if (testsuite.tests[i].expected) {
			if (typeof(testsuite.tests[i].expected) == 'object') {
				var expected = testsuite.tests[i].expected.join(' (or) ');
			} else {
				var expected = testsuite.tests[i].expected;
			}
			if (compact) {
				var expectedShort = expected;
				if (expected.length > 6) expectedShort = expected.substr(0,6) + '…';
				td.appendChild(document.createTextNode(expectedShort));
				td.setAttribute('title',expected);
			} else {
				td.appendChild(document.createTextNode(expected));
			}
		}
		tr.appendChild(td);
		for (var j in alltestsuites) {
			var td = document.createElement('td');
			var test = findTestById(alltestsuites[j].tests, testsuite.tests[i].id);
			if (test != null) {
				if (compact) {
					var result = test.result;
					if (result.length > 6) result = result.substr(0,6) + '…';
					td.appendChild(document.createTextNode(result));
					td.setAttribute('title', test.result);
				} else {
					td.appendChild(document.createTextNode(test.result));
				}
				if (test.error) {
					td.style.backgroundColor = '#bbb';
				} else if (testsuite.tests[i].expected) {
					td.style.backgroundColor = '#f88';
					if (typeof(testsuite.tests[i].expected) == 'object') {
						for (var k in testsuite.tests[i].expected)
							if (testsuite.tests[i].expected[k] == test.result)
								td.style.backgroundColor = '#8f8';
					} else {
						if (testsuite.tests[i].expected == test.result)
							td.style.backgroundColor = '#8f8';
					}
				}
				if (test.error)
					td.setAttribute('title',test.error + ': ' + test.errormessage);
			}
			tr.appendChild(td);
		}
		tbody.appendChild(tr);
	};
	table.appendChild(tbody);
	var output = document.getElementById('output');
	while (output.childNodes.length) {
		output.removeChild(output.childNodes.item(0));
	}
	document.getElementById('output').appendChild(table);
}

function findTestById(tests, testid) {
	for (var i in tests) {
		if (tests[i].id == testid) return tests[i];
	}
	return null;
}


/**
 * Toggle the output between compact and full table.
 */
function toggleCompactOutput() {
	compactOutput = compactOutput ? false : true;
	outputTestResults(compactOutput);
	return;
}
compactOutput = true;


/**
 * Match the test results with previous test results
 * @returns true if it matches previous tests
 */
function matchTestResults() {
	var matches = false;
	var resembles = false;
	var matchnames = [];
	var resemblenames = [];
	
	for (var i in previoustestresults) {
		var thismatches = true;
		var thisresembles = true;
		for (var j in testsuite.tests) {
			var prevtest = findTestById(previoustestresults[i].tests, testsuite.tests[j].id);
			if (prevtest == null || prevtest.result != testsuite.tests[j].result) {
				if (prevtest != null) {
					thisresembles = false;
				}
				thismatches = false;
			}
		}
		if (thismatches) {
			matchnames.push(previoustestresults[i].name);
			matches = true;
		}
		if (thisresembles) {
			resemblenames.push(previoustestresults[i].name);
			resembles = true;
		}
	}
	
	if (matchnames == []) {
		matchnames.push('Unknown');
		testsuite.matchname = 'Unknown';
	} else {
		testsuite.matchname = matchnames.toString();
	}
	if (resembles && !matches) {
		testsuite.matchname = resemblenames.toString();
	}
	
	var p = document.createElement('p');
	if (matches || !resembles) {
		p.appendChild(document.createTextNode('Your browser matches: ' + matchnames))
	} else {
		p.appendChild(document.createTextNode('Your browser resembles: ' + resemblenames))
	}
	document.getElementById('browsermatch').appendChild(p);
	
	return matches;
}

/**
 * Serialise test results to JSON
 * @param {boolean} allowsubmit If true, add a submit button for the test results
 */
function serialiseTestResults(allowsubmit) {
	var form = document.createElement('form');
	if (allowsubmit) {
		form.action = 'submit.php';
		form.method = 'post';
	}
	var textarea = document.createElement('textarea');
	textarea.readOnly = true;
	textarea.name = 'testresults';
	form.appendChild(textarea);
	if (allowsubmit) {
		var input = document.createElement('input');
		input.type = 'submit';
		input.value = 'Submit your test results';
		form.appendChild(input);
	}
	document.getElementById('outputform').appendChild(form);
	
	var results = '';
	results += '{\n';
	results += '\tname: ' + encodeJSONString(testsuite.matchname) + ',\n';
	results += '\tuseragent: ' + encodeJSONString(navigator.userAgent) + ',\n';
	var msxmlver = determineMSXMLVersion();
	if (msxmlver)
		results += '\tmsxmlver: ' + encodeJSONString(msxmlver) + ',\n';
	results += '\ttests: [\n';
	for (var i in testsuite.tests) {
		results += '\t\t{\n';
		if (testsuite.tests[i].id)
			results += '\t\t\tid: ' + encodeJSONString(testsuite.tests[i].id) + ',\n';
		if (testsuite.tests[i].error)
			results += '\t\t\terror: ' + encodeJSONString(testsuite.tests[i].error) + ',\n';
		results += '\t\t\tresult: ' + encodeJSONString(testsuite.tests[i].result) + '\n';
		if (i < testsuite.tests.length-1)
			results += '\t\t},\n';
		else
			results += '\t\t}\n';
	}
	results += '\t]\n';
	results += '}';
	
	textarea.value = results;
}

/**
 * Encode string as JavaScript string (except ")
 * @private
 */
function encodeJSONString(str) {
	str = str.replace(/\\/g, '\\\\');
	str = str.replace(/"/g, '\\"');
	str = str.replace(/\n/g, '\\n');
	str = str.replace(/\r/g, '\\r');
	str = str.replace(/\f/g, '\\f');
	str = str.replace(/\t/g, '\\t');
	str = str.replace(/[\b]/g, '\\b');
	return '"' + str + '"';
}

/**
 * Determine which versions of MSXML are installed
 * @returns list of MSXML ProgIDs
 * @private
 */
function determineMSXMLVersion() {
	var xml = '<?xml version="1.0"?><root>test</root>';
	var types = [
			'MSXML2.DOMDocument.8.0', // doesn’t exist yet
			'MSXML2.DOMDocument.7.0', // doesn’t exist yet
			'MSXML2.DOMDocument.6.0', // 6.0
			'MSXML2.DOMDocument.5.0', // 5.0
			'MSXML2.DOMDocument.4.0', // 4.0
			'MSXML2.DOMDocument.3.0', // 3.0, IE6
			'MSXML2.DOMDocument.2.6', // 2.6
			'MSXML2.DOMDocument',     // 3.0, IE6
			'MSXML.DOMDocument',      // 2.x, IE5.01 (2.5), IE4.01 (2.0), IE4 (1.0)
			'Microsoft.XMLDOM'        // 2.x, IE5.01 (2.5), IE4.01 (2.0), IE4 (1.0)
			];
	var result = [];
	for (var i in types) {
		try {
			var doc = new ActiveXObject(types[i]);
			doc.async = false;
			doc.loadXML(xml);
			result.push(types[i]);
		}
		catch(e) {}
	}
	if (result.length == 0) return null;
	return result.join(',');
}





/*
 * ‘Compatibility layer’
 */

// support XMLHttpRequest object in IE
if (!window.XMLHttpRequest) {
	window.XMLHttpRequest = function() {
		var types = [
				'MSXML2.XMLHTTP.8.0', // doesn’t exist yet
				'MSXML2.XMLHTTP.7.0', // doesn’t exist yet
				'MSXML2.XMLHTTP.6.0', // 6.0
				'MSXML2.XMLHTTP.5.0', // 5.0
				'MSXML2.XMLHTTP.4.0', // 4.0
				'MSXML2.XMLHTTP.3.0', // 3.0, IE6
				'MSXML2.XMLHTTP.2.6', // 2.6
				'MSXML2.XMLHTTP',     // 3.0, IE6
				'Microsoft.XMLHTTP'   // 2.x, IE5.01 (2.5), IE4.01 (2.0), IE4 (1.0)
				];
		for (var i in types) {
			try {
				return new ActiveXObject(types[i]);
			}
			catch(e) {}
		}
		throw new Error('Not able to instantiate XMLHttpRequest.');
	};
}
