/**********************************************************************************

Validation sample code and information available on the Datamark Wiki

http://wiki.datamark.com/index.php/Browser_Side_Validation

last updated 2007-10-31 (boo) by Jason Lloyd

**********************************************************************************/
var DtmkValidator = {

	report : function(e) {						//simple function to condense arrays into strings with newline
		return e.join("\n");
	},
	
	checkOptions : function(e) {				//generic validations for validation options
		errors = false;
		if(!e.message) {
			errorList.push(this.objRef.name+" : 'message' is not defined");
			errors=true;
		}
		if(e.options.min) {
			if (e.options.min<1) {
				errorList.push(e.objRef.name+".options : min can not be less than one");
				errors=true;
			}
		}
		if(e.options.max) {
			if(e.options.max<e.options.min) {
				errorList.push(e.objRef.name+".options : max can not be less than min");
				errors=true;
			}
		}
		if(errors) {
			return false;
		} else {
			return true;
		}
	},
	
	runValidate : function(e) {
		if (e.required!=true && e.required!='optional') return true;		//no validation required (mis-spelled required values will cause validation to abort)
		switch(e.type) {
			case 'text':
			case 'textarea':
				return this.isText(e);
			break;
			case 'select-one':
				return this.isSelect(e);
			break;
			case 'select-multiple':
				return this.isMultipleSelect(e);
			break;
			case 'radio':
				return this.isRadio(e);
			break;
			case 'checkbox':
				return this.isCheck(e);
			break;
			case 'regex':
				return this.isRegex(e);
			break;
			case 'phone':
				return this.isPhone(e);
			break;
			case 'postalCode':
			case 'zipCode':
				return this.isPostalCode(e);
			break;
			case 'email':
				return this.isEmail(e);
			break;
			case 'function':
				return this.runFunction(e);
			break;
			default:
				errorList.push(e.objRef.name+" : '"+e.type+"' is not a defined validation type");
				return true;
			break;
		}
	},
	
	isText : function(e) {
		eMin = (e.options.min) ? e.options.min : 1;									//if no min is specified, min is 1
		if (e.objRef.value.length >= eMin) {										//perform value evaluation
			if(e.options.max) {
				if(e.objRef.value.length > e.options.max) {							//if value exceeds max
					return false;													//failed validation
				}
			}
			return true;															//passed validation
		} else {																	//perform empty evaluation
			if (e.required=='optional' && e.objRef.value.length == 0) {				//check for optional
				return true;
			}
		return false;																//failed validation
		}
	},
	
	isSelect : function(e) {
		if (e.required=='optional') {												//check for optional
			return true;
		}
		if(e.objRef.options[e.objRef.selectedIndex].value == "") return false;		//first option is selected? not valid
		return true;																//passed validation
	},
	
	isMultipleSelect : function(e) {
		eMin = (e.options.min) ? e.options.min : 1;									//if no min is specified, min is 1
		eMax = (e.options.max) ? e.options.max : e.options.min;						//if no max is specified, max is min
		count = 0;
		for(x=0; x<e.objRef.options.length; x++) {
			if(e.objRef.options[x].selected) count++;
		}
		if (e.required=='optional' && !count) {										//check for optional
			return true;
		}
		if(count < eMin || count > eMax) return false;								//if not between min and max failed
		return true;																//passed validation
	},
	
	isRadio : function(e) {
		if (e.required=='optional') return true;									//optional radio is always valid
		var list = document.getElementsByName(e.objRef.name);						//get array of objects with same name
		items = list.length;														//number of items with same name
		for(x=0; x<items; x++) {													//loop through radio items
			if (list[x].checked) return true;										//found a checked radio button.
		}
		return false;																//no radio buttons found to be checked, invalid
	},
	
	isCheck : function(e) {
		eMin = (e.options.min) ? e.options.min : 1;									//if no min is specified, min is 1
		eMax = (e.options.max) ? e.options.max : e.options.min;						//if no max is specified, max is min
		var list = document.getElementsByName(e.objRef.name);						//get array of objects with same name
		items = list.length;														//number of items with same name
		count = 0;
		for(x=0; x<items; x++) {													//count of items marked with a check
			if (list[x].checked) count++;
		}
		sentance = " can not be greater than the number of check boxes available";
		if(eMin > items) {
			errorList.push(e.objRef.name+" : min"+sentance);						//some last minute validation
			return true;															//abort validation
		}
		if(eMax > items) {
			errorList.push(e.objRef.name+" : max"+sentance);						//some last minute validation
			return true;															//abort validation
		}
		if(!count && e.required=='optional') return true;							//optional means no selection is OK.
		if(count > eMax || count < eMin) return false;								//if count is outside min - max, invalid
		return true;																//otherwise, valid
	},
	
	isRegex : function(e) {
		if(e.objRef.value == '' && e.required == 'optional') return true;			//if blank and optional, good to go.
		if(!e.options.pattern) {
			errorList.push(e.objRef.name+" : Regular expression not found");		//some last minute validation
			return true;
		}
		return e.objRef.value.match(e.options.pattern);
	},
	
	isPhone : function(e) {															//isRegex function handles "optional" flag
		switch(e.options.format) {
			case 'intl':															//international phone format
				e.options.pattern = "^[0-9 +*~()-]+$";
			break;
			case 'canus':															//canus format
			default:																//as well as any unknown formats
				e.options.pattern = "^[0-9]{3}-[0-9]{3}-[0-9]{4}$";
			break;
		}
		return this.isRegex(e);
	},
	
	isEmail : function(e) {															//isRegex function handles "optional" flag
		e.options.pattern = "^[a-zA-Z0-9._+%-]+@([a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$";
		return this.isRegex(e);
	},
	
	isPostalCode : function(e) {													//isRegex function handles "optional" flag
		switch(e.options.format) {
			case 'intl':															//international postal code check
				e.options.pattern = "^[a-zA-Z0-9][a-zA-Z0-9 -]+[a-zA-Z0-9]$";		//min 3 chars, must begin and end with alphanums
				return this.isRegex(e);
			break;
			case 'canus':
				step1 = false;
				step2 = false;
				e.options.format = 'can';											//setup canadian check
				step1 = this.isPostalCode(e);										//check canadian
				e.options.format = 'us';											//setup us check
				step2 = this.isPostalCode(e);										//check us
				return (step1 || step2);											//return true if either of those are true
			break;
			case 'can':																//check postal code, canadian style
				e.options.pattern = "^[ABCEGHJKLMNPRSTVXY][0-9][A-Z] ?[0-9][A-Z][0-9]$";
				return this.isRegex(e);
			break;
			case 'us':
			default:																//check zip code, us style
				e.options.pattern = "^[0-9]{5}(-[0-9]{4})?$";
				return this.isRegex(e);
			break;
		}
	},
	
	runFunction : function(e) {
		return e.options.callback(e);												//run specified function
	},
	
	validate : function(e) {
		var valObj;
		var errorList = Array();																			//ensure error list is empty
		var valMessages = Array();																			//ensure validation message list is empty
		for(i=0;i<e.elements.length;i++){																	//loop through form elements
			var fe = e.elements[i];																			//create shortcut to specific element
			if(fe.getAttribute('dtmk:validation')){															//look for validation configuration on element
				try{
					valObj = eval('('+fe.getAttribute('dtmk:validation')+')');								//read validation JSON into a validation object
					valObj.options = (typeof(valObj.options)=='undefined') ? new Array() : valObj.options;	//ensure that the options object exists
					valObj.objRef = new Object(fe);															//append the actual form element object to the valObj
					valObj.type = valObj.type || fe.type;													//if type is specified, use it, otherwise, use the HTML type
					if(!this.checkOptions(valObj)) continue;												//validate the valObj options and message, skip the element if it doesn't validate
					if(!this.runValidate(valObj)) {															//perform validation
						valMessages.push(valObj.message);													//if element does not validate, queue message.
					}
				}
				catch(err) {
					errorList.push(fe.name+" : "+err);														//queue any unhandled errors in validation (including badly coded JSON statements)
				}
			}
		}
		if(errorList.length) {
			alert("Errors:\n\n"+this.report(errorList));													//errors occurred during validation processing (bad configuration)
		}
		if(valMessages.length) {
			alert("Missing information:\n\n"+this.report(valMessages));
			return false;																					//validation failed
		}
		return true;																						//validation successfull
	}
}

