/******************************************************
 * Form Validate
 * Version 1.0
 *
 * Copyright (c) 2006 Nortel
 * by Jason Vincent
 * (905) 863-1123 (ESN 333-1123)
 * jayv@nortel.com
 * 
 * Usage:  	<label for="<element name>" fv_required="yes" [fv_format="NUMERIC"]>
 *		<button onClick="if(fv_validate(<formId>)){submit();}else{return false;}">Submit</button>
 *
 * Aug 22 2006: Initial version
 * see tutorial for change history
 *******************************************************/


/*******************************************************
 ** Global variables
 *******************************************************/
var fv_IMG_PATH 			= 'images/';  // call fv_setImgPath() to change this value
var fv_REQSTRING			= '';	// this is place holder - do not set here - see fv_setImgPath
var fv_ERRORSTRING		= '';	// this is place holder - do not set here - see fv_setImgPath


/*******************************************************
 ** Default Formats
 ** (for new formats prefix must be fv_ + the format name)
 *******************************************************/

var fv_ALPHANUMERIC	= /^[0-9a-zA-Z]+$/; // no spaces
var fv_ALPHA 		= /^\D+$/;
var fv_NUMERIC		= /^[0-9]+$/;
var fv_ZIPPOSTAL		= /^\d{5}-\d{4}|\d{5}|[A-Z]\d[A-Z]([ ]?)\d[A-Z]\d$/i;
var fv_EMAIL		= /^[\w\.-]+@[\w\.-]+\.[a-zA-Z]+$/;
var fv_FLOAT		= /^\d+\.\d+/;
var fv_IPV4			= /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
var fv_PASSWORD		= /^\w*(?=\w{8})(?=\w*\d)(?=\w*[a-z])(?=\w*[A-Z])\w*$/; // min 8 chars - must contain at least 1 upper, 1 lower and 1 number
var fv_MAC			= /^([0-9a-fA-F][0-9a-fA-F]:){5}([0-9a-fA-F][0-9a-fA-F])$/;

/*******************************************************
 ** MAIN
 *******************************************************/

// set image path (and create img tag markup)
fv_setImgPath();

// call init onload
if (window.addEventListener) {
	window.addEventListener( "load", fv_init, false );
} else if (window.attachEvent) {  // for ms
	window.attachEvent( "onload", fv_init );
}


/*******************************************************
 ** Main Validate Function
 *******************************************************/
function fv_validate(formId){
	var supplied=0; // flag for tracking whether we found an invalid field
	var invalid=0; // error counter
	var labels = document.getElementsByTagName('LABEL');
	// loop through labels
	labelLoop:
	for(var l = 0; l < labels.length; l++){
	
		var elem = fv_getElementByLabel(labels[l],formId);
		if (!elem){ continue;}
		
		// getElementsByName can return more than one value, so loop through and set a 
		// flag if the field was supplied
		elementLoop:
		for (i=0; i<elem.length; i++){
			// check if a value was supplied for this required field
			switch (elem[i].type){
				case "text" : case "textarea" : case "password" :
					// for text fields, we want to validate format
					if (labels[l].getAttribute('fv_format')){
						if(elem[i].value != ""){ // if value was supplied
							supplied = fv_isValidFormat(elem[i],labels[l]); // returns 1 if valid, 0 if not
						}else{ // no value was supplied
							if (labels[l].getAttribute('fv_required') != "yes"){
								supplied=1;
							}
						}
					}else{
						if (labels[l].getAttribute('fv_required') != "yes"){
							continue labelLoop;
						}else{
							if (elem[i].value != "") {
								supplied=1;
							}
						}
					}
					break;
				case "select-one" :
					// skip this element if fv_required is not "yes"
					if (labels[l].getAttribute('fv_required') != "yes"){
						continue labelLoop;
					}
					
					// break if select has no options
					if(elem[i].length == 0){break;}					
					
					if(elem[i].options[elem[i].selectedIndex].value){ supplied=1; }
					break;
				case "select-multiple" :
					// skip this element if fv_required is not "yes"
					if (labels[l].getAttribute('fv_required') != "yes"){
						continue labelLoop;
					}
					for (var m=elem[i].options.length-1; m >= 0;m--){
						if (elem[i].options[m].selected){
							supplied=1;
						}
					}					
					break;
				case "radio" : case "checkbox" :
					// skip this element if fv_required is not "yes"
					if (labels[l].getAttribute('fv_required') != "yes"){
						continue labelLoop;
					}
					if(elem[i].checked){supplied=1; }
					break;
			}
		}
		
		// if no value was supplied (or value was invalid) change the image from denote to alert
		// otherwise, set it back to denote (incase the field was corrected)
		if (!supplied){
			labels[l].innerHTML = labels[l].innerHTML.replace(/WebIcon_RequiredField_denote/i,"WebIcon_RequiredField_alert");
			labels[l].innerHTML = labels[l].innerHTML.replace(/Required Field/i,"Missing or Invalid Format");
			invalid++;
		}else{
			// if field is not required but fv_format is specified and format is valid, then clear the image
			if (labels[l].getAttribute('fv_required') != "yes"){
				labels[l].innerHTML = labels[l].innerHTML.replace(/<img.*WebIcon_RequiredField.*>/gi,"");
			}else{
				labels[l].innerHTML = labels[l].innerHTML.replace(/WebIcon_RequiredField_alert/i,"WebIcon_RequiredField_denote");
				labels[l].innerHTML = labels[l].innerHTML.replace(/Missing or Invalid Format/i,"Required Field");
			}
		}
		supplied=0;
	}
	if(invalid){
		alert('INVALID VALUES: ' + invalid + '\n\nPlease correctly complete the denoted fields and try again.');
		return false;
	}else{
		return true;
	}
}


