/*  validator 0.2 (2007-12-21)
 *  (c) 2007 Goonoo Kim
 *
 *  validator is under the terms of a GPL(General Public License).
 */

/**
 * constructor
 * @return void
 */
var validator = function(form) {
    this.form = validator.getFormElement(form);
    if (!this.form) return false;

    this.conditions = [];
    this.groupConditions = [];
    this.errorCondition = null;
    this.errorGroupCondition = null;
    this.errorType = null;
};
/**
 * format check map (point function name)
 */
validator.FORMAT_MAP = {
	userid	   : "vFormat.userid",
	email      : "vFormat.email",
	domain     : "vFormat.domain",
	hangul     : "vFormat.hangul",
	alphanum   : "vFormat.alphanum",
	alphanum_   : "vFormat.alphanum_",
	engonly    : "vFormat.engonly",
	eng_only   : "vFormat.eng_only",
	number     : "vFormat.number",
	residentno : "vFormat.residentno",
	jumin      : "vFormat.jumin",
	foreignerno: "vFormat.foreignerno",
	bizno      : "vFormat.bizno",
	phone      : "vFormat.phone",
	homephone  : "vFormat.homephone",
	handphone  : "vFormat.handphone",
	isdate     : "vFormat.isdate",
	zip        : "vFormat.zip",
	jurino     : "vFormat.jurino",
	address    : "vFormat.address"
};
/**
 * message pattern to replace with getErrorMessage method
 *  {label} - name of form control
 *  {message} - invalid message
 */
validator.ERROR_MESSAGE_PATTERN = "[{label}] {message}";
/**
 * message preset for validator
 */
validator.ERROR_MESSAGE = {
    /* for element */
    required   : "¹Ýµå½Ã ÀÔ·ÂÇÏ¼Å¾ß ÇÏ´Â »çÇ×ÀÔ´Ï´Ù.",
    requiredstring : "¹Ýµå½Ã {required}(À¸)·Î ÀÔ·ÂÇÏ¼Å¾ß ÇÏ´Â »çÇ×ÀÔ´Ï´Ù.",
    match      : "ÀÔ·ÂµÈ ³»¿ëÀÌ ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù.",
    invalid    : "ÀÔ·ÂµÈ ³»¿ëÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    min        : "{min} ÀÌ»óÀÇ °ªÀ» ÀÔ·ÂÇØÁÖ¼¼¿ä.",
    max        : "{max} ÀÌÇÏÀÇ °ªÀ» ÀÔ·ÂÇØÁÖ¼¼¿ä.",
    minlength  : "ÀÔ·ÂµÈ ³»¿ëÀÌ {minlength}±ÛÀÚ ÀÌ»óÀÌ¾î¾ß ÇÕ´Ï´Ù.",
    maxlength  : "ÀÔ·ÂµÈ ³»¿ëÀÌ {maxlength}±ÛÀÚ ÀÌÇÏÀÌ¾î¾ß ÇÕ´Ï´Ù.",
    minbyte    : "ÀÔ·ÂµÈ ³»¿ëÀÇ ±æÀÌ°¡ {minbyte}Byte ÀÌ»óÀÌ¾î¾ß ÇÕ´Ï´Ù.",
    maxbyte    : "ÀÔ·ÂµÈ ³»¿ëÀÇ ±æÀÌ°¡ {maxbyte}Byte¸¦ ÃÊ°úÇÒ ¼ö ¾ø½À´Ï´Ù.",
    mincheck   : "{mincheck}°³ÀÇ Ç×¸ñ ÀÌ»óÀ¸·Î ¼±ÅÃÇÏ¼¼¿ä.",
    maxcheck   : "{maxcheck}°³ÀÇ Ç×¸ñ ÀÌÇÏ·Î ¼±ÅÃÇÏ¼¼¿ä.",
    minselect  : "{minselect}°³ÀÇ Ç×¸ñ ÀÌ»óÀ¸·Î ¼±ÅÃÇÏ¼¼¿ä.",
    maxselect  : "{maxselect}°³ÀÇ Ç×¸ñ ÀÌÇÏ·Î ¼±ÅÃÇÏ¼¼¿ä.",
    imageonly  : "ÀÌ¹ÌÁö ÆÄÀÏ¸¸ Ã·ºÎ ÇÒ ¼ö ÀÖ½À´Ï´Ù.",
    fileonly   : "{fileonly} Çü½ÄÀÇ ÆÄÀÏ¸¸ Ã·ºÎ ÇÒ ¼ö ÀÖ½À´Ï´Ù.",

    /* for group */
    requiremin : "{requiremin}°³ ÀÌ»óÀÇ Ç×¸ñÀÌ ÀÔ·ÂµÇ¾î¾ß ÇÕ´Ï´Ù.",
    requiremax : "{requiremax}°³ ÀÌÇÏÀÇ Ç×¸ñÀÌ ÀÔ·ÂµÇ¾î¾ß ÇÕ´Ï´Ù.",

    /* for format */
    userid	   : "¾ÆÀÌµð´Â ¿µ¹®/¼ýÀÚ·Î 4ÀÚÀÌ»ó 16ÀÚÀÌÇÏ·Î ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    email      : "ÀÌ¸ÞÀÏ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    domain	   : "ÀÔ·ÂµÈ µµ¸ÞÀÎÀÇ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    hangul     : "¹Ýµå½Ã ÇÑ±Û·Î ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    alphanum   : "¿µ¹®/¼ýÀÚ·Î¸¸ ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    alphanum_  : "¿µ¹®/¼ýÀÚ, ¾ð´õ¹Ù(_)·Î¸¸ ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    engonly    : "¿µ¹®À¸·Î¸¸ ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    eng_only   : "¿µ¹® ¹× ¾ð´õ¹Ù(_)·Î¸¸ ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    number     : "¼ýÀÚ·Î¸¸ ÀÔ·ÂÇÏ¼Å¾ß ÇÕ´Ï´Ù.",
    residentno : "ÁÖ¹Îµî·Ï¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    jumin      : "ÁÖ¹Îµî·Ï¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    foreignerno: "¿Ü±¹ÀÎµî·Ï¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    bizno      : "»ç¾÷ÀÚµî·Ï¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    phone      : "ÀüÈ­¹øÈ£  Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    homephone  : "À¯¼± ÀüÈ­¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    handphone  : "ÈÞ´ëÀüÈ­¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    isdate     : "³¯Â¥ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    zip        : "¿ìÆí¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    jurino     : "¹ýÀÎ¹øÈ£ Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù.",
    address    : "Çü½ÄÀÌ ¿Ã¹Ù¸£Áö ¾Ê½À´Ï´Ù."
};
/**
 * form control types
 */
