/**
 * formgen.js
 *
 * All functions associated with the FormGen PHP class.
 * - Validation
 * - Required element display
 * - Alerting of error messages
 * - Preventing form submission if errors
 *
 * Uses the Prototype javascript class
 *
 * @package FormGen
 * @see prototype.js <http://www.prototypejs.or>
 * @author Joshua Riddle <josh@theriddlebrothers.com>
 * @version 2.0
 */
 
/**
 * FormGen base class
 *
 * @package FormGen
 */
FormGen = new function() {
	this.fields;
	this.warnings = Array();
	
	/**
	 * Prepare and initialize necessary form elements
	 *
	 * @param	string	DOM id of form
	 * @return	void
	 */
	this.init = function(formId) {
		FormGen.prepareTooltips(formId);
		Event.observe(formId, "submit", FormGen.validate);
	}
	
	/**
	 * Bind tooltip events to display on element focus
	 *
	 * @param	string	DOM id of form
	 * @return	void
	 */
	this.prepareTooltips = function(formId) {
		var form = document.getElementById(formId);
		
		// Bind tooltips to radio/checkbox field groups
		var fieldsets = form.getElementsByTagName("fieldset");
		for (var i=0; i < fieldsets.length; i++) {
			var childFieldsets = fieldsets[i].getElementsByTagName("fieldset");
			if (childFieldsets.length > 0) {
				// Found radio/checkbox group.
				// Get any span elements that exist as a child of the fieldset
				for (var j=0; j < childFieldsets.length; j++) {
					var toolTip = childFieldsets[j].getElementsByTagName("span")[0];	// only one span per group
					if (toolTip != undefined) {
						childFieldsets[j].onfocus = function() {
							this.getElementsByTagName("span")[0].style.display = "inline";
						}
						childFieldsets[j].onblur = function() {
							this.getElementsByTagName("span")[0].style.display = "none";
						}
					}
					// Set focus on child checks/radio buttons for IE
					var chks = childFieldsets[j].getElementsByTagName("input");
					for (k=0; k < chks.length; k++) {
						chks[k].onfocus = function() {
							this.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("span")[0].style.display = "inline";
						}
						chks[k].onblur = function() {
							this.parentNode.parentNode.parentNode.parentNode.getElementsByTagName("span")[0].style.display = "none";
						}
					}
				}
			}
		}
		
		
		
		// Bind tooltips to text input fields
		var fields = form.getElementsByTagName("p");
		
		for (var i=0; i < fields.length; i++) {
			
		
			var curTip = $(fields[i]).select('span.hint')[0];	
			
			if (curTip != undefined) {
				
				// Found span element. Select the input to show this span for
				var input = fields[i].getElementsByTagName("input")[0];
				
				if (input == undefined) {
					// text area
					input = fields[i].getElementsByTagName("textarea")[0];
				}
				if (input == undefined) {
					// select elements
					input = fields[i].getElementsByTagName("select")[0];
				}
				
				if (input != undefined) {
					input.onfocus = function() {
						var p = this.parentNode.parentNode;
						$(p).select("span.hint")[0].style.display = "inline";
					}
					
					input.onblur = function() {
						var p = this.parentNode.parentNode;
						$(p).select("span.hint")[0].style.display = "none";
					}
				}
			}
		}
		
	}

	/**
	 * Window load event
	 * 
	 * @param	function	func	function to call when window loads
	 * @return	void
	 */
	this.addLoadEvent = function(func) {
		Event.observe(window, 'load', func);
	}
	
	this.validate = function(e) {
		FormGen.warnings.clear();
		for (i=0; i < FormGen.fields.length; i++) {
			for (j=0; j < FormGen.fields[i].validators.length; j++) {
				var field = FormGen.fields[i];
				var validator = FormGen.fields[i].validators[j];
				switch(FormGen.fields[i].validators[j].type) {
					case 'requiredfieldvalidator':
						FormGen.Validators.checkRequiredField(field, validator.warning);	
						break;
					case 'numericvalidator':
						FormGen.Validators.checkNumeric(field, validator.warning);
						break;
					case 'regexpvalidator':
						FormGen.Validators.checkRegularExp(field, validator.warning, field.expression);
						break;
					default:
						break;
				}
			}
		}
		if (FormGen.warnings.length > 0) {
			Event.stop(e);		
			FormGen.displayWarnings();
		}
	}
	
	/**
	 * Display error messages for warnings
	 *
	 * @return void;
	 */
	this.displayWarnings = function() {
		var warningText = '';
		for (i = 0; i < FormGen.warnings.length; i++) {
			warningText += FormGen.warnings[i] + '\n';
		}
		alert(warningText);
	}
	
	/**
	 * Add warning message to display
	 *
	 * @param	string	msg	message to add to warning messages
	 * @return	void
	 */
	this.addWarning = function(msg) {
		FormGen.warnings.push(msg);	
	}
}


