var Validator = {
	validate: function(form, vrs /* Optional */) {
		this.currentForm = form;
		
		var sucess = true;
		
		$(form).find(":input[validate]").each(function(i) {
			var j = $(this);
			var vrs = j.attr("validate");
			var rs = Validator.parseInlineVrs(vrs);
			for (var n in rs) {
				
				if (n != "label") {
					var param = rs[n];					
					var result = Validator.rules[n].check.call(Validator, j.val(), this, param);
					if (result != true) {
						alert("\"" + rs["label"] + "\" : " + Validator.formatMessage(Validator.rules[n].msg, param));
						sucess = false;
						return false;
					}
				}
			}
			return true;
		});
		
		return sucess;
	},

	validateField: function(element, vrs /* Optional */) {
	},
	
	parseInlineVrs: function(vrs) {
		var rs = new Object();

		var ss = vrs.split(";");
		for (var i = 0; i < ss.length; ++i) {
			var r = $.trim(ss[i]);
			if (r) {
				var rr = r.split(":");
				
				var ruleName = $.trim(rr[0]);
				var ruleValue = $.trim(rr[1]);
				
				if (Validator.rules[ruleName] && Validator.rules[ruleName].argc > 1) {
					var ruleValueCount = Validator.rules[ruleName].argc;
					var multiRuleValue = ruleValue.split(new RegExp(" +", "g"), ruleValueCount);
					for (var j = 0; j < multiRuleValue.length; ++j) {
						multiRuleValue[j] = eval(multiRuleValue[j]);
					}
					rs[ruleName] = multiRuleValue;
				}
				else {
					rs[ruleName] = eval(ruleValue);
				}
			}
		}
		
		return rs;
	},
	
	parseVrs: function(vrs) {
	},
	
	formatMessage: function(format, param) {
		if (arguments.length <= 1) {
			return format;
		}
		
		var s = format;
		if (param instanceof Array) {
			for (var i = 0; i < param.length; ++i) {
				s = s.replace(new RegExp("\\{" + i + "\\}", "g"), param[i]);
			}
		}
		else {
			for (var i = 1; i < arguments.length; ++i) {
				s = s.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
			}
		}

		return s;
	},
	
	getLength: function(value, element) {
		switch( element.nodeName.toLowerCase() ) {
		case 'select':
			return $("option:selected", element).length;
		case 'input':
			if( this.checkable( element) )
				return Validator.findByName(element.name).filter(':checked').length;
		}
		return value.length;
	},
	
	getByteLength: function(value, element) {
		switch( element.nodeName.toLowerCase() ) {
		case 'select':
			return $("option:selected", element).length;
		case 'input':
			if( Validator.checkable( element) )
				return Validator.findByName(element.name).filter(':checked').length;
		}
		return value.length;
	},

	depend: function(param, element) {
//		alert(param + " : " + typeof param);
		return Validator.dependTypes[typeof param]
			? Validator.dependTypes[typeof param](param, element)
			: true;
	},

	dependTypes: {
		"boolean": function(param, element) {
			return param;
		},
		"number": function(param, element) {
			return param;
		},
		"string": function(param, element) {
			return !!$(param, element.form).length;
		},
		"function": function(param, element) {
			return param(element);
		}
	},

	checkable: function( element ) {
		return /radio|checkbox/i.test(element.type);
	},

	findByName: function( name ) {
		// select by name and filter by form for performance over form.find("[name=...]")
		var form = Validator.currentForm;
		return $(document.getElementsByName(name)).map(function(index, element) {
			return element.form == form && element.name == name && element  || null;
		});
	},
	
	optional: function(element) {
		return !Validator.rules.required.check.call(Validator, $.trim(element.value), element);
	},

	rules: {
		required: {
			argc: 1,
			msg: "필수항목입니다.",
			check: function(value, element, param) {
				// check if dependency is met
				if ( !Validator.depend(param, element) ) {
					return false;
				}
				
				switch( element.nodeName.toLowerCase() ) {
				case 'select':
					// could be an array for select-multiple or a string, both are fine this way
					var val = $(element).val();
					return val && val.length > 0;
				case 'input':
					if ( Validator.checkable(element) )
						return Validator.getLength(value, element) > 0;
				default:
					return $.trim(value).length > 0;
				}
			}
		},
		
		email: {
			argc: 1,
			msg: "올바른 이메일 형식으로 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
			}
		},
		
		url: {
			argc: 1,
			msg: "올바른 주소 형식으로 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
			}
		},
		
		date: {
			argc: 1,
			msg: "올바른 날짜 형식으로 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
			}
		},
		
		number: {
			argc: 1,
			msg: "올바른 숫자 형식으로 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
			}
		},
		
		digit: {
			argc: 1,
			msg: "숫자만 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || /^\d+$/.test(value);
			}
		},
		
		equal: {
			argc: 1,
			msg: "{0}와 같은 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.findByName(param).val() == value;
			}
		},
		
		min: {
			argc: 1,
			msg: "{0} 이상의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || value >= param;
			}
		},
		
		max: {
			argc: 1,
			msg: "{0} 이하의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || value <= param;
			}
		},

		range: {
			argc: 2,
			msg: "{0} ~ {1} 사이의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || ( value >= param[0] && value <= param[1] );
			}
		},

		minlength: {
			argc: 1,
			msg: "{0}자 이상의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || Validator.getLength($.trim(value), element) >= param;
			}
		},
		
		maxlength: {
			argc: 1,
			msg: "{0}자 이하의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || Validator.getLength($.trim(value), element) <= param;
			}
		},

		rangelength: {
			argc: 2,
			msg: "{0}자 ~ {1}자 사이의 값을 입력하세요.",
			check: function(value, element, param) {
				var length = Validator.getLength($.trim(value), element);
				return Validator.optional(element) || ( length >= param[0] && length <= param[1] );
			}
		},

		minbytes: {
			argc: 1,
			msg: "{0}바이트 이상의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || Validator.getByteLength($.trim(value), element) >= param;
			}
		},
		
		maxbytes: {
			argc: 1,
			msg: "{0}바이트 이하의 값을 입력하세요.",
			check: function(value, element, param) {
				return Validator.optional(element) || Validator.getByteLength($.trim(value), element) <= param;
			}
		},
		
		rangebyte: {
			argc: 2,
			msg: "{0}바이트 ~ {1}바이트 사이의 값을 입력하세요.",
			check: function(value, element, param) {
				var length = Validator.getByteLength($.trim(value), element);
				return Validator.optional(element) || ( length >= param[0] && length <= param[1] );
			}
		}
	}
};


/**
 * 지정된 곳 외의 Enter 이벤트 방지.
 * 사용법
 * document.onkeydown = blockEnterKey;
 * 
 * @return
 */
function blockEnterKey() {
    try {
        if (!e) var e = window.event;
        
        if (document.all) var key = e.keyCode;
        else var key = e.which;
        
        if( key == 13 ) {
            //var tag = e.srcElement ? e.srcElement.tagName : e.target.nodeName ;
            //var tagId = e.srcElement ? e.srcElement.id : "" ;
            return false;
        }
    } catch(e) { return true; }
}

function handlerNumeral(e) {

	var keyCode = ( window.event ) ? window.event.keyCode : e.which;

	if( 48 <= keyCode && keyCode <= 57 || 96 <= keyCode && keyCode <= 105 ||
    	 keyCode == 8 || keyCode == 9 || keyCode == 17 || keyCode == 18 || 
     	keyCode == 37 || keyCode == 39 || keyCode == 46 || keyCode == 110 || keyCode == 190) 
     	return;
 	else
	{
		if( window.event )
			window.event.returnValue = false;
		else 
		{
			e.preventDefault();
			e.stopPropagation();
		}
	}
}