validator.TEXT = 1;
validator.SELECT = 2;
validator.MULTI_SELECT = 3;
validator.CHECK = 4;
validator.RADIO = 5;
validator.FILE = 6;
validator.HIDDEN = 7;
/**
 * add new validate condtion (see Condition constructor)
 * @return number or false
 */
validator.prototype.add = function(targetElement, targetOptions, targetLabel) {
    var condition = new vCondition(targetElement, targetOptions, targetLabel, this.form);
    if (validator.getElementType(condition.element))
		var newIndex = this.addCondition(condition);

	return newIndex ? newIndex : false;
};
/**
 * add new validate condition with vCondition Object (see vCondition constructor)
 * @return number or false
 */
validator.prototype.addCondition = function(condition) {
    var newIndex = this.conditions.length;

    if (condition) {
        this.conditions[newIndex] = condition;
        return newIndex;
    } else {
        return false;
    };
};
/**
 * add new validate condition group (see vGroupCondition constructor)
 * @return number or false
 */
validator.prototype.addGroup = function(targetConditions, targetOptions, targetLabel) {
    var groupCondition = new vGroupCondition(targetConditions, targetOptions, targetLabel);

    if (groupCondition)
        var newIndex = this.addGroupCondition(groupCondition);

    return newIndex ? newIndex : false;
};
/**
 * add new validate group condition with vGroupCondition Object (see vGroupCondition constructor)
 * @return number or false
 */
validator.prototype.addGroupCondition = function(groupCondition){
    var newIndex = this.groupConditions.length;
    
    if (groupCondition) {
        this.groupConditions[newIndex] = groupCondition;
        return newIndex;
    } else {
        return false;
    };
};
/**
 * do validation with added conditions
 * @return boolean - true on success
 */
validator.prototype.validate = function() {
    var isFailed = false;

    // validate conditions
    for (var i=0, l=this.conditions.length; i < l; i++) {
        isFailed = !this.validateCondition(this.conditions[i]);
        if (isFailed) return false;
    };

    // validate group conditions
    for (var i=0, l=this.groupConditions.length; i < l; i++) {
        isFailed = !this.validateGroup(this.groupConditions[i]);
        if (isFailed) return false;
    };

    return true;
};
/**
 * do validation of group condition
 * @return boolean - true on success
 */
