/*
  -------------------------------------------------------------------------
		      JavaScript Form Validator (gen_validatorv31.js)
              Version 3.1.1
	Copyright (C) 2003-2008 JavaScript-Coder.com. All rights reserved.
	You can freely use this script in your Web pages.
	You may adapt this script for your own needs, provided these opening credit
    lines are kept intact.
		
	The Form validation script is distributed free from JavaScript-Coder.com
	For updates, please visit:
	http://www.javascript-coder.com/html-form/javascript-form-validation.phtml
	
	Questions & comments please send to form.val at javascript-coder.com
  -------------------------------------------------------------------------  
*/
/*
  -------------------------------------------------------------
        TTY Adaptation of the Form Validator, extended and cleaned up
 	    with masking code from http://www.wowww.nl/wordpress/2008/03/06/non-intrusive-usage-of-javascript-in-client-side-form-validation/
 	    Copyright (C) 2009 TTY Internet Solutions. All rights reserved.
  -------------------------------------------------------------
*/

/**
 * Example usage: 
 * 
  	<form id="myform" action="someaction">
  		<table cellspacing="0" cellpadding="0" border="0">
			<tr><td class="td_header" colspan="2">Naam en adres gegevens aan te schrijven bedrijf</td></tr>
			<tr>
				<td class="td_form">*Naam:</td>
				<td class="td_form">
					<input type="text" name="bedrijfnaam" class="input_text"/>
					<div id="form_bedrijfnaam_errorloc" class="form_error"></div>
				</td>
			</tr>
		</table>
		<input type="submit" value="submit"/>
	</form>
 *	<script type="text/javascript">
		var frm = new Validator("form");
		frm.enableOnPageErrorDisplayMultibox();
		frm.enableMsgsTogether();		
		frm.addValidation("bedrijfnaam","req","Je bent vergeten de naam van het bedrijf in te vullen");
	</script>
 * 
 * 
 * 
 * 
 * 
 */




/* given object x and method f, will create a closure that will call f with 'this' pointer set to x */
function closure(object, method ){	
	return function(){
		return method.apply(object, arguments);
	}
}

 
 /* Superclass with getClosure method */
var ClosureClass = Class.create({
	 getClosure: function( method_name ){
	 		return closure( this, this[method_name] );
  	 }
});
 

// debug switch
var IF_DEBUG = false;
// IE detection
var isIE = (navigator.userAgent.indexOf("MSIE") != -1) && !((navigator.userAgent.indexOf("Opera") != -1));
// masking name space for global masking (non-W3C)
if ( typeof(masking) === 'undefined' ) {
	var masking = new Object();
}



// Global Utility methods
/**
This function is designed to stop all
events from continuing bubbling.
**/
function stopEvent(eventObj) {
	debug('stop event trigger');
	if (!isIE) {
		debug('not IE');
		eventObj.stopPropagation(); // DOM Level 2
	} else {
		debug(eventObj.cancelBubble);
		eventObj.cancelBubble = true;          
	}       
	//Now prevent any default action.
	if (!isIE) {
		debug('not IE');
		eventObj.preventDefault(); // DOM Level 2
	} else {
		debug('IE');
		eventObj.returnValue = false;                   // IE
	}
}

//Small function to show debugging info.
//provider for IE use since it has a worthless
//debugger. use FF with Firebug for decent
//debugging.
function debug(message,localDebug) {
	if (IF_DEBUG || (localDebug!=null && localDebug==true)) {
		var debugDiv = document.getElementById("debugWindow");
		if (debugDiv==null) {
			var bodyElem = document.body;
			var debugDiv = document.createElement("div");
			debugDiv.setAttribute('id', 'debugWindow');
			debugDiv.style.left="500px"
			debugDiv.style.position="absolute";
			debugDiv.style.overflow="auto";
			debugDiv.style.height="100px";
			debugDiv.style.borderBottom='1px solid #4A4D44';
			debugDiv.style.borderLeft='1px solid #4A4D44';
			debugDiv.style.borderRight='1px solid #4A4D44';
			debugDiv.style.width='400px';
			debugDiv.style.fontFamily="courier", "times new roman";
			debugDiv.style.fontSize='12px';
			debugDiv.style.padding='6px';
			debugDiv.style.color='#FFFFFF';
			debugDiv.style.background='#000000';
			debugDiv.style.filter ='alpha(opacity=95)'; // IE
			debugDiv.style.opacity='0.95'; // FF  	  
			bodyElem.appendChild(debugDiv);
		} 
		debugDiv.innerHTML = debugDiv.innerHTML+'<br/>'+message;
	}
}