/**
 * Validation object of FormGen
 *
 * @package	FormGen
 */
FormGen.Validators = new function() {
	
	/**
	 * Verify specified field is not empty (has input)
	 *
	 * @param	Field	field	element object (JSON output by FormGen PHP class)
	 * @param	string	warning	warning message to add to form if validation fails
	 */
	this.checkRequiredField = function(field, warning) {
		if ((field.type == 'checkboxlist') || (field.type == 'radiobuttonlist')) {
			var el = $(field.id);
			for (itemIndex = 0; itemIndex < field.items.length; itemIndex++) {
				var opt = $(field.items[itemIndex].id);
				if ((opt != null) && (opt.checked)) {
					return;	
				}
			}
			FormGen.addWarning(warning);
		} else if (field.type == 'listmenu') {
			var el = $(field.id);
			if ((el.selectedIndex < 0) || (el.options[el.selectedIndex].value.empty())) {
				// No items selected - "0" if multiple, -1 if none
				FormGen.addWarning(warning);
			}
		} else {
			// Check text input or text area for values
			var el = $(field.id);
			if (el != undefined) {
				if (el.value.blank()) {
					FormGen.addWarning(warning);
				}
			}
		}
	}
	
	
	/**
	 * Verify specified field is a number (decimal and negative included)
	 *
	 * @param	Field	field	element object (JSON output by FormGen PHP class)
	 * @param	string	warning	warning message to add to form if validation fails
	 */
	this.checkNumeric = function(field, warning) {
		var el = $(field.id);
		var valid = "^[0-9\.-]*$";
		if (!FormGen.Validators._testRegExp(valid, el.value.strip(), null)) {
			FormGen.addWarning(warning);
		}
	}
	
	
	/**
	 * Verify specified field is a valid (in format) email address
	 *
	 * @param	Field	field	element object (JSON output by FormGen PHP class)
	 * @param	string	warning	warning message to add to form if validation fails
	 */
	this.checkEmailAddress = function(field, warning) {
		// @todo create validation script
	}
	
	
	/**
	 * Verify specified field matches specified regular expression string
	 *
	 * @param	Field	field		element object (JSON output by FormGen PHP class)
	 * @param	string	warning		warning message to add to form if validation fails
	 * @param	string	expression	regular expression to validate against
	 */
	this.checkRegularExp = function(field, warning, expression) {
		var el = $(field.id);
		var valid = expression;
		if (!FormGen.Validators._testRegExp(valid, el.value.strip(), null)) {
			FormGen.addWarning(warning);
		}
	}

	/**
	 * Private method to test a regular expression.
	 *
	 * @param	string	expression	regular expression string to check
	 * @param	string	value		value to check against regular expression
	 * @return	boolean	if the value contains a match
	 */
	this._testRegExp = function(expression, value, flags) {
		if (flags == null) {
			reg = new RegExp(expression);
		} else {
			reg = new RegExp(expression, flags);
		}
		return reg.test(value);
	}
}