validator.prototype.validateGroup = function(groupCondition) {
    var conditions = groupCondition.conditions;
    var optionsObj = groupCondition.optionsObj;

    var requiremin = optionsObj.requiremin ? parseInt(optionsObj.requiremin, 10) : 0;
    if (requiremin > 0) {
        var countValid = 0;
        var firstInvalidCondition = null;
        for (var i=0, l=conditions.length; i < l; i++) {
            if (this.validateCondition(conditions[i])) {
                countValid++;
                if (countValid >= requiremin)
                    break;
            } else {
                if (!firstInvalidCondition) {
                    firstInvalidCondition = conditions[i];
                };
                // if error type isn't `required`, raise Condition's Error
                if (this.errorType != "required") {
                    return false; // validationCondition method already raised Error.
                };
            };
        };
        if (countValid < requiremin)
            return this.raiseGroupError(groupCondition, firstInvalidCondition, "requiremin");
    };

    var requiremax = optionsObj.requiremax ? parseInt(optionsObj.requiremax, 10) : 0;
    if (requiremax > 0) {
        var countValid = 0;
        var firstInvalidCondition = null;
        for (var i=0, l=conditions.length; i < l; i++) {
            if (this.validateCondition(conditions[i])) {
                countValid++;
                if (countValid >= requiremax)
                    break;
            } else {
                if (!firstInvalidCondition) {
                    firstInvalidCondition = conditions[i];
                };
                // if error type isn't `required`, raise Condition's Error
                if (this.errorType != "required") {
                    return false; // validationCondition method already raised Error.
                };
            };
        };
        if (countValid < requiremax)
            return this.raiseGroupError(groupCondition, firstInvalidCondition, "requiremax");
    };

    return true;
};
/**
 * do validation of condition
 * @return boolean - true on success
 */