//Since the event model of IE is different from the
//standard event model, this compat function makes sure
//that IE browsers will work with this lib.
function getEventTarget(eventObj) {
	  if (isIE) {
		  return window.event.srcElement; // event target for windows
	  } else {
		  return eventObj.target;
	  }
}

//Since IE has a broken inplementation
//of the DOM spec Iam somtimes forces to 
//do things the hard way. IE support is not
//provides and if it breaks, switch to FF
//This function will get the attributes value
//the hard way.
function getAttrValue(elem,attrName) {
	  for (var i=0;i<elem.attributes.length;i++) {
	  	  debug('nodeName => '+elem.attributes[i].nodeName);
	  	  if (elem.attributes[i].nodeName.toLowerCase() == attrName) {
	  	  	debug('nodeValue => '+elem.attributes[i].nodeValue);
	  	  	return elem.attributes[i].value;
	  	  } 
	  }
}

/**
 * Helper method to retrieve an element
 * In case of names, this method will retrieve the first element using the specified name.
 * @param elemIdOrName the id or name of the element.
 * @return an element or null
 */
function getElementByIdOrName(elemIdOrName){
	var elem = document.getElementById(elemIdOrName);
	if(elem){
		return elem;
	} else {
		var elems = document.getElementsByName(elemIdOrName);
		if(elems && elems.length > 0){
			return elems[0];
		} else {
			return null;
		}
	}
}
/**
 * Write the message string in the div with the given name
 * - divname = name of the div
 * - msgstring = the message to display 
 */
function show_div_msg(divname,msgstring)
{
	if(divname.length<=0) return false;

	if(document.layers)
	{
		divlayer = document.layers[divname];
        if(!divlayer){return;}
		divlayer.document.open();
		divlayer.document.write(msgstring);
		divlayer.document.close();
	}
	else
	if(document.all)
	{
		divlayer = document.all[divname];
        if(!divlayer){return;}
		divlayer.innerHTML=msgstring;
	}
	else
	if(document.getElementById)
	{
		divlayer = document.getElementById(divname);
        if(!divlayer){return;}
		divlayer.innerHTML =msgstring;
	}
	divlayer.style.visibility="visible";	
	return false;
}


/**
 * The Form Validator
 * The following is a short summary of the available API
 * API:
 * - addValidation(fieldName, desiredValidation, errorMessage, condition*)
 *    * condition = hook for a boolean expression determining whether or not to
 *                  execute the validation. This is an optional argument
 * - addMasking(itemname, mask, jumptarget)
 * 	   mask = the mask, only used when standardsCompliant == true
 *     jumptarget = name of field to jump to after mask has been filled.
 *                    works only with patternbased masks as opposed to regular expression based masks.
 *
 * - setCustomValidation(expression)
 *    * expression to evaluate at the end of all other validations as a custom validation hook.
 *    
 * - enableOnPageErrorDisplayMultiBox
 *    enable error/messages on the page (instead of alerts), with a specific location for 
 *    messages per inputfield
 * - enableOnPageErrorDisplaySingleBox
 * 	  enable error/messages on the page with a single location for all messages.
 */
