//+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+#
// JAVASCRIPT VALIDATION -- validate.js	v2																										     UPDATED: 07/11/06 CHRIS  #
//+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+#
//
//  Script Author: Chris Merry
//     
//	Description:   This script will validate any form data and enforce certain rules on the data entered via a validation obj alowing for multi level validation
//					   for formatting / extra validation objects via regular expressions.
//
//	Thanks to:	   Ben Clayton, Marc Woodhead :)													   
//
//+=+=+=+=+=+=+=+=+=+=+=+=+=[ (C) Chris Merry - Do not copy this script in anyway without permission. ]=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=#

var errorTxt = "Some of the fields below are required or the information given is not valid, for further information move your mouse over the highlighted boxes below.";

// ---------- VALIDATION REGULAR EXPRESSIONS ---------------------------------------------------------------------------------------
var urlRegxp			=	/^((http|https):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i
// -------------------------------------------------------------------------------------------------------------------------------------
var emailRegxp			=	/^([\w_-]+)(\.[\w_-]+)*@([\w_-]+)(\.[\w_-]*){0,1}(\.[a-zA-Z]{2,4}){1,2}$/; 
// -------------------------------------------------------------------------------------------------------------------------------------
var pcodeRegxp 			=	/^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {0,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$/i;
// -------------------------------------------------------------------------------------------------------------------------------------
var dateRegxp			=	/^([0-9]){1,2}[\/|:|-]{1}([0-9]){1,2}[\/|:|-]([0-9]){4}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var dateTimeRegxp		=	/^([0-9]){1,2}[\/|:|-]{1}([0-9]){1,2}[\/|:|-]([0-9]){4} [0-9]{2}:{1}[0-9]{2}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var carddateRegxp		=	/^([0-9]){1,2}[\/|:|-]{1}([0-9]){1,2}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var passwordRegxp		=	/^[a-zA-Z0-9]{6,20}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var timeRegxp			=	/^[0-9]{2}:{1}[0-9]{2}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var usernameReqxp		=	/[a-zA-Z0-9]{3,20}/;
// -------------------------------------------------------------------------------------------------------------------------------------
var secureusernameReqxp	=	/^[a-zA-Z0-9\-]{6,20}$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var currencyRegxp		=	/^\d+(\.\d{1,2})?$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var telnoRegxp			=	/^(^[0-9\+ \-\)\(]+)$/
// -------------------------------------------------------------------------------------------------------------------------------------
var numericRegxp		=	/^([0-9]+)$/;
// -------------------------------------------------------------------------------------------------------------------------------------
var notblankRegxp		=	/[a-zA-Z_0-9]+/;   // used by the isBlank function
// -------------------------------------------------------------------------------------------------------------------------------------
var ignoretagsRegxp		=	/fieldset/i;   // used by the isBlank function
	
var validate = {};

// EXAMPLES
//{	'Name':'text',
//	'Title':'select-one',
//	'Surname':{'type':'text','change_case':'upper','optional':1},
//	'Email':{'type':'email','change_case':'lower','conf_email':'confirm_email'},
//	'Contact':{'type':'radio'}
//};	

function verifylist(formobj,chklist){
	
	var fail=0;

	var debug=0;

	var result_array = new Array();
	
	for (var i=0; i<formobj.length; i++){
		var e = formobj.elements[i];
		if(ignoretagsRegxp.test(e.tagName) == true || e.tagName == '') continue;
		//alert("TAG NAME: '" +e.tagName + "' TEST: " + ignoretagsRegxp.test(e.tagName));
		var valobj = {};	
		var robj = {};
		robj.debug = '';
		
		var val = chklist[e.name];
		if (typeof val == 'string'){ valobj.type = val; }else{ valobj = val; }
		
		
		
		//var obj = chklist.Surname;
		//alert(obj.type + " " + obj.case);
		//return;
		
		//var str = "\nName: "+e.name;
		//for(var p in valobj){
		//	str += " - " + p + " = " + valobj[p] + " / ";
		//}
		//alert(str);

		robj.debug += "Field: " + e.name;

		if(valobj){	

			robj.element = e;
			robj.alt_tag = '';
			robj.good = 0;
			robj.type = valobj.type;
			
			//alert(valobj.type);
			//alert(e.type);
			robj.debug += " ("+valobj.type+")";
			if	((isdisplayed(e) && e.type != 'hidden') || valobj.hidden){					
				robj.debug += " Visible";
					
				//###############################################################
				// VALIDATE A SINGLE FORM ELEMENT
				//###############################################################
				validate_field_value(robj,valobj);											
				//###############################################################

			}else{
				robj.debug += " Invisible";
				robj.good = 1; // hidden so say it is good													 
			}
			
			//ADD ONTO RESULTS OBJECT
			result_array.push(robj);
		}else{
			robj.debug += " (NOT IN CHECKLIST)\n";										 
		}
	}

	var debugTxt="";

	for (var x=0;x<result_array.length;x++){
		var robj = result_array[x];
		if(robj.type == 'radio'){
			var nm = robj.element.name;
			for (var y=0;y<result_array.length;y++){
				var chk_robj = result_array[y];
				if(chk_robj.element.name == nm){
					//alert("CHK NAME: "+chk_robj.element.name+" GOOD: "+chk_robj.good+" ROBJ NAME: "+robj.element.name+" GOOD: "+robj.good);
					if(robj.good == 1 || chk_robj.good == 1){
						robj.good = 1;
						chk_robj.good = 1;
					}
				}
			}
		}

		if(robj.good) 	robj.debug += " - GOOD\n";
		else robj.debug += "\n  " + robj.alt_tag + " - ERROR\n";
		
		//alert("Name: "+robj.element.name+"  Result: "+robj.good+"  Error txt: "+robj.alt_tag);
		if (robj.good == 0){
			hilite_error(robj);
			fail = 1;
		}else if(robj.good == 1){
			clear_error(robj);
		}
		
		debugTxt += robj.debug;
	}

	if(debug) alert(debugTxt);
	
	display_error_dialogue(errorTxt,fail,1);

	return (fail)?false:true;
}

function validate_single_field(e,chklist){
	if(!e) return 1;
	
	var valobj = {};	
	var val = chklist[e.name];
	if (typeof val == 'string'){ valobj.type = val; }else{ valobj = val; }
	
	var robj = {};
	robj.element = e;
	robj.alt_tag = '';
	robj.good = 0;
	robj.type = valobj.type;
	
	//alert(e.name);
	if(valobj){
		if(isdisplayed(e)){		
			//###############################################################
			// VALIDATE A SINGLE FORM ELEMENT
			//###############################################################
			validate_field_value(robj,valobj);
			//###############################################################
		}else{
			robj.good=1; // hidden so say it is good													 
		}
	}
	
	if(robj.good == 0)	hilite_error(robj);
	else if(robj.good == 1) clear_error(robj);
	return robj.good;
}

//#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
//SUB FUNCTIONS
//#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#

function validate_field_value(robj,valobj){
	var value = '';

	//====[ REPLACEMENTS ]=============================================
	if(valobj.type != "wordcount" && valobj.type != "text" && valobj.type != "postcode" && valobj.type != "datetime"){
		value = robj.element.value.replace(/ /g,'');
		if(valobj.type == "currency"){
			value = value.replace(/[^0-9\.]/g,'');
		}else if(valobj.type != "email" && valobj.type != "numeric" && valobj.type != "meters"){
			value = value.replace(/\./g,'');
			value = value.replace(/\,/g,'');
		}
	}else{
		value = robj.element.value;
	}
	
	value = trim(value);		//TRIM WHITE SPACE FROM START + END OF STRING
	
	//====[ TEXT VALIDATION ]=============================================
	if (valobj.type=="text"){
		if ((valobj.optional == 1) && (value == '') || (value != "") && (!isBlank(value))) {
			robj.good = 1;
		}else{
			robj.alt_tag = "This is a required field";
		}
	}
	//====[ PAYMENT TYPE VALIDATION ]=========================================
	else if (valobj.type=="payment_type"){
		if (value != ''){
			robj.good = 1;
		}else{
			alert('Please select a payment method before clicking on checkout.');
		}
	}
	//====[ USERNAME VALIDATION ]=========================================
	else if (valobj.type=="username"){
		if (usernameReqxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Username must be between 3 - 20 characters long and contain only letters and numbers.";
		}
	}
	//====[ SECURE USERNAME VALIDATION ]==================================
	else if (valobj.type=="secure_username"){
		if (secureusernameReqxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Username must be between 6 - 20 characters long and contain only letters, numbers and hyphens.";
		}
	}
	//====[ PASSWORD VALIDATION ]=========================================
	else if (valobj.type=="password"){
		if (passwordRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Password must be between 6 - 20 characters long and contain only letters and numbers.";
		}
	}
	//====[ CHECKBOX ]==================================================
	else if (valobj.type=="checkbox"){
		robj.good = 1;
	}
	//====[ SELECT-ONE ]================================================
	else if (valobj.type=="select-one"){
		if (robj.element.value != "-1" || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Please select a value from the pulldown menu";
			robj.good = 0;
		}
	}
	//====[ RADIO ]======================================================
	else if (valobj.type=="radio"){
		if (robj.element.checked || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag="Please select at least one of these options";
		}
	}
	//====[ AGREE ]======================================================
	else if (valobj.type=="agree"){
		if (robj.element.checked){
			robj.good = 1;
		}else{
			robj.alt_tag = "Please tick to agree";
		}
	}
	//====[ DATE ]=======================================================
	else if (valobj.type=="date"){
		if (dateRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Date format must be eg. dd/mm/yyyy";
		}
	}
	//====[ DATE TIME ]=======================================================
	else if (valobj.type=="datetime"){
		if (dateTimeRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Date time format must be eg. dd/mm/yyyy hh:mm";
		}
	}
	//====[ CARD DATE ]=======================================================
	else if (valobj.type=="card_date"){
		if (carddateRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Please make a selection from this drop down.";
		}
	}
	//====[ TIME ]======================================================
	else if (valobj.type=="time"){
		if (timeRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Time format must be in 24hr format eg. 16:00";
		}
	}
	//====[ NUMERIC ]===================================================
	else if (valobj.type=="numeric"){
		if (numericRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Value entered should only contain digits eg. 1234";
		}
	}
	//====[ CSV ]===================================================
	else if (valobj.type=="csv"){
		if ((numericRegxp.test(value) == true && (value.length == 3 || value.length == 4)) || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Your security code should be either 3 or 4 digits.";
		}
	}
	//====[ CARD NUMBER ]===================================================
	else if (valobj.type=="card_number"){
		if (numericRegxp.test(value) == true && (value.length >= 12 && value.length <= 19) || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Please ensure you have entered your card number correctly.";
		}
	}
	//====[ INTEGER ]===================================================
	else if (valobj.type=="meters"){
		if (numericRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{
			robj.alt_tag = "Fabrics can only be purchased in whole meters.";
		}
	}
	//====[ CURRENCY ]==================================================
	else if (valobj.type=="currency"){
		if (currencyRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Must contain a positive currency value eg. 10.99"; 
		}
	}
	//====[ POSTCODE ]=================================================
	else if (valobj.type=="postcode"){
		//alert("TEST POSTCODE: "+value+ "RESULT: "+pcodeRegxp.test(value));
		if (pcodeRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Ensure postcode is valid eg. BN27 2GH"; 
		}
	}
	//====[ TELEPHONE ]=================================================
	else if (valobj.type=="telephone"){
		value = value.replace(/ /g, '');
		if (telnoRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1; 
		}else{ 
			robj.alt_tag = "Ensure this is a valid phone number."; 
		}
	}
	//====[ WEBSITE ADDRESS ]===========================================
	else if (valobj.type=="http"){
		if (urlRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{
			robj.alt_tag = "Please ensure you have entered a valid web address.";
		}
	}
	//====[ EMAIL ]=====================================================
	else if (valobj.type=="email"){
		//alert("RESULT: "+emailRegxp.test(value));
		if (emailRegxp.test(value) == true || (valobj.optional == 1 && value == '')){
			robj.good = 1;
		}else{ 
			robj.alt_tag = "Ensure this is a valid email address eg. somebody@domain.co.uk"; 
		}
	}

	//****[ SITE SPECIFIC VALIDATION ]***********************************
	else if (valobj.type=="cm" || (valobj.optional == 1 && value == '')){
		value = value.replace(/cm/,'');
		if (numericRegxp.test(value) == true){
			robj.good = 1;
		}else{
			robj.alt_tag = "This should be a value in cm eg. 100cm";
		}
	}
	//*******************************************************************

	//====[ NO VALIDATION TYPE FOUND ]===================================
	else{
		alert('Element ('+robj.element.name+') has unknown validation type! ('+valobj.type+')');
		robj.good = 1;
	}

	//###########################################################################
	// EXTRA VALIDATION TYPE PROCESSING ---------------------------------------------------------------------
	//###########################################################################
	// can be used to run secondary validation on form objects or to format existing value eg. round down to the 
	// nearest multiple of a given number. 
	//###########################################################################
	
	//====[ CHANGE CASE ]===========================================================================
	if(valobj.change_case){
		if(valobj.change_case == 'upper'){
			value = value.toUpperCase();
		}else if(valobj.change_case == 'lower'){
			value = value.toLowerCase();
		}else if(valobj.change_case == 'capitalise'){
			value = 'Coming soon to a validate script near you';
		}
	}

	//====[ MINIMUM ]===============================================================================
	if(valobj.minimum != null){
		if(value >= valobj.minimum){
			robj.alt_tag = "Entered value is below the minimum of " + valobj.minimum;
			robj.good = 0;
		}
	}
	//====[ MAXIMUM ]===============================================================================
	if(valobj.maximum != null){
		if(value <= valobj.maximum){
			robj.alt_tag = "Entered value is above the maximum of " + valobj.maximum;
			robj.good = 0;
		}
	}

	//====[ ROUND UP ]==============================================================================
	if(valobj.round_up){
		value = parseInt((value * valobj.round_up) / valobj.round_up);
	}

	//====[ ROUND DOWN ]============================================================================
	if(valobj.round_down){
		value = (value / valobj.round_down) * valobj.round_down;
	}

	//====[ EXPIRY DATE MULTIPLE SELECTS ]==========================================================
	// looks for dyn_expiry_month + dyn_expiry_year select fields
	if(valobj.expiry_date){
		var today = new Date();
		var mnth = rtnEl('dyn_expiry_month');
		var year = rtnEl('dyn_expiry_year');
		today.setDate(1);
		//alert(mnth.value + " " + year.value);
		if(mnth.value != -1 && year.value != -1){
			var checkdate = new Date();
			checkdate.setFullYear(parseInt('20'+year.value),(mnth.value - 1),2); // set as second day of month to remove time of day being problem, as you are comparing against first day of month always
			if(checkdate > today){
				clear_error({'element':mnth,'alt_tag':''}); clear_error({'element':year,'alt_tag':''});
			}else{
				var errstr = "This card has expired please ensure you have entered the information correctly.";
				hilite_error({'element':mnth,'alt_tag':errstr}); hilite_error({'element':year,'alt_tag':errstr});
				robj.good=0;
			}
		}
	}

	//====[ START DATE MULTIPLE SELECTS ]===========================================================
	// looks for dyn_start_month + dyn_start_year select fields
	if(valobj.start_date){
		var today = new Date();
		var mnth = rtnEl('dyn_start_month');
		var year = rtnEl('dyn_start_year');
		today.setDate(2);// set as second day of month to remove time of day being problem, as you are comparing against first day of month always

		//alert(mnth.value + " " + year.value);
		if(mnth.value != -1 && year.value != -1){
			var checkdate = new Date();
			checkdate.setFullYear(parseInt('20'+year.value),(mnth.value - 1),1);
			if(checkdate < today){
				clear_error({'element':mnth,'alt_tag':''}); clear_error({'element':year,'alt_tag':''});
			}else{
				var errstr = "This card is not active yet please ensure you have entered the information correctly.";
				hilite_error({'element':mnth,'alt_tag':errstr}); hilite_error({'element':year,'alt_tag':errstr});
				robj.good=0;
			}
		}
	}
	
	//====[ CONFIRMATION EMAIL ADDRESS ]============================================================
	if (valobj.conf_email){
		var errstr = "Please ensure both email address fields match before continuing";
		var confObj = rtnEl(valobj.conf_email);
		//alert(confObj);
		if(confObj){
			var test = (value == confObj.value)?1:0;
			if(test){
				clear_error({'element':confObj,'alt_tag':''});
			}else{
				robj.alt_tag = (robj.good)?errstr:robj.alt_tag;
				hilite_error({'element':confObj,'alt_tag':errstr});
				robj.good=0;
			}
		}else{
			alert("No email confirmation email field with id: "+valobj.conf_email);
		}	
	}
	
	//====[ IF BLANK CHECK ALTERNATE FIELD ]========================================================
	if(valobj.checkifblank){
		//alert("GOOD: "+robj.good+" CHECK OBJ: "+valobj.checkifblank+" Name: "+valobj.checkifblank.name+" Type: "+valobj.checkifblank.type);
		if(robj.good == 0){
			var cobj={};
			cobj.element = rtnEl(valobj.checkifblank.name);
			cobj.alt_tag = '';
			cobj.good = 0;
			cobj.type = valobj.checkifblank.type;

			var cvalobj={}
			cvalobj.type = valobj.checkifblank.type;

			if(cobj.element){
				validate_field_value(cobj,cvalobj);	
			}else{
				cobj.good = 0;
			}
			//alert("RESULT: "+cobj.good);
			robj.good = cobj.good;
		}
	}
	
	//====[ ON ERROR HILITE THESE ]=================================================================
	if(valobj.onerrorhilite){
		var elements = valobj.onerrorhilite.split(',');
		for(var x=0;x<elements.length;x++){
			var obj = rtnEl(elements[x]);
			//alert(obj.id);
			if(robj.good){
				clear_error({'element':obj,'alt_tag':''});
			}else{
				hilite_error({'element':obj,'alt_tag':robj.alt_tag});
			}
		}
	}
	
	// SET ELEMENT VALUE TO VALIDATED DATA AFTER PROCESSING
	robj.element.value = value;
}

//===========================================================

function clear_error(robj){
	if(robj.type = 'radio' && rtnEl(robj.element.name+"_div")) robj.element = rtnEl(robj.element.name+"_div");
	if(robj.element.style) robj.element.style.backgroundColor = "";
	if(robj.element.style) robj.element.style.color = "";
	robj.element.backgroundColor = "";
	robj.element.color = "";
	robj.element.title = (robj.alt_tag)?robj.alt_tag:"";
	robj.element.alt = (robj.alt_tag)?robj.alt_tag:"";
}

//===========================================================

function hilite_error(robj){
	if(robj.type = 'radio' && rtnEl(robj.element.name+"_div")) robj.element = rtnEl(robj.element.name+"_div");
	if(robj.element.style) robj.element.style.backgroundColor = "#AD3D36";
	if(robj.element.style) robj.element.style.color = "#ffffff";
	robj.element.backgroundColor = "#AD3D36";
	robj.element.color = "#ffffff";
	robj.element.title = (robj.alt_tag)?robj.alt_tag:"";
	robj.element.alt = (robj.alt_tag)?robj.alt_tag:"";
	
	if(robj.element.attachEvent){ robj.element.attachEvent('onchange',function(){ clear_error({'element':this}); } ); }
	else{ robj.element.addEventListener('change',function(){ clear_error({'element':this}); },false); }
}

//===========================================================

function display_error_dialogue(error,fail,scroll){
	var errorDivObj = rtnEl('message');
	if(errorDivObj){
		var display = (fail)?'block':'none';
		errorDivObj.style.display = display;
		errorDivObj.innerHTML = error;
	}
	if(scroll) window.scrollTo(0,0);
}

//===========================================================

function trim(s){
	s = s.replace(/ $/g,'');
	s = s.replace(/^ /g,'');
	return s;
}

function isBlank(s){  //BC 2006-12-05
	if (s == ''){return true}
	return !notblankRegxp.test(s);
}

//===========================================================

function isdisplayed(node){
	//This function is used to see if an INPUT is currently being displayed
	// ONLY looking for a encompassing DIV with a style including display:block or display:none
	// test with something like this:
	//       alert(isdisplayed(rtnEl("surname")))
	var n=0;
	var fnd=0;
	while (fnd==0){
		if(n>1) { node = node.parentNode; }
		if (node){
			if (node.style){
				if (node.style.display){
					//alert(node.style.display);
					if (node.style.display=='block'){
						fnd=0;
						//return true;
					}
					if (node.style.display=='none'){
						fnd=1;
						//return false;
					}
				}
			}
		}else{
			fnd=3;
		}
		if (n>20){
			return true;
		}
		n++;
	}
	if(fnd==1){
		return false;
	}
	if(fnd==0||fnd==3){
		return true;
	}
}
