var lb_loggingEnabled = true;
var lb_currentControlFocus = null;
var lb_formDataSessionId = null;

var lb_sessionStartUrlBase = "forms.leadbay.co.uk/FormData/SessionStart.aspx";
var lb_sessionStepUrlBase = "forms.leadbay.co.uk/FormData/SessionStep.aspx";
var lb_sessionStartUrlBase_Test = "testforms.leadbay.co.uk/FormData/SessionStart.aspx";
var lb_sessionStepUrlBase_Test = "testforms.leadbay.co.uk/FormData/SessionStep.aspx";

/* ==================================================================================
   UTILITY FUNCTIONS
   ================================================================================== */

function sessionStartCallback( callbackData )
{
	if( callbackData.sessionId )
	{
		lb_formDataSessionId = callbackData.sessionId;
	}
}

function sessionStartPing( formTypeRc, leadTypeRc )
{
		var lb_sessionStartUrl = (lb_testing ? lb_sessionStartUrlBase_Test : lb_sessionStartUrlBase);
		lb_sessionStartUrl += "?r=" + base64Encode(document.referrer);
		lb_sessionStartUrl += "&p=" + window.location.href;
		lb_sessionStartUrl += "&ft=" + formTypeRc;
		lb_sessionStartUrl += "&lt=" + leadTypeRc;
		lb_sessionStartUrl += "&ai=" + (lb_testing ? lb_testAffiliateId : lb_affiliateId);
		lb_sessionStartUrl += "&s=STEP1";
		lb_sessionStartUrl += "&w=" + screen.width;
		lb_sessionStartUrl += "&h=" + screen.height;
		lb_sessionStartUrl += "&bw=" + document.body.clientWidth;
		lb_sessionStartUrl += "&bh=" + document.body.clientHeight;
//		log(lb_sessionStartUrl);
		remoteJson({"uri": getPageProtocol() + lb_sessionStartUrl});
}

function sessionStepPing( stepRc, converted, additionalParameters )
{
	if( lb_formDataSessionId )
	{
		var lb_sessionStepUrl = (lb_testing ? lb_sessionStepUrlBase_Test : lb_sessionStepUrlBase);
		lb_sessionStepUrl += "?id=" + lb_formDataSessionId;
		lb_sessionStepUrl += "&s=" + stepRc;
		lb_sessionStepUrl += "&c=" + converted;

		if( additionalParameters )
		{
			lb_sessionStepUrl += additionalParameters;
		}

//		log(lb_sessionStepUrl);
		remoteJson({"uri": getPageProtocol() + lb_sessionStepUrl});
	}
}

// Adds commas into a number string
function addCommas( numberString )
{
	numberString += '';
	x = numberString.replace(/[^0-9.\-]*/g, "").split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1))
	{
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}

	return x1 + x2;
}

function addCommasToNumberTextbox()
{
	this.controls[0].value = addCommas( this.controls[0].value );
}

function getIntegerFromTextbox( control )
{
	return parseInt( control.value.replace(/[^0-9]*/g, ""), 10 );
}

function getFloatFromTextbox( control )
{
	return parseFloat( control.value.replace(/[^0-9.]*/g, ""), 10 );
}

function getPageProtocol()
{
	if( window.location.protocol.indexOf("https") >= 0 )
	{
		return "https://";
	}

	return "http://";
}

function generateGuid()
{
	var guid = '';

	for (var i = 1; i <= 32; i++) 
	{ 
		var n = Math.floor(Math.random() * 16.0).toString(16); 
		guid += n; 
		
		if ((i == 8) || (i == 12) || (i == 16) || (i == 20)) guid += "-"; 
	}

	return guid;
}

function remoteJson(listener)
{
	if (listener && listener.uri)
	{
		var script = document.createElement("script"); // new script element.
		script.setAttribute("type", "text/javascript");
		script.setAttribute("id", "remotejson" + generateGuid());

		if( listener.uri.indexOf("?") > 0 )
		{
			script.setAttribute("src", listener.uri + "&nocache=" + generateGuid() );
		}
		else
		{
			script.setAttribute("src", listener.uri + "?nocache=" + generateGuid() );
		}

		document.getElementsByTagName("head")[0].appendChild(script);
	}
}