var Validator = Class.create(ClosureClass,{
	initialize: function(frmname){
		this.formobj=document.forms[frmname];
		if(!this.formobj) {
			alert("Error: couldnot get Form object "+frmname);
			return;
		}
		if(this.formobj.onsubmit) {
			this.formobj.old_onsubmit = this.formobj.onsubmit;
			this.formobj.onsubmit=null;
		} else {
			this.formobj.old_onsubmit = null;
		}
		
		this.formobj._sfm_form_name=frmname;
		this.formobj.onsubmit= this.getClosure("form_submit_handler");
	    this.disable_validations = false;//new
	    document.error_disp_handler = new ErrorDisplayHandler();	    		
	    this.show_errors_together=true;	    
	    this.validate_on_change=false;	    
	},
	/**
	 * Experimental feature
	 */
	enableValidateOnChange: function(){
		this.validate_on_change = true;
	},
	enableMsgsTogether:function(){
		this.show_errors_together = true;
		this.formobj.show_errors_together=true;
	},
	/**
	 * Enable aggregate error display
	 */
	enableOnPageErrorDisplaySingleBox: function(){
		document.error_disp_handler.EnableOnPageDisplay(true);
	},
	/**
	 * Enable per-field error displays
	 */
	enableOnPageErrorDisplayMultibox: function(){
		document.error_disp_handler.EnableOnPageDisplay(false);
	},
	/**
	 * Clear all validation messages in the validator
	 */
	clearAllValidations:function(){
		for(var itr=0;itr < this.formobj.elements.length;itr++)
		{
			this.formobj.elements[itr].validationset = null;
		}
	},
	/**
	 * setCustomValidation
	 */
	setCustomValidation: function(functionname){
		this.formobj.addnlvalidation = functionname;
	},
	/**
	 * Add masking
	 */
	addMasking : function(itemname, mask, jumptarget) {
		if(!this.formobj) {
			alert("Error: The form object is not set properly");
			return;
		}
		var itemobj = this.formobj[itemname];
	    //for radio button; don't do for 'select' item
	    if(itemobj.length && isNaN(itemobj.selectedIndex) ) {
			itemobj = itemobj[0];
		}	
	    if(!itemobj) {
			alert("Error: Couldnot get the input object named: "+itemname);
			return;
		}    
	    itemobj.onkeypress = function(event){
	    	new Mask(mask, jumptarget).apply(event);
	    };
	},
	/**
	 * 
	 * Add validation for the inputfield indicated by itemname.
	 * The descriptor describes the desired validation. 
	 * You can add more than one validation to a single inputfield.
	 * Optionally you can add an extra parameter with a callback for conditional evaluation.
	 * Example: addValidation(myInputField, "gt=100", "Please enter more than 100.", "checkSelectedBulk");
	 * In this case, the validation is not performed unless the callBack method with the name "checkSelectedBulk" returns true.
	 * 
	 * Existing validations are:
	 * greaterthan=
	 * gt=  				Greater than XX
	 * lessthan=
	 * lt=  				Less than XX
	 * req  				Required field
	 * maxlen=			
	 * maxlength=			Maximum length=XX
	 * minlen=
	 * minlength=			Minimum length=XX 			
	 * alnum				
	 * alphanumeric 		Alphanumeric only
	 * alnum_s
	 * alphanumeric_space	Alphanumeric and space only
	 * num
	 * numeric				Numeric only
	 * dec
	 * decimal				Decimal only
	 * alpha
	 * alphabetic			Alphabetic only
	 * alpha_s
	 * alphabetic_space		Alphabetic and space only
	 * email				Email
	 * regexp=				Regular Expression
	 * dontselect=			Selected index should not be XX
	 * dontselectchk		Radio button must not be selected
	 * dontselectchk=       Index should not be selected
	 * shouldselchk=        Value or index should be selected
	 * selone_radio			only one radio button should be selected.			
	 * - itemname	the input field
	 * - descriptor the validation descriptor
	 * - errstr the error string
	 * 
	 */
	addValidation:function(itemname,descriptor,errstr) {
		var condition = null;
		if(arguments.length > 3) {
			condition = arguments[3]; 
		}
		if(!this.formobj){
			alert("Error: The form object is not set properly");
			return;
		}//if
		var itemobj = this.formobj[itemname];
	    //for radio button; don't do for 'select' item
	    if(itemobj.length && isNaN(itemobj.selectedIndex) ) {
			itemobj = itemobj[0];
		}	
	    if(!itemobj) {
			alert("Error: Couldnot get the input object named: "+itemname);
			return;
		}
		if(!itemobj.validationset) {
			itemobj.validationset = new ValidationSet(itemobj,this.show_errors_together);
		}
		itemobj.validationset.add(descriptor,errstr,condition);
	    itemobj.validatorobj=this;
	    // Untested, does not work yet
	    if(this.validate_on_change) {
	    	itemobj.onchange = itemobj.validationset.applyValidation;
	    }
	},	
	/**
	 * Overrides the form.submit method to do validation.
	 */
	form_submit_handler: function(){		
		var bRet = true;
	    document.error_disp_handler.clear_msgs();	    
		for(var itr=0;itr < this.formobj.elements.length;itr++) {
			if(this.formobj.elements[itr].validationset &&
			  !this.formobj.elements[itr].validationset.validate()) {
				bRet = false;
			}
	        if(!bRet && !this.show_errors_together){
	        	break;
	        }
		}
		
	    if(!bRet) {
	    	document.error_disp_handler.FinalShowMsg();      
	    }	   
		if(this.addnlvalidation){
			str =" var ret = "+this.addnlvalidation+"()";
			eval(str);			
			bRet = bRet && ret; 
		}
		//alert("onSubmit: " + bRet);
		return bRet;
	}
	
});

/**
 * The ValidationDesc(riptor) class
 * - Construct using the inputfield, the description, the error message and the condition.
 */
