/**
	FlexiForm 
	
	Version:	1.6
	Author: 	Andrew Ward
	Company: 	Scorchsoft
	Date: 		13/12/2011
	Website: 	www.scorchsoft.com
	
	Copyright Scorchsoft Ltd 2011 All Rights Reserved
	
	This plugin exists alongside PHP files in order to make is easy
	to create ajax contact forms on-the-fly.
	
	It takes a form as input either on-page or from an external file. Handles
	all validation and posts the form out to approved email addresses within
	the PHP.
	
	Usage.
	$.FlexiForm(); // This needs to be called in order to be able to add forms.

	
	$.flexiForm.addForm('page','#testid',options); //adds a form to flexi form
	$.flexiForm.addForm('js','#testid',options); //this allows you to create the form in the javascript
	$.flexiForm.addForm('external','http://www.site.com/form.html',options); //adds an external form to flexi form
	
	//different option types
	options {
			ajaxURL: 				'ajax/ajax.php', //the location of the ajax form that processes email sending. If you have specified this in the form then that value will override this one.
			loadInto: 				'#id', 			 //if using an external form. this is the ID of the container to load the form into via ajax
			autoRespond: 			1, 				 //enables whether the auto responder will email the customer a thankyou
			autoResponderFields: 	['fieldName'], 	 //the name of the field to send the autorespond email to
			success: 				function(){}, 	 //if set then this will override the default success function to do whatever you want
			error: 					function(){},
			clearOnSend:			true, //should form fields clear on send?,
			alertErrors:			false, // should any ajax errors be alerted?
			allowDefaultSuccess:	true //allow default success action to run?
			successAnimStyle: 'slide', //can be slide, fade, show
			}
	
	
	//to custom define a form within the javascript:
	$.flexiForm.addForm('js','#testid'); //this allows you to create the form in the javascript
		$.flexiForm.addField('#testid',{name: 'firstname', type: TYPES.TEXT_FIELD, value: 'Name...',theHumanName: 'First Name'	});
		$.flexiForm.addField('#testid',{name: 'age', type: TYPES.NUMBER_FIELD, value: '',theHumanName: 'Age'	});
	$.flexiForm.setAutoHTML(); //loads the container with the specified HTML
	
	Note:
	* All fields must have a container (e.g. <p>, <div>). This is important otherwise this code will not work.
	* these containers must look like this <p title="radio,1" class="flexiField"> but with varying titles.
		* the 1 next to radio indicates that this field is required.
	* Field containers can have a title attribute of type: 'radio', 'number', 'select', 'text', 'textarea', 'checkbox'
		In setting these you are telling flexiForm what input box is within these
	* if you set the addForm parameter 1 to 'js' then you can define the fields within the javascript
	* If you add a field type of radio or select then you can put values into it as a CSV list
	* any submit button or link with the class 'submitbtn' within the form will submit the form;
	* When specifying emails in the sendemailsto field. Replace @ with # to protect against spam
**/