function log( message )
{
	if( lb_loggingEnabled ) $('LB_Log').innerHTML += message + "<br />";
}

// Determines if an element is rendered on the page
function isVisible( object )
{
	return (object.offsetWidth > 0 && object.offsetHeight > 0);
}

// addEvent and removeEvent, designed by Aaron Moore
function addEvent(element, listener, handler)
{
	//if the system is not set up, set it up, and
	// store any outside script's event registration in the first handler slot
	if(typeof element[listener] != 'function' || 
	typeof element[listener + '_num'] == 'undefined'){
		element[listener + '_num'] = 0;
		if(typeof element[listener] == 'function'){
			element[listener + 0] = element[listener];
			element[listener + '_num']++;
		}
		element[listener] = function(e){
			var r = true;
			e = (e) ? e : window.event;
			for(var i = 0; i < element[listener + '_num']; i++)
				if(element[listener + i](e) === false) r = false;
			return r;
		}
	}
	//if handler is not already stored, assign it
	for(var i = 0; i < element[listener + '_num']; i++)
		if(element[listener + i] == handler) return;
	element[listener + element[listener + '_num']] = handler;
	element[listener + '_num']++;
}

var urlSafeBase64Characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789*-.";

function base64Encode( input )
{
	var output = "";
	var chr1, chr2, chr3;
	var enc1, enc2, enc3, enc4;
	var i = 0;

	do
	{
		chr1 = input.charCodeAt(i++);
		chr2 = input.charCodeAt(i++);
		chr3 = input.charCodeAt(i++);

		enc1 = chr1 >> 2;
		enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		enc4 = chr3 & 63;

		if (isNaN(chr2))
		{
			enc3 = enc4 = 64;
		}
		else if (isNaN(chr3))
		{
			enc4 = 64;
		}

		output = output + urlSafeBase64Characters.charAt(enc1) + urlSafeBase64Characters.charAt(enc2) + urlSafeBase64Characters.charAt(enc3) + urlSafeBase64Characters.charAt(enc4);
	}
	while (i < input.length);

	return output;
}

/* ==================================================================================
   "HAS VALUE" FUNCTIONS FOR BUILT-IN TYPES
   ================================================================================== */

function textboxHasValueTest( controls )
{
	if( controls[0].value.length > 0 )
	{
		return true;
	}
	else
	{
		return false;
	}
}

/* ==================================================================================
   VALIDATION SETUP FUNCTIONS
   ================================================================================== */

function addValidation( controls, validations, labelNode, errorAdviceNode, trimData, stripNonNumericCharacters, stopOnFirstError, submitButton, trackFocus, validateOnlyIfAllVisible, captureClickEventForSelectElement, extraValidators, successValidators, failureValidators, afterValidationFunctions, hasValueFunction, requiredValueValidators, beforeValidationFunctions )
{
	var validatorData = {eventControl: null, controls: controls, validations: validations, labelNode: labelNode, errorAdviceNode: errorAdviceNode, trimData: trimData, stripNonNumericCharacters: stripNonNumericCharacters, stopOnFirstError: stopOnFirstError, validateOnlyIfAllVisible: validateOnlyIfAllVisible, extraValidators: extraValidators, successValidators: successValidators, failureValidators: failureValidators, afterValidationFunctions: afterValidationFunctions, hasValueFunction: hasValueFunction, requiredValueValidators: requiredValueValidators, beforeValidationFunctions: beforeValidationFunctions };

	for( var controlIndex = 0 ; controlIndex < controls.length ; controlIndex++ )
	{
		validatorData.eventControl = controls[controlIndex];

		var subControls = document.getElementsByName( controls[controlIndex].name );
		
		for( var subControlIndex = 0 ; subControlIndex < subControls.length ; subControlIndex++ )
		{
			var eventTypeToCatch = 'blur';
			switch( subControls[subControlIndex].type.substring(0,7) )
			{
				case 'radio':
				{
					eventTypeToCatch = 'click';
					break;
				}
				
				case 'select-':
				{
					if( captureClickEventForSelectElement )
					{
						eventTypeToCatch = 'click';
					}
					break;
				}
			}

			addEvent( subControls[subControlIndex], 'on' + eventTypeToCatch, validateFromEvent.bindAsEventListener( validatorData ) );

			if( trackFocus )
			{
				addEvent(subControls[subControlIndex], 'onfocus', hasFocusEvent.bindAsEventListener( validatorData ) );
			}

			if( submitButton )
			{
				if( !submitButton.validators )
				{
					submitButton.validators = new Array();
				}
				
				submitButton.validators.extend([validatorData]);
			}
		}
	}
	validatorData.eventControl = controls[0];
	return validatorData;
}