var ValidationDesc = Class.create({
	initialize: function(inputitem,desc,error,condition){
		this.desc = desc;
		this.error = error;
		this.itemobj = inputitem;
		this.condition = condition;		
	},
	validate: function(){
		if(this.condition != null ){
			if(!eval(this.condition)){
				return true;
			}
		}
		if(!this.validateInput(this.desc,this.itemobj,this.error)) {
			this.itemobj.validatorobj.disable_validations=true;
			this.itemobj.focus();
			return false;
		}
		return true;
	},
	validateEmail: function(email)
	{
	    var splitted = email.match("^(.+)@(.+)$");
	    if(splitted == null) return false;
	    if(splitted[1] != null )
	    {
	      var regexp_user=/^\"?[\w-_\.]*\"?$/;
	      if(splitted[1].match(regexp_user) == null) return false;
	    }
	    if(splitted[2] != null)
	    {
	      var regexp_domain=/^[\w-\.]*\.[A-Za-z]{2,4}$/;
	      if(splitted[2].match(regexp_domain) == null) 
	      {
		    var regexp_ip =/^\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]$/;
		    if(splitted[2].match(regexp_ip) == null) return false;
	      }// if
	      return true;
	    }
	    return false;
	},
	IsCheckSelected: function(objValue,chkValue)
	{
	    var selected=false;
		var objcheck = objValue.form.elements[objValue.name];
	    if(objcheck.length)
		{
			var idxchk=-1;
			for(var c=0;c < objcheck.length;c++)
			{
			   if(objcheck[c].value == chkValue)
			   {
			     idxchk=c;
				 break;
			   }//if
			}//for
			if(idxchk>= 0)
			{
			  if(objcheck[idxchk].checked=="1")
			  {
			    selected=true;
			  }
			}//if
		}
		else
		{
			if(objValue.checked == "1")
			{
				selected=true;
			}//if
		}//else	

		return selected;
	},
	TestDontSelectChk:function(objValue,chkValue,strError)
	{
		var pass = true;
		pass = IsCheckSelected(objValue,chkValue)?false:true;

		if(pass==false)
		{
	     if(!strError || strError.length ==0) 
	        { 
	        	strError = "Can't Proceed as you selected "+objValue.name;  
	        }//if			  
		  document.error_disp_handler.ShowMsg(strError,objValue);
		  
		}
	    return pass;
	},
	TestShouldSelectChk:function(objValue,chkValue,strError)
	{
		var pass = true;

		pass = IsCheckSelected(objValue,chkValue)?true:false;

		if(pass==false)
		{
	     if(!strError || strError.length ==0) 
	        { 
	        	strError = "You should select "+objValue.name;  
	        }//if			  
		  document.error_disp_handler.ShowMsg(strError,objValue);
		  
		}
	    return pass;
	},
	TestRequiredInput:function(objValue,strError)
	{
	 var ret = true;
	 var val = objValue.value;
	 val = val.replace(/^\s+|\s+$/g,"");//trim
	    if(eval(val.length) == 0) 
	    { 
	       if(!strError || strError.length ==0) 
	       { 
	         strError = objValue.name + " : Required Field"; 
	       }//if 
	       document.error_disp_handler.ShowMsg(strError,objValue); 
	       ret=false; 
	    }//if 
	return ret;
	},
	TestMaxLen:function(objValue,strMaxLen,strError)
	{
		var ret = true;
	    if(eval(objValue.value.length) > eval(strMaxLen)) 
	    { 
	      if(!strError || strError.length ==0) 
	      { 
	        strError = objValue.name + " : "+ strMaxLen +" characters maximum "; 
	      }//if 
	      document.error_disp_handler.ShowMsg(strError,objValue); 
	      ret = false; 
	    }//if 
	    return ret;
	},
	TestMinLen:function(objValue,strMinLen,strError)
	{
		var ret = true;
	    if(eval(objValue.value.length) <  eval(strMinLen)) 
	    { 
	      if(!strError || strError.length ==0) 
	      { 
	        strError = objValue.name + " : " + strMinLen + " characters minimum  "; 
	      }//if               
	      document.error_disp_handler.ShowMsg(strError,objValue); 
	      ret = false;   
	    }//if 
	    return ret;
	},
	TestInputType:function(objValue,strRegExp,strError,strDefaultError)
	{
		var ret = true;
	    var charpos = objValue.value.search(strRegExp); 
	    if(objValue.value.length > 0 &&  charpos >= 0) 
	    { 
	     if(!strError || strError.length ==0) 
	      { 
	        strError = strDefaultError;
	      }//if 
	      document.error_disp_handler.ShowMsg(strError,objValue); 
	      ret = false; 
	    }//if 
	    return ret;
	},
	TestEmail:function(objValue,strError)
	{
		var ret = true;
	    if(objValue.value.length > 0 && !validateEmail(objValue.value)	 ) 
	     { 
	       if(!strError || strError.length ==0) 
	       { 
	          strError = objValue.name+": Enter a valid Email address "; 
	       }//if                                               
	       document.error_disp_handler.ShowMsg(strError,objValue); 
	       ret = false; 
	     }//if 
	    return ret;
	},
	TestLessThan:function(objValue,strLessThan,strError)
	{
		var ret = true;
		if(isNaN(objValue.value)) 
		{ 
		    document.error_disp_handler.ShowMsg(objValue.name +": Should be a number ",objValue); 
		    ret = false; 
		  }//if 
		  else
		  if(eval(objValue.value) >=  eval(strLessThan)) 
		  { 
		    if(!strError || strError.length ==0) 
		    { 
		      strError = objValue.name + " : value should be less than "+ strLessThan; 
		    }//if               
		    document.error_disp_handler.ShowMsg(strError,objValue); 
		    ret = false;                 
		   }//if   
		return ret;          
	},
	TestGreaterThan:function(objValue,strGreaterThan,strError)
	{
		var ret = true;
	     if(isNaN(objValue.value)) 
	     { 
	       document.error_disp_handler.ShowMsg(objValue.name+": Should be a number ",objValue); 
	       ret = false; 
	     }//if 
		 else
	     if(eval(objValue.value) <=  eval(strGreaterThan)) 
	      { 
	        if(!strError || strError.length ==0) 
	        { 
	          strError = objValue.name + " : value should be greater than "+ strGreaterThan; 
	        }//if               
	        document.error_disp_handler.ShowMsg(strError,objValue);  
	        ret = false;
	      }//if  
	     return ret;           
	},
	TestRegExp:function(objValue,strRegExp,strError)
	{
		var ret = true;
	    if( objValue.value.length > 0 && 
	        !objValue.value.match(strRegExp) ) 
	    { 
	      if(!strError || strError.length ==0) 
	      { 
	        strError = objValue.name+": Invalid characters found "; 
	      }//if                                                               
	      document.error_disp_handler.ShowMsg(strError,objValue); 
	      ret = false;                   
	    }//if 
	    return ret;
	},
	TestDontSelect:function(objValue,dont_sel_index,strError)
	{
		var ret = true;
	    if(objValue.selectedIndex == null) 
	    { 
	      document.error_disp_handler.ShowMsg("ERROR: dontselect command for non-select Item"); 
	      ret =  false; 
	    } 
	    if(objValue.selectedIndex == eval(dont_sel_index)) 
	    { 
	     if(!strError || strError.length ==0) 
	      { 
	      strError = objValue.name+": Please Select one option "; 
	      }//if                                                               
	      document.error_disp_handler.ShowMsg(strError,objValue); 
	      ret =  false;                                   
	     } 
	    return ret;
	},
	TestSelectOneRadio: function(objValue,strError)
	{
		var objradio = objValue.form.elements[objValue.name];
		var one_selected=false;
		for(var r=0;r < objradio.length;r++)
		{
		  if(objradio[r].checked)
		  {
		  	one_selected=true;
			break;
		  }
		}
		if(false == one_selected)
		{
	      if(!strError || strError.length ==0) 
	       {
		    strError = "Please select one option from "+objValue.name;
		   }	
		  document.error_disp_handler.ShowMsg(strError,objValue);
		}
		return one_selected;
	},
	/** the real input validation **/
	validateInput: function(strValidateStr,objValue,strError){
		var ret = true;
	    var epos = strValidateStr.search("="); 
	    var  command  = ""; 
	    var  cmdvalue = ""; 
	    if(epos >= 0){ 
	    	command  = strValidateStr.substring(0,epos); 
	    	cmdvalue = strValidateStr.substr(epos+1); 
	    } else { 
	     command = strValidateStr; 
	    } 
	    switch(command) 
	    { 
	        case "req": 
	        case "required": 
	         { 
			   ret = this.TestRequiredInput(objValue,strError)
	           break;             
	         }//case required 
	        case "maxlength": 
	        case "maxlen": 
	          { 
				 ret = this.TestMaxLen(objValue,cmdvalue,strError)
	             break; 
	          }//case maxlen 
	        case "minlength": 
	        case "minlen": 
	           { 
				 ret = this.TestMinLen(objValue,cmdvalue,strError)
	             break; 
	            }//case minlen 
	        case "alnum": 
	        case "alphanumeric": 
	           { 
					ret = this.TestInputType(objValue,"[^A-Za-z0-9]",strError, 
							objValue.name+": Only alpha-numeric characters allowed ");
					break; 
	           }
	        case "alnum_s": 
	        case "alphanumeric_space": 
	           { 
					ret = TestInputType(objValue,"[^A-Za-z0-9\\s]",strError, 
							objValue.name+": Only alpha-numeric characters and space allowed ");
					break; 
	           }		   
	        case "num": 
	        case "numeric": 
	           { 
	                ret = this.TestInputType(objValue,"[^0-9]",strError, 
							objValue.name+": Only digits allowed ");
	                break;               
	           }
	        case "dec": 
	        case "decimal": 
	           { 
	                ret = this.TestInputType(objValue,"[^0-9\.]",strError, 
							objValue.name+": Only numbers allowed ");
	                break;               
	           }
	        case "alphabetic": 
	        case "alpha": 
	           { 
	                ret = TestInputType(objValue,"[^A-Za-z]",strError, 
							objValue.name+": Only alphabetic characters allowed ");
	                break; 
	           }
	        case "alphabetic_space": 
	        case "alpha_s": 
	           { 
	                ret = this.TestInputType(objValue,"[^A-Za-z\\s]",strError, 
							objValue.name+": Only alphabetic characters and space allowed ");
	                break; 
	           }
	        case "email": 
	          { 
				   ret = this.TestEmail(objValue,strError);
	               break; 
	          }
	        case "lt": 
	        case "lessthan": 
	         { 
	    	      ret = this.TestLessThan(objValue,cmdvalue,strError);
	              break; 
	         }
	        case "gt": 
	        case "greaterthan": 
	         { 
				ret = this.TestGreaterThan(objValue,cmdvalue,strError);
	            break; 
	         }//case greaterthan 
	        case "regexp": 
	         { 
				ret = this.TestRegExp(objValue,cmdvalue,strError);
	           break; 
	         }
	        case "dontselect": 
	         { 
				 ret = this.TestDontSelect(objValue,cmdvalue,strError)
	             break; 
	         }
			case "dontselectchk":
			{
				ret = this.TestDontSelectChk(objValue,cmdvalue,strError)
				break;
			}
			case "shouldselchk":
			{
				ret = this.TestShouldSelectChk(objValue,cmdvalue,strError)
				break;
			}
			case "selone_radio":
			{
				ret = this.TestSelectOneRadio(objValue,strError);
			    break;
			}		 
	    }//switch 
		return ret;
	}
});