/***********************************************************
 ** init function - adds required field denotation to labels
 ***********************************************************/
function fv_init() {
	var labels = document.getElementsByTagName('LABEL');
	for(var l = 0; l < labels.length; l++){
		// clear existing images so we start fresh
		labels[l].innerHTML = labels[l].innerHTML.replace(/<img.*WebIcon_RequiredField.*>/gi,"");
		if (labels[l].getAttribute('fv_required') == 'yes'){
			labels[l].innerHTML = fv_REQSTRING + labels[l].innerHTML;
		}
		
		// see if fv_format is defined, if so attach onblur
		if (labels[l].getAttribute('fv_format')){
			// figure out the associated element for this label (comes back as array)
			elem = fv_getElementByLabel(labels[l]);
			for (i=0; i<elem.length; i++){
				// if its a text field attach an on blur function
				if (elem[i].type == "text" || elem[i].type == "textarea" || elem[i].type == "password"){
					elem[i].onblur = fv_validateFormatOnBlur;
				}
			}
		}
	}
}


/***********************************************************
 ** figure out the associated element by using the "for" attribute of the label.
 ** formId (optional): if element does not belong to the form id provided, return false.
 ** returns an array of elements as elements are retreived by Name not ID
 ***********************************************************/
function fv_getElementByLabel(label,formId){
	var elem;
	if (label.getAttribute('htmlFor')){ // IE
		elem = document.getElementsByName(label.getAttribute('htmlFor'));
		// if a formId was provided, then we want to ignore the element if it
		// does not belong to the form in question (we could have multiple forms)
		if(formId){
			//if (!eval('document.'+formId+'.'+label.getAttribute('htmlFor'))){
			if (formId != label.form.id){
				return false;
			}
		}
	}else{
		if (label.getAttribute('for')){
			elem = document.getElementsByName(label.getAttribute('for'));
			// if a formId was provided, then we want to ignore the element if it
			// does not belong to the form in question (we could have multiple forms)
			if(formId){
				if (formId != label.form.id){
					return false;
				}
			}
		}
	}
	return elem;
}


/***********************************************************
 ** gets label by element 
 ** labels may not have an id or name, so we loop through them
 ** and match on the for/htmlfor attribute
 ***********************************************************/
function fv_getLabelByElement(elem){
	var label;
	var labels = document.getElementsByTagName('LABEL');
	// loop through labels
	for(var l = 0; l < labels.length; l++){
		if (labels[l].getAttribute('htmlfor') == elem.name || labels[l].getAttribute('for') == elem.name){
			return labels[l];
			break;
		}
	}
}


/***********************************************************
 ** Checks if field value matches the specified format or regex 
 ** returns 1 if regex matches, 0 if not
 ***********************************************************/
function fv_isValidFormat(elem,label){
	var RegEx;
	var isRegEx = /^\//;
	// if a regex was supplied as the format, use it
	// otherwise, evaluate one of the predefined types
	if (isRegEx.test(label.getAttribute('fv_format'))){
		RegEx = eval(label.getAttribute('fv_format'));
	}else{
		RegEx = eval('fv_'+label.getAttribute('fv_format'));
	}
	if (RegEx.test(elem.value)){
		return 1;
	}else{
		return 0;
	}
}


/***********************************************************
 ** function that is attached to text elements on init
 ** should not be called directly
 ***********************************************************/
function fv_validateFormatOnBlur(){
	if (this.value != ""){ // we only care to validate the content not empty fields
		var label = fv_getLabelByElement(this);
		if (label){
			var valid = fv_isValidFormat(this,label);
			if (valid == 0){
				// if field is not required but fv_format is specified and format is invalid, then set image
				if (label.getAttribute('fv_required') != "yes"){
					// remove any existing image first in case it was already there
					label.innerHTML = label.innerHTML.replace(/<img.*WebIcon_RequiredField.*>/gi,"");
					label.innerHTML = fv_ERRORSTRING + label.innerHTML;
				}else{
					// else this is a required field, so just replace the image
					label.innerHTML = label.innerHTML.replace(/WebIcon_RequiredField_denote/i,"WebIcon_RequiredField_alert");
					label.innerHTML = label.innerHTML.replace(/Required Field/i,"Missing or Invalid Format");
				}
			}else{
				// if field is not required but fv_format is specified and format is valid, then clear the image
				if (label.getAttribute('fv_required') != "yes"){
					label.innerHTML = label.innerHTML.replace(/<img.*WebIcon_RequiredField.*>/gi,"");
				}else{
					// else this is a required field, so just replace the image
					label.innerHTML = label.innerHTML.replace(/WebIcon_RequiredField_alert/i,"WebIcon_RequiredField_denote");
					label.innerHTML = label.innerHTML.replace(/Missing or Invalid Format/i,"Required Field");
				}
			}
		}
	}else{
		// if field is not required and there was no value supplied, then its considered ok, so clear image
		var label = fv_getLabelByElement(this);
		if (label.getAttribute('fv_required') != "yes"){
			label.innerHTML = label.innerHTML.replace(/<img.*WebIcon_RequiredField.*>/gi,"");
		}
	}
}


/***********************************************************
 ** function to reset default image path
 ***********************************************************/
function fv_setImgPath(path){
	if (path){
		fv_IMG_PATH = path;
	}
	// create markup for img tag
	fv_REQSTRING = '<img style="vertical-align:top;margin-right:5px;" src="'+fv_IMG_PATH+'WebIcon_RequiredField_denote.gif" title="Required Field">';
	fv_ERRORSTRING = '<img style="vertical-align:top;margin-right:5px;" src="'+fv_IMG_PATH+'WebIcon_RequiredField_alert.gif" title="Missing or Invalid Format">';
}	