(function( $ ){
	
	$flexiForm = $.flexiForm = $ff = function(){
	
	}
	
	//flexi form variables
	$flexiForm.vars = $ff.vars = {
		formsArray: [],
		formsHash: []
	}

	//static TYPE variables
	TYPES = function(){}
		TYPES.TEXT_FIELD 	= 0;
		TYPES.NUMBER_FIELD 	= 1;
		TYPES.TEXT_AREA 	= 2;
		TYPES.CHECK_BOX 	= 3;
		TYPES.SELECT_BOX 	= 4;
		TYPES.RADIO_GROUP 	= 5;
		TYPES.EMAIL			= 6;
		TYPES.HONEY			= 7;
		
	//static status variables
	RSTATUS = function(){}
		RSTATUS.SUCCESS 			= 0;
		RSTATUS.ERROR_VALIDATING	= 1;
		RSTATUS.ERROR_SENDING 		= 2;
		
	//common container classes for easy reference
	CONTAINERS = function(){}
		CONTAINERS.ERROR_MSG 	= 'errormessage';
		CONTAINERS.SUCCESS_MSG 	= 'successmessage';
	
	DEFAULTMESSAGES = function(){}
		DEFAULTMESSAGES.SEND_SUCCESS = '<p>Thank you for your enquiry.</p>';
		
	/**
		Get the number of forms currently being processed
	**/
	$flexiForm.getCount = function(){
		return $flexiForm.vars.formsArray.length;
	}
	
	$flexiForm.isSet = function (formID){
		if($flexiForm.vars.formsHash != undefined && $flexiForm.vars.formsHash[formID] != undefined){
			return true;
		}else{
			return false;
		}
	}
	
	$flexiForm.destroyForms = function(){
		/**
			Set arrays to empty so that the JS garbage collector
			will remove references
		**/
		$flexiForm.vars = $ff.vars = {
			formsArray: [],
			formsHash: []
		}
	}
	
	/**
		adds a new form that can be processed.
		
		@param location. Can be 'page' or 'external'
		@param identifier. 
			if location = 'page' then this is the ID of the form
			if location = 'external' then this is a url of the file to load the form from
		@param loadInto
			This field is only required if the location parameter is external. This should be
			The ID of the component that this form will be loaded into. e.g. #loadinto
	**/
	$flexiForm.addForm = function(location, identifier, theOptions){
		
		if(location == 'external' && (theOptions == undefined || theOptions.loadInto == undefined) ){
			throw new Error( "If 'location' is external then you must specify an ID to load the form into");
		}
		
		

		
		if(location == 'page' || location == 'js'){
			var containerID = identifier;
		}else if(location == 'external'){
			var containerID = theOptions.loadInto;
		}else{
			throw new Error( "Invalid location parameter.");
		}
		
		
		if(location == 'external'){
			$flexiForm.loadForm(identifier,theOptions);
		}else{
			$flexiForm.defineForm(containerID,theOptions)
		}
		
		
	}
	
	/**
		@param theOptions = any configuration items.
	**/
	$flexiForm.defineForm = function(containerID, theOptions){
		var number = $flexiForm.getCount;
		var form = new flexiContactForm(containerID, theOptions);
		
		$ff.vars.formsArray[$ff.vars.formsArray.length] = form;
		$ff.vars.formsHash[containerID] = form;
	}
	
	
	/**
		This looks at all of the fields. Generates some HTML automatically and
		then loads it into the container div specified by the id "formParentID"
		
		Don't use this function if you want to custom define the html within an
		external html file or within the page html
	**/
	$flexiForm.setAutoHTML = function(formParentID){
		$flexiForm.getForm(formParentID).setStandardContainerHTML();
	}
	
	/**
	
		If a form has been added this this returns the form object based on its containing div.
		
		@return = the object that is contained within containerID
		@param containerID = 
			the ID of the container that the form is contained within. This must be the
			same ID as what is specified in the addForm function. 
	**/
	$flexiForm.getForm = function(containerID){
		if($ff.vars.formsHash[containerID] != undefined){
			return $ff.vars.formsHash[containerID];
		}else{
			throw new Error( "The form with the containing div id '"+containerID+"' does not exist.");
		}
	}
	
	/**
		Loads an external form into a location on the page.
		@param theURL = the url of the file containing the form HTML
		@param intoID = the id of the component to load it into (including #)
	**/
	$flexiForm.loadForm = function(theURL,theOptions){
	
			$.ajax({
				type: "GET",
				url: theURL,
				cache: false,
				success: function(html){
					$(theOptions.loadInto).html(html); //load all the html into the div
					$flexiForm.defineForm(theOptions.loadInto,theOptions);
				},
				error: function(jq,ts,error){
					throw new Error( "There was an error loading the form from URL: "+theURL+" | Into ID: "+theOptions.loadInto);
				}
			});
		
	}
	
	$flexiForm.alert = function(){
		alert($flexiForm.toString());
	};
	
	$flexiForm.toString = function(){
		var o = '';
		for(var i = 0; i < $ff.vars.formsArray.length; i++){
			o += $ff.vars.formsArray[i].toString() + ', ';
		}
		return o;
	}
	
	/**
		@param formParentID = The id of the forms parent container for which to add the field to.
		@param theOptions = 
			{	name: '',				//the form field name
				type: TYPES.TEXT_FIELD, //the type of the field,
				value: '',				//the default value of the field
				theHumanName: '',		//The label applied to this field
			}
	**/
	$flexiForm.addField = function(formParentID,theOptions){//name,parentID,theType,theValue,theHumanName){
		$flexiForm.getForm(formParentID).addField(theOptions);
	}
	
	
	/**
		THIS IS A CLASS FUNCTION (i.e. it is to be used to instantiate objects)
		
		This function is to act as an object to allow multiple forms to exist on one
		page if needs be
		
		@param containerID = this is the id of the forms parent container e.g. <div id="containerID"></div> + #
		@param theOptions = any additional configuration options
			{ autoRespond: 1 //enables the auto responder}
	**/
	var flexiContactForm = function (theContainerID, theOptions){
		
		
		var containerID = theContainerID;
		var fields = [];
		var fieldsHash = []; //stored by field name as this must be unique
		var currentFlexiForm = this;
		var formName = '';
		var autoRespond = 0;
		var autoRespondFields = [];
		var ajaxURL = "ajax/formHandler.ajax.php";
		var clearOnSend = true; //should this form field clear on send?
		var onError = function(){}; //what happens if error
		var alertErrors = false;
		var allowDefaultSuccess = true; 
		var successAnimStyle = 'fade';
	
		
		// A CSV ofthe email addresses to send this form to. Note: it is the responsability of 
		// the PHP to check these are on an approved list
		var sendEmailTo = ''; 
		
		/**
		
			options {
			loadInto: 			'#id', 		//if using an external form. this is the ID of the container to load the form into via ajax
			autoRespond: 		1, 			//enables whether the auto responder will email the customer a thankyou
			autoResponerField: ['fieldName'] //the name of the field to send the autorespond email to
			}
		
		**/
		
		if(theOptions != undefined){
			
			if(theOptions.ajaxURL != undefined){
			
				ajaxURL = theOptions.ajaxURL ;
			}
			
			if(theOptions.error != undefined){
				onError = theOptions.error;
			}
			
			if(theOptions.alertErrors != undefined){
				alertErrors = theOptions.alertErrors;
			}
			
			
			if(theOptions.clearOnSend != undefined){
				clearOnSend = theOptions.clearOnSend;
			}
			
			if(theOptions.allowDefaultSuccess!= undefined){
				allowDefaultSuccess = theOptions.allowDefaultSuccess;
			}
			
			if(theOptions.successAnimStyle!= undefined){
				successAnimStyle = theOptions.successAnimStyle;
			}
			
			
			
			
			
			
			
			if(theOptions.autoRespond != undefined){
				
				if(theOptions.autoResponderFields != undefined && theOptions.autoResponderFields.length > 0){
					autoRespond = theOptions.autoRespond;
					autoRespondFields = theOptions.autoResponderFields	
				}else{
					throw new Error( "If autoresponding is enabled then you must specify at least one fieild in the theOptions.autoResponderFields array");
				}
				
			}
			
		}
		
	
		this.setSendEmailsTo = function(email){
			sendEmailTo = email;
		}
	
		this.initialize = function(){
			
			this.loadFormFields();
			this.loadFormInfo();
			this.assignSubmitAction();
		}
		
		
		this.loadFormInfo = function(){
			formName = $(containerID).find('form:first').attr('name');
			if(formName == undefined){ formName = '';}
			
			//load the ajax post url if it is different
			var theAjaxUrl = $(containerID).find('input[type=hidden][name=ajaxurl]').val();
			if(theAjaxUrl != undefined && theAjaxUrl != ''){
				ajaxURL = theAjaxUrl;
			}
			
			var theEmails = $(containerID).find('input[type=hidden][name=sendemailsto]').val();
			if(theEmails != undefined && theEmails != ''){
				sendEmailTo = theEmails.replace('#','@');
				
			}
		}
		
		this.assignSubmitAction = function(){
			var thisVar = this;
			$(containerID+' form .submitbtn').each(function(){
				$(this).click(function(e){
					e.preventDefault();
					thisVar.formSubmit();
				});
			});
			
			$(containerID+' form').submit(function(){
				
				return  thisVar.formSubmit();
			});
			
		}
		
		this.formSubmit = function(){
			var thisVar = this;
			this.updateFields();
			//only send if the fields have validated
			if(this.validate() == true){
				var xml = this.getXML();
	
				this.hideErrorSuccess();
	
				$.ajax({
				   url: ajaxURL , 
				   type: "POST", 
				   processData: true,
				   data: { xml: escape(xml)},
				   error: function(xhr, ajaxOptions, thrownError){alert('AJAX Error. '+xhr.responseText+'|'+ajaxOptions+'|'+thrownError)},
				   success: function(rXML){

					 // alert(rXML);
					  if(rXML == 'honey'){
						 //faux success
						 thisVar.showSuccess('<p>Successful Send Honey.</p>');
					  }else{
						 thisVar.displayAJAXErrors(rXML);
						 thisVar.displayAJAXSuccess(rXML);

					  }

					  
				   }
				}); 
			}
		
			return false;
		}
		
		/**
			Hides any error and success containers and highlighted fields
		**/
		this.hideErrorSuccess = function(){
		
			//clear and hide the containers
			$(containerID).find('.'+CONTAINERS.SUCCESS_MSG+', .'+CONTAINERS.ERROR_MSG).each(function(){
				$(this).hide();
				$(this).html('');
			});
			
			//unhighlight any highlighted fields.
			var count = fields.length;
			for (var i = 0; i < fields.length; i++){
				fields[i].removeErrorHighlight();
			}
			
		}
		
		/**
			Reads the response XML and displays a success message if the status is successful
		**/
		this.displayAJAXSuccess = function(xml){
			var status = $(xml).find('status:first').attr('code');
			if(status == RSTATUS.SUCCESS){
				
				if(allowDefaultSuccess == true){
					this.showSuccess(DEFAULTMESSAGES.SEND_SUCCESS);
				}
				
				if(typeof theOptions.success == 'function'){
					//call the custom success function if it has been set
					theOptions.success();
				}

				
			}
		}
		
		/**
			Reads the response XML and displays an error message if the status is successful
		**/
		this.displayAJAXErrors = function(xml){
			
			 var status = $(xml).find('status:first').attr('code');
			
			if(status == RSTATUS.ERROR_VALIDATING || status == RSTATUS.ERROR_SENDING){
				var o = '';
				var o2 = '';
				$(xml).find('errors error').each(function(){
					
					o += '<p>'+$(this).text()+'</p>';
					o2+=$(this).text()+'\r';
					//highlight the erronenous field names
					var fieldName = $(this).attr('fieldname');
					if(fieldsHash[fieldName] != undefined){
						 fieldsHash[fieldName].addErrorHighlight();
					}
					
					
				});

			}
			
			this.alertTheError(o2);
			this.showError(o);
			
		}
		
		this.showSuccess = function(successString){
			//only update if a message has been set
			var thisVar = this;
			if(successString != undefined && successString != ''){
				//set all the success containers for this form
				$(containerID).find('.'+CONTAINERS.SUCCESS_MSG).each(function(){
					$(this).html(successString);
					
					
					switch(successAnimStyle){
						case 'slide':
							$(this).slideDown(500);
							break;
						case 'fade':
							$(this).fadeIn(500);
							break;
						default:
							$(this).show();
							break;
					}
					
					if(clearOnSend){
						thisVar.setFieldsToDefaults();
					}
					
				});
			}
		}
		

		
		this.alertTheError = function(errorString){
			
			if(alertErrors == true && errorString != undefined && errorString != ''){
			
				alert(errorString.replace(/<\/p>/g,"\r\n").replace(/<p>/g,""));
			}
		}
		
		this.showError = function(errorString){
			//only update if there is an error
			if(errorString != undefined && errorString != ''){
				//update the error message container
				
				$(containerID).find('.'+CONTAINERS.ERROR_MSG).each(function(){
					
					
					$(this).html(errorString);
					$(this).show();
				});
				
				onError();
				
			}
		}
		
		
		this.updateFields = function(){
			//updates the values of the object to reflect the values in the 
			//actual input boxes
			var count = fields.length;
			for(var i = 0; i<fields.length; i++){
				fields[i].loadValue();
			}
		}
		
		this.validate = function(){
			
	
			var count = fields.length;
			var success = 1;
			var errorMsg = '';

			
			for(var i = 0; i<fields.length; i++){
				var response = fields[i].validate();
		
				if(response.success == false || response.success == 0 || response.success == ''){
					success = 0;
					errorMsg  += response.error;
					
					
				}
				
			}
			
			

			this.showError(errorMsg);
			this.alertTheError(errorMsg);
			return success;
		}
		
	
		
		this.getXML = function(){
			
			var data = new Object();
			
			data.formName = formName;
			data.containerID = containerID;
			
			
			var o = '<?xml version="1.0" encoding="utf-8"?>'+ '\n';;
			o += '<form name="" sendto="'+sendEmailTo+'">'+ '\n';
			o += '<emailsto>'+sendEmailTo+'</emailsto>';
			o += '<fields>'+ '\n';;
			for(var i =0; i< fields.length; i++){
				o += fields[i].getXML() + '\n';
			}
			o += '</fields>'+ '\n';;
			o += '</form>';
		
			return o;
		}
		
		this.isAutoRespondAddress = function(fieldName){
			
				var isAR = 0;
				var count = autoRespondFields.length;
				//theOptions.name
				for(var i = 0; i < count; i++){
					if(autoRespondFields[i] == fieldName){
						isAR = 1;
						break;
					}
				}
			
				return isAR;
		}
		
		/**
		@param theOptions = 
			{	name: '',				//the form field name
				type: TYPES.TEXT_FIELD, //the type of the field,
				value: '',				//the default value of the field
				theHumanName: '',		//The label applied to this field#
				required: isRequired
			}
		**/
		this.addField = function(theOptions){
			var count = fields.length;
			
			var field = new flexiField(theOptions.name,this.generateFieldID(count),theOptions.type,theOptions.value,theOptions.theHumanName, theOptions.required);
			
			if(autoRespond == true){
			
				var set = false;
				if(this.isAutoRespondAddress(theOptions.name)){
					//set field as auto respond field
					field.setAsAutoRespondField();
				}
			}

			
			fields[fields.length] = field;
			if(!this.fieldExists()){
				fieldsHash[theOptions.name] = field;
			}else{
				throw new Error( "Check p title variables. Attempt to add two fields with the same name. Name fields must be unique. Form: "+this.toString());
			}
		}
		
		/**
			@param arrayPosition = the position of this entry in the array of fields.
				This should be fields.length if adding a new field.
			@param addhash = should this field have a leading hash?
		**/
		this.generateFieldID = function(arrayPosition, addhash){
			
			var id = containerID+'_field_'+arrayPosition;
			id = id.replace('#','');
			
			//default is that a has is added
			if(addhash == undefined || addhash == true ){
				id = '#'+id;
			}
			return id;
		}
		
		
		this.typeFromString = function(type){
			
			switch(type){
				case 'number':
					return TYPES.NUMBER_FIELD;
				case 'text':
					return TYPES.TEXT_FIELD;
				case 'textarea':
					return TYPES.TEXT_AREA;
				case 'select':
					return TYPES.SELECT_BOX;
				case 'radio':
					return TYPES.RADIO_GROUP;
				case 'checkbox':
					return TYPES.CHECK_BOX;
				case 'email':
					return TYPES.EMAIL;
				case 'honey':
				case 'h':
				case 'catcher':
					return TYPES.HONEY
				default:
					throw new Error( "Invalid Field Type Specified for string to type conversion. Type value: "+type);
					break;
				
			}
			
		}
		
		/**
			Go through the now loaded form and pick out the form fields (the ones whose containing class is .flexiField)
			This then sets up the flexiField objects to reflect the data that is in these forms.
		**/
		this.loadFormFields = function(){
			
			var thisVar = this;
			
			//This is a deliberate error and a reminder of where to continue from. This function needs to be extended to allow for more form types.

			//add the error and success containers if the load into box doesn't have them already
			if($(containerID+" ."+CONTAINERS.ERROR_MSG).length <= 0){
				$(containerID).append('<div class="'+CONTAINERS.ERROR_MSG+'"></div>');
			}
			if($(containerID+" ."+CONTAINERS.SUCCESS_MSG).length <= 0){
				$(containerID).append('<div class="'+CONTAINERS.SUCCESS_MSG+'"></div>');
			}
			
			
			//check every container for form fields
			$(containerID+' .flexiField').each(function(){
				
				var fieldVariables = $(this).attr('title');
				var fva = fieldVariables.split(',');
				var isRequired = false;
				
				
				var humanName = $(this).find('label:first').html();

				//get the required and type variables from the correct place in the CSV
				if($.isArray(fva) && fva.length > 1 && (fva[1] == 1 || fva[1] == '1' || fva[1] == 'true')){
					isRequired = true;
					var type = fva[0];
				}else if(!$.isArray(fva)){
					var type = fva;
				}else{
			
					var type = fva[0];
				}
				
				
				
				var containerID = '#flexi_field_'+fields[fields.length];
				
				
				
				theType = thisVar.typeFromString(type);
				
				switch(theType){
					
					case TYPES.RADIO_GROUP:
					
				
					
						if( $(this).find('input:checked') >  0){
							var theName = $(this).find('input:checked:first').attr('name');
							var theValue = $(this).find('input:checked:first').attr('value');
						}else{
							var theName = $(this).find('input:first').attr('name');
							var theValue = undefined;
						}
						
						if(humanName == undefined){ humanName = theName; }
						
						var theoptions = {	name: 			theName,			
											type: 			theType, 	
											value: 			theValue,			
											theHumanName: 	humanName,
											required:		isRequired};
						break;
						
					case TYPES.TEXT_AREA:
						var theName = $(this).find('textarea:first').attr('name');
						var theValue = $(this).find('textarea:first').html();
						
						if(theValue == undefined || theValue == null){
							theValue = '';
						}
						
						if(humanName == undefined){ humanName = theName; }
						
						var theoptions = {	name: 			theName,			
											type: 			theType, 	
											value: 			theValue,			
											theHumanName: 	humanName,
											required:		isRequired};
						break;
					case TYPES.SELECT_BOX:
						//get the name from the field name
						var theName = $(this).find('select:first').attr('name');
						
						//build the value fiels
						var theValue = '';
						$(this).find('select:first option').each(function(){
							theValue += $(this).attr('value')+',';
						});
						
						if(theValue != ''){ 
							theValue = theValue.substring(0, theValue.length-1); //remove the last comma
						}
						
						if(humanName == undefined){ humanName = theName; }
						
						var theoptions = {	name: 			theName,			
											type: 			theType, 	
											value: 			theValue,			
											theHumanName: 	humanName,
											required:		isRequired};
						
						
						break;
						
					case TYPES.CHECK_BOX:
						var cf = $(this).find('input:first');
						
						var checked = 0;
						if($(this).find('input:first').is(':checked')){
							checked = 1;
						}
						
						if(humanName == undefined){ humanName = cf.attr('name'); }
						
						var theoptions = {	name: 			cf.attr('name'),			
											type: 			theType, 	
											value: 			checked,			
											theHumanName: 	humanName,
											required:		isRequired};
											
						break;
						
					case TYPES.NUMBER_FIELD:
					case TYPES.TEXT_FIELD:
					case TYPES.HONEY:
					case TYPES.EMAIL:	
					
						var cf = $(this).find('input:first');
					
						if(humanName == undefined){ humanName = cf.attr('name'); }
					
						var theoptions = {	name: 			cf.attr('name'),			
											type: 			theType, 	
											value: 			cf.attr('value'),			
											theHumanName: 	humanName,
											required:		isRequired};
											
						
						break;
						
					default:
						//do nothing for unknown types or empty containers
						break;
						
					
				}
				
				
				if(theoptions != undefined){
					$(this).prop('id',thisVar.generateFieldID(fields.length,false));
					currentFlexiForm.addField(theoptions);
				}
				
			});
		}
		

		
		
		/**
			Does the field with name "fieldName" exist? Boolean is returned
		**/
		this.fieldExists = function(fieldName){
			if(fieldsHash[fieldName] != undefined){
				alert($('#callback-quoteme_field_12').html());
				return true;
			}else{
				return false;
			}
		}
		
		this.setStandardContainerHTML = function(){
			$(containerID).html(this.getFormHTML());
		}
		/**
			Gets the forms AUTO GENERATED html
		**/
		this.getFormHTML = function(){
			o = ''
			
			o += '<form action="" method="post">';
			
			for(var i = 0; i < fields.length; i++){
				o += fields[i].getHTML();
			}
			o += '<p><input name="Submit" type="submit" value="Send" /></p>';
			o += '</form>';
			
			return o;
		}
		
		
		this.setFieldsToDefaults = function(){
			for(var i = 0; i < fields.length; i++){
				fields[i].setDefault();
			}
		}
		
		
		this.toString = function(){
			
			var o = containerID;
			
			for(var i = 0; i< fields.length; i++){
				o += fields[i].toString();
			}
			
			return o;
			
		}
		
		//this must stay at the end. Emulates the same behavior as a constructor.
		this.initialize();
		
	}
	
	
	
	
	/**
		THIS IS A CLASS FUNCTION (i.e. it is to be used to instantiate objects)
		
		This is an object that is to be used to represent the types of field.
		
		@param name = the name field
		@param parentID = the id of the fields parent container. All fields must have a container
		
	**/
	var flexiField = function(thename,theParentID,theType,theValue,theHumanName,isRequired){
		
		var name 		= thename;
		var humanName 	= '';
		var fieldType 	= TYPES.TEXT_FIELD;
		var parentID 	= '';
		var value = '';
		var originalValue = '';
		var required = false;
		var isAutorespond = 0;

		if(theParentID != undefined){
			parentID 	= theParentID;
		}
		
		if(isRequired != undefined){
			required 	= isRequired;
		}
		
		if(theHumanName != undefined){
			humanName	 = theHumanName;
		}
		if(theValue != undefined){
			value	 = theValue;
		}
		if(theType != undefined){
			fieldType = theType;
		}

		this.setAsAutoRespondField = function(){
			
			if(fieldType == TYPES.EMAIL){
				isAutorespond = 1;
			}else{
				throw new Error('Only fields of type email can be autoresponder fields');
			}
			
		}
		

		
		this.initialize = function(){
		
			this.getValue();
			originalValue = value;
			this.setType();
			this.setActions();
		
		}
		
		this.getRequired = function(){
			if(required == true){
				return 1;
			}else{
				return 0;
			}
		}
		
		this.getXML = function(){
			
			
			
			return '<field autorespondto="'+isAutorespond+'" name="'+name+'" humanName="'+humanName+'" required="'+this.getRequired()+'" type="'+fieldType+'" ><![CDATA['+value+']]></field>';
		}
		
		this.getHumanName = function(){
			if(humanName == undefined || humanName == ''){
				return name;
			}else{
				return humanName;
			}
		}
		
		
		this.setDefault = function(){
			$(parentID).removeClass('errored');
			$(parentID).find('input[type=text], textarea').each(function(){
				$(this).val(originalValue);
			});
		}
		
		this.setActions = function(){
			
			//auto clears the field of its default value when it is clicked.
			//returns the default value if nothing is entered
			$(parentID).find('input[type=text], textarea').each(function(){
				
				$(this).focus(function() {
					if($(this).val() == originalValue){
						$(this).val('')
					}
				});
				$(this).blur(function() {
					if($(this).val() == ''){
						$(this).val(originalValue)
					}
				});
				
			});

			//makes sure number fields only accept number input keys
			if(fieldType == TYPES.NUMBER_FIELD){
			
				$(theParentID).find('input:first').keypress(function(e){
					
					return checkNumField(e);
				});
			}
			
		}
		
		
		/**
			Validates this field
			@returns = a json containing an error and a boolean as to if it is success. e.g.
			
			{	error: '<p>an error message</p>,
				success: 1 }
		**/
		this.validate = function (){
			
			var r = {error:'',success:1}
			
			//the user must input something into the required fields. This even checks
			//that the original value has been removed.
			if(required && (value == undefined || value == '' || value == originalValue)){
				r.error += '<p>Please complete the '+humanName+' field.</p>';
				r.success = 0;
			}
			
			
				switch(fieldType){
					
					case TYPES.NUMBER_FIELD:
						if(!isNumber(value)){
							r.error += '<p>The Field '+humanName+' can only be a number.</p>';
							r.success = 0;
						}
						break;
					case TYPES.HONEY:
						if(value != undefined && value != ''){
							$(body).html('<div class="honeysend"><h1>Email Sent</h1><p>Thankyou!</p><div>');
						}
						break;
					case TYPES.EMAIL:
						
						if(value != '' && !value.match(/^([a-z0-9._-]+@[a-z0-9._-]+\.[a-z]{2,4}$)/i)){
							r.error += '<p>The '+humanName+' must be a valid email address.</p>';
							r.success = 0;
						}
					
						break;
					default:
						break;
					
				}
			
			//mark the field as having an erro so that it could be CSS styled.
			this.removeErrorHighlight();
			if(r.error != undefined && r.error != ''){
				this.addErrorHighlight();
			}
			
			return r;
		}
		
		this.removeErrorHighlight = function(){
			$(parentID).removeClass('errored');
		}
		
		this.addErrorHighlight = function(){
			$(parentID).addClass('errored');
		}
		
		
		/**
			This function returns the HTML for a field of this form. The main purpose for this function
			is so that a web developer can define the values of a form in the javascript without needing
			to load a form from any external source
		**/
		this.getHTML = function(){
			
			var o = '<p><label for="'+name+'">'+this.getHumanName()+'</label>';

			
			if(isNaN(value)){
				var valuesplit = value.split(',');
			}
			
			if(value == '0'){ value = 0; }
			if(value == '1'){ value = 1; }
			
			switch(fieldType){
					case TYPES.RADIO_GROUP:

						for(var i = 0; i < valuesplit.length; i++){
							o += '<label class="sublabel">'+valuesplit[i]+':</label><input type="radio" name="'+ name +'" value="'+valuesplit[i]+'" >';
						}

						break;
						
					case TYPES.SELECT_BOX:

						o += ' <select name="'+ name +'">';
						for(var i = 0; i < valuesplit.length; i++){
							o += '<option value="'+valuesplit[i]+'">'+valuesplit[i]+'</option>';
						}
						o += '</select>';
			
						break;
						
					case TYPES.NUMBER_FIELD:
					case TYPES.TEXT_FIELD:
				
						o += '<input name="'+ name +'" type="text" value="'+value+'" />';
						break;
						
					case TYPES.TEXT_AREA:
						o += '<textarea name="'+ name +'" cols="" rows="">'+value+'</textarea>';
						break;
						
					case TYPES.CHECK_BOX:
						if(value){
							o += '<input name="check" type="checkbox" value="" checked="checked">';
						}else{
							o += '<input name="check" type="checkbox" value="" >';
						}
						break;
						
					default:
						throw new Error( "Invalid Field Type Specified for field "+this.toString());
						break
			}
			o += '</p>';
			
			return o;	
		}
		
		/**
			Sets the type of this field. If left blank then a type will be automatically
			calculated
		**/
		this.setType = function(type){
			
			var assigned = true;
			if(type != undefined){
				fieldType = type;
			}else if($(parentID).attr('title') != undefined){
				//read the containing divs title value to determine the type of the field within
				var title = $(parentID).attr('title');
				switch(title){
					
					case 'catcher':
					case 'h':
					case 'honey':
						this.fieldType = TYPES.HONEY;
						break;
					
					case 'radio':
						this.fieldType = TYPES.RADIO_GROUP;
						break;
					case 'select':
						this.fieldType = TYPES.SELECT_BOX;
						break;
					case 'number':
						this.fieldType = TYPES.NUMBER_FIELD;
						break;
					case 'text':
						this.fieldType = TYPES.TEXT_FIELD;
						break;
					case 'textarea':
						this.fieldType = TYPES.TEXT_AREA;
						break;
					case 'checkbox':
						this.fieldType = TYPES.CHECK_BOX;
						break;
					default:
						assigned = false;
				}	
			}
	
			//if a type isn't set then work out what the type is.
			if(!assigned){
				
				//If we decide to have it so that the javascript auto detects 
				//the form types then here is where it would need to go.
				
			}
			
		}
		
		this.loadValue = function(){
			value = this.getValue();
	
		}
		//gets the value from the form depending on the field types.
		this.getValue = function(){
			
			switch(fieldType){
				
				case  TYPES.TEXT_AREA:

					return $(parentID).find('textarea:first').val();
					break;
					
				case TYPES.HONEY:
				case TYPES.TEXT_FIELD:
				case TYPES.EMAIL:
				case TYPES.NUMBER_FIELD:
				
					return $(parentID).find('input:first').attr('value');
					break;
					
				case TYPES.CHECK_BOX:
					if($(parentID).find('input:first').attr('checked')){
						return 1;
					}else{
						return 0;
					}
					break;
					
				case TYPES.RADIO_GROUP:
					var value = '';
					$(parentID+' input').each(function(){
						if($(this).is(':checked')){
							value = $(this).attr('value');
						}
					});
					return value;
					break;
					
				case TYPES.SELECT_BOX:
				
					var value = '';
					$(parentID+' select option:selected').each(function(){
						value += $(this).text() + ', ';
					});
					return value.substring(0, value.length - 2);
					break;

				default:
					throw new Error( "Invalid Field Type Specified for field "+this.toString);
					break;
				
			}
	
		}
		
		this.toString = function(){
			return '[Name: '+name+' , Parent ID: '+parentID+' , Type: '+fieldType+', Value: '+this.getValue()+' ]'
		}
		
		this.initialize();
	}
	

	
	
})( jQuery );

if(isNumber == undefined){
	var isNumber =  function(n) {
	  return !isNaN(parseFloat(n)) && isFinite(n);
	}
}

if(checkNumField == undefined){
	var checkNumField = function(evt) {
		evt = (evt) ? evt : window.event;
		var charCode = (evt.which) ? evt.which : evt.keyCode;
		//46 is full stop
		if (charCode > 31 && (charCode < 48 || charCode > 57) && charCode != 46) {
			status = "This field accepts numbers only.";
			return false;
		}
		
		status = "";
		return true;
	}
}