/**
 * The ValidationSet class
 * - Construct using the inputfield and whether or not it should halt on the first validation
 *   (msgs_together == false == halt on first error)
 * - add(description,error, condition)
 * 		add a validation rule to the validations for the current inputfield.
 * 		* description = the validation recipe
 * 		* error = the error message to display
 * 		* condition = the condition under which to proceed or skip evaluation (null means no condition)
 * - validate()
 * 		validate the current set, return true if valid, false otherwise.
 * - applyValidation(eventArgs)
 * 		clear all validation message and validate only this field.
 */
var ValidationSet = Class.create({
	
	initialize: function(inputitem, msgs_together){
		this.vSet = new Array();
		this.itemobj = inputitem;
		this.msgs_together = msgs_together;		
	},
	add:function(description, error, condition){
		this.vSet[this.vSet.length] = 
			new ValidationDesc(this.itemobj,description, error, condition);
	},
	validate:function(){
		var bRet = true;
	    for(var itr=0;itr<this.vSet.length;itr++) {
	        bRet = bRet && this.vSet[itr].validate();
	        if(!bRet && !this.msgs_together) {
	            break;
	        }
	    }
	    return bRet;		
	},
	applyValidation:function(eventArgs){
		document.error_disp_handler.clear_msgs();
		if(this.validate()){		
			document.error_disp_handler.FinalShowMsg();
		} else {
			alert("DEBUG: no validation possible due to lack of validation set..");
		}
	}
});