function trackFocus( controls )
{
	for( var controlIndex = 0 ; controlIndex < controls.length ; controlIndex++ )
	{
		controls[controlIndex].addEvent('focus', hasFocusEvent.bindAsEventListener() );
	}
}


function validateMultiple( event )
{
	var errors = 0;

	if( event.target.validators )
	{
		for( var i = 0 ; i < event.target.validators.length ; i++ )
		{
			errors += delayedValidate.attempt( event, event.target.validators[i] );
		}
	}

	return errors == 0;
}

function validateFromEvent( event )
{
	var event = new Event(event);

	delayedValidate.delay( 10, this, event );
}

function delayedValidate( event )
{
	if( this.beforeValidationFunctions )
	{
		for( var i = 0 ; i < this.beforeValidationFunctions.length ; i++ )
		{
			this.beforeValidationFunctions[i].attempt( event, this );
		}
	}	

	// If this validator relies on another field on the page being filled out, then this section checks for that
	if( this.requiredValueValidators )
	{
		// Loop round all the validators that need checking before this validator can run
		for( var validatorIndex = 0 ; validatorIndex < this.requiredValueValidators.length ; validatorIndex++ )
		{
			// Check if the validator incorporates a "hasValue" function
			if( this.requiredValueValidators[validatorIndex].hasValueFunction )
			{
				// It does, so run it and see if it actually has a value in the other field
				if( !this.requiredValueValidators[validatorIndex].hasValueFunction( this.requiredValueValidators[validatorIndex].controls ) )
				{
					// No value, so we can't continue - clear out any existing error message and return
					this.errorAdviceNode.innerHTML = '&nbsp;';
					this.errorAdviceNode.style.visibility = 'hidden';
					this.labelNode.className = this.labelNode.className.replace(/ LB_ErrorLabel/gi, '').replace(/LB_ErrorLabel/gi, '');
					return 0;
				}
			}
		}
	}

	for( var controlIndex = 0 ; controlIndex < this.controls.length ; controlIndex++ )
	{
		if( this.trimData && this.controls[controlIndex].type == 'text' )
		{
			this.controls[controlIndex].value = this.controls[controlIndex].value.trim();
		}

		if( this.stripNonNumericCharacters && this.controls[controlIndex].type == 'text' )
		{
			this.controls[controlIndex].value = this.controls[controlIndex].value.replace(/[^0-9]*/g, "");
		}
		
		if( this.validateOnlyIfAllVisible && !isVisible( this.controls[controlIndex] ) )
		{
			return 0;
		}
	}

	for( var validationIndex = 0 ; validationIndex < this.validations.length ; validationIndex++ )
	{
		var validationState = this.validations[validationIndex].validator( event, this.controls, this.validations[validationIndex], this.extraValidators );

		if( validationState.errorCode >= 0 )
		{
			var errorMessage = '';
	
			if( validationState.errorMessage )
			{
				errorMessage = validationState.errorMessage;
			}
			else
			{
				errorMessage = this.validations[validationIndex].errorMessages[validationState.errorCode];
			}

			setControlToErrorState( this, errorMessage );

			if( this.stopOnFirstError )
			{
				if( this.afterValidationFunctions )
				{
					for( var i = 0 ; i < this.afterValidationFunctions.length ; i++ )
					{
						this.afterValidationFunctions[i].attempt( event, this );
					}
				}			

				return 1;
			}
		}
		else
		{
			this.errorAdviceNode.innerHTML = '&nbsp;';
			this.errorAdviceNode.style.visibility = 'hidden';
			this.labelNode.className = this.labelNode.className.replace(/ LB_ErrorLabel/gi, '').replace(/LB_ErrorLabel/gi, '');
		}
	}

	if( this.successValidators )
	{
		for( var i = 0 ; i < this.successValidators.length ; i++ )
		{
			delayedValidate.attempt( event, this.successValidators[i] );
		}
	}

	if( this.afterValidationFunctions )
	{
		for( var i = 0 ; i < this.afterValidationFunctions.length ; i++ )
		{
			this.afterValidationFunctions[i].attempt( event, this );
		}
	}

	return 0;
}

