function isNumeric(sText)
{
   return !!(sText.match(/^-?([0-9]+(\.[0-9]+)?|\.[0-9]+)$/));
}


function ErrorHolder(input, div) {
	function removeError() { if (div.firstChild) div.removeChild(div.firstChild); }
	this.clear = function() { 
		removeError();
		input.style.backgroundColor = "";
	}
	this.setError = function(message) {
		removeError();
		div.appendChild(document.createTextNode(message));
		input.style.backgroundColor = "#ffdddd";
	}
	this.setWarning = function(message) {
		removeError();
		div.appendChild(document.createTextNode(message));
		input.style.backgroundColor = "#fff3c6";
	}
}


function initNumericField(name)
{
	var spec = attrInfo[name];
	
	// Set unknown values to NaN -- this forces the "recheck" tests to fail
	if (spec.absMin == null) spec.absMin = NaN;
	if (spec.recMin == null) spec.recMin = NaN;
	if (spec.absMax == null) spec.absMax = NaN;
	if (spec.recMax == null) spec.recMax = NaN;
	
	var input = document.getElementById(name);
	var select = nextElem(input);
	var div = nextElem(select);
	
	// Some attributes don't have the drop-down list
	if (select.nodeName != "SELECT")
	{
		div = select;
		select = null;
	}
	
	var err = new ErrorHolder(input, div);
	
	function recheck() {
		
		if (input.value == "")
			if (spec.isReq) return err.setError("Required");
			else return err.clear();
		
		if (!isNumeric(input.value)) return err.setError("Invalid, must be numeric");
		
		var conversionRatio = select ? conversions[+select.value] / spec.defConversion : 1;
		
		var val = parseFloat(input.value) * conversionRatio;
		
		if (val < spec.absMin) // check against max val, if supplied
		{
			err.setError("Invalid input - minimum is " + spec.absMin/conversionRatio);
			return;
		}
		
		if (val > spec.absMax) // check against max val, if supplied
		{
			err.setError("Invalid input - maximum is " + spec.absMax/conversionRatio);
			return;
		}
		
		if (val < spec.recMin) // check against rec min, if supplied
		{
			err.setWarning("Recommended minimum is " + spec.recMin/conversionRatio);
			return;
		}
		
		if (val > spec.recMax) // check against max val, if supplied
		{
			err.setWarning("Recommended maximum is " + spec.recMax/conversionRatio);
			return;
		}
		err.clear();
	}
	
	handle(input, "onblur", recheck);
	if (select)
	{
		handle(select, "onkeypress", recheck);
		handle(select, "onclick", recheck);
		handle(select, "onchange", recheck);
	}
}

function initRequiredField(name)
{
	var input = document.getElementById(name);
	var div = nextElem(input);
	var err = new ErrorHolder(input, div);
	
	function recheck()
	{
		if (input.value == "") return err.setError("Required");
		err.clear();
	}
	handle(input, "onblur", recheck);
}

function registerNumericField(name) {
	window.addonload(function() { initNumericField(name); });
}

function registerRequiredField(name) {
	window.addonload(function() { initRequiredField(name); });
}