/**
 * The Mask class W3C compliant
 */
var Mask = Class.create({
	initialize: function(mask, jumptarget){
		this.mask = mask;
		this.targetElement = jumptarget;
		/* Each mask has a format and regex property.  The format consists
		  * of spaces and non-spaces.  A space is a placeholder for a value the user
		  * enters.  A non-space is a literal character that gets copied to that
		  * position in the value.  The regex is used to validate each character, one
		  * at a time (it is not applied against the entire value in the form field,
		  * just the characters the user enters).
		  *
		  * The way you name your masks is significant.  If you create a mask called
		  * date_us, you cause it to be applied to a form field by a) adding the
		  * input_mask class to that form field, which triggers this script to treat
		  * it specially, and b) adding the class mask_date_us to the form field,
		  * which causes this script to apply the date_us mask to it.
		  */
		this.masks = {
				date_iso: {
			    	format: '    -  -  ',
			    	regex:  /\d/
			    },
			    date_us: {
			    	format: '  /  /    ',
			    	regex:  /\d/
			    },
			    time: {
			    	format: '  :  :  ',
			    	regex:  /\d/
			    },
			    phone: {
			    	format: '(   )   -    ',
			    	regex:  /\d/
			    },
			    ssn: {
			    	format: '   -  -    ',
			    	regex:  /\d/
			    },
			    visa: {
			    	format: '    -    -    -    ',
			    	regex:  /\d/
			    }
		};
	},
	apply:function(event){
		var FORMAT = 1;
		var REGULA = 2;
	  	event = (isIE)?window.event:event;
	  	var element = getEventTarget(event);
	  	var maskType = this.mask;
	  	var jumpType = this.targetElement;  	
	  	debug("masktype => " + maskType);
	  	debug("jumptype => " + jumpType);
	  	var jumpElement = null;	  	
	  	if(jumpType) {
	  		jumpElement = getElementByIdOrName(jumpType);
	  	}
	  	var maskKind = null;
	  	// This test is not strictly nessesary since all codes are let through
	  	// But in the future this can be extended.
	    var match = /(\w+)/.exec(maskType);
	    if ( match.length == 2 && this.masks[match[1]] ) {
	       var mask = this.masks[match[1]];
	       var key  = this.getKey(event);
	       if ( this.isPrintable(key) ) {
	          var ch      = String.fromCharCode(key);
	          var str     = element.value + ch;
	          var pos     = str.length;
	          if ( mask.regex.test(ch) && pos <= mask.format.length ) {
	             if ( mask.format.charAt(pos - 1) != ' ' ) {
	                str = element.value + mask.format.charAt(pos - 1) + ch;
	             }
	             element.value = str;
	          }
	          stopEvent(event);
	       }
	       maskKind = FORMAT;
	    } else {
	    	var key  = this.getKey(event);
		    debug("Key detected => " + key);
	    	// Determine if this is a regular expression mask. Regular expressionmasks start with the code 
	    	// 'RE=' 
	    	var start = maskType.substr(0,3);
	    	if(start == "RE="){
	    		debug("regular expression based masking");
	    		if ( this.isPrintable(key) ) {
	    			var ch      = String.fromCharCode(key);
	    			var futureValue = element.value + ch;
	    			var regex = new RegExp(maskType.substr(3));
	    			if(regex.test(futureValue)){
	    				element.value=futureValue;
	    			}
	    			stopEvent(event);
	    		}
	    		maskKind = REGULA;
	    	} else {
	    		debug("format based masking");
		    	// We assume it is a custom mask. 999,AA, XX and ZZ are tested mask values
		    	// 99 is numerical only, AA not numerical and XX is all characters, ZZ is only [a-zA-Z]       
		       if ( this.isPrintable(key) ) {
		          var ch      = String.fromCharCode(key);
		          var pos     = element.value.length;
		          var idx = pos;
		          var found = false;
		          debug("Printable form => " + ch);
		          while (idx<maskType.length && !found) {
		            debug("idx => " + idx);
		            if (maskType.charAt(idx)=='9' || maskType.charAt(idx)=='A' || maskType.charAt(idx)=='X' || maskType.charAt(idx)=='Z') {
		    	        debug("masktype => " + maskType.charAt(idx));
		              var regex   = this.getRegExpression(maskType.charAt(idx));
		              if ( regex.test(ch)) {
		                debug("Test succeeded.");
		                element.value = element.value+ch;
		              }  
		              found=true;
		            } else {
		    	        debug("Test failed.. fixed char added => "+maskType.charAt(idx));
		    	        element.value=element.value+maskType.charAt(idx);
		    	        idx++;
		            }
		          }
		          stopEvent(event);	          
		      }
		      maskKind = FORMAT;
	    	}
	    }
	    // Do element jumps if appropriate
	    if(jumpElement && maskKind == FORMAT){
	    	if(element.value.length == maskType.length){
	    		jumpElement.focus();
	    	}
	    } else {
	    	debug("Cannot perform jump on this type of mask.");
	    }
	},
	// Retrieving the right regular expression for the
	  // char at the position we are at.
	  getRegExpression: function(maskChar) {
	  		if (maskChar == '9') return /\d/;
	  		if (maskChar == 'A') return /\D/;
	  		if (maskChar == 'X') return /\w/;
	  		if (maskChar == 'Z') return /[a-zA-Z]/;
	  		return //;
	  },
	 
	  /* 
	   * Returns true if the key is a printable character.
	   */
	  isPrintable: function(key) {
		  return ( key >= 32 && key < 127 );
	  },

	  /* 
	   * Returns the key code associated with the event.
	   */
	  getKey: function(e) {
		  return window.event ? window.event.keyCode
	         : e            ? e.which
	         :                0;
	  }
});

