Validator = Class.create();
Object.extend(Validator.prototype,{
	initialize: function(element, options)
	{
		// Options
		this.options = new Object.extend(
		{
			live: false,
			errorClass: 'error_ins',
			alert: false,
			box: false,
			split: false,
			boxClass: false,
			onStart: false,
			onEnd: false,
			onSubmit: false,
			image: false

        }, options || {});

		this.element = $(element);
		this.validate = [];
		this.index = 0;


		/*
			Handlers
		*/
		this.clickHandler = this.submitForm.bindAsEventListener(this);

		this.element.observe('submit', function(e) { Event.stop(e); }.bind(this));

		if (!this.options.image)
		{
			this.element.select('INPUT[type="submit"]').each(
				function(s)
				{
					s.observe('click', this.clickHandler);
				}.bind(this)
			);
		}
		else
		{
			this.element.select('INPUT[type="image"]').each(
				function(s)
				{
					s.observe('click', this.clickHandler);
				}.bind(this)
			);
		}

	},
	remove: function (element, method)
	{
		this.validate.each(
			function(s, i)
			{
				if (s.element == element && s.method == method)
				{
                    this.validate.splice(i, 1);
					this.hideError(s);
				}
			}.bind(this)
		);
	},
	add: function(element, method, options)
	{

//document.mainForm.element1
//this.element

		options.element = element;
		options.method = method;

		if (typeof(options.live) == 'undefined')
		{
			if (this.options.live) $(element).observe(options.event, this.eventHandler.bindAsEventListener(this, this.index, options));
		}

		this.validate.push(options);
		this.index++;
	},
	eventHandler: function(e, index, options)
	{
		this.singleProcess(index, this.validator(options, index));
	},
	submitForm: function(e)
	{
		if (this.options.onStart) eval(this.options.onStart);

		this.errors = [];
		this.show = [];
		this.wait = [];

		this.validate.each(
			function(s, i)
			{
				if (s.method != 'Ajax') Object.extend(this.validate[i], {error: this.validator(s, i)});
				else
				{
					this.validator(s, i);
					this.wait[i] = false;
				}

			}.bind(this)
		);

		if (!this.wait) this.process(e);
		else
		{
		  new PeriodicalExecuter(function(pe)
		  {
		  	var wait = 0;
			this.wait.each(
				function (s)
				{
					if (s != 'undefined')
					{
						if (s === false) wait++;
					}
				}
			);


   			if (wait == 0)
			{
				this.process(e);
				pe.stop();
	  		}

		  }.bind(this), 1);
		}

		return false;
	},
	hideAllError: function()
	{
		this.validate.each(
			function(s, i)
			{
				this.hideError(s);
			}.bind(this)
		);
	},
	hideError: function(opt)
	{
		if (typeof(opt.show) != 'undefined')
		{
			if (typeof(opt.value) != 'undefined') $(opt.show).update(opt.value);
			else $(opt.show).update();
		}

		if (typeof(opt.errorClass) != 'undefined')
		{
			$(opt.element).removeClassName(opt.errorClass);
		}
		else
		{
			if (this.options.errorClass) $(opt.element).removeClassName(this.options.errorClass);
		}
	},
	showError: function(opt)
	{
		if (typeof(opt.show) != 'undefined') $(opt.show).update(opt.text);
		else this.collector.push(opt.text);

		if (typeof(opt.errorClass) != 'undefined')
		{
			$(opt.element).addClassName(opt.errorClass);
		}
		else
		{
			if (this.options.errorClass) $(opt.element).addClassName(this.options.errorClass);
		}
	},
	showErrors: function(array)
	{
		this.collector = [];
		this.validate.each(
			function(s, i)
			{
				var res = array.get(s.element);

				if (res == 0 && !s.error) return true;

				if (res == 0 && s.error) this.hideError(s);

				if (res != 0 && !s.error) this.showError(s);

			}.bind(this)
		);

		if (this.collector)
		{
			if(this.options.alert) alert(this.collector.join(', '));
			if(this.options.box)
			{
				if (this.options.split)
				{
					$(this.options.box).update(this.collector.join(', '));
				}
				else
				{
					$(this.options.box).update('');
					var ul = new Element('ul');

					this.collector.each(
						function (s)
						{
							var li = new Element('li').update(s);
							ul.appendChild(li);
						}
					);

					$(this.options.box).appendChild(ul).scrollTo();

					if (this.options.boxClass) $(this.options.box).addClassName(this.options.boxClass);
				}
			}
		}
		else
		{
			if(this.options.box)
			{
				$(this.options.box).update('');
				if (this.options.boxClass) $(this.options.box).removeClassName(this.options.boxClass);
			}
		}

	},
	singleProcess: function(index, mode)
	{
		if (!mode) this.showError(this.validate[index]);
		else this.hideError(this.validate[index]);
	},
	process: function(e)
	{
		var errors = 0;
		var array = $H({});
		this.validate.each(
			function(s, i)
			{
				if (typeof(array.get(s.element)) == 'undefined') array.set(s.element, 0)
				if (!s.error)
				{
					var tt = array.get(s.element);
					tt++;
					array.set(s.element, tt);
					errors++;
				}

			}.bind(this)
		);

		if (this.options.onEnd) eval(this.options.onEnd);

		if (errors == 0)
		{
			if (this.options.onSubmit)
			{
           		if(this.options.box) $(this.options.box).update('');
				this.hideAllError();
				eval(this.options.onSubmit);
			}
			else
			{
				var el = new Element('input', {type: 'hidden', value: Event.element(e).value, name: Event.element(e).name});
				this.element.appendChild(el);

				this.element.submit();
			}
		}
		else this.showErrors(array);
	},
	validator: function(object, iterator)
	{
		var element = object.element;
		var method = object.method;

		switch(method)
		{
			case 'isNotEmpty':
				return !$F(element).empty();
			break;
			case 'isString':
				return !!$F(element).strip().match(/^[\-\w\W]+$/gi);
			break;
			case 'isLatinString':
				return !!$F(element).strip().match(/^[\-\w]+$/gi);
			break;
			case 'isInteger':
				return !!$F(element).strip().match(/(^-?\d+$)/);
			break;
			case 'isNotNull':
				if ($F(element) == 0) return false;
				else return true;
			break;
			case 'isMax':
				return $F(element).strip().length <= object.max;
			break;
			case 'isMin':
				return $F(element).strip().length >= object.min;
			break;
			case 'isEqual':
				return $F(element).strip().length == object.equal;
			break;
			case 'isDate':
				if (typeof(object.date) == 'undefined') // d.m.Y
				{
					d = $F(element).match(/^(\d{2})\.(\d{2})\.(\d{4})$/);
				  	return d && !!(d[1]<=31 && d[2]<=12 && d[3]<=9999) || false;
				}
				else if (object.date = 'Y-m-d')
				{
					d = $F(element).match(/^(\d{4})-(\d{2})-(\d{2})$/);
				  	return d && !!(d[1]<=9999 && d[2]<=12 && d[3]<=31) || false;
				}
			break;
			case 'isDatetime':
				if (typeof(object.datetime) == 'undefined') // d.m.Y H:i:s
				{
				  	dt = $F(element).match(/^(\d{2})\.(\d{2})\.(\d{4})\s(\d{2}):(\d{2}):(\d{2})$/);
				  	return dt && !!(dt[1]<=31 && dt[2]<=12 && dt[3]<=9999 && dt[4]<=59 && dt[5]<=59 && dt[6]<=59) || false;
				}
				else if (object.datetime = 'Y-m-d H:i:s')
				{
				  	dt = $F(element).match(/^(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})$/);
				  	return dt && !!(dt[1]<=9999 && dt[2]<=12 && dt[3]<=31 && dt[4]<=59 && dt[5]<=59 && dt[6]<=59) || false;
				}
			break;
			case 'isTime':
			  	dt = $F(element).match(/^(\d{2}):(\d{2}):(\d{2})$/);
			  	return dt && !!(dt[1]<=59 && dt[2]<=59 && dt[3]<=59) || false;
			break;
			case 'isEmail':
			  	return !!$F(element).strip().match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i);
			break;
			case 'isRange':
				return $F(element).strip().length >= object.min && $F(element).strip().length <= object.max;;
			break;
			case 'isMatch':
				var value = $F(object.match).strip();
				return $F(element).strip() == value;
			break;
			case 'isCheck':
				return $(element).checked;
			break;
			case 'Ajax':

				if (typeof(object.params) != 'undefined')
				{
					if (typeof(object.params) == 'function') var add_params = object.params();
					else var add_params = object.params;
				}
				else var add_params = '';

				var params = 'value=' + escape(encodeURIComponent($F(element).strip()));



				new Ajax.Request(object.url,{method:'post', parameters: params + add_params, onSuccess: function(req)
				{
					this.processForm(element, method, iterator, req);
					}.bind(this)
				});
			break;
		}
	},
	processForm: function(element, method, iterator, req)
	{
		var error = req.responseText
		//console.log(error);
		if(error != 'error') Object.extend(this.validate[iterator], {error: true});
		else Object.extend(this.validate[iterator], {error: false});
		this.wait[iterator] = true;
	},
	destroy: function()
	{
		this.validate = [];
		this.index = 0;

		this.element.select('INPUT[type="submit"]').each(
			function(s)
			{
				Event.stopObserving(s, 'click', this.clickHandler);
			}.bind(this)
		);
	}
});
if(typeof(Object.Event) != 'undefined')
	Object.Event.extend(Validator);