validator.prototype.validateCondition = function(condition) {
    var element = condition.element;
    var optionsObj = condition.optionsObj;
    var value = validator.getValue(element);

    if (!validator.isActiveFormControl(element)) return true; // ignore

    var type = validator.getElementType(element);
    if (!type) return true; // ignore

    var useifvalid = optionsObj.useifvalid || null;
    var useifinvalid = optionsObj.useifinvalid || null;
    if (useifvalid) {
        if (!this.validateCondition(useifvalid))
            return true; // regard as valid
    };
    if (useifinvalid) {
        if (this.validateCondition(useifvalid))
            return true; // regard as valid
    };

    var isEmpty = validator.isEmptyElement(element, type);

    var trim = optionsObj.trim || null;
    if (trim) {
        if (type == validator.TEXT || type == validator.HIDDEN) {
            switch (trim) {
                case true: case "trim":
                    value = value.replace(/^\s+/, "").replace(/\s+$/, "");
                    break;
                case "compress":
                    value = value.replace(/\s+/g, "");
                    break;
                case "ltrim":
                    value = value.replace(/^\s+/, "");
                    break;
                case "rtrim":
                    value = value.replace(/\s+$/, "");
                    break;
            };
        };
        if (value == "" && !isEmpty) isEmpty = true;
    };

    var required = optionsObj.required || null;
    if (required && isEmpty) {
        return this.raiseError(condition, "required");
    } else if (typeof required == "string" && required != value) {
        return this.raiseError(condition, "requiredstring");
    };

    var min = parseInt(optionsObj.min, 10) || null;
    var max = parseInt(optionsObj.max, 10) || null;
    if ((min != null || max != null) && !isEmpty) {
        if (type == validator.TEXT || type == validator.HIDDEN) {
            var isNumber = /^[0-9]*$/.test(value);
            if (!isNumber)
                return this.raiseError(condition, "number");
            var intValue = parseInt(value, 10);
            if (min != null && intValue < min)
                return this.raiseError(condition, "min");
            if (max != null && intValue > max)
                return this.raiseError(condition, "max");
        };
    };

    var minlength = parseInt(optionsObj.minlength, 10) || null;
    if (minlength > 0 && !isEmpty) {
        if (type == validator.TEXT || type == validator.HIDDEN) {
            if (value.length < minlength)
                return this.raiseError(condition, "minlength");
        };
    };
    
    var maxlength = parseInt(optionsObj.maxlength, 10) || null;
    if (maxlength > 0 && !isEmpty) {
        if (type == validator.TEXT || type == validator.HIDDEN) {
            if (value.length > maxlength)
                return this.raiseError(condition, "maxlength");
        };
    };

    var minbyte = parseInt(optionsObj.minbyte, 10) || null;
    var maxbyte = parseInt(optionsObj.maxbyte, 10) || null;
    if ((minbyte > 0 || maxbyte > 0) && !isEmpty) {
        if (type == validator.TEXT || type == validator.HIDDEN) {
            var valueByte = value.length;
            for (i=0, l=value.length; i < l; i++) {
                if (value.charCodeAt(i) > 128)
                    valueByte++;
            };
            if (minbyte > 0 && valueByte < minbyte)
                return this.raiseError(condition, "minbyte");
            if (maxbyte > 0 && valueByte > maxbyte)
                return this.raiseError(condition, "maxbyte");
        };
    };

    var match = optionsObj.match || null;
    if (match) {
        if (type != validator.FILE) {
            var matchElement = validator.getElement(match, this.form);
            if (matchElement && value != validator.getValue(matchElement))
                return this.raiseError(condition, "match");
        };
    };

    var option = optionsObj.option || null;
    if (option && !isEmpty) {
        if (type != validator.FILE && validator.FORMAT_MAP[option]) {
            var formatFunction = eval(validator.FORMAT_MAP[option]);
            var formatResult;
            var span = parseInt(optionsObj.span, 10) || null;
            var glue = optionsObj.glue || "";

            var _options = option.split(/\s/);
            for (var i=0, l=_options.length; i < l; i++) {
                var format = validator.FORMAT_MAP[_options[i]];

                if (span && typeof value != "object") {
                    var spanedValue = value;
                    var _elementForSpan = element;
                    for (var j = 1; j < span; j++) {
                        _elementForSpan = validator.getNextElement(_elementForSpan);
                        if (_elementForSpan) {
                            if (typeof glue == "object" && glue.length + 1 == span) {
                                spanedValue += glue[j-1] + validator.getValue(_elementForSpan);
                            } else {
                                spanedValue += glue + validator.getValue(_elementForSpan);
                            }
                        };
                    };
                    formatResult = formatFunction(element, spanedValue);
                } else {
                    formatResult = formatFunction(element, value);
                };
                if (typeof formatResult == "string")
                    return this.raiseError(condition, formatResult);
            };
        };
    };

    var pattern = optionsObj.pattern || null;
    if (pattern && !isEmpty) {
        if (type != validator.FILE) {
            patternRegExp = new RegExp(pattern);
            if (typeof value == "object") {
                for (var i=0; i<value.length; i++) {
                    if (!patternRegExp.test(value[i])) {
                        return this.raiseError(condition, "invalid");
                    };
                };
            } else if (!patternRegExp.test(value)) {
                return this.raiseError(condition, "invalid");
            };
        };
    };

    var mincheck = parseInt(optionsObj.mincheck, 10) || null;
    var maxcheck = parseInt(optionsObj.maxcheck, 10) || null;
    if (mincheck > 0 || maxcheck > 0) {
        if (type == validator.CHECK) {
            if (mincheck > 0 && value.length < mincheck)
                return this.raiseError(condition, "mincheck");
            if (maxcheck > 0 && value.length > maxcheck)
                return this.raiseError(condition, "maxcheck");
        };
    };

    var minselect = parseInt(optionsObj.minselect, 10) || null;
    var maxselect = parseInt(optionsObj.maxselect, 10) || null;
    if (minselect > 0 || maxselect > 0) {
        if (type == validator.MULTI_SELECT) {
            if (minselect > 0 && value.length < minselect)
                return this.raiseError(condition, "minselect");
            if (maxselect > 0 && value.length > maxselect)
                return this.raiseError(condition, "maxselect");
        };
    };

    var imageonly = optionsObj.imageonly || null;
    if (imageonly && !isEmpty) {
        if (type == validator.FILE) {
            var dotIndex = value.lastIndexOf(".");
            var ext = value.substr(dotIndex + 1).toLowerCase();
            if(ext != "jpg" && ext != "jpeg" && ext != "gif" && ext != "png")
                return this.raiseError(condition, "imageonly");
        };
    };

    var fileonly = optionsObj.fileonly || null;
    if (fileonly && !isEmpty) {
        if (type == validator.FILE) {
            var dotIndex = value.lastIndexOf(".");
            var ext = value.substr(dotIndex + 1).toLowerCase();

			var isValidFile = false;
			for (var i=0, l=fileonly.length; i < l; i++) {
				if (ext == fileonly[i].toLowerCase()) {
					isValidFile = true;
					break;
				};
			};

			if (!isValidFile) {
				return this.raiseError(condition, "fileonly");
			};
        };
    };

    return true;
};
/**
 * set information of invalid condition
 * @param vCondition condition
 * @param string errorType - key of optionsObj (required, match, and so on...)
 * @return false
 */
validator.prototype.raiseError = function(condition, errorType) {
    this.errorCondition = condition;
    this.errorType = errorType;

    return false;
};
/**
 * set information of invalid group condition
 * @param vGroupCondition groupCondition
 * @param vCondition firstInvalidCondition
 * @param string errorType - key of optionsObj (requiremin, requiremax, ...)
 * @return false
 */
validator.prototype.raiseGroupError = function(groupCondition, firstInvalidCondition, errorType) {
    this.errorGroupCondition = groupCondition;
    this.errorCondition = firstInvalidCondition;
    this.errorType = errorType;
    
    return false;
};
/**
 * get first invalid element what validator detected
 */