/**
 * The error display Handler
 * - EnableOnPageDisplay(singlebox)
 *    + singlebox, if true use a single div for all messages, 
 *                 otherwise use a div for each input element
 * - ShowMsg(message, input_element)
 *    - prepare to show the message for the specified input element
 * - FinalShowMsg()
 *     - show all prepared messages
 * - clear_msgs()
 *    - clear all prepared messages, clear all displayed messages.
 */
var ErrorDisplayHandler = Class.create({
	initialize: function(){
		this.msgdisplay = new AlertMessageDisplayer();
		this.all_msgs=new Array();
	},
	/**
	 * Use divs to display errors in line.
	 * - singleBox= if true, use a single div, if false, use div for each field.
	 */
	EnableOnPageDisplay: function(singleBox){
		if(true == singleBox) {
			this.msgdisplay = new SingleBoxErrorDisplay();
		} else {
			this.msgdisplay = new DivMessageDisplayer();		
		}
	},
	ShowMsg: function(msg, input_element){
		var objmsg = new Array();
		objmsg["input_element"] = input_element;
		objmsg["msg"] =  msg;
		this.all_msgs.push(objmsg);
	},
	FinalShowMsg: function(){
		this.msgdisplay.showmsg(this.all_msgs);
	},
	clear_msgs: function(){
		this.msgdisplay.clearmsg(this.all_msgs);
		this.all_msgs= new Array();
	}
});

