/**
 * @constructor Validation
 * @version 1.5
 * @copyright (c) SICORO.COM
 * @license Free for not commercial use
 * @author Ilya Sabelnikov, fruit@sicoro.com
 * @use CUstomForm class.CustomForm.js
 * @manual no bottom of this doc
 */
function Validation()
{
    var div_id = null;
	var formID = null;
   	var form_valid = true;
   	var defines = new Array();
   	var thisForm = false;
   	var last_result = null;
   	
    function isRegExp()
	{
		if (new RegExp())
			return true;
		else
			return false;		
	}
	
	function getLinkedObject(name)
	{
		var obj = thisForm[name];
		if (!obj)
		{
			debug('incorrect link Object ' + name);
			return false;
		}
		return obj;
	}
	
	function debug(text)
	{
		var obj = document.getElementById('insertDiv');
		if (obj)
		{
			obj.innerHTML = obj.innerHTML + text + "<br />";
		}
	}
	
	function is_radio(type, nodename) 
	{
	 	if ((type == 'radio') && (nodename.toLowerCase() == 'input'))
	 		return true;
	 	else 
	 		return false;
	}
	
	function is_checkbox(type, nodename) 
	{
	 	if ((type == 'checkbox') && (nodename.toLowerCase() == 'input'))
	 		return true;
	 	else 
	 		return false;
	}
	
	function is_file(type, nodename) 
	{
	 	if ((type == 'file') && (nodename.toLowerCase() == 'input'))
	 		return true;
	 	else 
	 		return false;
	}
		
	function is_text(type, nodename) 
	{
	 	if (((nodename.toLowerCase() == 'input') && ((type.toLowerCase() == 'hidden') || (type.toLowerCase() == 'text') || (type.toLowerCase() == 'password'))) || (nodename.toLowerCase() == 'textarea'))
	 		return true;
	 	else 
	 		return false;
	}
	
	function display_error(obj_message)
	{
		var div = document.getElementById(div_id);
		
		div.style.cssText = 'display: block; padding:0px; margin:0px;';
		
		if (obj_message.value)
		{
			div.innerHTML = obj_message.msg.replace(/\(\$\)/g, obj_message.value);			
		}
		else
		{
			div.innerHTML = obj_message.msg;
		}
		form_valid = false;
	}
	
	function clear_work_divs()
	{
        // clear error box
        var div = document.getElementById('insertDiv');
    	if (div)
    		div.innerHTML = '';		
	}
	
	this.define = function(define_array)
	{
		if (!define_array.name || !define_array.target)
			return false;
		else
			defines.push(define_array);
		return true;
	}    

	this.run = function (form_id)
	{
		formID = form_id;
		clear_work_divs();
		
		thisForm = document.getElementById(form_id);
		if (!thisForm)
		{
			debug('No such form founded: ' + form_id)
			return false;
		}
		form_valid = true;	// must be init every validation time
		
        if (defines.length == 0)
		{
            debug('No elemets to validate!');
        	return false;
		}
		
		for (var i = 0; i < defines.length; i++)
		{	
			last_result = null; // quick exit in multi name
			
			if ((typeof defines[i].name) != 'string') // is object => array ;>
			{
				for (var j = 0; j < defines[i].name.length; j++) 
				{
					//alert(defines[i].name[j]);
					prepacking(defines[i].name[j], defines[i]);						
				}
			}
			else
			{
				prepacking(defines[i].name, defines[i]);
			}
        }
        
        return form_valid;
	}
		
	this.reset = function()
	{
		if (!formID)
		{
            debug('No such Form ID!');
			return false;
		}

		thisForm = document.getElementById(formID);
		
		if (defines.length == 0)
		{
            debug('No elemets to empty!');
        	return false;
		}
		
		for (var i = 0; i < defines.length; i++)
		{
			var obj = document.getElementById(defines[i].target);
			if (obj)
			{
				obj.innerHTML = '';
				obj.style.display = 'none';
			}
		}
	}
		
	function prepacking(elementName, define)
	{
		var atr_type = '';
		var node_name = '';
		var node_list = false;
		
		var obj = FormControl.hasElemByName(formID, elementName);
		if (!obj)
		{
			//alert('comment: form has not element ' + elementName);		
		    return false;
		}

		var div = document.getElementById(define.target);
		if (!div)
		{
			debug("Incorrect div target: " + define.target);
			return false;
		}
		div.innerHTML = '';
		div.style.display = 'none';
		
		div_id = define.target;	// set error target
        
		// < setting "atr_type" and "node_name" arguments 
		
		if ((elementName.substr(elementName.length - 2, 2) == '[]') && (obj.length > 1)) // is node list
		{
			
			node_list = true;
			if (obj[0].type) // node list
			{
				var type = obj[0].type;
					
				type = type.toLowerCase();
				
				if ((type == 'radio') ||  (type == 'file') || (type == 'checkbox'))
				{
					atr_type = type;
					node_name = obj[0].nodeName;
				}
				else
				{
					debug("Incorrect NodeList type: " + type);
					return false;
				}
			}
		}
		else
		{
			if (obj.type)
			{
				atr_type = obj.type;
				atr_type = atr_type.toLowerCase();
			}
			else
				atr_type = null;
			
			node_name = obj.nodeName;
		}
		
		debug(elementName + ' : ' + atr_type + ' ' + node_name);
		
		if (is_radio(atr_type, node_name) && define.necessary)
		{
			last_result = validate_radio(obj, define, node_list);
        }
        else if (is_checkbox(atr_type, node_name))
        {
        	last_result = validate_checkbox(obj, define, node_list);
        }
        else if (is_file(atr_type, node_name) && (define.allowed || define.denied))
        {
        	last_result = validate_file(obj, define, node_list);
        }
        else if (is_text(atr_type, node_name))
		{
		    last_result = validate_text(obj, define);
        }
        else if (node_name == 'SELECT')
        {
            last_result = validate_select(obj, define);
        }
        else
        {
        	debug('unknown: ' + elementName + ' len: ' + obj.length);
        }
        return true;
	}

	function validate_radio(obj, define, nodelist)
	{
		//debug('name:' + define.name + ' nodelist=' + !nodelist + ' ?' + !obj.checked);
		if (!nodelist && !obj.checked)
        {
        	display_error(define.necessary);
        	return false;
        }
        else if (nodelist && (obj.length > 0))
        {
            var isChecked = false;
            for (var j = 0; j < obj.length; j++)
		    {
		        if (obj[j].checked)
                {
                    isChecked = true;
                    break;
                }
		    }
		    
		    if (!isChecked)
		    {
		    	display_error(define.necessary);
		    	return false;
		    }
        }
        return true;
	}
	
	function validate_file(obj, define, nodelist)
	{
		function basename (path)
		{
			var pos = path.lastIndexOf('\\');
			if (!pos)
			{
				return path;
			}
			else
			{
				path = path.substring(pos + 1, path.length);
				return path;
			}
		}
		
		// Linux FS allow, but add slashes to:
		var lnx_pat = new RegExp(/^[^!@$^ &\*\(\)\\=\/\{\[\}\]:;"'\|<,>\?`]+\.[^!@$^ &\*\(\)\\=\/\{\[\}\]:;"'\|<,>\?`]+$/);
		var lnx_pat_sybmols = '( ! @ $ ^ space & * ( ) \\ = / { } [ ] : ; " \' | < > , ? ` )';
		
		// denieded simbols in Windows OS
		var win_pat = new RegExp(/^[^\/\\:\?"\*<>\|]+\.[^\/\\:\?"<>\|]+$/);
		var win_pat_sybmols = '( / \\ : ? " * > < | )';
		
		if (!nodelist)
	    {
	    	var file_base = basename(obj.value);
	    	
	    	if (/^[\s]*$/.test(file_base)) // is empty
	    	{
	    		if (define.necessary)  // if empty and necessary, then show error
	    		{
	    			display_error(define.necessary);
		    		return false;
	    		}
	    	}
	    	else // is not empty
	    	{
		    	if (define.allowed && !define.allowed.pattern.test(file_base))
		    	{
		    		display_error(define.allowed);
		    		return false;
		    	}
		    	else if (define.denied && define.denied.pattern.test(file_base))
		    	{
		    		display_error(define.denied);
		    		return false;
		    	}
            	else if (define.windows && !win_pat.test(file_base))
            	{
            		define.windows.value = new Object();
            		define.windows.value = win_pat_sybmols;
            		display_error(define.windows);
					return false;
            	}
            	else if (define.linux && !lnx_pat.test(file_base))
            	{
            		define.linux.value = new Object();
            		define.linux.value = lnx_pat_sybmols;
            		display_error(define.linux);
            		return false;
            	}
 	    	}
	    	return true;
	    }
	    else if (nodelist && (obj.length > 0))
	    {
	    	var correct_files = 0;
	    	var blocked = false;
	    	
	    	for (var j = 0; j < obj.length; j++) 
	    	{
		    	var file_base = basename(obj[j].value);
    		
	    		// if no empty, test by patterns
	    		
	    		if (!/^[\s]*$/.test(file_base))
	        	{
					if (define.allowed && !define.allowed.pattern.test(file_base))
	            	{
	            		display_error(define.allowed);
	            		blocked = true;
	            		break;
	            	}
	            	else if (define.denied && define.denied.pattern.test(file_base))
	            	{
	            		display_error(define.denied);
	            		blocked = true;
	            		break;	                    		
	            	}
	            	else if (define.windows && !win_pat.test(file_base))
	            	{
	            		define.windows.value = new Object();
	            		define.windows.value = win_pat_sybmols;
	            		display_error(define.windows);
	            		blocked = true;
	            		break;
	            	}
	            	else if (define.linux && !lnx_pat.test(file_base))
	            	{
	            		define.linux.value = new Object();
	            		define.windows.value = lnx_pat_sybmols;
	            		display_error(define.linux);
	            		blocked = true;
	            		break;
	            	}
	            	else
	            	{
	            		++correct_files;
	            	}
	        	}
	    	}
	    	if (define.minCount && !blocked)
	    	{	
	    		if (correct_files < define.minCount.value)
	    		{
	    			display_error(define.minCount);
	    			return false;
	    		}
	    	}
	    }
	    //debug(file_base);
	    return true;
	}
	
	function validate_text(obj, define)
	{
		// if is set empty = true|false;
		if (define.empty)
		{
			if (define.empty.value && (obj.value.length == 0))
			{
				return true;
			}
			else if (!define.empty.value && /^[\s]*$/.test(obj.value)) //is empty
			{
				display_error(define.empty);
				return false;
			}
		}
		
		if (define.email)
		{
			//((obj.value.indexOf(".") == -1) || (obj.value.indexOf("@") == -1))
			
			var email_pattern = new RegExp(/^[a-zA-Z\d][\w\.-]+@[a-zA-Z\d][a-zA-Z\d\.-]+\.[a-zA-Z]{2,4}$/);
            if (!email_pattern.test(obj.value))
			{
                display_error(define.email); 
                return false;
            }
		}

		if (define.password)
		{
			var copy_obj = getLinkedObject(define.password.value);	// if defined element not exists
			if (!copy_obj)
			{
			    debug("Incorrect object '" + define.password.value + "'");
			    return false;
			}
			else
			{
				if (copy_obj.value != obj.value)
				{
					display_error(define.password); 
                	return false;
				}
			}
			return true;
		}
		
        if (define.eqLen && (obj.value.length != define.eqLen.value))
		{
			display_error(define.eqLen);
			return false;
		}
        else if (!define.eqLen)
        {
	        if (define.minLen && (obj.value.length < define.minLen.value))
			{
				display_error(define.minLen);
				return false;
			}
			
			if (define.maxLen && (obj.value.length > define.maxLen.value))
			{
				display_error(define.maxLen);
				return false;
			}			
        }

		if (define.minVal && (obj.value < define.minVal.value))
		{
			display_error(define.minVal);
			return false;
		}
		
		if (define.maxVal && (obj.value > define.maxVal.value))
		{
			display_error(define.maxVal);
			return false;
		}
		
		if (isRegExp() && define.regexp)
		{
            var res = define.regexp.pattern.test(obj.value);
            if (!res)
            {
                display_error(define.regexp);
                return false;
            }
        }
        return true;
	}
	
	function validate_select(obj, define)
	{
		if (obj.multiple)
        {
            var count = 0;
            for (var j = 0; j < obj.options.length; j++ )
            {
                if (obj.options[j].selected)
                    ++count;
            }
            
            if (define.eqSel && (define.eqSel.value != count))
            {
                display_error(define.eqSel);
                return false;
            }
            else if (!define.eqSel)
            {
	            if (define.minSel && (count < define.minSel.value))
	            {
	                display_error(define.minSel);
	                return false;
	            }
	            
	            if (define.maxSel && (count > define.maxSel.value))
	            {
	                display_error(define.maxSel);
	                return false;
	            }
            }
        }
        else
        {
          	if (define.necessary)
            {
               	if (obj.value == "")
                {
                    display_error(define.necessary);
                    return false;
                }
            }
            if (define.dimention)
            {
            	// RegExp 
            	var RE_year = /^[1-9][0-9]{3}$/; // OK if (1000 - 9999)   
            	var RE_month = /^(0?[1-9]|1[0-2])$/; // OK if (01 - 12) or (1 - 12)
            	var RE_day = /^(0?[1-9]|[1-2][0-9]|3[0-1])$/;  // OK if (01 - 31) or (1 - 31)
            	
            	var year_equel = true;
            	var month_equel = true;
            	var day_equel = true;
            	
            	if (define.dimention.year && define.dimention.year.start && define.dimention.year.stop)
            	{
            		var yearStart = getLinkedObject(define.dimention.year.start);
            		if (!yearStart)
            			return false;
            		
            		var yearStop = getLinkedObject(define.dimention.year.stop);
            		if (!yearStop)
            			return false;
            		
            		//yearStart.value = new Number(yearStart.value);
            		//yearStop.value = new Number(yearStop.value);
            		
            		if (!RE_year.test(yearStart.value) || !RE_year.test(yearStop.value))
            		{
            			display_error(define.dimention.year);
            			return false;
            		}
            		/*if (yearStart.value > yearStop.value)
            		{
            			display_error(define.dimention);
            			return false;
            		}
            		
            		if (yearStart.value != yearStop.value)
            		{
            			year_equel = false;
            		}*/
            	}
            	if (define.dimention.month && define.dimention.month.start && define.dimention.month.stop && year_equel)
            	{
            		var monthStart = getLinkedObject(define.dimention.month.start);
            		if (!monthStart)
            			return false;
            		
            		var monthStop = getLinkedObject(define.dimention.month.stop);
            		if (!monthStop)
            			return false;
            		
            		//monthStart.value = new Number(monthStart.value);
            		//monthStop.value = new Number(monthStop.value);
            		
            		if (!RE_month.test(monthStart.value) || !RE_month.test(monthStop.value))
            		{
            			display_error(define.dimention.month);
            			return false;
            		}
            		
            		/*if (monthStart.value > monthStop.value)
            		{
            			display_error(define.dimention);
            			return false;
            		}
            		
            		if (monthStart.value != monthStop.value)
            		{
            			month_equel = false;
            		}*/
            	}
            	if (define.dimention.day && define.dimention.day.start && define.dimention.day.stop && month_equel)
            	{
            		var dayStart = getLinkedObject(define.dimention.day.start);
            		if (!dayStart)
            			return false;
            		
            		var dayStop = getLinkedObject(define.dimention.day.stop);
            		if (!dayStop)
            			return false;
            		
            		//dayStart.value = new Number(dayStart.value);
            		//dayStop.value = new Number(dayStop.value);
            		
            		if (!RE_day.test(dayStart.value) || !RE_day.test(dayStop.value))
            		{
            			display_error(define.dimention.day);
            			return false;
            		}
            		
            		/*if (dayStart.value > dayStop.value)
            		{
            			display_error(define.dimention);
            			return false;
            		}
            		
            		if (dayStart.value != dayStop.value)
            		{
            			day_equel = false;
            		}*/
            	}
            	
            	var start_number = 0;
            	var stop_number = 0;
            	
            	if (dayStart && dayStop)
            	{
            		start_number += parseInt(dayStart.value) * 1;
            		stop_number += parseInt(dayStop.value) * 1;
            	}

            	if (monthStart && monthStop)
            	{
            		start_number += (parseInt(monthStart.value) - 1) * 30;
            		stop_number += (parseInt(monthStop.value) - 1) * 30;
            	}

            	if (yearStart && yearStop)
            	{
            		start_number += (parseInt(yearStart.value) - 1) * 12 * 60;
            		stop_number += (parseInt(yearStop.value) - 1) * 12 * 60;
            	}
            	
            	if (start_number > stop_number)
            	{
            		display_error(define.dimention);
            		return false;
            	}
            		
            	// if min min.start...
            	
            	// if sec sec.start...
            	
            	// if msec msec.start...
            }
        }
        return true;
	}
	
	function validate_checkbox(obj, define,  nodelist)
	{
		if (!nodelist || (!define.minSel && !define.maxSel && !define.eqSel))
			return true;
		
		
		var checked_count = 0;
		
		for (var j = 0; j < obj.length; j++) 
		{
			if (obj[j].checked)
			{
				++checked_count;
			}
		}
		
		if (define.eqSel)
		{
			if (define.eqSel.value != checked_count)
			{
				display_error(define.eqSel);
				return false;
			}
		}
		else
		{
			if (define.minSel && (checked_count < define.minSel.value))
			{
				display_error(define.minSel);
				return false;
			}
			
			if (define.maxSel && (checked_count > define.maxSel.value))
			{
				display_error(define.maxSel);
				return false;
			}
			
		}
		return true;
	}
}


/*
*
* Available options
* 
* + File
* 	+ (allowed OR denied) [ RegExp:pattern, String:msg ]
* 	+ minCount [ Number:value, String:msg ] 						!!! only in "file[]"
* 	+ necessary [ String:msg ] 										!!! only in "file"
* 	+ (linux OR windows) [ String:msg ]
* 
* + Text
* 	+ email [ String:msg ]
* 	+ password [ String:value, String:msg ]
* 	+ ((minLen AND maxLen) OR eqLen) [ Number:value, String:msg ]
* 	+ regexp [ RegExp:pattern, String:msg ]
* 	+ minVal [ Number:value, String:msg ]
* 	+ maxVal [ Number:value, String:msg ]
*   + empty [ Boolen:value, [String:msg ]]
* 
* + Radio
* 	+ necessary [ String:msg ]
* 
* + Checkbox
* 	+ ((minSel AND maxSel) OR eqSel) [ Number:value, String:msg ]	!!! only in "checkbox[]"
* 
* + Select
* 	+ ((minSel AND maxSel) OR eqSel) [ Number:value, String:msg ] 	!!! only in multi SELECT
* 	+ necessary [ String:msg ] !!! only in simple SELECT
* 	+ dimention [ year :
* 
* new in v.1.1
*   * similar form element by one rule could be setted using array of name
* 		Check.define
* 		({	
* 	  		name : ['select1', 'select2', 'select3'],
* 	  		target : 'selectDiv',
* 	  		necessary: {msg:"Select something one"}
* 		});
* 
* new in v.1.4
*	* можно проверить реальность промежутока времени в Select элементах, если заданы 2 даты
*   * начала (год/месяц/день) и конец (год/месяц/день)
* 	Check.define
*	({
*		name : 'start_day',
*		target : 'dimentionError',
*		dimention : 
*		{
*			day: {start: 'start_day', stop: 'stop_day', msg: "Не корректные числа дней!"}, 
*			month : {start: 'start_month', stop: 'stop_month', msg: "Не корректные числа месецев!"},
*			year : {start: 'start_year', stop: 'stop_year', msg: "Не корректные числа года!"},
*			msg : "Некорректно задан промежуток времени!"	
*		}
*	});
* 	
* new in v.1.5
* 	 * now it using addon JS class class.CustomForm.js 
*/

/*
 * validation start
 * 
 * var Check = new Validation();
 * 
 * тут следуют описания правил к нужным элементам формы
 * 
 * затем добавить событие "onsubmit"
 * <form id="formMulti" onsubmit="return Check.run('formMulti')"></form>
 * 
 * обязательно тип кнопки должен быть "submit"
 * <input type="submit" value="Send data" />
 * 
 
 * 
 * примеры Validation.define({});
 * 

Check.define
({	
    name : ['select1', 'select2', 'select3'],
    target : 'selectDiv',
    necessary: {msg:"Select something one"}
});

/*Check.define
({
	name : 'pass',
	target : 'passDiv',
	minLen : {value: 5, msg : "min = ($)"},
	maxLen : {value: 10, msg : "max = ($)"},
	regexp : 
	{ 
		pattern: new RegExp(/^[\w]{5,10}$/),
		msg: "Denieded simbols"
	}
});

Check.define
({
	name : 'passConf',
	target : 'passConfDiv',
	password : {value: 'pass', msg : 'Password does not equal'}
});

Check.define
({
    name : 'nickname',
    target : 'nickNameDiv',
    minLen : {value : 3, msg : "At least ($) simbols."},
    maxLen : {value : 20, msg : "At most ($) simbols."},
    regexp : 
    {
    	pattern : new RegExp(/^[a-zA-Z][a-zA-Z0-9_.-]{2,20}$/), 
    	msg : "Allowed: a-z, A-Z, 0-9, slash, dot, underline\nMust start with word: exp. \"My.nick-93\""
    }
});

Check.define
({
    name : 'email',
    target : 'emailDiv',
    email: {msg:"Email is not correct"}
});

Check.define
({
    name : 'radioField',
    target : 'radioDiv',
    necessary : { msg:"Select something one"}
});

Check.define
({
    name : 'radioList[]',
    target : 'radioListDiv',
    necessary : { msg:"Select something one"}
});

Check.define
({
    name : 'checkBoxList[]',
    target : 'checkBoxListDiv',
    minSel : {value: 1, msg: "At least ($) selects."},
    //eqSel : {value: 2, msg: "You must select ($) items."},
    maxSel : {value: 2, msg: "At most ($) selects."}
});

Check.define
({
    name : 'areaField',
    target : 'areaDiv',
    minVal : {value : -2, msg : "Value must be more then ($)."},
    maxVal : {value : 12, msg : "Value must be less then ($)."},
    regexp : {pattern: new RegExp(/^[\-]?[0-9]+$/), msg: "It must be numeric"}
});

Check.define
({
    name : 'multySelect',
    target : 'selectMultiDiv',
    minSel : {value: 1, msg: "At least ($) selects."},
    //eqSel : {value: 4, msg: "You must select ($) items."},
    maxSel : {value: 2, msg: "At most ($) selects."}
});

Check.define
({
	name: 'myFile',
	target : "fileDiv",
	//allowed : {pattern: new RegExp(/(php|txt)$/i), msg: "Sorry, allow only files by format '*.txt', '*.php'"},
	denied : {pattern: new RegExp(/(.jpg|.psd)$/i), msg: "Sorry, types '*.jpg', '*.psd' denieded here"},
	necessary : {msg : "File is required."},
	//linuxOS: {msg: "Sorry, you file name contain denieded file name simbols"},
	windows : {msg: "Sorry, you file name contain denieded file name simbols"}
});

Check.define
({
	name: 'myFileList[]',
	target : "fileDivList",
	allowed : {pattern: new RegExp(/(.php|.txt)$/i), msg: "Sorry, allow only files by format '*.txt', '*.php'"},
	//denied : {pattern: new RegExp(/(.jpg|.psd)$/i), msg: "Sorry, types '*.jpg', '*.psd' denieded here"},
	minCount : {value: 1, msg : "Select minimum ($) file"},
	linuxStrike : { msg: "No exclusive simbols here!"}
});
*/