oControllers=new Object();
API.formControllers=oControllers;
oControllers.length=0;
oControllers.New=function(formId) {
	API.formControllers[this.length]=new formController(this.length,formId);
	this.length++;
	return API.formControllers[this.length-1];

}
oControllers.Get=function(id) {
	if(API.formControllers[id])
		return API.formControllers[id];
	return false;
}

function formController(controllerId,formId) {
	this.id=controllerId;
	this.form=document.getElementById(formId);
	this.form.onsubmit=function() {
		return false;
	}
	if(this.form.getAttribute('_mode')=='rpc')
		this.mode='rpc';
	else
		this.mode='default';
	this.inputs=Array();

	this.msg_err=null;
	this.fdr_err=null;

	this.msg_vld=null;
	this.fdr_vld=null;

	this.msg_inf=null;
	this.fdr_inf=null;

	this.radioGroups=Array();

	// BC20080215: We'll work with mailer_ext_rpc by default now
	this.rpcObject='mailer_ext_rpc';
	this.rpcFunction='post';

	// BC20080215: Input name selection
	this.rpcInputId='name';

	// AF20080215: Protect flooding
	this.rpcFloodProtect=true;
	this.rpcFloodProtected=false;

	this.rpc=null;
	
	// AF20081117:	Captcha support.
	this.captchaRPC=null;
	
	this.captchaImage=null;	
	this.captchaInput=null;
	
	// We're not sure whether we're using a captcha, so it's valid by default	
	this.captchaValid=true;
	this.captchaCheck=false;
	
	this.captchaSubmitted=false;
	
	// AF20081117:	Error flag, we need it for the RPC.
	//				This flag get set on "verifyInputs"
	this.error=false;
	
	this.submit=function() {
		// Notify our captcha a submit took place		
		this.captchaSubmitted=true;
		this.hideInvalid();
		this.disableSubmit();			
		if(this.verifyInputs()) {
			// Good chance the captcha specified is still being checked, dont proceed if it is.
			if(this.captchaCheck==true) {
				return;
			}
			// If our captcha input got verified but we're in here, something went really wrong. Show error
			else if(this.captchaValid==false) {
				this.showError("The captcha you entered is invalid!");
				return;			
			}
			
			if(this.mode=='default') {
				this.form.submit();
			}
			else if(this.mode=='rpc') {
				if(!this.rpc) {
					this.rpc=API.RPC.New(this.rpcObject,this.rpcFunction);
					this.rpc.controller=this;
				}
				this.rpc.callBack=function(status,xml,text) {
					this.controller.submitted(status,xml,text);
				}
				for(cInputs=0;cInputs<this.inputs.length;cInputs++) {
					// BC20080215: 	We no longer check for _id attribute
					//				We determine field name on either _id or name
					//if(this.inputs[cInputs].getAttribute('_id')!==null) {
						switch(this.inputs[cInputs].tagName) {
							case 'SELECT':
							case 'TEXTAREA':
							case 'INPUT':
								// BC20080215: Input name selection
								inputName=null;
								switch(this.rpcInputId) {
									case 'name':
										inputName=this.inputs[cInputs].name;
										break;
									case '_id':
									default:
										inputName=this.inputs[cInputs].getAttribute('_id')
										break;
								}
								if(inputName) {
									switch(this.inputs[cInputs].type) {
										case 'checkbox':
											this.rpc.setParameter(inputName,(this.inputs[cInputs].checked ? (this.inputs[cInputs].value ? this.inputs[cInputs].value : '1') : '' ));
											break;
										default:
											this.rpc.setParameter(inputName,this.inputs[cInputs].value);
											break;
									}
								}
						}
					//}					
				}

				this.rpc.execute();
			}
		}
	}
	this.submitted=function(status,xml,text) {
		success=false;		
		switch(status) {
			case 200:
				node=xml.firstChild;
				switch(node.nodeName) {
				case 'error':
					this.showError(this.form.getAttribute('_error')+'<br />'+node.firstChild.nodeValue);
					break;
				case 'function':
					node=node.firstChild;
					switch(node.nodeName) {
						case 'submit':
						case 'reset':
							break;
		 				case 'error':
							this.showError(this.form.getAttribute('_error')+'<br />'+node.firstChild.nodeValue);
		  					break;
						case 'success':
		  					this.showInfo(this.form.getAttribute('_success')+'<br />'+node.firstChild.nodeValue);
		  					success=true;
		  				break;
					}
					break;
				}
				break;
			default:
				this.showError(this.form.getAttribute('_error')+'<br />'+'RPC failed with statuscode '+status);
		}

		if(success==false || this.rpcFloodProtect==false)
			this.enableSubmit();
		else if(success==true)
			this.rpcFloodProtected=true;
	}
	
	this.disableSubmit=function() {
		for(i=0;i<this.inputs.length;i++) {
			if(this.inputs[i].type=='submit' || this.inputs[i].type=='image') {
				this.inputs[i].disabled=false;
			}
		}
	}
	
	this.enableSubmit=function() {
		for(i=0;i<this.inputs.length;i++) {
			if(this.inputs[i].type=='submit' || this.inputs[i].type=='image') {
				this.inputs[i].disabled=false;
			}
		}
	}

	this.setupInputs= function() {
		cInputs=this.form.getElementsByTagName('input');
		for(i=0;i<cInputs.length;i++) {
			oInput=this.setupInput(cInputs[i])
			if(oInput!==false)
				this.inputs.push(oInput);
		}
		cInputs=this.form.getElementsByTagName('textarea');
		for(i=0;i<cInputs.length;i++) {
			this.inputs.push(this.setupInput(cInputs[i]));
		}
		cInputs=this.form.getElementsByTagName('select');
		for(i=0;i<cInputs.length;i++) {
			this.inputs.push(this.setupInput(cInputs[i]));
		}
		cDivs=this.form.getElementsByTagName('div');
		for(i=0;i<cDivs.length;i++) {
			if(cDivs[i].getAttribute('message')=="error") {
				this.msg_err=cDivs[i];
				this.fdr_err=API.faders.New(this.msg_err,.5);
				this.msg_err.onclick=function() {
					this.fader.fadeOut();
				}
			}
			if(cDivs[i].getAttribute('message')=="invalid") {
				this.msg_vld=cDivs[i];
				this.fdr_vld=API.faders.New(this.msg_vld,.5);
				this.msg_vld.onclick=function() {
					this.fader.fadeOut();
				}
			}
			if(cDivs[i].getAttribute('message')=="info") {
				this.msg_inf=cDivs[i];
				this.fdr_inf=API.faders.New(this.msg_inf,.5);
				this.msg_inf.onclick=function() {
					this.fader.fadeOut();
				}
			}
		}
	}
	this.showError=function(message) {
		if(!this.msg_err)
			return;
		cDivs=this.msg_err.getElementsByTagName('div');
		for(divc=0;divc<cDivs.length;divc++) {
			if(cDivs[divc].getAttribute('content')!==false) {
				cDivs[divc].innerHTML=message;
				break;
			}
		}
		this.fdr_err.fadeIn();
	}
	this.showInvalid=function(message,addExisting) {
		if(!this.msg_vld)
			return;
		cDivs=this.msg_vld.getElementsByTagName('div');
		for(divc=0;divc<cDivs.length;divc++) {
			if(cDivs[divc].getAttribute('content')!==false) {
				if(!addExisting)
					cDivs[divc].innerHTML=message;
				else {
					cDivs[divc].innerHTML+=message;
				}
				break;
			}
		}		
		this.fdr_vld.fadeIn();
	}	
	
	this.showInfo=function(message) {
		if(!this.msg_inf)
			return;
		cDivs=this.msg_inf.getElementsByTagName('div');
		for(divc=0;divc<cDivs.length;divc++) {
			if(cDivs[divc].getAttribute('content')!==false) {
				cDivs[divc].innerHTML=message;
				break;
			}
		}
		this.fdr_inf.fadeIn();
	}
	
	this.hideInvalid=function() {
		if(!this.msg_vld)
			return;		
		this.fdr_vld.fadeOut();
	}
	
	this.setupInput=function(oInput) {
		if(oInput.type=='radio') {
			if(!oInput.name)
				return false;

			for(cGroups=0;cGroups<this.radioGroups.length;cGroups++) {
				if(this.radioGroups[cGroups].name==oInput.name) {
					this.radioGroups[cGroups].addRadio(oInput);
					return false;
				}
			}
			oRadioGroup=new Object();
			oRadioGroup.tagName='INPUT';
			oRadioGroup.id=null;
			oRadioGroup.type='radiogroup';
			oRadioGroup.name=oInput.name;
			oRadioGroup.value='';
			oRadioGroup.checked=false;
			oRadioGroup.required=false;
			oRadioGroup.inputs=Array();
			oRadioGroup.controller=this;
			oRadioGroup.className='default';
			oRadioGroup.error='Input invalid, no description specified!';
			oRadioGroup.onchange=function () {
				this.controller.verifyInput(this,true);
			}
			oRadioGroup.getAttribute=function(attribute) {
				switch(attribute) {
					case '_required':
						return this.required;
					case '_defaultClass':
						return 'default';
					case '_errorClass':
						return 'error';
					case '_error':
						return this.error;
					case '_id':
						return this.id;
					default:
						return null;
				}
			}
			oRadioGroup.apply=function() {
				for(cRadios=0;cRadios<this.inputs.length;cRadios++) {
					if(this.className=='default')
						this.inputs[cRadios].className=this.inputs[cRadios].getAttribute('_defaultClass');
					if(this.className=='error')
						this.inputs[cRadios].className=this.inputs[cRadios].getAttribute('_errorClass');
				}
			}
			oRadioGroup.addRadio=function(oRadio) {
				if(!oRadio.getAttribute('_defaultClass'))
					oRadio.setAttribute('_defaultClass',oRadio.className);
				if(!oRadio.getAttribute('_errorClass'))
					oRadio.setAttribute('_errorClass',oRadio.className);
				this.inputs.push(oRadio);
				oRadio.radioGroup=this;
				bRequired=oRadio.getAttribute('_required');
				if(bRequired!==null)
					this.required=bRequired;
				sError=oRadio.getAttribute('_error');
				if(sError!==null)
					this.error=sError;
				sId=oRadio.getAttribute('_id');
				if(sId!==null)
					this.id=sId;
				oRadio.onchange=function() {
					this.radioGroup.onchange();
				}
				oRadio.onclick=oRadio.onchange;
			}
			oRadioGroup.validate=function() {
				this.value='';
				this.checked=false;
				for(cRadios=0;cRadios<this.inputs.length;cRadios++) {
					if(this.inputs[cRadios].checked) {
						this.value=this.inputs[cRadios].value;
						this.checked=true;
						return;
					}
				}
			}
			oRadioGroup.addRadio(oInput);
			this.radioGroups.push(oRadioGroup);
			return oRadioGroup;
		}
		else {
			if(!oInput.getAttribute('_defaultClass'))
				oInput.setAttribute('_defaultClass',oInput.className);
			if(!oInput.getAttribute('_errorClass'))
				oInput.setAttribute('_errorClass',oInput.className);
			if(!oInput.getAttribute('_error'))
				oInput.setAttribute('_error',"Input invalid, no description specified!");
			if(oInput.type=='submit' || oInput.type=='image') {
				oInput.onclick=function() {
					this.controller.submit();
				}
			}
	
			// We've got a captcha, so we're invalid by default
			if(oInput.getAttribute('_captcha_id')!==null ) {				
				this.captchaValid=false;
			}
			oInput.controller=this;
			oInput.onchange=function() {
				this.controller.verifyInput(this,true);
				return true;
			}
			return oInput;
		}
	}
	this.verifyInputs=function() {	
		var i=0;
		var errors="";
		var valid2=true;
		this.error=false;
		for(i=0;i<this.inputs.length;i++) {
			if(!this.verifyInput(this.inputs[i],false)) {
				errors=errors+this.inputs[i].getAttribute('_error')+'<br/>';
				valid2=false;
			}
		}
		if(valid2==false) {
			this.showInvalid(errors);
			this.error=true;
		}
		
		return valid2;
	}
	
	this.verifyInput=function(oInput,onchange) {
		valid=true;
		if(this.rpcFloodProtected==true) {
			this.enableSubmit();
			this.rpcFloodProtected=false;
		}
		switch(oInput.tagName) {
			case 'TEXTAREA':
			case 'INPUT':
				switch(oInput.type) {
					case 'submit':
					case 'reset':
						break;
					case 'checkbox':
						if(oInput.getAttribute('_required')!==null && !oInput.checked) {
							valid=false;
						}
						break;
					case 'radio':
						alert('Radio slipped through!')
						break;
					case 'radiogroup':
						oInput.validate();
						if(oInput.getAttribute('_required')!==null && !oInput.checked) {
							valid=false;
						}
						break;
					default:
						// Process as default textfield
						if(oInput.getAttribute('_required')!==null && !oInput.value) {
							valid=false;
						}
						if(oInput.getAttribute('_maxlen')!==null && oInput.value.length>oInput.getAttribute('_maxlen')) {
							valid=false;
						}
						if(oInput.getAttribute('_minlen')!==null && oInput.value.length<oInput.getAttribute('_minlen')) {
							valid=false;
						}
						// This input is our captcha only check if its valid anyway
						// and when we arent checking already.
						if(oInput.getAttribute('_captcha_id')!==null && valid==true && this.captchaCheck==false) {
							// If the captcha was valid already and we havent changed, skip it.
							if(this.captchaValid==false && onchange==false) {
								// If we've changed, set valid to false
								this.captchaValid=false;
								
								// Detect our image
								try {							
									this.captchaImage=document.getElementById(oInput.getAttribute('_captcha_image'));
								}
								catch(e) {
									alert('Captcha image id not set or invalid!');
									break;								
								}
								
								// Extract the captcha id
								sId=oInput.getAttribute('_captcha_id');
								
								// Prepare RPC
								if(!this.captchaRPC) {
									this.captchaRPC=API.RPC.New('is_captcha_rpc','check');
									this.captchaRPC.controller=this;
								}
								this.captchaRPC.callBack=function(status,xml,text) {
									this.controller.verifyCaptcha(status,xml,text);
								}
								this.captchaRPC.setParameter('id',sId);
								this.captchaRPC.setParameter('answer',oInput.value);
								
								// Cache the input
								this.captchaInput=oInput;								
								// And disable it
								this.captchaInput.disabled=true;
															
								// If we arent checking already, set check flag and execute RPC															
								if(this.captchaCheck==false) {
									this.captchaCheck=true;									
									this.captchaRPC.execute();								
								}
							}
							else if (onchange==true) {
								this.captchaValid=false;
							}
							break;							
						}
						if(oInput.getAttribute('_mask')!==null) {
							switch(oInput.getAttribute('_mask')) {
								case 'hh:mm':
									mask=new RegExp('[0-2][0-9]:[0-5][0-9]','gm');
									break;
								case 'dd/mm/yy':
									mask=new RegExp('[0-3][0-9]/[0-1][0-9]/[0-9][0-9]','gm');
									break;
								case 'dd-mm-yy':
									mask=new RegExp('[0-3][0-9]-[0-1][0-9]-[0-9][0-9]','gm');
									break;
								case 'dd/mm/yyyy':
									mask=new RegExp('[0-3][0-9]/[0-1][0-9]/[1-2][089][0-9][0-9]','gm');
									break;
								case 'dd-mm-yyyy':
									mask=new RegExp('[0-3][0-9]-[0-1][0-9]-[1-2][089][0-9][0-9]','gm');
									break;
								case 'mm/dd/yy':
									mask=new RegExp('[0-1][0-9]/[0-3][0-9]/[0-9][0-9]','gm');
									break;
								case 'mm-dd-yy':
									mask=new RegExp('[0-1][0-9]-[0-3][0-9]-[0-9][0-9]','gm');
									break;
								case 'mm/dd/yyyy':
									mask=new RegExp('[0-1][0-9]/[0-3][0-9]/[1-2][089][0-9][0-9]','gm');
									break;
								case 'mm-mm-yyyy':
									mask=new RegExp('[0-1][0-9]-[0-3][0-9]-[1-2][089][0-9][0-9]','gm');
									break;
								case 'email':
									mask=new RegExp('[a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}','gm');
									break;
								case 'postal_nl':
									mask=new RegExp('[0-9]{4}[ ]{0,1}[A-Za-z]{2}','gm');
									break;
								case 'phone_nl':
									mask=new RegExp('(0[0-9]{9})|(0[0-9]{1}[ -]{1}[0-9]{8})|(0[0-9]{2}[ -]{1}[0-9]{7})|(0[0-9]{3}[ -]{1}[0-9]{6})','gm');
									break;
								default:
									mask=new RegExp(oInput.getAttribute('_mask'),'gm');
									break;
							}
							if(oInput.value.replace(mask,'')!='')
								valid=false;
						}
						break;
				}
				break;
			case 'SELECT':
				if(oInput.getAttribute('_required')!==false && !oInput.value) {
					valid=false;
				}
				break;
		}
		//this.valid=valid;
		if(valid==true) {
			oInput.className=oInput.getAttribute('_defaultClass');
			if(oInput.type=='radiogroup')
				oInput.apply();
			return true;
		}
		else {
			oInput.className=oInput.getAttribute('_errorClass');
			if(oInput.type=='radiogroup')
				oInput.apply();
			return false;
		}
	}
	this.verifyCaptcha=function(status,xml,text) {
		success=false;	
		this.captchaCheck=false;
		this.captchaInput.disabled=false;
		switch(status) {
			case 200:
				node=xml.firstChild;
				switch(node.nodeName) {
				case 'error':
					this.showError(this.form.getAttribute('_error')+'<br />'+node.firstChild.nodeValue);
					break;
				case 'function':
					node=node.firstChild;
					switch(node.nodeName) {
						case 'expired':						
		 				case 'failed':
							this.captchaInput.className=this.captchaInput.getAttribute('_errorClass');
							imgSrc=this.captchaImage.src;
							newImgSrc=imgSrc.replace(/&[.0-9]+/,"&"+Math.random());
							if(newImgSrc==imgSrc) {
								newImgSrc=imgSrc+"&"+Math.random();								
							}
							
							this.captchaImage.src=newImgSrc;
														
							// Show our invalid message. If we're already "in error" add our error.
							// if not, make a clean box.
							if(node.nodeName=='expired') {
								this.showInvalid(node.firstChild.nodeValue+'<br/>',this.error);								
							}
							else {
								this.showInvalid(this.captchaInput.getAttribute('_error')+'<br/>',this.error);
							}							
		  					break;
						case 'success':
							this.captchaInput.className=this.captchaInput.getAttribute('_defaultClass');		  					
		  					this.captchaValid=true;		  					
		  					if(this.captchaSubmitted==true) {		  					
								this.submit();
								return;			  					
							}							
		  					break;
		  				default:
		  					this.captchaInput.className=this.captchaInput.getAttribute('_errorClass');		  					
		  					break;
					}
					break;
				}
				break;
			default:
				this.showError(this.form.getAttribute('_error')+'<br />'+'RPC failed with statuscode '+status);
		}
		if(this.captchaSubmitted==true)
			this.enableSubmit();
	}
	this.setupInputs();
}