/**
 * Error display implementation using multiple divs.
 */
var DivMessageDisplayer = Class.create({
	showmsg: function(msgs){
    	var whole_msg;
    	var first_elmnt=null;
    	for(var m in msgs) {
    		if(null == first_elmnt) {
    			first_elmnt = msgs[m]["input_element"];
    		}
    		if(msgs[m]["input_element"]){
    			var divname = this.element_error_div_name(msgs[m]["input_element"]);
    			show_div_msg(divname,msgs[m]["msg"]);
    		}
    	}
    	if(null != first_elmnt) {
    		first_elmnt.focus();
    	}
	},	
	clearmsg: function(msgs){
		if(msgs.length > 0){
			for(var m in msgs) {
				if(msgs[m]["input_element"]){
		    		var divname = this.element_error_div_name(msgs[m]["input_element"]);
		    		show_div_msg(divname,"");
		    	}
		    }
		}
	},
	element_error_div_name: function(input_element){
		  var divname = input_element.form._sfm_form_name + "_" + 
          input_element.name + "_errorloc";
		  divname = divname.replace(/[\[\]]/gi,"");
		  return divname;
	}
});

/**
 * Error display implementation using a single div.
 */
var SingleBoxMessageDisplayer = Class.create({
	showmsg: function(msgs){
		var whole_msg="<ul>\n";
		for(var m=0;m < msgs.length;m++) {
				whole_msg += "<li>" + msgs[m]["msg"] + "</li>\n";
		}
		whole_msg += "</ul>";
		var divname = this.form_error_div_name(msgs);
		show_div_msg(divname,whole_msg);
	},
	clearmsg: function(msgs){
		var divname = this.form_error_div_name(msgs);
		show_div_msg(divname,"");
	},
	form_error_div_name: function(msgs){
		var input_element= null;

		for(var m in msgs) {
			input_element = msgs[m]["input_element"];
			if(input_element){
				break;
			}
		}
		var divname ="";
		if(input_element){
			divname = input_element.form._sfm_form_name + "_errorloc";
		}
		return divname;
	}
});

/**
 * Error display implementation using alerts.
 * (very annoying)
 */
var AlertMessageDisplayer = Class.create({
	showmsg: function(msgs){
		var whole_msg="";
		var first_elmnt=null;
		for(var m=0;m < msgs.length;m++) {
			if(null == first_elmnt){
				first_elmnt = msgs[m]["input_element"];
			}
			whole_msg += msgs[m]["msg"] + "\n";
		}	
		alert(whole_msg);
		if(null != first_elmnt) {
			first_elmnt.focus();
		}
	},
	clearmsg: function(msgs){
	}
});