validator.prototype.getErrorElement = function() {
    return this.errorCondition ? this.errorCondition.element : null;
};
/**
 * get error message with errorMessagePattern, when errorMessagePattern is null, default pattern is used.
 * @param string errorMessagePattern
 * @return string
 */
validator.prototype.getErrorMessage = function(errorMessagePattern) {
    if (!this.errorCondition || !this.errorType)
        return null;

    if (this.errorGroupCondition) {
        var optionsObj = this.errorGroupCondition.optionsObj;
        //var label = this.errorGroupCondition.label;
    } else {
        var optionsObj = this.errorCondition.optionsObj;
        //var label = this.errorCondition.label;
    };
    var label = optionsObj['label'];
    var typeMessage = validator.ERROR_MESSAGE[this.errorType] || this.errorType;
    var message = optionsObj.message || typeMessage;

    var dynamicPattern = /\{([a-z0-9_]+)\}/i;
    if (dynamicPattern.test(message) == true) {
        while (dynamicPattern.exec(message)) {
            if (RegExp.$1 && optionsObj[RegExp.$1]) {
                var optionTxt = optionsObj[RegExp.$1];
                if (optionTxt.constructor &&
                        optionTxt.constructor.toString().indexOf("Array") > -1) {
                    optionTxt = optionTxt.join(", ");
                };
                message = message.replace(dynamicPattern, optionTxt);
            } else {
                break;
            };
        };
    };

    var messagePattern = errorMessagePattern
                            ? errorMessagePattern
                            : validator.ERROR_MESSAGE_PATTERN;
    if (label=="" || label=="undefined") {
    	var errorMessage = messagePattern.replace(/\[{label}\]/, "");
    } else {
    	var errorMessage = messagePattern.replace(/{label}/, label);
    }
    var errorMessage = errorMessage.replace(/{message}/, message);
    return errorMessage;
};
/**
 * check element is active form control
 * @param DOM Element element
 * @return boolean - true if element is active form control
 * @static
 */
validator.isActiveFormControl = function(element) {
    return element.tagName
            && element.tagName.match(/^input|select|textarea$/i)
            && !element.disabled;
};
/**
 * check element's value is empty
 * @param DOM Element element
 * @return boolean - true if element is empty
 * @static
 */
validator.isEmptyElement = function(element) {
    var value = validator.getValue(element);
    return !value;
};
/**
 * get DOM Element of form
 * @param mixed form - can be DOM Element, id attribute or name attribute.
 * @return DOM Element or false
 */
validator.getFormElement = function(form) {
    if (form.tagName)
        return form;
    else if (document.getElementById
            && document.getElementById(form))
        return document.getElementById(form);
    else if (document.forms
            && document.forms[form])
        return document.forms[form];
    else
        return false;
};
/**
 * get DOM Element of form control
 * @param mixed element - can be DOM Element, id attribute or name attribute.
                          if name attribute presented, elementForm is required.
 * @param DOM Element elementForm
 * @return DOM Element or false
 * @static
 */
validator.getElement = function(element, elementForm) {
    if (element) {
	    if (element.tagName) {
	        return element;
	    } else if (elementForm && elementForm.elements && elementForm.elements[element]) {
	        if (!elementForm.elements[element].tagName
	                && elementForm.elements[element].length)
	            return elementForm.elements[element][0];
	        else
	            return elementForm.elements[element];
	    } else if (document.getElementById
	            && document.getElementById(element)) {
	        return document.getElementById(element);
	    } else {
	        return false;
	    }
	} else {
		return false;
	}
};
/**
 * get type of form control
 * @param DOM Element element
 * @return number or false
 */
validator.getElementType = function(element) {
	if (!element) return false;
    if (!element.tagName) return false;

    var tagName = element.tagName.toLowerCase();
    var type = element.type;
    var multiple = element.multiple;

    if (tagName == "textarea" ||
            (tagName == "input" && (type == "text" || type == "password")))
        return validator.TEXT;
    else if (tagName == "select" && multiple)
        return validator.MULTI_SELECT;
    else if (tagName == "select")
        return validator.SELECT;
    else if (tagName == "input" && type == "checkbox")
        return validator.CHECK;
    else if (tagName == "input" && type == "radio")
        return validator.RADIO;
    else if (tagName == "input" && type == "file")
        return validator.FILE;
    else if (tagName == "input" && type == "hidden")
        return validator.HIDDEN;
    else
        return false;
};
/**
 * get value of form control
 * @param DOM Element element
 * @return string
 */
