
/* Copyright 2010 The Firebox.
 * All rights reserved.
 * Written by Nicholas De Cicco.
 */

/* If an element with the id `errorLog' is found, appends the error text
 * to the `innerHTML' of that element.
 */
function logError(errorText)
{
	var errorLog = document.getElementById("errorLog");
	if (errorLog == null) {
		alert(errorText);
	} else {
		errorLog.innerHTML += errorText + "<br>";
	}
}

/* Here we define a method to ensure that we cannot accidentally
 * attempt to parse a missing node; if the node is missing, this
 * method will return an empty string. The reason is that if we
 * attempt to parse a non-existant node, JavaScript will get angry.
 */
function getNodeValue(node) {
	try { return node.childNodes[0].nodeValue; }
	catch (e) { return ""; }
}

/* Converts a date string to a Date object. If endOfDay is true, sets the time
 * of day on the date given by the string to one millisecond before midnight of
 * the next day (i.e., 23 hours, 59 minutes, 59 seconds, and 999 milliseconds);
 * otherwise, sets the time to midnight.
 */
function parseDate(dateAsString, endOfDay)
{
	/* We could use the Date.parse() method if the date were in the format
	 * Mon, 04 Oct 2010 13:30:00 GMT-0430, but it's not, so we can't. Our
	 * dates are in the format month/day/year, so instead we use the split()
	 * method of String to yield the individual elements and parse those.
	 */
	var substrings = dateAsString.split("/");

	/* If there are fewer than three elements in the date, fill in the
	 * remaining elements with zeros. If there are more than three, ignore
	 * them.
	 */
	if (substrings.length != 3) {
		logError("parseDate(): error: invalid number of date components");
		if (substrings.length < 3) {
			for (var i = substrings.length; i < 3; i++) {
				substrings[i] = 0;
			}
		}
	}

	resetIfNaN = function(value) {
		if (isNaN(value)) {
			return 0;
		} else {
			return value;
		}
	};

	/* Note that months start at 0:
	 */
	var year = resetIfNaN(parseInt(substrings[2], 10));
	var month = resetIfNaN(parseInt(substrings[0], 10))-1;
	var day = resetIfNaN(parseInt(substrings[1], 10));

	if (endOfDay) {
		return new Date(year, month, day, 23, 59, 59, 999);
	} else {
		return new Date(year, month, day, 0, 0, 0, 0);
	}
}

/* Gets the specified style of a given element.
 */
function getStyle(element, property)
{
	if (element == null || property == null) { return; }
	if ("currentStyle" in element) {
		/* Internet Explorer */
		return element.currentStyle[property];
	} else if ("getComputedStyle" in window) {
		/* Pretty much everything else */
		return document.defaultView.getComputedStyle(
			element, null).getPropertyValue(property);
	}
}

function getBorders(element)
{
	if (!element) {
		logError("getBorders(): element was null!");
		return null;
	}

	/* Internet Explorer */
	if ("currentStyle" in element) {
		var borders = element.currentStyle["borderWidth"].split(" ");
		for (var i = 0; i < borders.length; i++) {
			if (borders[i] !== "auto") {
				borders[i] = parseInt(borders[i], 10);
				if (isNaN(borders[i])) {
					logError("getBorders(): invalid border parameter:" +
						borders[i]);
				}
			} else {
				borders[i] = -1;
			}
		}

		/* The border can be determined by one to four parameters. See
		 * http://www.w3.org/TR/CSS/box.html#propdef-border for a description
		 * of how each number of parameters applies.
		 */
		var borderTop, borderRight, borderBottom, borderLeft;
		switch (borders.length) {
			case 1:
				borderTop = borderRight = borderBottom = borderLeft =
					borders[0];
				break;
			case 2:
				borderTop = borderBottom = borders[0];
				borderRight = borderLeft = borders[1];
				break;
			case 3:
				borderTop = borders[0];
				borderRight = borderLeft = borders[1];
				borderBottom = borders[2];
				break;
			case 4:
				borderTop = borders[0];
				borderRight = borders[1];
				borderLeft = borders[2];
				borderBottom = borders[3];
				break;
			default:
				logError("getBorders(): invalid number of borders!");
		}

		return [borderTop, borderRight, borderBottom, borderLeft];

	/* All other (non-Internet Explorer) browsers */
	} else if ("getComputedStyle" in window) {
		var borders = new Array(4);
		var sides = ["top", "right", "bottom", "left"];
		for (var i = 0; i < 4; i++) {
			borders[i] = parseInt(
				document.defaultView.getComputedStyle(element,
				null).getPropertyValue("border-" + sides[i] + "-width"), 10);
		}
		return borders;

	/* I have no idea what browsers do not support either mechanism. */
	} else {
		logError("getBorders(): fatal: don't know how to get borders!");
	}
}