function setControlToErrorState( validatorData, errorMessage )
{
	validatorData.errorAdviceNode.innerHTML = errorMessage;
	validatorData.errorAdviceNode.style.visibility = 'visible';

	if( validatorData.labelNode.className.indexOf('LB_ErrorLabel') < 0 )
	{
		validatorData.labelNode.className += ' LB_ErrorLabel';
	}
}


function setCurrentControlFocus( control )
{
	lb_currentControlFocus = control;
}

function hasFocusEvent( event )
{
	var event = new Event(event);
	setCurrentControlFocus( event.target );
}

function getRadioButtonListSelectedValue(name)
{
	var controls = document.getElementsByName(name);
	
	for( var i = 0 ; i < controls.length ; i++ )
	{
		if( controls[i].checked )
		{
			return controls[i].value;
		}
	}
	
	return null;
}

function textboxHasValueValidator( event, controls, validation, extraValidators )
{
	if( controls[0].value.length < 1 )
	{
		return {errorCode: 0};
	}
	
	return {errorCode: -1};
}

function radioButtonListHasValueValidator( event, controls, validation, extraValidators )
{
	var radioButtonListValue = getRadioButtonListSelectedValue( controls[0].name );

	if( radioButtonListValue == null || radioButtonListValue.length <= 0 || radioButtonListValue == "" )
	{
		return {errorCode: 0};
	}

	return {errorCode: -1};
}

function selectHasValueValidator( event, controls, validation, extraValidators )
{
	if( controls[0].value == null || controls[0].value.length <= 0 || controls[0].value == "" )
	{
		return {errorCode: 0};
	}

	return {errorCode: -1};
}

/* ==================================================================================
   VALIDATORS - GENERIC
   ================================================================================== */

function EmailAddressValidator( event, controls, validation, extraValidators )
{
	if( controls[0].value.test("^([-\\w\\.!#\\$%\+`'_]+@[-A-Za-z0-9]+(\\.[-A-Za-z0-9\]{2,})+)$") )
	{
		return {errorCode: -1};
	}

	return {errorCode: 0};
}

function RegexValidator( event, controls, validation, extraValidators )
{
	if( controls[0].value.test(validation.validationData) )
	{
		return {errorCode: -1};
	}

	return {errorCode: 0};
}

function dateGroupValidator( event, controls, validation, extraValidators )
{
	var validate = true;

	// If the eventControl is equal to the controlWithFocus it means the user has clicked on a control that doesn't fire our
	// special onfocus event, therefore it's not one of the other drop-downs in the group and we definitely need to validate
	
	// If it isn't equal then we need to check if it's some other random control or one of the other 2 in the date group
	if( lb_currentControlFocus != event.target )
	{
		// Search the other date drop-downs to see if it's one of them that now has focus
		for( var i = 0 ; i < controls.length ; i++ )
		{
			if( controls[i] == lb_currentControlFocus )
			{
				// It is one of them, so don't validate it yet
				validate = false;
			}
		}
	}

	if( validate )
	{
		// This loop checks to see there is something selected in each of the 3 drop-downs in the date group
		for( var i = 0 ; i < controls.length ; i++ )
		{
			if( controls[i].value.length < 1 || controls[i].value == "" )
			{
				// One of the drop-downs is not selected, so return that error
				return {errorCode: 0};
			}
		}

		// Get the date as a string from the group of SELECTs
		var dateString = controls[0].value + controls[1].value + controls[2].value;

		if ( dateString.match(/^[0-3][0-9][01][0-9][12][0-9]{3}$/) == null )
		{
			return {errorCode: 1};
		}
		
		// Extract the separate date components from the string
		var day = parseInt( dateString.substring(0,2), 10 );
		var month = parseInt( dateString.substring(2,4), 10 );
		var year = parseInt( dateString.substring(4,8), 10 );
	
		// Check if the day is valid for the month (e.g. 30th Feb, 31st April is never valid)
		if( daysInMonth( month, year ) < day )
		{
			return {errorCode: 1};
		}
	}
	
	return {errorCode: -1};
}

