/********************
* Input Validation 
********************/

/*
USAGE NOTES:
The function to call is checkInput(). Usually you'll need want to have 3 parameters.
The first parameter is an object where properties map to error messages. You can declare it like so:
	var rulesA =
	{
		"a"   : "We reject all strings that include the letter 'a'"
		"b"   : "We reject all strings that include the letter 'b'"
	}
	Actually, we convert the properties "a" and "b" to regexes first, so if you wanted, you could also do
	var rulesA =
	{
		"[a-z]"   : "We reject all strings that include lowercase letters"
	}
	When there are multiple properties, validation stops at the first error it encounters, so prioritize accordingly.
	
The second parameter is an element id. The element's "value" property gets validated, so you'll want to
pass in the id of a textfield or textarea.

An optional third parameter is another element id. The element's "innerHTML" gets updated with error messages.

An optional fourth parameter is a boolean. Set it to "false" if you don't want validation errors to update anything.
Otherwise, the error element will get updated with error messages, and both the input element and the error element
will get the "error" classname if there is an error.

checkInput() returns whether or not the input was valid.

See IntuitCorp\IntuitCorp_Build_QS39\get\website-building-software\index.cffhtml and
IntuitCorp\IntuitCorp_Build_QS39\get\site\javascript\domain_search.ptjs for full examples.


Sample use case:

<script type="text/javascript" src="/site/javascript/input_utils.js"></script>
<script type="text/javascript">
	var domainNameValidation =
	{
		"^-|-$"    : "We're sorry, but we can't begin or end a domain with hyphens.",
		"[^\\w-]"  : "Please use letters, numbers, or hyphens only.",
		"^.{0,1}$" : "Please enter a valid domain name of at least 2 characters."
	}
</script>
<style type="text/css">
	input.error { border: 2px solid red; }
	div.error { color: red }
</style>

<input id="inputID1" onchange="checkInput(domainNameValidation, 'inputID1','errorID1')" onkeyup="checkInput(domainNameValidation, 'inputID1','errorID1')"/>
<div id="errorID1"></div>

*/

/*
checkInput
------------
validationRules:	an object of regex string/error message pairs. see example above
inputID:		id of whatever element's "value" property you want validated. so actually works with both input fields and textarea
errorID:		optional. id for an element whose innerHTML will get updated with error messages (or cleared if there is no error)
bUpdateError:	optional, defaults to true. flags that the element with errorID should get updated, and also that both the inputID and errorID elements
			should have the "error" classname appended or removed depending on error status.
			set this to false if you want to do a validation check without side effects.
*/
function checkInput(validationRules, inputID, errorID, bUpdateError)
{
	var input = document.getElementById(inputID);
	var inputValueOverride = (hasClass(input, "ghost") ? "" : null);
	
	return checkBasic(validationRules, inputID, errorID, bUpdateError, inputValueOverride);
}

/*
checkBasic
------------
checkInput is a wrapper for this function.
generally speaking, if additional cases come up and we need to extend functionality, checkBasic should handle the extra parameters,
and a wrapper should be created to call checkBasic.

inputValueOverride: optional, and sort of internal use only. currently this allows for weird cases like validating input fields that also use ghost text.
*/
function checkBasic(validationRules, inputID, errorID, bUpdateError, inputValueOverride)
{
	var input = document.getElementById(inputID);
	
	var errorMsg;
	if (inputValueOverride != null)
	{
		errorMsg = validateInput(validationRules, inputValueOverride);
	}
	else
	{
		errorMsg = validateInput(validationRules, input.value);
	}
	
	if (bUpdateError == null || bUpdateError)
	{
		updateError(errorMsg, inputID, errorID);
	}
	return (errorMsg == "");
}

/*
validationRules is a set of regex/error message pairs.
If there is a non-null match for string.match(regex), the corresponding error message is returned.
Otherwise an empty string is returned.
*/
function validateInput(validationRules, value)
{
	
	var errorMessage = "";
	
	for (var exp in validationRules)
	{
		var regex = new RegExp(exp);
		var match = value.match(regex);
		if (match)
		{
			errorMessage = validationRules[exp];
			break;
		}
	}
	
	return errorMessage;
}

/*
Depending on whether or not errorMsg is empty, will add or remove the "error" classname
to the inputID and errorID elements. also sets the errorID element's innerHTML to errorMsg.
*/
function updateError(errorMsg, inputID, errorID)
{
	var hasError = (errorMsg != "");
	var classChange = (hasError ? addClass : removeClass);
	
	var input = document.getElementById(inputID);
	classChange(input, "error");
	
	if (errorID)
	{
		var errorElem = document.getElementById(errorID);
		if (errorElem)
		{
			/* Set the error message, or clear it if errorMsg is empty */
			errorElem.innerHTML = errorMsg; 
			classChange(errorElem, "error");
		}
	}
}


/********************
* Input field default text (aka Ghost Text)
********************/

/*
USAGE NOTES:
The following method is used for default text for input fields, displayed
when the field is empty and disappearing when receiving focus.
Note: call this after the element has been loaded (e.g. body.onload)
Note: calling this without jQuery will replace the input's existing
onblur and onfocus events.
*/

var ghostMap = new Object();

/*
ghostText
------------
id:		id of whatever element's "value" property you want to have default text. so actually works with both input fields and textarea
text:		the default text you want for this element
*/
function ghostText(id, text)
{
	ghostMap[id] = text;
	var elem = document.getElementById(id);
	
	if (typeof jQuery != "undefined")
	{
		jQuery(elem).focus(function() { ghostFocus(this); })
				.blur(function() { ghostBlur(this); });
	}
	else
	{
		elem.onfocus = function()
		{
			ghostFocus(this);
		}
		elem.onblur = function()
		{
			ghostBlur(this);
		}
	}
	
	/* Add the ghost class immediately if the input is empty or got prefilled by the browser with the same string as the default text */
	var value = elem.value;
	if (value == "" || value == text)
	{
		if (typeof jQuery != "undefined")
		{
			jQuery(elem).blur();
		}
		else if (elem.onblur)
		{
			elem.onblur();
		}
		addClass(elem, "ghost");
		elem.value = text;
	}
}

function ghostFocus(elem)
{
	if (hasClass(elem, "ghost"))
	{
		elem.value = "";
		removeClass(elem, "ghost");
	}
}

function ghostBlur(elem)
{
	if (elem.value == "")
	{
		addClass(elem, "ghost");
		elem.value = ghostMap[elem.id];
	}
}

/********************
* Other functions...
********************/

function enterKey(e)
{
	if (!e)
	{
		e = window.event;
	}
	if (e.keyCode == 13)
	{
		return true;
	}
	return false;
}

function hasClass(input, className)
{
	var classes = input.className.split(/\s+/);
	for (var i in classes)
	{
		if (classes[i] == className) return true;
	}
	return false;
}

function addClass(input, className)
{
	if (input.className == "")
	{
		input.className = className;
	}
	else if (!hasClass(input, className))
	{
		input.className += " " + className;
	}
}

function removeClass(input, className)
{
	var classes = input.className.split(/\s+/);
	for (var i = classes.length - 1; i >= 0; i--)
	{
		if (classes[i] == className) classes.splice(i, 1);
	}
	input.className = classes.join(" ");
}
