// TODO: move these functions to a global utils library javascript file...
function attachEventHandler(o, e, f) {
	if(o) {
		if(o.addEventListener) {
			o.addEventListener(e, f, false);
		} else if(o.attachEvent) {
			o.attachEvent("on" + e, f);
		}
	}
}

function getEvent(e) {
	if(e) {
		return e;
	} else if(window.event) {
		return window.event;
	}
}

function getEventSource(e, o) {
	if(e.srcElement) {
		return e.srcElement;
	} else {
		return o;
	}
}

function cancelEvent(e) {
	if(e) {
		e.cancelBubble = true;
		if(e.preventDefault) {
			e.preventDefault();
		} else {
			e.returnValue = false;
		}
	}
}

String.prototype.capitaliseFirstWord = function() {
	return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
}

// These two are not complete, but works fine for now
String.prototype.encode = function() {
	var s = this.replace(/</g, "&lt;");
	s = s.replace(/>/g, "&gt;");
	s = s.replace(/&/g, "&amp;");
	s = s.replace(/"/g, "&quot;");
	s = s.replace(/'/g, "&apos;");
	return s;
}

String.prototype.decode = function() {
	var s = this.replace(/&lt;/g, "<");
	s = s.replace(/&gt;/g, ">");
	s = s.replace(/&amp;/g, "&");
	s = s.replace(/&quot;/g, "\"");
	s = s.replace(/&apos;/g, "'");
	return s;
}

// This method does a standard escape and also escapes forward slashes. We may want to extend this further?
function escapeAll(str) {
	str = escape(str);
	str = str.replace(/\//g, "%2F%C2"); 
	return str;
}

function node_setTextValue(node, textValue) {
	if(node.nodeType == 3) {	// Text node
		node.nodeValue = textValue;
	} else {
		while(node.firstChild) {
			node.removeChild(node.firstChild);
		}
		node.appendChild(document.createTextNode(textValue));
	}
}
	

// -------------------------------------------------------------------------------------------------------------------------------------------------

document.ignoreButton = false;
document.formItems = new Array();
document.activeButtonId = null;

Array.prototype.resetRequiresValidation = function() {
	for(var i in this) {
		if (typeof(this[i]) == 'object' && this[i].requiresValidation) {
			this[i].requiresValidation = true;
		}
	}
}

function formItem(name) {
	this.validators = new Array();
	this.name = name;
	this.isMultiLine = false;
	this.requiresValidation = true;
	this.details = {};
}

// = CUSTOM VALIDATORS ==================================================================================================================================

// Regular expression validator
function RegExValidator(message, expression, details) {
	this.type = "RegExValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.expression = expression;
	
	this.validate = function(formItem, form) {
		if(_getFormElementValue(formItem) != "") {
			this.hasError = !this.expression.test(_getFormElementValue(formItem));
		} else {
			this.hasError = false;
		}
		return !this.hasError;
	};
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------

// Is Required validator
function IsRequiredValidator(message, details) {
	this.type = "IsRequiredValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.validate = function(formItem, form) {
		this.hasError = (_getFormElementValue(formItem).trim() == "");
		return !this.hasError;
	};
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------

// Confirmation validator
function ConfirmValidator(message, confirmName, ignoreCase, details) {
	this.type = "ConfirmValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.confirmName = confirmName;
	this.ignoreCase = ignoreCase;
	
	this.validate = function(formItem, form) {
		var oConfirmValue = form.elements[this.confirmName];
		if(oConfirmValue) {
			if(this.ignoreCase) {
				this.hasError = (oConfirmValue.value.toLowerCase() != _getFormElementValue(formItem).toLowerCase());
			} else {
				this.hasError = (oConfirmValue.value != _getFormElementValue(formItem));
			}
		} else {
			this.hasError = false;
		}
		return !this.hasError;
	};
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------

// Compare validator
function CompareValidator(message, compareName, expression, match, validator, details) {
	this.type = "CompareValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.compareName = compareName;
	if(expression != "") {
		this.expression = new RegExp(expression);
	}
	this.match = match;
	this.validator = validator;
	
	this.validate = function(formItem, form) {
		if(this.formItem.isMultiLine) {
			if(!this.baseName) {
				this.baseName = this.compareName;
			}
			
			var formItemName = new String(formItem.name);
			var iIndex = formItemName.indexOf("_");
			if(iIndex != -1) {
				this.compareName = this.baseName + "_" + formItemName.substr(iIndex + 1);
			} else {
				return;
			}
		}
		
		var oCompareValue = form.elements[this.compareName];
		this.hasError = false;
		if(oCompareValue) {
			var sValue;
			if(!oCompareValue.type) {
				sValue = _getRadioValue(oCompareValue);
			} else {
				sValue = oCompareValue.value;
			}
	
			if((this.expression && this.expression.test(sValue)) || (this.match == sValue)) {
				this.hasError = !this.validator.validate(formItem, form);
				if(this.hasError) {
					this.message = this.validator.message;
				}
			}
		}
		return !this.hasError;
	};
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------

// Number validator
function NumberValidator(message, decimalPlaces, allowZero, allowNegative, details) {
	this.type = "NumberValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.decimalPlaces = decimalPlaces;
	this.allowZero = allowZero;
	this.allowNegative = allowNegative;
	
	this.validate = function(formItem, form) {
		this.hasError = false;
	
		var val = _getFormElementValue(formItem);
		
		if(val != "") {
			if(isNaN(val)) {
				this.hasError = true;
			} else {
				if((!this.allowNegative && val < 0) ||
						(!this.allowZero && val == 0)) {
					this.hasError = true;
				} else {
					var decimalIndex = val.indexOf(".");
					if(decimalIndex != -1 && this.decimalPlaces == 0) {
						this.hasError = true;
					} else {
						var d = val.substr(decimalIndex + 1);
						if(decimalIndex != -1 && d.length > this.decimalPlaces) {
							this.hasError = true;
						}
					}
				}
			}
		}
		
		return !this.hasError;
	};
}

// ------------------------------------------------------------------------------------------------------------------------------------------------------

// Number range validator
function RangeValidator(message, min, max, details) {
	this.type = "RangeValidator";
	this.formItem; // Parent
	this.hasError = false;
	this.message = message.decode();
	this.details = details;
	
	this.max = max;
	this.min = min;
	
	this.validate = function(formItem, form) {
		this.hasError = false;
	
		var val = _getFormElementValue(formItem);
		
		if(val != "") {
			if(isNaN(val)) {
				this.hasError = true;
			} else {
				if(val < this.min || val > this.max) {
					this.hasError = true;
				}
			}
		}
		
		return !this.hasError;
	};
}

// ======================================================================================================================================================

function _getFormElementValue(formElement) {
	if(formElement.type == "checkbox") {
		if(formElement.checked) {
			return formElement.value;
		} else {
			return "";
		}
	} else if(formElement.type == "radio") {
		return _getRadioValue(document.getElementsByName(formElement.name));
	} else if(formElement.type == "textarea") {
		// Ensure that a new line in a textarea is always treated as 2 characters in the js...
		return formElement.value.replace(/\r{0,}\n/g, "\r\n");
	}
	
	return formElement.value;
}

function _getRadioValue(o) {
	if(o) {
		for(var i = 0; i < o.length; i++) {
			if(o[i].checked) {
				return o[i].value;
			}
		}
	}
	return "";
}


// Add Regular expression validator
function v(name, validator, isDisabled, details) {
	_appendValidator(name, validator, false, isDisabled == true, details);
}

// Multiline validators
function vml(name, validator, isDisabled, details) {
	_appendValidator(name, validator, true, isDisabled == true, details);
}

function _appendValidator(name, validator, isMultiLine, isDisabled, details) {
	if(document.getElementById && document.getElementsByName) {
		var fi = document.formItems[name];
		if(!fi) {
			fi = new formItem(name);
			fi.isMultiLine = isMultiLine;
			fi.requiresValidation = !isDisabled;
			if(details) {
				fi.details = details;
			}
			document.formItems[name] = fi;
		}
		validator.formItem = fi;
		fi.validators[fi.validators.length] = validator;
	}
}

// Attach Form Events
function afe(formId, associatedButtons) {
	if(document.getElementById && document.getElementsByName) {
		var oForm = document.getElementById(formId);
		if(oForm) {
			attachEventHandler(oForm, "submit", validateForm);
			
			if(associatedButtons) {
				var oFormItem =  null;
				for(var i = 0; i < associatedButtons.length; i++) {
					oFormItem = document.getElementById(associatedButtons[i].formItem);
					oFormItem.setAttribute("associatedButtonId", associatedButtons[i].associatedButtonId);
					if(oFormItem) {
						attachEventHandler(oFormItem, "keypress", function(e) {
							var oEvent = getEvent(e);
							var oSource = getEventSource(e, this);
							var o = document.getElementById(oSource.getAttribute("associatedButtonId"));
							if(o) {
								if(oEvent.keyCode == 13) {
									o.click();
									cancelEvent(oEvent);
								}
							}
						});
					}
				}
			}
			
			// Events can be attached to individual form elements here...
			try {
				afeCallback(formId, oForm)
			} catch(e) {
			}
		}
	}
}

function ignoreValidation() {
	document.ignoreButton = true;
}

// Set button name - sets document.activeButtonId to the ID of the button pressed
function sbn(o) {
	document.activeButtonId = o.name;
}

function validateFormCallbackArgs(form, event) {
	this.form = form;
	this.event = event;
	
	this.control;
	this.message;
}

function validateForm(e) {
	if(document.ignoreButton) {
		document.ignoreButton = false;
		return;
	} else {
		document.ignoreButton = false;
	}
	
	document.isValidating = true;
	
	var validator;
	var fi;
	var firstError;
	var message = "";

	var event = getEvent(e);
	var oForm = getEventSource(e, this);
	var oErrorMsg = document.getElementById("h-error");
	if(oErrorMsg) {
		oErrorMsg.style.display = "none";
	}

	var errorCount, totalErrorCount = 0, oFormElement, validatorMessage, radioElementNames = "", bValidate, iIndex, baseName, formElementName, fi, runOnButtonId = null;
	for(var i = 0; i < oForm.elements.length; i++) {
		oFormElement = oForm.elements[i];
		if(oFormElement.name && oFormElement.type != "hidden" && oFormElement.type != "button" && oFormElement.type != "submit") {
			bValidate = true;
			if(oFormElement.type == "radio") {
				if(radioElementNames.indexOf("[" + oFormElement.name + "]") == -1) {
					radioElementNames += ("[" + oFormElement.name + "]");
				} else {
					bValidate = false;
				}
			}
			if(bValidate) {
				fi = null;
				formElementName = new String(oFormElement.name);
				iIndex = formElementName.indexOf("_");
				if(iIndex != -1) {
					baseName = formElementName.substr(0, iIndex);
					if(baseName) {
						fi = document.formItems[baseName];
					}
				}
				if(!fi) {
					fi = document.formItems[oFormElement.name];
				}
				if(fi && fi.requiresValidation == true && fi.validators) {
					errorCount = 0, validatorMessage = "";
					for(var ii = 0; ii < fi.validators.length; ii++) {
						runOnButtonId = (fi.validators[ii].details ? "confirm-" + fi.validators[ii].details.runOnButtonId : null);
						if(!runOnButtonId || (runOnButtonId == document.activeButtonId)) {
							fi.validators[ii].validate(oFormElement, oForm);
							if(fi.validators[ii].hasError) {
								errorCount++;
								totalErrorCount++;
	
								if(!validatorMessage) {
									validatorMessage = fi.validators[ii].message;
								}
			
								if(!firstError) {
									firstError = oFormElement;
									message = validatorMessage;
								}
							}
						}
					}

					_setControlMessage((errorCount > 0), oFormElement, validatorMessage);

					if(fi.details.relatedFormItemName) {
						_setControlMessage((errorCount > 0), oForm.elements[fi.details.relatedFormItemName], "");
					}
				}
			}
		}
	}

	if(!firstError) {
		try {
			var args = new validateFormCallbackArgs(oForm, event);
			validateFormCallback(args)
			firstError = args.control;
			if(args.message) {
				_setControlMessage(true, firstError, args.message);
			}
		} catch(e) {
		}
	}
	
	if(firstError) {
		if(message) {
			var msg = message;
			
			if("“".length > 1 && String.fromCharCode) {	// Check to see of the browser fully supports UTF-8 in javascript...
				// If not then replace the “ and ” character to a simple " character...
				msg = msg.replace(String.fromCharCode(8220), "\"");
				msg = msg.replace(String.fromCharCode(8221), "\"");
			}
			
			alert(msg);
		}
		firstError.focus();

		cancelEvent(event);
	}

	try {
		postValidationCallBack(totalErrorCount > 0, oForm);
	} catch(e) {
	}

	document.isValidating = false;
}

function clearControlMessage(formElement) {
	if(formElement) {
		_setControlMessage(false, formElement);
	}
}

function _setControlMessage(hasError, oFormElement, validatorMessage) {
	var id;
	var oControls = document.getElementsByName(oFormElement.name);
	if(oControls && oControls.length > 1) {
		var index;
		var i = oFormElement.id.indexOf("-");
		if(i > 0) {
			index = parseInt(oFormElement.id.substr(i + 1));
			if(isNaN(index)) {
				index = null;
			}
		}
		id = oFormElement.name + "-" + index + "-emsg";
	} else {
		id = oFormElement.name + "-emsg";
	}
	var o = document.getElementById(id);

	if(hasError) {
		if(oFormElement.type == "radio") {
			oFormElement.parentNode.parentNode.className = "r error";
		} else {
			oFormElement.parentNode.className = "error";
		}
		if(validatorMessage != "") {
			if(!o) {
				o = document.createElement("p");
				o.id = id;
				o.className = "error-m";
				if(oFormElement.type == "radio") {
					oFormElement.parentNode.parentNode.parentNode.appendChild(o, oFormElement.parentNode.parentNode);
				} else {
					oFormElement.parentNode.parentNode.insertBefore(o, oFormElement.parentNode);
				}
			}
			if(o.style) {
				o.style.display = "block";
			}
	
			node_setTextValue(o, validatorMessage);
		}
	} else {
		if(oFormElement.type == "radio") {
			oFormElement.parentNode.parentNode.className = "r";
		} else {
			oFormElement.parentNode.className = "";
		}
		
		if(o) {
			o.style.display = "none";
		}
	}
}


