/* Shared self-check harness for the flex/grid conformance fixtures.
 *
 * Every case is a card in the page; the card is visible and meaningful with
 * JS off, and this file only decorates it with a verdict. Two rules keep the
 * numbers comparable between engines:
 *
 *  - never read an absolute offsetLeft/offsetTop. NetSurf's offsetParent is a
 *    stub, so its offsets are document-absolute while Chrome's are relative to
 *    the nearest positioned ancestor. Differences between two elements that
 *    share an offsetParent are the same number in both, which is why every
 *    position assertion below goes through dx()/dy().
 *  - measure in onload. NetSurf defers layout, so parse-time reads are 0.
 *
 * Expected values are Chrome's, verified by loading these pages in Chrome and
 * requiring 0 failures there before the fixture is considered correct.
 */
var FG = (function () {
	var cases = [];
	var pass = 0, fail = 0, errs = [];

	function n(v) {
		if (typeof v !== "number") return String(v);
		return Math.round(v * 100) / 100;
	}

	function T(id) { this.id = id; this.bad = []; this.n = 0; }

	T.prototype.eq = function (label, got, want, tol) {
		if (tol === undefined) tol = 1;
		this.n++;
		if (typeof got === "number" && !isNaN(got) &&
				Math.abs(got - want) <= tol) {
			pass++;
		} else {
			fail++;
			this.bad.push(label + ": got " + n(got) + ", want " + n(want));
		}
	};

	T.prototype.ok = function (label, cond) {
		this.n++;
		if (cond) pass++;
		else { fail++; this.bad.push(label + ": not true"); }
	};

	T.prototype.lt = function (label, a, b) {
		this.ok(label + " (" + n(a) + " < " + n(b) + ")", a < b);
	};

	T.prototype.gt = function (label, a, b) {
		this.ok(label + " (" + n(a) + " > " + n(b) + ")", a > b);
	};

	function $(id) {
		var e = document.getElementById(id);
		if (!e) throw new Error("missing #" + id);
		return e;
	}

	/* Geometry, all relative — see the header note. */
	function dx(a, b) { return $(a).offsetLeft - $(b).offsetLeft; }
	function dy(a, b) { return $(a).offsetTop - $(b).offsetTop; }
	function w(a) { return $(a).offsetWidth; }
	function h(a) { return $(a).offsetHeight; }

	function test(id, fn) { cases.push([id, fn]); }

	function run() {
		var i, j, id, t, card, badge, txt;

		for (i = 0; i < cases.length; i++) {
			id = cases[i][0];
			t = new T(id);
			try {
				cases[i][1](t);
			} catch (e) {
				fail++; t.n++;
				t.bad.push("threw: " + (e && e.message ? e.message : e));
			}
			card = document.getElementById(id);
			if (card) {
				badge = document.createElement("div");
				txt = t.bad.length ?
					("FAIL " + t.bad.length + "/" + t.n) : ("PASS " + t.n);
				badge.className = "badge " + (t.bad.length ? "bad" : "ok");
				badge.appendChild(document.createTextNode(txt));
				card.appendChild(badge);
				if (t.bad.length) card.className = card.className + " failed";
			} else {
				fail++;
				t.bad.push("no card element #" + id + " in the page");
			}
			for (j = 0; j < t.bad.length; j++)
				errs.push(id + " -- " + t.bad[j]);
		}

		var b = document.getElementById("banner");
		if (b) {
			b.className = fail ? "bad" : "ok";
			b.textContent = (fail ? "FAIL" : "ALL PASS") + " -- " +
				pass + "/" + (pass + fail) + " checks, " +
				cases.length + " cases" +
				(fail ? ", " + fail + " failed" : "");
		}
		var l = document.getElementById("log");
		if (l) {
			l.textContent = errs.length ?
				("failures:\n" + errs.join("\n")) :
				"no failures";
		}
		upload(b ? b.textContent : "", errs);
	}

	/* When the fixtures are served over HTTP, hand the result back to
	 * tools/testsrv.py so the host can read it without a screenshot. Over
	 * file:// there is no host to talk to, so nothing is attempted: an
	 * offline run must not cost a failed resolve. */
	function upload(summary, list) {
		var name, x;
		if (String(location.protocol).indexOf("http") !== 0) return;
		try {
			name = String(location.pathname).split("/").pop()
				.replace(/\.html?$/, "") || "fg";
			x = new XMLHttpRequest();
			x.open("POST", "/collect?name=fg-" + name, true);
			x.send(summary + "\n" +
			       (list.length ? list.join("\n") : "no failures") +
			       "\n");
		} catch (e) { /* no XHR, or no route to the host */ }
	}

	function go() {
		if (window.addEventListener)
			window.addEventListener("load", run, false);
		else
			window.onload = run;
	}

	return { test: test, run: run, go: go, $: $,
		 dx: dx, dy: dy, w: w, h: h };
})();
