
// Javascript Validation Form Element Attributes
// *********************************************
// jvRequired="False"
// jvRequiredError="<optional text shown if required field is empty>"
// jvRequiredShowError="True"
// jvRequiredAlts="<id>(,<id>...)"
// jvFormat="(Email|DateTime|Date|Time|Integer|Decimal|Currency)"
// jvFormatError="<optional text shown if format validation fails>"
// jvFormatShowError="True"
// jvCustomFunction="<custom validation function name>"
// jvCustomFunctionError="<optional text shown if custom validation fails>"
// jvCausesValidation="True"
// jvErrorCssClass="<optional css class applied to element if validation fails>"
// jvErrorElementID="<optional id of element to apply css class to if validation fails>"
// jvLabelElementID="<optional id of label element>"
// jvLabel="<optional friendly name for constructing error text>"

// <input type=submit|image> buttons bypass the post back mechanism, raising the form onsumbit event
// directly, but I see no way to get the element the triggered form submission from onsubmit to evaluate
// it's jvCausesValidation attribute.  Microsoft validation emits an array of elements that cause validation
// to handle this.  For now, we set jvSkipValidation to true in <input type=submit|image> buttons onclick event.
var jvSkipValidation = false;

// Is the form valid (or is validation skipped).
function jvValidateForm( eventTarget, container ) {
	
	// If skipping validation, return true.
	if ( jvSkipValidation ) return true;

	var oEventTarget;
	var sEventTargetCausesValidation;

	// If submit was fired by control that does not cause validation, return true.
	if ( eventTarget ) {
		oEventTarget = document.getElementById( eventTarget.replace( '$', '_' ) );
		if ( oEventTarget ) {
			sEventTargetCausesValidation = oEventTarget.getAttribute( 'jvCausesValidation' );
			if ( sEventTargetCausesValidation ) {
				if ( sEventTargetCausesValidation.toString().toLowerCase() == 'false' )
					return true;
			}
		}
	}

    // If a container was specified, an html element that contains the form field elements to validate,
    // get an array of the form field elements (input, select, textarea) within the container;
    // otherwise, default to validating the elements contained in forms[0].elements.
    if ( container ) {
        oElements = $( container ).select( 'INPUT', 'SELECT', 'TEXTAREA' );
    } else {
        oElements = document.forms[0].elements;
    }
    
	var bFormIsValid = true;
	var bElementIsValid = true;
	var oElement;
	var sElementType;
	var bElementRequired;
	var sElementRequiredError;
	var bElementRequiredShowError;
	var sElementRequiredAlts;
	var sElementFormat;
	var sElementFormatError;
	var bElementFormatShowError;
	var sElementCustomFunction;
	var sElementCustomFunctionError;
	var sErrorCssClass;
	var sErrorElementID;
	var oErrorElement;
	var sLabelElementID;
	var oLabelElement;
	var sElementLabel;
	var sElementValue;
	var sErrorText;
	var aErrorText = new Array();
	
	// Iterate through the elements being validated.
	for ( var i = 0; i < oElements.length; i++ ) {
	
		bElementIsValid = true;
		oElement = oElements[i];

		// If element is disabled,
		// skip validation of current element.
		if ( oElement.disabled ) continue;

		if ( ( oElement.nodeName == 'INPUT' && ( oElement.type == 'text' || oElement.type == 'hidden' ) ) || ( oElement.nodeName == 'TEXTAREA' && oElement.type == 'textarea' ) ) {
		    sElementType = 'text';
		} else if ( oElement.nodeName == 'SELECT' && oElement.type == 'select-one' ) {
		    sElementType = 'dropdown';
		} else {
		    sElementType = null;
		}

		bElementRequired = oElement.getAttribute( 'jvRequired' );
		sElementRequiredError = oElement.getAttribute( 'jvRequiredError' );
		bElementRequiredShowError = oElement.getAttribute( 'jvRequiredShowError' );
		sElementRequiredAlts = oElement.getAttribute( 'jvRequiredAlts' );
		sElementFormat = oElement.getAttribute( 'jvFormat' );
		sElementFormatError = oElement.getAttribute( 'jvFormatError' );
		bElementFormatShowError = oElement.getAttribute( 'jvFormatShowError' );
		sElementCustomFunction = oElement.getAttribute( 'jvCustomFunction' );
		sElementCustomFunctionError = oElement.getAttribute( 'jvCustomFunctionError' );
		sErrorCssClass = oElement.getAttribute( 'jvErrorCssClass' );
		sErrorElementID = oElement.getAttribute( 'jvErrorElementID' );
		oErrorElement = ( sErrorElementID ) ? document.getElementById( sErrorElementID ) : oElement;
		sLabelElementID = oElement.getAttribute( 'jvLabelElementID' );
		oLabelElement = document.getElementById( sLabelElementID ? sLabelElementID : 'lbl' + oElement.id.substr( 3 ) );
		sElementLabel = ( oLabelElement ) ? oLabelElement.innerHTML : oElement.getAttribute( 'jvLabel' );

		bElementRequired = ( bElementRequired ) ? ( ( bElementRequired.toString().toLowerCase() == 'true' ) ? true : false ) : false;
		bElementRequiredShowError = ( bElementRequiredShowError ) ? ( ( bElementRequiredShowError.toString().toLowerCase() == 'false' ) ? false : true ) : true;
		bElementFormatShowError = ( bElementFormatShowError ) ? ( ( bElementFormatShowError.toString().toLowerCase() == 'false' ) ? false : true ) : true;
		
		// Remember the error element's className property.
		if ( !oErrorElement.defaultCssClass ) {
		    oErrorElement.defaultCssClass = oErrorElement.className;
		}

		// Element required.
		if ( bElementRequired ) {

			if ( sElementType == 'text' ) {
				sElementValue = oElement.value.trim();
				if ( sElementValue == '' ) {
					bElementIsValid = false;
				}
			}
			else if ( sElementType == 'dropdown' ) {
				if ( oElement.selectedIndex == -1 ) {
					bElementIsValid = false;
				} else {
					sElementValue = oElement.options[ oElement.selectedIndex ].value;
					if ( sElementValue == '' || sElementValue == '0' ) {
						bElementIsValid = false;
					}
				}
			}
			
			// element required alts
			if ( !bElementIsValid ) {
				if ( sElementRequiredAlts ) {
					var aElementRequiredAlts = sElementRequiredAlts.split(',');
					var oElementRequiredAlt;
					for ( var j = 0; j < aElementRequiredAlts.length; j++ ) {
						oElementRequiredAlt = document.getElementById( aElementRequiredAlts[j] );
						if ( oElementRequiredAlt ) {
							if ( oElementRequiredAlt.nodeName == 'INPUT' && oElementRequiredAlt.type == 'text' ) {
								sElementValue = oElementRequiredAlt.value.trim();
								if ( sElementValue != '' ) {
									bElementIsValid = true;
									break;
								}
							}
							else if ( oElementRequiredAlt.nodeName == 'SELECT' && oElementRequiredAlt.type == 'select-one' ) {
								if ( oElementRequiredAlt.selectedIndex > -1 ) {
									sElementValue = oElementRequiredAlt.options[ oElementRequiredAlt.selectedIndex ].value;
									if ( sElementValue != '' && sElementValue != '0' ) {
										bElementIsValid = true;
										break;
									}
								}
							}
						}
					}
				}
			}

			if ( !bElementIsValid && bElementRequiredShowError ) {
				if ( sElementRequiredError ) {
					aErrorText.push( sElementRequiredError );
				} else if ( sElementLabel ) {			
					aErrorText.push( sElementLabel + ' is required.' );
				}
			}
				
		}
		
		// element format
		if ( sElementFormat && bElementIsValid ) {
			sElementFormat = sElementFormat.toString().toLowerCase();

			// element format validated when element value is not empty
			sElementValue = oElement.value.trim();
			if ( sElementValue != '' ) {

				// element format: email
				if ( sElementFormat == 'email' ) {
					if ( sElementType == 'text' ) {
						var oRegExp = /^(([a-zA-Z0-9_'-]+\.)*[a-zA-Z0-9_'-]+@([a-zA-Z0-9_'-]+\.)+[a-zA-Z]+)*$/i;
						if ( oRegExp.test( sElementValue ) == false ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
							    if ( sElementFormatError ) {
								    aErrorText.push( sElementFormatError );
					            } else if ( sElementLabel ) {			
								    aErrorText.push( sElementLabel + ' is not a valid email address.' );
							    }
							}
						}
					}
				}
			
				// element format: datetime
				else if ( sElementFormat == 'datetime' ) {
					if ( sElementType == 'text' ) {
						if ( !IsDateTime( sElementValue ) ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
    							if ( sElementFormatError ) {
	    							aErrorText.push( sElementFormatError );
				    	        } else if ( sElementLabel ) {			
					    			aErrorText.push( sElementLabel + ' requires MM/DD/YYYY HH:MM [AM|PM] format.' );
						    	}
						    }
						}
					}
				}

				// element format: date
				else if ( sElementFormat == 'date' ) {
					if ( sElementType == 'text' ) {
						if ( !IsDate( sElementValue ) ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
    							if ( sElementFormatError ) {
	    							aErrorText.push( sElementFormatError );
				    	        } else if ( sElementLabel ) {			
					    			aErrorText.push( sElementLabel + ' requires MM/DD/YYYY format.' );
						    	}
						    }
						}
					}
				}

				// element format: time
				else if ( sElementFormat == 'time' ) {
					if ( sElementType == 'text' ) {
						if ( !IsTime( sElementValue ) ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
							    if ( sElementFormatError ) {
								    aErrorText.push( sElementFormatError );
							    } else if ( sElementLabel ) {
								    aErrorText.push( sElementLabel + ' requires HH:MM [AM|PM] format.' );
							    }
							}
						}
					}
				}

				// element format: integer
				else if ( sElementFormat == 'integer' ) {
					if ( sElementType == 'text' ) {
						var oRegExp = /^\d+[\d\,]*$/;
						if ( oRegExp.test( sElementValue ) == false ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
							    if ( sElementFormatError ) {
								    aErrorText.push( sElementFormatError );
							    } else if ( sElementLabel ) {
								    aErrorText.push( sElementLabel + ' is not a valid number.' );
							    }
							}
						}
					}
				}
				
				// element format: decimal
				else if ( sElementFormat == 'decimal' ) {
					if ( sElementType == 'text' ) {
						var oRegExp = /^\d+[\d\,]*\.?\d*$/;
						if ( oRegExp.test( sElementValue ) == false ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
							    if ( sElementFormatError ) {
								    aErrorText.push( sElementFormatError );
							    } else if ( sElementLabel ) {
								    aErrorText.push( sElementLabel + ' is not a valid number.' );
							    }
							}
						}
					}
				}

				// element format: currency
				else if ( sElementFormat == 'currency' ) {
					if ( sElementType == 'text' ) {
						var oRegExp = /^\-?\$?\d+[\d\,]*\.?\d*$/;
						if ( oRegExp.test(sElementValue) == false ) {
							bElementIsValid = false;
                            if ( bElementFormatShowError ) {
							    if ( sElementFormatError ) {
								    aErrorText.push( sElementFormatError );
							    } else if ( sElementLabel ) {			
								    aErrorText.push( sElementLabel + ' is not a valid amount.' );
							    }
							}
						}
					}
				}
				
			}
		}

		// element custom function
		if ( sElementCustomFunction && bElementIsValid ) {
			var oElementCustomFunction = eval( sElementCustomFunction );
			if ( oElementCustomFunction( oElement ) != true ) {
				bElementIsValid = false;
				if ( sElementCustomFunctionError ) {
					aErrorText.push( sElementCustomFunctionError );
				} else if ( sElementLabel ) {			
					aErrorText.push( sElementLabel + ' is not valid.' );
				}
			}
		}

		// element label color
		if ( oLabelElement ) {
			if ( bElementIsValid ) {
				oLabelElement.style.color = '';
			} else {
				oLabelElement.style.color = '#ff0000';
			}
		}
		
		// element css class
		if ( sErrorCssClass ) {
		    if ( bElementIsValid ) {
		        oErrorElement.className = oErrorElement.defaultCssClass;
		    } else {
		        oErrorElement.className = oErrorElement.defaultCssClass + ' ' + sErrorCssClass;
		    }
		}
		
		// form invalid
		if ( !bElementIsValid ) {
			bFormIsValid = false;
		}
		
	}
	
	// show error in help area
	if ( !bFormIsValid ) {
		
		var tblHelp = document.getElementById( 'tblHelp' );
		
		if ( tblHelp ) {

			document.getElementById( 'tblHelp' ).className = 'tblHelpError';
			document.getElementById( 'imgHelp' ).src = 'images/icnHelpError.gif';
			sErrorText = 'The fields marked in red are incomplete or invalid.<ul style="margin:8px 0px 0px 15px; padding:0px 0px 0px 0px;">'
			for ( var i = 0; i < aErrorText.length; i++ ) {
				sErrorText = sErrorText + '<li>' + aErrorText[i] + '</li>';
			}
			sErrorText = sErrorText + '</ul>';
			document.getElementById( 'lblHelp' ).innerHTML = sErrorText;

		} else {
	
			// build alert text with array and push method
			// (this is faster than concatenating with +)
			var string = new Array();

			string.push( 'The fields marked in red are incomplete or invalid.\n\r' );
			for ( var i = 0; i < aErrorText.length; i++ ) {
				string.push( ' - ' );
				string.push( aErrorText[i] );
				string.push( '\n\r' );
			}
			alert( string.join( '' ) );

		}
	}
	
	return bFormIsValid;
}