function daysInFebruary(year)
{
	// February has 29 days in any year evenly divisible by four,
	// EXCEPT for centurial years which are not also divisible by 400.
	return (((year % 4 == 0) && ( (!(year % 100 == 0)) || (year % 400 == 0))) ? 29 : 28 );
}

function daysInMonth( month, year )
{
	// April, June, September, November
	if( month == 4 || month == 6 || month == 9 || month == 11 )
	{
		return 30;
	}
	else if( month == 2 ) // February
	{
		return daysInFebruary( year );
	}
	else // January, March, May, July, August, October, December
	{
		return 31;
	}
}

// Checks a date group value is inside an age range. Expects the date to be validated already.
function ageValidator( event, controls, validation, extraValidators )
{
	// Get the date as a string from the group of SELECTs
	var dateString = controls[0].value + controls[1].value + controls[2].value;

	// Extract the separate date components from the string
	var day = parseInt( dateString.substring(0,2), 10 );
	var month = parseInt( dateString.substring(2,4), 10 );
	var year = parseInt( dateString.substring(4,8), 10 );

	// Get current date
	var now = new Date();
	var nowYear = now.getFullYear();
	var nowMonth = now.getMonth() + 1; // +1 converts from JavaScript month numbering (0-11)
	var nowDay = now.getDate();

	var age = nowYear - year;

	// Decrease age if we're too late in the year (before day and month of birth)
	if ( ( month > nowMonth ) || ( month == nowMonth && day > nowDay ) )
	{
		age --;
	}

//	var minAge = validatorData.split(',')[0].length > 0 ? validatorData.split(',')[0] : -1;
//	var maxAge = validatorData.split(',')[1].length > 0 ? validatorData.split(',')[1] : 1000;

	if( age < validation.validationData.minAge )
	{
		return {errorCode: 0};
	}

	if( age > validation.validationData.maxAge )
	{
		return {errorCode: 1};
	}

	return {errorCode: -1};
}

function GreaterThanValueValidator( event, controls, validation, extraValidators )
{
	if( parseInt( controls[0].value, 10 ) <= validation.validationData.minValue )
	{
		return {errorCode: 0};
	}

	return {errorCode: -1};
}

function LessThanValueValidator( event, controls, validation, extraValidators )
{
	if( parseInt( controls[0].value, 10 ) >= validation.validationData.maxValue )
	{
		return {errorCode: 0};
	}

	return {errorCode: -1};
}

function ValidIntegerValidator( event, controls, validation, extraValidators )
{
	if( isNaN( parseInt( controls[0].value, 10 ) ) )
	{
		return {errorCode: 0};
	}

	return {errorCode: -1};
}

var UkPhoneNumberErrorMessages = ["Enter without country code", "Should be 10 or 11 digits", "Should start with a zero", "06 / 070 numbers not accepted", "Enter home, work or mobile number"];

function UkPhoneNumberValidator( event, controls, validation, extraValidators )
{
	// Get the phone number as a string from the text box
	var telephoneNumber = controls[0].value;

	// Don't allow country codes to be included (assumes a leading "+")
	if ( telephoneNumber.match(/^\+.+$/) )
	{
		return {errorCode: 0};
	}

	// Remove everything but numeric digits from the telephone number to help validation
	telephoneNumber = telephoneNumber.replace(/[^0-9]*([0-9]*)/g, "$1");

	// Now check that it is 10 or 11 digits long
	if( telephoneNumber.match(/^[0-9]{10,11}$/) == null )
	{
		return {errorCode: 1};
	}

	// Now check that the first digit is 0
	if ( telephoneNumber.match(/^0[0-9]{9,10}$/) == null )
	{
		return {errorCode: 2};
	}

	// Finally check that the telephone number is appropriate.
	if ( telephoneNumber.match(/^(06|070)[0-9]+$/) )
	{
		return {errorCode: 3};
	}

	// Check that the telephone number is appropriate.
	if (telephoneNumber.match(/^(01|02|03|05|07|08)[0-9]+$/) == null )
	{
		return {errorCode: 4};
	}

	// Check for too many repeating digits
	if (telephoneNumber.match(/^\d*(\d)\1{7}\d*$/) )
	{
		return {errorCode: 4};
	}

	return {errorCode: -1};
}

