/***********************************************************
 * 处理传统web程序的表单验证
 * 请注意: 如果要实时到服务器端验证某元素,仍然需要使用prototype库!!!
 * ----------------------
 * 默认行为: 在每个注册了验证函数的元素后新建一个隐藏的文本域, 用来显示验证信息
 *    onchang:
 *       验证错误: 显示红色x
 *    onsubmit:
 *       遍历所有元素
 *       依次调用错误元素的错误处理函数, 如果有错误元素, 则将焦点移到第一个错误元素位置
 *       如果有远程验证的元素,在未远程验证通过之前,表单不会提交. 表单将在远程返回正确信
 *       息后再提交.
 *       如果全部通过, 提交表单.
 * 
 * @note 成员函数以"_"起始的为私有函数,不直接使用
 * 
 * 表单验证规则定义在che[ck]_rule.js里,某些不具代表性的验证规则也可定义在各个网页对应的脚本里
 *
 * lastmodify: 2008-1-4
 * author: wanhua
 ************************************************************/

/**
 * Class Name: wh_Form
 *
 * @param string|object form FormElementObject或其id
 * @param object options(带*号必须定义):
 *  key           type            default     description
 *  [affix_func]  function        none        附加函数. wh_Form.inputs全部验证完毕后执行,以表单元素为参数
 *                                            返回bool值,影响表单提交
 *  [cycAll]      bool            true        表单提交时,遍历所有元素最后显示错误,或是遇到第一个错误就停止.默认遍历全部
 */
function wh_Form(form,options){
    var form = wh$(form);
    this.form = form;
    this.inputs = [];
    this.disabler = [];     //禁止使用的
    
    var subHandle = this._get_submitForm(this,options||{});
    form.onsubmit = function(){
        return subHandle();
    }
}
wh_Form.prototype = {
    pushInput: function(input,options){
        var inputObj = null;
        if((input instanceof wh_Input)) inputObj = input;
        else inputObj = new wh_Input(input,options,this['form']);
        this.inputs.push(inputObj);
    },
    //清理内存
    uninit: function(){
        this.form = null;
        var inputs = this.inputs;
        for(var i=0;i<inputs.length;++i){
            inputs[i].input = null;
        }
    },
    //-------------- 以下为私有成员函数 -----------------
    _get_submitForm: function(wh_form,options){
        return function(){
            var inputs = wh_form.inputs;
            var remotes = 0;
            var err_obj = null;
            var cycAll = typeof(options['cycAll'])=='undefined' ? true : !!(options['cycAll']);
            for(var i=0;i<inputs.length;++i){
                var input = inputs[i];
                if(input.isRemote() && !input.remote_right){
                    remotes++;
                }
                var result = input.handle();
                if(!err_obj && !result) err_obj = input;
                if(!cycAll && !result) break;
            }
            if(err_obj){
                err_obj.input.focus();
                return false;
            }
            
            var affix_func = options.affix_func;
            if(affix_func){
                if(!affix_func(wh_form['form'])){
                    return false;
                }
            }

            var poll = function(wf){
                var inputs = wf.inputs;
                var remote_inputs = [];
                var allRight = true;
                for(var i=0;i<inputs.length;++i){
                    if(inputs[i].isRemote()){
                        remote_inputs.push(inputs[i]);
                    }
                }

                return function(){
                    if(remote_inputs.length==0){
                        window.clearInterval(wf.timer);
                        wf.timer = null;
                        if(allRight){
                            wf.form.submit();
                        }else{
                            //远程验证未通过,提示用户重新输入
                        }
                    }else{
                        for(var i=0;i<remote_inputs.length;++i){
                            if(remote_inputs[i].remote_finish){
                                if(!remote_inputs[i].remote_right){
                                    allRight = false;
                                }
                                remote_inputs.splice(i,1);
                            }
                        }
                    }
                }
            }(wh_form);
            
            if(remotes > 0){
                //轮循,检查远程验证是否完毕
                wh_form.timer = window.setInterval(poll,200);
            }else{
                wh_form.form.submit();
            }
            return false;
        }
    }
};