// Attach jvValidateForm call to form onsubmit event.
// This will call jvValidateForm when an <input type=submit|image> button is clicked.
// __doPostBack will call jvValidateForm directly, ignoring form onsubmit (see hijack below).
// Direct form.submit() calls bypass form.onsubmit and are not handled.
// (Tried to attach call to onsubmit using Mozilla addEventListener,
//	but the submit action is not cancelled when call returns false.)
// (Tried to call form.onsubmit(eventTarget) in __doPostBack,
//  but passing an argument causes IE to throw an error.)
function jvAttachValidationToFormSubmit() {
	window.document.forms[0].onsubmit = function() { return jvValidateForm(); };
}

// When page loads, call jvAttachValidationToFormSubmit.
if ( window.attachEvent )
	window.attachEvent( "onload", jvAttachValidationToFormSubmit );
else if ( window.addEventListener )
	window.addEventListener( "load", jvAttachValidationToFormSubmit, false );

// Microsoft's __doPostBack calls form.onsubmit BEFORE setting the value of __EVENTTARGET
// so onsubmit does not know what control (eventTarget) fired __doPostBack.
// IE's window.event and Mozilla's e argument are useless here.

//function __doPostBack(eventTarget, eventArgument) {
//    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
//        theForm.__EVENTTARGET.value = eventTarget;
//        theForm.__EVENTARGUMENT.value = eventArgument;
//        theForm.submit();
//    }
//}

// Custom __doPostBack.
// Call jvValidateForm with eventTarget argument.
// (allows jvValidateForm to skip validation if a control
//  with jvCausesValidation=False fired the post back)
function RADD__doPostBack( eventTarget, eventArgument ) {
    if ( jvValidateForm( eventTarget ) ) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

// Hijack __doPostBack.
function HijackDoPostBack() {
	__doPostBack = RADD__doPostBack;
}

// When page loads, call HijackDoPostBack.
if ( window.attachEvent )
	window.attachEvent( "onload", HijackDoPostBack );
else if ( window.addEventListener )
	window.addEventListener( "load", HijackDoPostBack, false );