var postcodeOutcodes = new Array();
postcodeOutcodes[0]="AB";
postcodeOutcodes[1]="DG";
postcodeOutcodes[2]="DD";
postcodeOutcodes[3]="FK";
postcodeOutcodes[4]="EH";
postcodeOutcodes[5]="KY";
postcodeOutcodes[6]="KA";
postcodeOutcodes[7]="IV";
postcodeOutcodes[8]="KW";
postcodeOutcodes[9]="PA";
postcodeOutcodes[10]="PH";
postcodeOutcodes[11]="ML";
postcodeOutcodes[12]="HS";
postcodeOutcodes[13]="ZE";
postcodeOutcodes[14]="CF";
postcodeOutcodes[15]="LD";
postcodeOutcodes[16]="LL";
postcodeOutcodes[17]="NP";
postcodeOutcodes[18]="SA";
postcodeOutcodes[19]="SY";
postcodeOutcodes[20]="BD";
postcodeOutcodes[21]="DH";
postcodeOutcodes[22]="DL";
postcodeOutcodes[23]="DN";
postcodeOutcodes[24]="HD";
postcodeOutcodes[25]="HG";
postcodeOutcodes[26]="HU";
postcodeOutcodes[27]="HX";
postcodeOutcodes[28]="LN";
postcodeOutcodes[29]="LS";
postcodeOutcodes[30]="NE";
postcodeOutcodes[31]="SR";
postcodeOutcodes[32]="TS";
postcodeOutcodes[33]="WF";
postcodeOutcodes[34]="YO";
postcodeOutcodes[35]="BB";
postcodeOutcodes[36]="BL";
postcodeOutcodes[37]="CA";
postcodeOutcodes[38]="CH";
postcodeOutcodes[39]="CW";
postcodeOutcodes[40]="FY";
postcodeOutcodes[41]="LA";
postcodeOutcodes[42]="M";
postcodeOutcodes[43]="OL";
postcodeOutcodes[44]="PR";
postcodeOutcodes[45]="SK";
postcodeOutcodes[46]="TF";
postcodeOutcodes[47]="WA";
postcodeOutcodes[48]="WN";
postcodeOutcodes[49]="B";
postcodeOutcodes[50]="CV";
postcodeOutcodes[51]="DE";
postcodeOutcodes[52]="DY";
postcodeOutcodes[53]="LE";
postcodeOutcodes[54]="NG";
postcodeOutcodes[55]="NN";
postcodeOutcodes[56]="ST";
postcodeOutcodes[57]="WS";
postcodeOutcodes[58]="WV";
postcodeOutcodes[59]="AL";
postcodeOutcodes[60]="CB";
postcodeOutcodes[61]="CM";
postcodeOutcodes[62]="CO";
postcodeOutcodes[63]="EN";
postcodeOutcodes[64]="IG";
postcodeOutcodes[65]="IP";
postcodeOutcodes[66]="LU";
postcodeOutcodes[67]="MK";
postcodeOutcodes[68]="NR";
postcodeOutcodes[69]="PE";
postcodeOutcodes[70]="RM";
postcodeOutcodes[71]="SG";
postcodeOutcodes[72]="SS";
postcodeOutcodes[73]="WD";
postcodeOutcodes[74]="BA";
postcodeOutcodes[75]="BH";
postcodeOutcodes[76]="BS";
postcodeOutcodes[77]="DT";
postcodeOutcodes[78]="EX";
postcodeOutcodes[79]="GL";
postcodeOutcodes[80]="HR";
postcodeOutcodes[81]="PL";
postcodeOutcodes[82]="TA";
postcodeOutcodes[83]="TQ";
postcodeOutcodes[84]="TR";
postcodeOutcodes[85]="WR";
postcodeOutcodes[86]="GU";
postcodeOutcodes[87]="HA";
postcodeOutcodes[88]="HP";
postcodeOutcodes[89]="OX";
postcodeOutcodes[90]="PO";
postcodeOutcodes[91]="RG";
postcodeOutcodes[92]="SL";
postcodeOutcodes[93]="SN";
postcodeOutcodes[94]="SO";
postcodeOutcodes[95]="SP";
postcodeOutcodes[96]="UB";
postcodeOutcodes[97]="BN";
postcodeOutcodes[98]="BR";
postcodeOutcodes[99]="CR";
postcodeOutcodes[100]="CT";
postcodeOutcodes[101]="DA";
postcodeOutcodes[102]="KT";
postcodeOutcodes[103]="ME";
postcodeOutcodes[104]="RH";
postcodeOutcodes[105]="SM";
postcodeOutcodes[106]="TN";
postcodeOutcodes[107]="TW";
postcodeOutcodes[108]="E";
postcodeOutcodes[109]="EC";
postcodeOutcodes[110]="N";
postcodeOutcodes[111]="NW";
postcodeOutcodes[112]="SE";
postcodeOutcodes[113]="SW";
postcodeOutcodes[114]="W";
postcodeOutcodes[115]="WC";
postcodeOutcodes[116]="G";
postcodeOutcodes[117]="TD";
postcodeOutcodes[118]="GY";
postcodeOutcodes[119]="JE";
postcodeOutcodes[120]="BT";
postcodeOutcodes[121]="IM";
postcodeOutcodes[122]="S";
postcodeOutcodes[123]="L";