/**
 * 一个type='text'的input元素
 * @param string|object input元素或其id
 * @param object options(带*号必须定义):
 *      key           type            default     description
 -----------------------------------------------------------------
 *      [event_name]  string          onchange    元素触发什么事件时调用验证函数
 *      [msgbox]      string|object   new span    如果未定义,将在文本框后创建一个span用于显示验证信息
 -----------------------------------------------------------------
 *      [url          string          ''          远程验证url,也作为判断是否需要远程验证的标志
 *      [checking_str] string         正在验证...  正在远程验证时显示的字符串
 *      [remote_wstr] string          值重复       远程验证后值未通过验证显示的字符串
 *      [param]       string          ''          远程验证的附加参数. 如果未定义,只传递input元素的值: value=input.value
 *      [onSuccess]]  function        none        处理远程验证结果的函数,接收参数(responseText[,wh_Input对象]),返回bool值,反映验证结果
 ------------------------------------------------------------------
 *      [rstr]        string          ''          验证正确后显示的文字
 *      [wstr]        string          ×           验证错误显示的文字
 *      [rclass]      string          ''          正确提示信息的className      
 *      [wclass]      string          ''          错误提示信息的className
 *      check         function        none        验证函数,必须定义,否则就失去了该类的意义
 *      [onRight]     function        this._r_func 当验证正确时执行的函数,接受wh_Input对象为参数
 *      [onWrong]     function        this._w_func 当验证错误时执行的函数,接受wh_Input对象为参数
 * @param string|HTMLFormElement form 表单id或表单对象,如果指定,只在该表单中查找name为input的元素(此时input必须为string)
 */
function wh_Input(input,options,form){
    if(!options.check){
        alert('wh_Input init failing:\nYou must define options.check!');
        return;
    }

    var input,f;
    if(form) f = wh$(form);
    if(f&&'string'==typeof input) input = f[input];
    else input = wh$(input);

    if((input.nodeName + '').toLowerCase() != 'input'||'text' != input.type && 'password'!=input.type){
        alert("'"+(input['name']||input['id']) +"' is not a valid InputElement!");
        return;
    }
    this.input = input;
    
    //显示验证结果的元素
    var msgbox = wh$(options.msgbox);
    if(!msgbox) msgbox = this._createMsgbox(input);
    this.msgbox = msgbox;
    
    //显示正确或错误信息的函数
    var showRstr = options.onRight||this._get_showmsgfunc(this,options.rstr||'',options.rclass||'');
    var repeatWrong = this._get_showmsgfunc(this,options.remote_wstr||'值重复',options.wclass||'');
    
    //是否需要远程验证
    this.isRemote = this._isRemote(options.url);
    if(this.isRemote()){
        this.remote_right = false;  //初始化时,远程验证未通过
        this.remote_finish = false;
        var remoteCheck = this._getRemoteFunc(this,
            options.url,
            options.checking_str || '正在验证...',
            showRstr,
            repeatWrong,
            options.param || '',
            options.onSuccess||function(){alert('complete');});
    }

    this.handle = this._getHandle(
        this,
        options.check,
        showRstr,
        options.onWrong||this._get_showmsgfunc(this,options.wstr||'×',options.wclass||''),
        remoteCheck,
        repeatWrong);
    //Event.observe(input,options.event_name||'change',this.handle);
    input[options.event_name||'onchange'] = this.handle;
}
wh_Input.prototype = {
    //----------- 以下为私有成员函数 ------------------
    _isRemote:function(url){
        return function(){
            return !!url;
        }
    },
    /**
     * 根据用户定义的函数,得到事件处理函数
     */
    _getHandle: function(proto, c_func, r_func, w_func, remoteCheck, remoteWrong){
        return function(e){
            var e = e || window.event;
            if(e && e.type=='change'){
                proto.remote_right = false;
                proto.remote_finish = false;
            }
            var val =proto['input']['value'];
            if(c_func(val)){
                r_func(proto);

                if(proto.isRemote()){
                    if(!proto.remote_right && !proto.remote_finish){                  
                        remoteCheck();
                    }else if(!proto.remote_right && proto.remote_finish){
                        remoteWrong();
                        return false;
                    }
                }
                return true;
            }else w_func(proto);
            return false;
        }
    },
    /**
     * 得到验证处理函数
     */
    _get_showmsgfunc: function(proto,msg,classname){
        var mb = proto.msgbox;
        return function(obj){
            mb.className = classname;
            mb.innerHTML = msg;
            mb.style.display = '';//show();
        }
    },
    /**
     *  远程验证函数
     */
    _getRemoteFunc:function(proto,url,checking_str,rfunc,wfunc,param,func){
        return function(){
            proto.msgbox.innerHTML = checking_str;
            new Ajax.Request(url,
            {
                method: 'get',
                parameters: 'val='+proto.input.value + (param ? '&' + param : ''),
                onSuccess: function(req){
                    var result = req.responseText;
                    if(func(result,proto)){
                        proto.remote_right = true;
                        rfunc(proto);
                    }else{
                        proto.remote_right = false;
                        wfunc(proto);
                    }
                },
                onComplete: function(){
                    proto.remote_finish = true;
                }
            })
        }
    },
    _createMsgbox:function(input){
        var input = wh$(input);
        var span = document.createElement('span');
        //请注意msgbox初创建时并没有设置display='none'. 这是为了以后可以用msg.show()使其可见
        span.id = input.id + '_msg';
        input.parentNode.insertBefore(span, input.nextSibling);
        return span;
    }
};

//返回一个HTML元素的引用
function wh$(ele){
    if(typeof(ele) == 'string')
        return document.getElementById(ele);
    else return ele;
}