/*
* FormBuilder Javascript Class (clientValidation.js)
*
* This file contains the main FormBuilder JS class used to
* add client-side validation to a form.
*
* You shouldn't have to change anything in this file
* because it's all basic code that's used by the script.
*
* There shouldn't be any conflicts with any custom scripts
* either, since no namespace is used at all, except the 'FormBuilder'
* namespace.
*/

var formBuilderCancelLast = false;

function FormBuilder(formName) {
	// Get form element
	var el = document.forms.namedItem(formName);

	if (el == null) {
		throw('Unable to initialize validation for "' + formName + '". Can\'t find form. (FormBuilder)');
		return false;
	}

	// Save as property
	this.formElement = el;

	// other properties of this class
	this.rules = [];
	this.errors = [];
	this._isValid = false;

	// Set onSubmit event handler
	var oThis = this;
	this.addEvent(el, 'submit', function(event) { oThis.validate(event); }, false);
}

// holds the validation status of the form
FormBuilder.prototype.isValid = function() { return this._isValid; }

FormBuilder.prototype.addRules = function(ruleList) {
	// loop through each rule and add it to the array
	for(var i=0; i < ruleList.length; i++) {
		var ruleObj = ruleList[i].rule;
		var errorIds = ruleList[i].errorIds;

		// set some properties on the rule object
		ruleObj.formElement = this.formElement;
		ruleObj.parent = this;

		// add to array
		this.rules[this.rules.length] = {"rule": ruleObj, "errorIds": errorIds};
	}

	return true;
}

FormBuilder.prototype.validate = function(event) {
	formBuilderCancelLast = false;
	var cancelSubmit = false

	// hide each error
	this.hideAllErrors();

	var showList = [];

	// loop through each rule and check if it's valid
	for(var i=0; i < this.rules.length; i++) {
		var ruleObj = this.rules[i]['rule'];
		var errorIds = this.rules[i]['errorIds'];

		// rule validates or not?
		if (ruleObj.validate() != true) {
			// cancel form submission and show errors
			cancelSubmit = true;
			showList = showList.concat( errorIds );
		}
	}

	// show errors that need to be shown
	this.showErrors(showList);

	// set valid status of form (opposite of cancelSubmit)
	this._isValid = !cancelSubmit;

	// Stop submission of form?
	if (cancelSubmit == true) {
		formBuilderCancelLast = true;

		event.returnValue = false;
		event.cancelBubble = true;

		// use try/catch, because IE throws an error
		// because it doesn't support this
		try {
			event.preventDefault();
			event.stopPropagation();
		}
		catch (e) { }
	}
}

FormBuilder.prototype.hideAllErrors = function() {
	// loop through each error
	// get its element
	// and hide it
	for(id in this.errors) {
		var error = this.errors[id];

		var el = document.getElementById(error.htmlId);
		if (typeof(el) != 'object' || el == null) { continue; }

		el.style.display = 'none';
	}

	return true;
}

FormBuilder.prototype.showErrors = function(list) {

	for(var i=0; i < list.length; i++) {
		var id = list[i];

		// try to get error
		var error = this.errors[id];
		if (typeof(error) != 'object' ){ continue; }

		// get error element
		var oldElement = document.getElementById(error.htmlId);
		if (typeof(oldElement) != 'object' || oldElement == null) { continue; }

		// create new error element
		var newEl = document.createElement(error.tag);
		newEl.id = oldElement.id;
		newEl.className = error.className;
		newEl.style.cssText = error.style;
		newEl.innerHTML = oldElement.innerHTML;

		// insert new element and remove old element
		oldElement.parentNode.insertBefore(newEl, oldElement);
		oldElement.parentNode.removeChild(oldElement);

		// automatically scroll to first error (or actually: slightly before the error to show context)
		//DISABLED
		if (false && i == 0) {
			var scrollHeight = this.getElementY(newEl) - 150;
			if (scrollHeight < 0) {
				scrollHeight = 0;
			}

			window.scrollTo(0, scrollHeight);
		}
	}

	return true;
}

FormBuilder.prototype.addErrors = function(errArray) {
	this.errors = errArray;
}

FormBuilder.prototype.hideError = function(errorId) {
	this.changeErrorDisplay(errorId, 'none');
}

FormBuilder.prototype.showError = function(errorId) {
	this.changeErrorDisplay(errorId, 'inline');
}

FormBuilder.prototype.changeErrorDisplay = function(errorId, newDisplay) {
	// make sure argument is an array
	if (typeof(errorId) != 'object') {
		errorId = [errorId];
	}

	for(var i=0; i < errorId.length; i++) {
		var id = errorId[i];

		// try and get error element
		var el = document.getElementById(id);
		if (typeof(el) != 'object' || el == null) { continue; }

		el.style.display = newDisplay;
	}

	return true;
}

FormBuilder.prototype.addEvent = function(elm, evType, fn, useCapture) {
	if (elm.addEventListener) {
		elm.addEventListener(evType, fn, useCapture);
		return true;
	}
	else if (elm.attachEvent) {
		var r = elm.attachEvent('on' + evType, fn);
		return r;
	}
	else {
		elm['on' + evType] = fn;
	}
}

FormBuilder.prototype.getValue = function(fieldName, defaultValue) {
	// default value has been passed?
	if (typeof(defaultValue) == 'undefined') {
		var defaultValue = null;
	}

	// try and get element; return default value if it can't be found
	var el = this.formElement.elements[fieldName];
	if (el == null) { return defaultValue; }

	// textarea or select? that's easy, just use .value property
	if (el.nodeName == 'TEXTAREA' || el.nodeName == 'SELECT') {
		return el.value;
	}

	// text field? also easy
	if (el.nodeName == 'INPUT' && (el.type == 'text' || el.type == 'hidden' || el.type == 'password' || el.type == '')) {
		return el.value;
	}

	// checkbox or input; loop through each item and find checked value
	for (var i=0; i < el.length; i++) {
		if (el[i] == null) continue;

		if (el[i].checked == true) {
			return el[i].value;
		}
	}

	// single checkbox or radio button? checked => return value
	if (el.checked && el.checked == true) {
		return el.value;
	}

	// unknown element or no value; just return default value
	return defaultValue;
}

FormBuilder.prototype.getElementY = function(element) {
	var iReturnValue = 0;
	while( element != null ) {
		iReturnValue += element.offsetTop;
		element = element.offsetParent;
	}

	return iReturnValue;
}