function postcodeTest( postcode )
{
	// Permitted letters depend upon their position in the postcode.
	var alpha1 = "[abcdefghijklmnoprstuwyz]";                       // Character 1
	var alpha2 = "[abcdefghklmnopqrstuvwxy]";                       // Character 2
	var alpha3 = "[abcdefghjkstuw]";                                // Character 3
	var alpha4 = "[abehmnprvwxy]";                                  // Character 4
	var alpha5 = "[abdefghjlnpqrstuwxyz]";                          // Character 5

	// Array holds the regular expressions for the valid postcodes
	var pcexp = new Array ();

	// Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA
	pcexp.push (new RegExp ("^(" + alpha1 + "{1}" + alpha2 + "?[0-9]{1,2})(\\s*)([0-9]{1}" + alpha5 + "{2})$","i"));
	
	// Expression for postcodes: ANA NAA
	pcexp.push (new RegExp ("^(" + alpha1 + "{1}[0-9]{1}" + alpha3 + "{1})(\\s*)([0-9]{1}" + alpha5 + "{2})$","i"));

	// Expression for postcodes: AANA  NAA
	pcexp.push (new RegExp ("^(" + alpha1 + "{1}" + alpha2 + "?[0-9]{1}" + alpha4 +"{1})(\\s*)([0-9]{1}" + alpha5 + "{2})$","i"));

	// Assume we're not going to find a valid postcode
	var valid = false;
	
	// Check the string against the types of post codes
	for ( var i=0; i<pcexp.length; i++ )
	{
		if (pcexp[i].test(postcode))
		{
		  // The post code is valid - split the post code into component parts
		  pcexp[i].exec(postcode);
		  
		  // Copy it back into the original string, converting it to uppercase and
		  // inserting a space between the inward and outward codes
		  postcode = RegExp.$1.toUpperCase() + " " + RegExp.$3.toUpperCase();
		  
		  // Load new postcode back into the form element
		  valid = true;
		  
		  // Remember that we have found that the code is valid and break from loop
		  break;
		}
	}
  
	if( valid )
	{
		var postcodeOutcode = '';
		
		// Pull just the Outcode from the Postcode
		if( isFinite( postcode.charAt(1) ) )
		{
			postcodeOutcode = postcode.charAt(0);
		}
		else
		{
			postcodeOutcode = postcode.substring(0, 2);
		}

		// Check the Outcode is in the list
		for( index in postcodeOutcodes )
		{
			if( postcodeOutcode == postcodeOutcodes[index] )
			{
				// Appears valid
				return -1;
			}
		}
	}

	// Is invalid
	return 0;
}

function PostcodeValidator( event, controls, validation, extraValidators )
{
	// Get the postcode as a string from the text box
	var postcode = controls[0].value;

	switch( postcodeTest( postcode ) )
	{
		case 0:
			return {errorCode: 0};
		case -1:
			return {errorCode: -1};
	}
}