/*
	Plugin Validate v2.1.0
	http://imperavi.com/
 
	Copyright 2010, Imperavi Ltd.
	Dual licensed under the MIT or GPL Version 2 licenses.
	

*/
(function($){

	// Initialization	
	$.validate = function(el, options)
	{		
		var obj = new Construct(el, options);
		obj.init();
		return obj;
	};
	
	// Options and variables	
	function Construct(el, options) {

		this.opts = $.extend({
			callback: false,
			trigger: false,
			box: false,
			errorClassName: 'error_ins'
		}, options);
		
		this.$el = $(el);
	};

	// Functionality
	Construct.prototype = {
		init: function()
		{	
			this.collection = [];		
			
			this.$el.submit(function() { return false });
						
			if (!this.opts.trigger) this.opts.trigger = this.$el.find('input[type="submit"]');
			else this.opts.trigger = $(this.opts.trigger);
			
			this.opts.trigger.click(function() { this.validate(); }.bind(this));
		},
		add: function(element, method, options)
		{
			if (typeof(options) == 'undefined') var options = {};
		
			options.name = element;
			options.element = $(document.getElementsByName(element));
			options.method = method;
			options.error = true;
		
			this.collection.push(options);		
		},
		remove: function(element)
		{
			$.each(this.collection, 
				function(i, s)
				{

					if (s.element.name == element && s.method == method)
					{
    	                this.collection.splice(i, 1);
						this.hideError(s);
					}
					
				}.bind(this)
			);	
		},
		destroy: function()
		{
			this.collection = [];
			this.hideAllError();
		},
		showError: function(object)
		{
			if (object.method != 'isCheckAny') object.element.addClass(this.opts.errorClassName);
			
			if (!this.opts.box && typeof(object.text) != 'undefined')
			{
				$('#' + object.name + '_error').text(object.text);
			}
		},
		hideError: function(object)
		{
			object.element.removeClass(this.opts.errorClassName);
			
			if (!this.opts.box && typeof(object.text) != 'undefined')
			{
				$('#' + object.name + '_error').text('');
			}		
		},
		hideAllError: function()
		{
			$.each(this.collection, 
				function(i, s)
				{
					this.hideError(s);
				}.bind(this)
			);
		},	
		validate: function()
		{
			this.wait = [];
			
			$.each(this.collection, 
				function(i, s)
				{
					if (s.method != 'Ajax') $.extend(this.collection[i], { error: this.validator(s, i)} );
					else
					{
						this.validator(s, i);
						this.wait[i] = false;
					}
				}.bind(this)
			);
			
			if (!this.wait) this.process();
			else
			{
			  var re = setInterval(function()
			  {
	
			  	var wait = 0;
				$.each(this.wait, 
					function (s)
					{
						if (s != 'undefined' && s === false) wait++;
					}
				);
	
	   			if (wait == 0)
				{
	
					this.process();
					clearInterval(re);
		  		}
	
			  }.bind(this), 300);
			}
			
	
			return false;
				
		},
		process: function()
		{
			var errors = 0;
			var errors_obj = [];
			
			if (this.opts.box)
			{
				var box = $('<ul>');
				$('#' + this.opts.box).html('').hide();
			}
			
			$.each(this.collection, 
				function(i, s)
				{
					if (!s.error)
					{
	
						errors_obj.push(s);
					
						errors++;
					
					}
					else this.hideError(s);
					
				}.bind(this)
			);
			
	
			if (errors != 0)
			{		
				$.each(errors_obj, function(i, s)
				{
					if (this.opts.box)
					{
						var li = $('<li>').text(s.text);
						$(li).appendTo(box);
						this.showError(s);
					}
					else this.showError(s);
				}.bind(this));
			}
	
			if (this.opts.box && errors != 0)
			{
				$(box).appendTo($('#' + this.opts.box));
				$('#' + this.opts.box).show();
				this.scrollTo(this.opts.box);
			}
	
			if (errors == 0)
			{
				if (this.opts.callback)
				{
					this.hideAllError();
					this.opts.callback();
				}
				else
				{
					$('<input type="hidden" name="' + this.opts.trigger.get(0).name + '" value="' + this.opts.trigger.get(0).name + '" />').appendTo(this.$el);	
					this.$el.unbind('submit').submit();				
				}
			}
		
		},	
		unbindsubmit: function()
		{
			this.$el.unbind('submit').submit();
		},
		validator: function(object, iterator)
		{
			var element = object.element;
			var method = object.method;
			var value = element.val();
	
			switch(method)
			{
				case 'isNotEmpty':
					return !this.blank(value);
				break;
				case 'isNotNull':
					if (value == 0) return false;
					else return true;
				break;			
				case 'isString':
					return !!value.match(/^[\-\w\W]+$/gi);
				break;
				case 'isLatinString':
					return !!value.match(/^[\-\w]+$/gi);
				break;
				case 'isPhone':	
					if(value == '') return false;						
					return !!value.match(/^[0-9\s\.\;\:\#\(\)\*\+\-\/\=\[\]\\]+$/gi);				
				break;
				case 'isNumber':			
					return !!value.match(/(^-?\d+$)/);
					
				break;
				case 'isMax':
					return value.length <= object.max;
				break;
				case 'isMin':
					return value.length >= object.min;
				break;
				case 'isEqual':
					return value.length == object.equal;
				break;
				case 'isDate':
					if (typeof(object.date) == 'undefined') // d.m.Y
					{
						d = value.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 = value.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 = value.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 = value.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 = value.match(/^(\d{2}):(\d{2}):(\d{2})$/);
				  	return dt && !!(dt[1]<=59 && dt[2]<=59 && dt[3]<=59) || false;
				break;
				case 'isEmail':
				  	return !!value.match(/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i);
				break;
				case 'isRange':
					return value.length >= object.min && val.length <= object.max;;
				break;
				case 'isMatch':
					var value2 =  $(document.getElementsByName(object.target)).val();
					if (this.blank(value2))return false;
					return value == value2;
				break;
				case 'isCheck':
					return element.get(0).checked;
				break;
				case 'isCheckAny':
						
					var count = 0;	
					var cl = object.className;
					
					$.each($('.' + cl),
						function (i, s)
						{
							if ($(s).get(0).checked) count++;
						}
					);
					
					if (count == 0) return false;			
					else return true;
					
				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 = '';
	
					if (element !== false) var params = 'value=' + escape(encodeURIComponent(value));
					else var params = '';
				
					$.ajax({url: object.url, type:'POST', data: params + add_params, success: function(data)
						{
							this.processAjax(element, method, iterator, data);
						}.bind(this)
					});
					
				break;
			
			}		
		},
		processAjax: function(element, method, iterator, data)
		{
	
			if(data != 'error') this.collection[iterator].error = true;
			else this.collection[iterator].error = false;
	
			this.wait[iterator] = true;
		},
		blank: function(text)
		{
	       return !text || !/\S/.test(text);
		},
		scrollTo: function(element)
	  	{
		    element = $('#' + element);
	    	var pos = element.offset();
		    window.scrollTo(pos['left'], pos['top']);
		},
		normalize: function(str)
		{
		  return new Number((str.replace('px','')));
		}			
	};

	// bind
	Function.prototype.bind = function(object)
	{
	    var method = this; var oldArguments = $.makeArray(arguments).slice(1);
	    return function (argument)
	    {
	        if (argument == new Object) { method = null; oldArguments = null; }
	        else if (method == null) throw "Attempt to invoke destructed method reference.";
	        else { var newArguments = $.makeArray(arguments); return method.apply(object, oldArguments.concat(newArguments)); }
	    };
	}
	
	
})($);
