﻿//rules: {inputName:{"name":{check;function,msg:""},..},input:{},...};
//template:{plain:$(),error:$()/"className",success:$(),};
//handle:function($("input"),$(msgBox)/null);
//submit:$();
$.fn.validate = function (rules, templates, handle) {
    var ths = this;
    var plainTemp = templates.plain;
    var sucTemp = templates.success;
    var errClass = typeof templates.error == "string" ? templates.error : null;
    var errTemp = templates.error.jquery ? templates.error : null;
    var allowNull = "ALLOWNULL";
    var sucTempKey = "sucTemp";
    var plainTempKey = "plainTemp";
    var errTempKey = "errTemp";
    var validKey = "valids";
    var invalidRuleKey = "invalid";
    var msgSelector = templates.selector;
    var input = function (name) { return $("input[name='" + name + "']"); };
    var createMsgbox = function (temp, msg) {
        var box;
        if (msgSelector)
            box = temp.find(msgSelector).html(msg).end();
        else
            box = temp.html(msg);
        return box;
    }
    var init = function () {
        $.each(rules, function (inputName, ruleObjs) {
            var inp = input(inputName);
            if (errClass)
                inp.data(plainTempKey, plainTemp.clone().removeAttr("id"));
            else if (errTemp)
                inp.data(errTempKey, errTemp.clone().removeAttr("id"));
            inp.data(sucTempKey, sucTemp.clone().removeAttr("id"));
            inp.data(validKey, {});
        })
        $.each(rules, function (inputName, ruleObjs) {
            var inp = input(inputName);
            inp.focus(function () {
                var ele = $(this);
                var msg = "";
                if (ruleObjs["ALLOWNULL"])
                    msg = ruleObjs["ALLOWNULL"];
                else {
                    for (var r in ruleObjs) {
                        msg = ruleObjs[r].msg;
                        break;
                    };
                }
                handle(ele, createMsgbox(plainTemp, msg));
            });
            inp.blur(function () {
                var isValid = true;
                var isAsyn = false;
                var ele = $(this);
                var val = ele.val();
                if (ruleObjs["ALLOWNULL"] && (!val))
                    return false;
                for (var ruleName in ruleObjs) {
                    if (ruleName == allowNull)
                        continue;
                    //when check validity,add to this collection,or delete it:    ele.data(validKey):{ruleName:fieldValue,...}
                    if (ele.data(validKey)[ruleName] == val)   //this value is confirmed;
                        continue;
                    var thsRule = ruleObjs[ruleName];
                    if (thsRule.check.length == 2) {
                        thsRule.check(val, function (isValid, msg) {
                            if (isValid) {
                                ele.data(validKey)[ruleName] = val;
                                if ((!inp.data(invalidRuleKey)) || ruleName == inp.data(invalidRuleKey)) {
                                    handle(inp, inp.data(sucTempKey));
                                    inp.data(invalidRuleKey, 0);
                                }
                            }
                            else {
                                if (errTemp) {
                                    if (!msg)
                                        msg = thsRule.msg;
                                    handle(ele, createMsgbox(inp.data(errTempKey), msg));
                                }
                                else if (errClass)
                                    inp.data(plainTempKey).addClass(errClass);
                                delete ele.data(validKey)[ruleName];
                                inp.data(invalidRuleKey, ruleName);
                            }
                        });
                        isAsyn = true;
                        isValid = false;
                        break;
                    } else {
                        if (!thsRule.check(val)) {
                            if (errTemp)
                                handle(ele, createMsgbox(inp.data(errTempKey), thsRule.msg));
                            else if (errClass)
                                inp.data(plainTempKey).addClass(errClass);
                            delete ele.data(validKey)[ruleName];
                            isValid = false;
                            //store the current invalid ruleName;
                            inp.data(invalidRuleKey, ruleName);
                            break;
                        } else {
                            ele.data(validKey)[ruleName] = val;
                        }
                    };
                };
                if (isValid) {
                    handle(inp, inp.data(sucTempKey));
                }
                if ((!isValid) && isAsyn) {
                    handle(ele, createMsgbox(inp.data(errTempKey), "等待验证中......"));
                }
            });
        });
        ths[0].check = function (nocheckArray) {
            var isValid = true;
            var inputs = $(ths).find("input");
            var len = inputs.length;
            for (var i = 0; i < len; ++i) {
                var ele = $(inputs[i]);
                var val = ele.val();

                if (nocheckArray.length > 0) {
                    var has = false;
                    for (var j = 0; j < nocheckArray.length; j++) {
                        if (nocheckArray[j] == ele.attr("name")) {
                            has = true;
                            break;
                        }
                    }
                    if (has)
                        continue;
                }


                var rs = rules[ele.attr("name")]
                if (!rs)
                    continue;
                if (rs["ALLOWNULL"] && (!val))
                    continue;
                for (var ruleName in rs) {
                    if (ruleName == allowNull)
                        continue;
                    if (ele.data(validKey)[ruleName] == val)
                        continue;
                    else {
                        ele.blur();
                        isValid = false;
                        break;
                    }
                }
                if (!isValid)
                    break;
            }
            return isValid;
        };
        ths[0].forceCheck = function (nam) {
            var ele = input(nam);
            ele.data(validKey, {});
            ele.blur();
        }
        ths.submit(ths[0].check);
    }
    init();
}
