﻿~function(){
$.fn.form = function(settings){
	if ($(this).data('formsettings')) {
		$(this).data('form').setting(settings);
	} else {
		settings = settings || {};
		return this.each(function(){
			var $this = $(this), formFunc;
			if ($this.is('form')) {
				settings.method = $this.attr('method');
				settings.url = $this.attr('action');
			}
			settings = $.extend({
				type : 'ajax',
				validation : false
			}, settings);
			formFunc = new form(settings, $this);
			$this.attr({isform:1}).data({'formsettings':true, 'form':formFunc}).bind('submit', function(){
				return formFunc.submit();
			});
		});
	}
}

function form(settings, object) {
	this._data = {};
	this.isSubmit = false;
	this.object = object;
	this.dataNode = $('<div/>').appendTo(object).hide();
	this.settings = {
		onSubmit : function(){},
		onSubmited : function(){},
		onSubmitEnd : function(){},
		onSubmitError : function(){}
	};
	this.setting(settings);
}
	
$.extend(form.prototype, {
	setting : function(settings){
		var This = this;
		$.extend(this.settings, settings);
		This.getControl().attr('autoComplete', 'off');
		settings.validation && this.getControl().each(function(){
			var $this = $(this), name = $this.attr('name'), option;
			settings.validation[name] = option = settings.validation[name] || {};
			if (option.vdt) return;
			option.vdt = 1;
			$.each(settings.fn || {}, function(key, fn){
				if (option[key] === undefined) option[key] = fn;
			});
			if (!option.$tips) option.$tips = $('<span/>');
			option.$node = $this;
			if (!option.key) option.key = name;
			if (option.same) {
				This.getControl(option.same.key).blur(function(){
					$this.blur();
				});
			}
			if (option.server) {
				option.server.oldval = $this.val();
				isFunction(option.server.val) && (option.server.oldval = option.server.val.apply(option, [option.server.oldval]));
			}
			option.blur !== false && $this.is(':password,:text,textarea') && $this.blur(function(){
				This.validation.match($this.val(), option, This.data());
			}).focus(function(){
				isFunction(option.tips) && option.$tips && option.$tips.show().removeClass().addClass('ml10') && option.tips.apply(option);
			});
			$this.is(':visible') && isFunction(option.run) && option.run.apply(option, [settings.validation]);
		}) && (this.validation = new validation(settings.validation)) && $.each(settings.validation, function(){
			if (this.vdt) return;
			isFunction(this.run) && this.run.apply(this, [settings.validation]);
		});	
	},
	getControl : function(name){ return name ? this.object.find(':input[name='+name+']') : this.object.find(':input[name]'); },
	data : function(key, val){
		if (key === undefined || val === undefined) {
			var data = {};
			this.getControl().each(function(){
				var $this = $(this), 
					name  = $this.attr('name'), val = ($this.val() || '').trim();
				if ($this.is(':checkbox')) {
					!data[name] && (data[name] = []);
					if ($this.is('[checked]')) {
						data[name].push(val);
					}
				} else if (!$this.is(':radio') || $this.is('[checked]')) {
					data[name] = val;
				} else if (!data[name]) {
					data[name] = '';
				}
			});
			$.extend(data, this._data);
			return key ? data[key] : data;
		} else {
			this._data[key] = val;
		}
	},
	submit : function(){
		if (!this.isSubmit) {
			var This = this, settings = This.settings, data;
			if (this.settings.validation && this.validation.matchAll(this.data()) === false) return false;
			if (this.settings.onSubmit.apply(this, [this.settings.validation, this.object]) === false) return false;
			this._data['issubmit'] = 1;
			data = this.data();
			data.issubmit = this.isSubmit = true;
			var enable = This.object.find(':button').not('[disabled]');
			if (this.settings.type == 'ajax') {
				enable.attr({disabled:true});
				return $.ajax.server(this.settings.url, data, [function(response, text){
					settings.onSubmitEnd.apply(This, [response, text])
				}, function(){ This.isSubmit = false;enable.removeAttr('disabled'); settings.onSubmited.apply(This); }], this.settings.method || 'post'),  false;
			} else if (this.settings.type == 'iframe') {
				enable.attr({disabled:true});
				var iframeName = $.util.randomName('iframe'), iframe = $('<div/>').hide().appendTo(This.object).html('<iframe name="'+iframeName+'"></iframe>'),
					checkbox = This.object.find('[name]:checkbox').each(function(){
						$(this).attr('oldname', $(this).attr('name')).attr('name', $(this).attr('name')+'[]');
					});
				if (!settings.download) This._data['isajax'] = 1;
				iframe.find('iframe').bind('load', function(){
					enable.removeAttr('disabled');
					checkbox.each(function(){ $(this).attr('name', $(this).attr('oldname')); });
					This.isSubmit = false;
					var body = $(this).contents().find('body').children().remove().end(), text = body.html(), response = jsonDecode(text);
					settings.onSubmited.apply(This, [response, text]);
					setTimeout(function(){
						if (response) {
							response.msg && callback('user.msg', response.msg);
							if (callback(response.fn, response.fndata) !== false) {
								settings.onSubmitEnd.apply(This, [response, text]);
							} else {
								settings.onSubmitError.apply(This, [response, text]);
							}
						} else if(!settings.download) {
							alert('error:'+text);
							settings.onSubmitError.apply(This, [text]);
						}
						iframe.remove()
					}, 0);
				});
				settings.download && this.object.attr('target', '_blank') || this.object.attr('target', iframeName);
			}
			This.dataNode.formEncode(this._data);
		} else {
			return false;
		}
	}
});

window.validation = function(settings){
	this.settings = settings;
}

$.extend(validation, {
	regexp : {
		userName : /^[a-zA-Z][a-zA-Z0-9_]{5,15}$/i,
		userPwd : /.{6,20}/i,
		email : /^[a-z0-9-_.]+@([a-z0-9-]+[.])+[a-z]{2,4}$/i,
		phone : /^[0-9]{7,8}$/i,
		codephone : /^([0-9]{3,4}-)?[0-9]{7,8}$/i,
		mobile : /^((13)|(15)|(18))[0-9]{9}$/i,
		qq : /^[0-9]{5,11}$/i,
		url : /^(http:\/\/)?(www\.)?.*\.(com|cn|hk|tw|net|org|info)/i,
		code : /^[0-9]{6}$/i,
		num:/^[0-9]+$/,
		money:/^[0-9]{1,9}(.[0-9]{1,2})?$/
	}
});

$.extend(validation.prototype, {
	matchAll : function(data){
		var This = this, check = $.extend({}, data, This.settings);
		$.each(check, function(key, val){
			if (This.settings[key] && This.match(data[key], This.settings[key], data) === false) {
				return data = false;
			}
		});
		return data;
	},
	match : function(val, option, data){
		option.$tips && option.$tips.hide();
		
		if (isFunction(option.fn) && option.fn.apply(option, [val]) === false) {
			option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html('');
			isFunction(option.error) && option.error.apply(option);
			return false;
		} 
		
		if (!val) {//验证不能为空
			if (isFunction(option.empty)){
				option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html('');
				option.empty.apply(option);
				return false;
			}
			isFunction(option.ok) && option.ok.apply(option);
			option.$tips && option.$tips.attr({'class':'tips_ok ml10'}).css({display:''}).html('');
			return true;
		}
		if (option.len) {//验证长度
			if (isString(option.len)) option.len = [0, option.len];
			if (val.toString().length < option.len[0]) {
				option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html(option.key+'的长度不能小于'+option.len[0]);
				return isFunction(option.error) && option.error.apply(option, [option.key+'的长度不能小于'+option.len[0]]), false;
			}
			if (val.toString().length > option.len[1]) {
				option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html(option.key+'的长度不能大于'+option.len[1]);
				return isFunction(option.error) && option.error.apply(option, [option.key+'的长度不能大于'+option.len[1]]), false;
			}
		}
		if (option.regexp){
			if (isString(option.regexp) && validation.regexp[option.regexp]) {
				option.regexp = validation.regexp[option.regexp];
			}
			if (!val.match(option.regexp)) {
				option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html(option.key+'格式不正确');
				isFunction(option.error) && option.error.apply(option, [option.key+'格式不正确']);
				return false;
			}
		}
		if (option.same){
			if (data[option.same.key] && val != data[option.same.key]) {
				option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html('');
				isFunction(option.same.error) && option.same.error.apply(option);
				return false;
			}
		}
		if (option.server){
			option.$tips && option.$tips.hide().show();
			isFunction(option.server.val) && (val = option.server.val.apply(option, [val]));
			if (option.server.oldval != val) {
				if (option.server.ov != val) {
					option.server.ok = '';
					option.server.isSend = '';
				}
				if (!option.server.ok && !option.server.isSend && !option.server.isChecking) {
					option.server.isChecking = true;
					option.server.ov = val;
					isFunction(option.server.tips) && option.server.tips.apply(option);
					$.ajax.server(option.server.url, {data:val}, [function(response){
						if (response.status == 'ok') {
							option.server.ok = true;
							option.$tips && option.$tips.attr({'class':'tips_ok ml10'}).css({display:''}).html('');
							isFunction(option.ok) && option.ok.apply(option);
						} else {
							option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html('');
							isFunction(option.server.error) && option.server.error.apply(option);
						}
					}, function(){option.server.isChecking = false; option.server.isSend = true;}]);
					return false;
				} else if (!option.server.ok) {
					option.$tips && option.$tips.attr({'class':'tips_error ml10'}).css({display:''}).html('');
					option.server.isSend && isFunction(option.server.error) && option.server.error.apply(option);
					return false;
				}
			}
		}
	
		option.$tips && option.$tips.attr({'class':'tips_ok ml10'}).css({display:''}).html('');
		isFunction(option.ok) && option.ok.apply(option);

		return true;
	}
});

}();