/* Gets the four margins of an element as an array, sorted in the usual CSS
 * order: top, right, bottom, left. This is only necessary in Internet Explorer,
 * which will not provide individual margins (e.g., "margin-top", "margin-
 * right", etc).
 */
function getMargins(element)
{
	if (!element) {
		logError("getMargins(): element was null!");
		return null;
	}

	/* Internet Explorer */
	if ("currentStyle" in element) {
		var margins = element.currentStyle["margin"].split(" ");
		for (var i = 0; i < margins.length; i++) {
			if (margins[i] !== "auto") {
				margins[i] = parseInt(margins[i], 10);
				if (isNaN(margins[i])) {
					logError("getMargins(): invalid margin parameter:" +
						margins[i]);
				}
			} else {
				margins[i] = -1;
			}
		}

		/* The margin can be determined by one to four parameters. See
		 * http://www.w3.org/TR/CSS/box.html#propdef-margin for a description
		 * of how each number of parameters applies.
		 */
		var marginTop, marginRight, marginBottom, marginLeft;
		switch (margins.length) {
			case 1:
				marginTop = marginRight = marginBottom = marginLeft =
					margins[0];
				break;
			case 2:
				marginTop = marginBottom = margins[0];
				marginRight = marginLeft = margins[1];
				break;
			case 3:
				marginTop = margins[0];
				marginRight = marginLeft = margins[1];
				marginBottom = margins[2];
				break;
			case 4:
				marginTop = margins[0];
				marginRight = margins[1];
				marginLeft = margins[2];
				marginBottom = margins[3];
				break;
			default:
				logError("getMargins(): invalid number of margins!");
		}

		return [marginTop, marginRight, marginBottom, marginLeft];

	/* All other (non-Internet Explorer) browsers */
	} else if ("getComputedStyle" in window) {
		var margins = new Array(4);
		var sides = ["top", "right", "bottom", "left"];
		for (var i = 0; i < 4; i++) {
			margins[i] = parseInt(
				document.defaultView.getComputedStyle(element,
				null).getPropertyValue("margin-" + sides[i]), 10);
		}
		return margins;

	/* I have no idea what browsers do not support either mechanism. */
	} else {
		logError("getMargins(): fatal: don't know how to get margins!");
	}
}

/* Modifies anchors with rel="external" such that their target attribute is
 * "_blank", which is not standard compliant, but at least allows the code to
 * pass validation.
 */
function modifyExternalLinks() {
	if (!("getElementsByTagName" in document)) {
		return;
	}
	var anchors = document.getElementsByTagName("a");
	for (var i = 0 ; i < anchors.length; i++) {
		if (anchors[i].getAttribute("href") &&
			anchors[i].getAttribute("rel") === "external") {
			anchors[i].target = "_blank";
		}
	}
}

/* Automatically fix external links on load with modifyExternalLinks().
 */
if ("addEventListener" in window) {
	/* Firefox et al. */
	window.addEventListener("load", modifyExternalLinks, true);
} else {
	/* Internet Explorer */
	attachEvent("onload", modifyExternalLinks);
}