validator.getValue = function(element) {
    var type = validator.getElementType(element);

    switch (type) {
        case validator.TEXT:
        case validator.HIDDEN:
        case validator.FILE:
            return element.value;
            break;
        case validator.SELECT:
            for (var i=0, l=element.options.length; i<l; i++) {
                if (element.options[i].selected) {
                    return element.options[i].value;
                };
            };
            break;
        case validator.MULTI_SELECT:
            var values = [];
            for (var i=0, l=element.options.length; i<l; i++) {
                if (element.options[i].selected) {
                    values[values.length] = element.options[i].value;
                };
            };
            return values.length > 0 ? values : null;
            break;
        case validator.CHECK:
            if (element.form && element.name) {
                var checkElements = element.form.elements[element.name];
                var values = [];
                if (checkElements.length) {
                    for (var i=0, l=checkElements.length; i<l; i++) {
                        if (checkElements[i].checked == true)
                            values[values.length] = checkElements[i].value;
                    };
                } else {
                    if (checkElements.checked == true)
                        values[values.length] = checkElements.value;
                };
                return values.length > 0 ? values : null;
            };
            break;
        case validator.RADIO:
            if (element.form && element.name) {
                var checkElements = element.form.elements[element.name];
                if (checkElements.length) {
                    for (var i=0, l=checkElements.length; i<l; i++) {
                        if (checkElements[i].checked == true)
                            return checkElements[i].value;
                    };
                } else {
                    if (checkElements.checked == true)
                        return checkElements.value;
                };
            };
            break;
    };
    return null;
};
/**
 * get next form control of form control
 * @param DOM Element element
 * @return DOM Element or null
 */
validator.getNextElement = function(element) {
    if (element.form) {
        var form = element.form;
        for (var i=0, l=form.elements.length; i<l; i++) {
            if (form.elements[i] == element) {
                var count = i;
                while (++count < form.elements.length
                        && validator.isActiveFormControl(form.elements[count])) {
                    return form.elements[count];
                };
            };
        };
    } else if (element.nextSibling) {
        var _nextElement = element;
        while (_nextElement = element.nextSibling) {
            if (validator.isActiveFormControl(_nextElement))
                return _nextElement;
        };
    };
    return null;
};

/**
 * constructor
 * @param mixed targetElement - can be DOM Element, id attribute or name attribute of form control.
                                if name attribute presented, targetForm is required.
 * @param Object targetOptions - validation options of form control
 * @param string targetLabel - label of form control. if not presented,
 *                             vCondition try to find label from HTML Document
 * @param DOM Element targetForm
 * @return void
 */
var vCondition = function(targetElement, targetOptions, targetLabel, targetForm) {
    var element = validator.getElement(targetElement, targetForm);
   	if (!validator.isActiveFormControl(element)) return false;

   	this.element = element;
   	this.optionsObj = targetOptions || {};
   	//this.label = targetLabel || vCondition.getHTMLLabel(element);
   	this.label = targetLabel;

};
/**
 * add new option, overwrite if option exist.
 * @param string key
 * @param mixed val
 * @return void
 */
vCondition.prototype.addOption = function(key, val) {
    this.optionsObj[key] = val;
};
/**
 * remove exist option.
 * @param string key
 * @return void
 */
vCondition.prototype.removeOption = function(key) {
    this.optionsObj[key] = null;
};
/**
 * get option with key
 * @param string key
 * @return mixed(value of option) or null
 */
vCondition.prototype.getOption = function(key) {
    return this.optionsObj[key] ? this.optionsObj[key] : null;
};
/**
 * detect label of form control from HTML Document. if no label find, return "noname"
 * @param DOM Element element
 * @return string
 * @static
 */
vCondition.getHTMLLabel = function(element) {
    var currentLabel = "";
    if (document.getElementsByTagName) {
        var currentLabelElement;
        labelLoop:
        for (var i=0, l=document.getElementsByTagName("label").length; i<l; i++) {
            var labelElement = document.getElementsByTagName("label")[i];
            var labelChilds = labelElement.childNodes;

            if (labelElement.htmlFor && labelElement.htmlFor == element.id) {
                currentLabelElement = labelElement;
                break labelLoop;
            };
            for (var _i=0, _l=labelChilds.length; _i<_l; _i++) {
                if (labelChilds[_i] == element) {
                    currentLabelElement = labelElement;
                    break labelLoop;
                };
            };
        };
        if (currentLabelElement) {
            var labelChilds = currentLabelElement.childNodes;
            for (var i=0, l=labelChilds.length; i<l; i++) {
                if (!labelChilds[i].tagName) // check is text node
                    currentLabel += labelChilds[i].nodeValue.replace(/^\s+/, "").replace(/\s+$/, "");
            };
        };
    };
    if (!currentLabel) {
        if (element.title)
            currentLabel = element.title;
        else if (element.id)
            currentLabel = element.id;
        else if (element.name)
            currentLabel = element.name;
        else
            currentLabel = "noname";
    };
    return currentLabel;
};

