// regular expressions or function to validate the format
var regexpDate = /^(\d{1,2})\-(\d{1,2})\-(\d{4})$/,
regexpTime = /^(\d{1,2})\:(\d{1,2})\:(\d{1,2})$/,
arrFormats = {
	'alpha'   		: /^[a-zA-Z\.\-]*$/,
	'domainname'  : /^[a-zA-Z0-9\-]*$/,
	'freetext' 		: /^.*/,
	'alphanum'		: /^\w+$/,
	'unsigned'		: /^\d+$/,
	'integer' 		: /^[\+\-]?\d*$/,
	'real'    		: /^[\+\-]?\d*\.?\d*$/,
	'currency'    	: /^\$?([1-9]{1}[0-9]{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$|^\-?\$?([1-9]{1}\d{0,2}(\,\d{3})*(\.\d{0,2})?|[1-9]{1}\d{0,}(\.\d{0,2})?|0(\.\d{0,2})?|(\.\d{1,2}))$/,
	'email'   		: /^[a-zA-Z][\w\.-]*[a-zA-Z0-9]?@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/,
	'phone'   		: /^[\d\.\s\-\(\)\{\[\}\]]+$/,
	'date'    		: function (strDate) {
		// check format
		if (!regexpDate.test(strDate))
			return false;
		// check allowed ranges	
		if (RegExp.$1 > 31 || RegExp.$2 > 12)
			return false;
		// check number of day in month
		var dt_test = new Date(RegExp.$3, Number(RegExp.$2-1), RegExp.$1);
		if (dt_test.getMonth() != Number(RegExp.$2-1))
			return false;
		return true;
	},
	'time'    : function validate_time(strTime) {
		// check format
		if (!regexpTime.test(strTime))
			return false;
		// check allowed ranges	
		if (RegExp.$1 > 23 || RegExp.$2 > 59 || RegExp.$3 > 59)
			return false;
		return true;
	}
},
arrErrorMessages = [
	'No form name passed to validator construction routine',
	'No array of "%form%" form fields passed to validator construction routine',
	'Form "%form%" can not be found in this document',
	'Incomplete "%n%" form field descriptor entry. "l" attribute is missing',
	'Can not find form field "%n%" in the form "%form%"',
	'Can not find label tag (id="%t%")',
	'Can not verify match. Field "%m%" was not found',
	'"%l%" is a required field',
	'Value for "%l%" must be %mn% characters or more',
	'Value for "%l%" must be no longer than %mx% characters',
	'"%v%" is not a valid value for "%l%"',
	'"%l%" must match "%ml%"',
	'Value for "%l%" must be different from it\'s default value of "%d%"' 
]

// validator construction routine
function validator(strFormName, arrFields, objConfig) {
	this.fncError = validator_error;
	this.fncAlert = objConfig && objConfig.alert
		? function(strMessage) { alert(strMessage); return false }
		: function() { return false };
		
	// check required parameters
	if (!strFormName)	
		return this.fncAlert(this.fncError(0));
	this.strFormName = strFormName;
	
	if (!arrFields || typeof(arrFields) != 'object')
		return this.fncAlert(this.fncError(1));
	this.arrFields = arrFields;

	this.a_2disable = objConfig && objConfig['to_disable'] && typeof(objConfig['to_disable']) == 'object'
		? objConfig['to_disable']
		: [];
		
	this.exec = validator_exec;
}

// validator execution method
function validator_exec() {
	var objForm = document.getElementById(this.strFormName);
	if (!objForm)	
		return this.fncAlert(this.fncError(2));
		
	booDOM = document.body && document.body.innerHTML;
	
	// check integrity of the form fields description structure
	for (var n_key in this.arrFields) {
		// check input description entry
		this.arrFields[n_key]['n'] = n_key;
		if (!this.arrFields[n_key]['l'])
			return this.fncAlert(this.fncError(3, this.arrFields[n_key]));
		objInput = objForm.elements[n_key];
		if (!objInput)
			return this.fncAlert(this.fncError(4, this.arrFields[n_key]));
		this.arrFields[n_key].objInput = objInput;
	}

	// reset labels highlight
	if (booDOM)
		for (var n_key in this.arrFields) 
			if (this.arrFields[n_key]['t']) {
				var strLabeltag = this.arrFields[n_key]['t'], objLabelTag = get_element(strLabeltag);
				if (!objLabelTag)
					return this.fncAlert(this.fncError(5, this.arrFields[n_key]));
				this.arrFields[n_key].o_tag = objLabelTag;
				
				// normal state parameters assigned here
				cssjs('remove',objLabelTag,'alert');
				//objLabelTag.className = '';
			}

	// collect values depending on the type of the input
	for (var n_key in this.arrFields) {
		objInput = this.arrFields[n_key].objInput;
		if (objInput.value || objInput.checked) // text, password, hidden, checkbox
			this.arrFields[n_key]['v'] = objInput.value;
		else if (objInput.options) // select
			this.arrFields[n_key]['v'] = objInput.options[objInput.selectedIndex].value;
		else if (objInput.length > 0) // radiobuton
			for (var n_index = 0; n_index < objInput.length; n_index++)
				if (objInput[n_index].checked) {
					this.arrFields[n_key]['v'] = objInput[n_index].value;
					break;
				}
	}
	
	// check for errors
	var lngErrorCount = 0,
		n_another, objFormat_check;
	for (var n_key in this.arrFields) {

		objFormat_check = this.arrFields[n_key]['f'] && arrFormats[this.arrFields[n_key]['f']]
			? arrFormats[this.arrFields[n_key]['f']]
			: null;

		// reset previous error if any
		this.arrFields[n_key].lngError = null;

		// check reqired fields
		if (this.arrFields[n_key]['r'] && !this.arrFields[n_key]['v']) {
			this.arrFields[n_key].lngError = 1;
			lngErrorCount++;
		}
		// check length
		else if (this.arrFields[n_key]['mn'] && String(this.arrFields[n_key]['v']).length < this.arrFields[n_key]['mn']) {
			this.arrFields[n_key].lngError = 2;
			lngErrorCount++;
		}
		else if (this.arrFields[n_key]['mx'] && String(this.arrFields[n_key]['v']).length > this.arrFields[n_key]['mx']) {
			this.arrFields[n_key].lngError = 3;
			lngErrorCount++;
		}
		// check default
		else if ( (this.arrFields[n_key]['d'] ) && ((this.arrFields[n_key]['d'] == this.arrFields[n_key]['v']) && (String(this.arrFields[n_key]['v']).length != 0)))
		{
			this.arrFields[n_key].lngError = 6;
			lngErrorCount++;	
		}		
		
		// check format
		else if (this.arrFields[n_key]['v'] && this.arrFields[n_key]['f'] && (
			(typeof(objFormat_check) == 'function'
			&& !objFormat_check(this.arrFields[n_key]['v']))
			|| (typeof(objFormat_check) != 'function'
			&& !objFormat_check.test(this.arrFields[n_key]['v'])))
			) {
			if(this.arrFields[n_key]['v'].length>25)
			{
				this.arrFields[n_key]['v'] = this.arrFields[n_key]['v'].substr(0, 20) + "..."
			}
			
			this.arrFields[n_key].lngError = 4;
			lngErrorCount++;
		}
		// check match	
		else if (this.arrFields[n_key]['m']) {
			for (var n_key2 in this.arrFields)
				if (n_key2 == this.arrFields[n_key]['m']) {
					n_another = n_key2;
					break;
				}
			if (n_another == null)
				return this.fncAlert(this.fncError(6, this.arrFields[n_key]));
			if (this.arrFields[n_another]['v'] != this.arrFields[n_key]['v']) {
				this.arrFields[n_key]['ml'] = this.arrFields[n_another]['l'];
				this.arrFields[n_key].lngError = 5;
				lngErrorCount++;
			}
		}

		
	}

	// collect error messages and highlight captions for errorneous fields
	var strAlertMessage = '',
		objFirstErrorField;

	if (lngErrorCount) {
		for (var n_key in this.arrFields) {
			var lngError_type = this.arrFields[n_key].lngError,
				strMessage = '';
				
			if (lngError_type)
				strMessage = this.fncError(lngError_type + 6, this.arrFields[n_key]);
			else
			{
				//cssjs('remove',this.arrFields[n_key].o_tag,'taskNotDone');
				//cssjs('add',this.arrFields[n_key].o_tag,'taskDone');
			}
				

			if (strMessage) {
				if (!objFirstErrorField)
					objFirstErrorField = objForm.elements[n_key];
				strAlertMessage += strMessage + "\n";
				// highlighted state parameters assigned here
				if (booDOM && this.arrFields[n_key].o_tag)
				{
					//cssjs('remove',this.arrFields[n_key].o_tag,'taskDone');
					//cssjs('add',this.arrFields[n_key].o_tag,'taskNotDone');
				}
					cssjs('add',this.arrFields[n_key].o_tag,'alert');
			}
		}
		//alert("There were problems with the form :\n\n" + strAlertMessage);
		// set focus to first errorneous field
		if (objFirstErrorField.focus)
			objFirstErrorField.focus();
		// cancel form submission if errors detected
		//return false;
		return strAlertMessage;
	}
	
	for (n_key in this.a_2disable)
		if (objForm.elements[this.a_2disable[n_key]])
			objForm.elements[this.a_2disable[n_key]].disabled = true;
}

function validator_error(n_index) {
	var s_ = arrErrorMessages[n_index], n_i = 1, s_key;
	for (; n_i < arguments.length; n_i ++)
		for (s_key in arguments[n_i])
			s_ = s_.replace('%' + s_key + '%', arguments[n_i][s_key]);
	s_ = s_.replace('%form%', this.strFormName);
	return s_
}

function get_element (s_id) {
	return (document.all ? document.all[s_id] : (document.getElementById ? document.getElementById(s_id) : null));
}

	function cssjs(a,o,c1,c2)
	{
		switch (a){
			case 'swap':
				o.className=!cssjs('check',o,c1)?o.className.replace(c2,c1):o.className.replace(c1,c2);
			break;
			case 'add':
				if(!cssjs('check',o,c1)){o.className+=o.className?' '+c1:c1;}
			break;
			case 'remove':
				var rep=o.className.match(' '+c1)?' '+c1:c1;
				o.className=o.className.replace(rep,'');
			break;
			case 'check':
				return new RegExp('\\b'+c1+'\\b').test(o.className)
			break;
		}
	}