/**
 * constructor
 * @param Array targetConditions - items of array must be vCondition instance
 * @param Object targetOptions - validation options of group condition
 * @param string targetLabel - label for group condition. if not presented,
                               labels of targetConditions is used.
 * @return void
 */
var vGroupCondition = function(targetConditions, targetOptions, targetLabel) {
    this.conditions = targetConditions || [];
    this.optionsObj = targetOptions || {};
    if (targetLabel) {
        this.label = targetLabel;
    } else {
        this.label = "";
        for (var i=0, l=this.conditions.length; i < l; i++) {
            this.label += this.label ? ", " : "";
            this.label += this.conditions[i].label;
        };
    };
};
/**
 * add new condition
 * @param {Object} condition
 * @return number - index of condition Array
 */
vGroupCondition.prototype.addCondition = function(condition) {
    var index = this.conditions.length;
    this.conditions[index] = condition;
    return index;
};
/**
 * get condition
 * @param number index - index of condition Array
 * @return vCondition or null
 */
vGroupCondition.prototype.getCondition = function(index) {
    return this.conditions[index] ? this.conditions[index] : null;
};
/**
 * remove exist condition
 * @param number index - index of condition Array
 * @return void
 */
vGroupCondition.prototype.removeCondition = function(index) {
    var len = this.conditions.length;
    if (this.conditions[index]) {
        var flag = false;
        for (var i=0; i<len; i++) {
            if (i == index)
                flag = true;

            if (flag == true && i+1 < len) {
                this.conditions[i] = this.conditions[i+1];
            };
        };
        this.conditions.length = len - 1;
    };
};

var vFormat = {};
vFormat.email = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[_a-zA-Z0-9-\.]+@[\.a-zA-Z0-9-]+\.[a-zA-Z]+$/;
    return pattern.test(value) ? true : "email";
};
vFormat.userid = function(el,value)
{
	var value = value ? value : el.value;
	var pattern = /^[a-zA-Z]{1}[a-zA-Z0-9_]{3,17}$/;
	return (pattern.test(el.value)) ? true : "userid";
}
vFormat.domain= function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[a-zA-Z0-9-]{2,63}\.([a-zA-Z0-9-]{2,63}\.){0,1}[a-zA-Z-.]+$/;
    var pattern_hangul = /^[a-zA-Z¤¡-¤¾¤¿-¤Ó°¡-ÆR0-9-]{2,63}\.(kr|com|net|org|biz|info)$/;

    var tmp = value.toLowerCase();
    if (tmp.substr(0, 4)=="www."){
        value = tmp.substr(4);
    }

	if (pattern.test(value.toLowerCase())) {
		return true;
	} else {
		return (pattern_hangul.test(value.toLowerCase())) ? true : "domain";
    }
};
vFormat.hangul = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[°¡-ÆR]+$/;
    return pattern.test(value) ? true : "hangul";
};
vFormat.alphanum = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[a-zA-Z0-9]+$/;
    return pattern.test(value) ? true : "alphanum";
};
vFormat.alphanum_ = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[a-zA-Z0-9_]+$/;
    return pattern.test(value) ? true : "alphanum_";
};
vFormat.engonly = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[a-zA-Z]+$/;
    return pattern.test(value) ? true : "engonly";
};
vFormat.eng_only = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[a-zA-Z_]+$/;
    return pattern.test(value) ? true : "eng_only";
};
vFormat.number = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[0-9]+$/;
    return pattern.test(value) ? true : "number";
};
vFormat.residentno = function(el,value) {
    var pattern = /^(\d{6})-?(\d{5}(\d{1})\d{1})$/;
    var num = value ? value : el.value;
    if (!pattern.test(num)) return "invalid";
    num = RegExp.$1 + RegExp.$2;
    if (RegExp.$3 == 7 || RegExp.$3 == 8 || RegExp.$4 == 9)
        if ((num[7]*10 + num[8]) %2) return "residentno";

    var sum = 0;
    var last = num.charCodeAt(12) - 0x30;
    var bases = "234567892345";
    for (var i=0; i<12; i++) {
        if (isNaN(num.substring(i,i+1))) return "residentno";
        sum += (num.charCodeAt(i) - 0x30) * (bases.charCodeAt(i) - 0x30);
    };
    var mod = sum % 11;
    if(RegExp.$3 == 7 || RegExp.$3 == 8 || RegExp.$4 == 9)
        return (11 - mod + 2) % 10 == last ? true : "residentno";
    else
        return (11 - mod) % 10 == last ? true : "residentno";
};
vFormat.jumin = function(el,value) {
    var pattern = /^([0-9]{6})-?([0-9]{7})$/;
    var num = value ? value : el.value;
    if (!pattern.test(num)) return "jumin";
    num = RegExp.$1 + RegExp.$2;

    var sum = 0;
    var last = num.charCodeAt(12) - 0x30;
    var bases = "234567892345";
    for (var i=0; i<12; i++) {
        if (isNaN(num.substring(i,i+1))) return "jumin";
        sum += (num.charCodeAt(i) - 0x30) * (bases.charCodeAt(i) - 0x30);
    };
    var mod = sum % 11;
    return (11 - mod) % 10 == last ? true : "jumin";
};
vFormat.foreignerno = function(el,value) {
    var pattern = /^(\d{6})-?(\d{5}[7-9]\d{1})$/;
    var num = value ? value : el.value;
    if (!pattern.test(num)) return "foreignerno";
    num = RegExp.$1 + RegExp.$2;
    if ((num[7]*10 + num[8]) %2) return "foreignerno";

    var sum = 0;
    var last = num.charCodeAt(12) - 0x30;
    var bases = "234567892345";
    for (var i=0; i<12; i++) {
        if (isNaN(num.substring(i,i+1))) return "foreignerno";
        sum += (num.charCodeAt(i) - 0x30) * (bases.charCodeAt(i) - 0x30);
    };
    var mod = sum % 11;
    return (11 - mod + 2) % 10 == last ? true : "foreignerno";
};
vFormat.bizno = function(el,value) {
    var pattern = /([0-9]{3})-?([0-9]{2})-?([0-9]{5})/;
    var num = value ? value : el.value;
    if (!pattern.test(num)) return "bizno";
    num = RegExp.$1 + RegExp.$2 + RegExp.$3;
    var cVal = 0;
    for (var i=0; i<8; i++) {
        var cKeyNum = parseInt(((_tmp = i % 3) == 0) ? 1 : ( _tmp  == 1 ) ? 3 : 7);
        cVal += (parseFloat(num.substring(i,i+1)) * cKeyNum) % 10;
    };
    var li_temp = parseFloat(num.substring(i,i+1)) * 5 + "0";
    cVal += parseFloat(li_temp.substring(0,1)) + parseFloat(li_temp.substring(1,2));
    return parseInt(num.substring(9,10)) == 10-(cVal % 10)%10 ? true : "bizno";
};
vFormat.phone = function(el,value) {
    var pattern = /^(0[0-9][0-9]?|01[01346-9])-?([1-9]{1}[0-9]{2,3})-?([0-9]{4})$/;
    var pattern15xx = /^(1544|1566|1577|1588|1644|1688)-?([0-9]{4})$/;
    var num = value ? value : el.value;
    return pattern.test(num) || pattern15xx.test(num) ? true : "phone";
};
vFormat.homephone = function(el,value) {
    var pattern = /^(0[2-8][0-5]?)-?([1-9]{1}[0-9]{2,3})-?([0-9]{4})$/;
    var pattern15xx = /^(1544|1566|1577|1588|1644|1688)-?([0-9]{4})$/;
    var num = value ? value : el.value;
    return pattern.test(num) || pattern15xx.test(num) ? true : "homephone";
};
vFormat.handphone = function(el,value) {
    var pattern = /^(01[01346-9])-?([1-9]{1}[0-9]{2,3})-?([0-9]{4})$/;
    var num = value ? value : el.value;
    return pattern.test(num) ? true : "handphone";
};
vFormat.isdate = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[12][0-9]{3}\-[01]?[0-9]\-[0-3]?[0-9]$/;
    return pattern.test(value) ? true : "isdate";
};
vFormat.zip = function(el,value) {
    var value = value ? value : el.value;
    var pattern = /^[0-9]{3}\-[0-9]{3}$/;
    return pattern.test(value) ? true : "zip";
};
vFormat.jurino = function(el,value) {
    var num = value ? value : el.value;
    var pattern = /^([0-9]{6})-?([0-9]{7})$/;
    if (!pattern.test(num)) return "jurino";
    num = RegExp.$1 + RegExp.$2;
    var sum = 0;
    var last = parseInt(num.charAt(12), 10);
	for (var i=0; i<12; i++) {
		if (i % 2 == 0) {  // * 1
			sum += parseInt(num.charAt(i), 10);
		} else {    // * 2
			sum += parseInt(num.charAt(i), 10) * 2;
		};
	};
    var mod = sum % 10;
    return (10 - mod) % 10 == last ? true : "jurino";
};
vFormat.address = function(el,value) {
    var value = value ? value : el.value;
    return value ? true : "address";
    //var pattern = /^[0-9]{3}\-[0-9]{3}$/;
    //return pattern.test(value) ? true : "address";
};

