

/* misc helpers */

if ( !( $.is_null instanceof Function ) )
    $.is_null = function(o) { return o== undefined || o== null; };
if ( !( $.to_array instanceof Function ) )
    $.to_array = function(a) { if ( a instanceof Array ) return a; if ( a instanceof String ) return [a]; if ( !$.is_null( a.length ) ) { var aa =[]; for ( var i= 0; i< a.length; i++) aa.push( a[i] ); return aa; } return [a]; };
if ( !( $.merge_hashes instanceof Function ) )
    $.merge_hashes = function( hash_left, hash_right, overwrite_left_params ) {
        if ( $.is_null(overwrite_left_params) ) overwrite_left_params= false;
        for ( key in hash_right ) {
            if ( hash_left[ key ] == undefined || overwrite_left_params )
                hash_left[ key ] = hash_right[ key ];
        }
        return hash_left;
    }
if (! Function.prototype.ref )
    Function.prototype.ref = function() { 
        var f = this, a = $.to_array( arguments ), o = a.shift();
        return function() {
            var aa = a.concat( $.to_array( arguments ) );
            return f.apply( o, aa );
        };
    };


// SETUP CONSTRUCTOR

jQuery.fn.validate = function( params ) {
    return new jQuery.FormValidator( this, params );
};

jQuery.fn.observing_validator = function( options, params ){
    var validator = new jQuery.FormValidator( this, options );
    validator.observe( params );
    
    return validator;
};



// SETUP REGEX

$.merge_hashes( jQuery.fn.validate, {
    REGEX: {
        numeric:            /^\d+$/,
        alphabetic:         /^[a-z]+$/i,
        alphabetic_lower:   /^[a-z]+$/,
        alphabetic_upper:   /^[A-Z]+$/,
        alphanumeric:       /^[a-zA-Z\d]+$/,
        not_empty:          /.+/,
        standard:           /^\w+$/i,           // matches all a-z, A-Z, 0-9 and _
        
        phone:              /^\+?(?:\d\d[\/\s]?)+$/,
        email:              /^[a-z\d]+(?:[\-\.\_][a-z\d]+)*[a-z\d]+@[\w\d]+(?:[-\.][a-z\d][a-z\d\-]*[a-z\d])*[a-z\d]+\.([a-z]{2,4})$/i,
        
        username:           /^[-\w_]+$/,
        password:           /^[-\w_\$\!&%]+$/,
        
        date_short_pre:     /^(?:0?[1-9]|[12][0-9]|3[01])([-\/\.])(?:0?[1-9]|1[0-2])\1\d\d$/,
        date_short_suf:     /^\d\d([-\/\.])(?:0?[1-9]|1[0-2])\2(?:0?[1-9]|[12][0-9]|3[01])$/,
        
        date_long_pre:      /^(?:0?[1-9]|[12][0-9]|3[01])([-\/\.])(?:0?[1-9]|1[0-2])\1(19|20)\d\d$/,
        date_long_suf:      /^(19|20)\d\d([-\/\.])(?:0?[1-9]|1[0-2])\2(?:0?[1-9]|[12][0-9]|3[01])$/
    }
    
});
        


// SETUP METHODS

jQuery.FormValidator = function( formular, params ) {
    this._params = $.merge_hashes( {
        trim_whitespaces: true,
        
        min_username_length: 6,
        max_username_length: 20,
        
        min_password_length: 6,
        max_password_length: 20,
        
        empty_ok: false,
        
        call_on_success: function( inp ) { $( inp ).removeClass('error'); },
        call_on_failure: function( inp ) { $( inp ).addClass('error') }
        
    }, params|| {}, true );
    
    this._last_check = {};
    
    this._formular = $( formular );
    
}

jQuery.FormValidator.prototype = {
    
    /* ---- INTERNAL ---- */
    
    _trim_whitespaces: function( value ) {
        value = value.replace( /^\s*/, '' );
        value = value.replace( /\s*$/, '' );
        return value;
    },
    
    _get_value: function( input ) {
        var value = $(input).val();
        if ( this._params.trim_whitespaces )
            value = this._trim_whitespaces( value );
        return value;
    },
    
    _get_all_checkers_for_type: function( type, obj_element ) {
        if ( !( obj_element instanceof Array ) )
            obj_element = [ obj_element ];
        var res = [];
        
        // first run -> transform plain-forms to complex forms
        var obj_element_new = [];
        
        for ( var i= 0; i< obj_element.length; i++ ) {
            var el = obj_element[i];
            if ( el instanceof String ) {
                obj_element_new = obj_element_new.concat( $(el).get() );
            } else if ( !$.is_null( el.field ) ) {
                var els = $(el.field).get();
                
                for ( var j = 0; j< els.length; j++ ) {
                    var el_new = $.merge_hashes( {}, el );
                    el_new.element = els[j];
                    obj_element_new.push( el_new );
                }
            } else if ( !$.is_null( el.element ) ) {
                obj_element_new.push( el );
            }
        }
        
        // second run -> generate checker
        ALL_ELEMENTS:
        for ( var i= 0; i< obj_element_new.length; i++ ) {
            var e = obj_element_new[i];
            
            
            var type = e.type;
            if ($.is_null( type ) && $.is_null( e.regex ) ) continue ALL_ELEMENTS;
            
            
            // get regex
            if (!$.is_null( jQuery.fn.validate.REGEX[ type ] ) )
                e.regex = [ jQuery.fn.validate.REGEX[ type ] ];
                
            else if ( type== 'date' )
                e.regex = [
                    jQuery.fn.validate.REGEX.date_long_pre,
                    jQuery.fn.validate.REGEX.date_long_suf,
                    jQuery.fn.validate.REGEX.date_short_pre,
                    jQuery.fn.validate.REGEX.date_short_suf
                ];
                
            else if ( type== 'date_long' )
                e.regex = [
                    jQuery.fn.validate.REGEX.date_long_pre,
                    jQuery.fn.validate.REGEX.date_long_suf
                ];
                
                
            else if ( type== 'date_short' )
                e.regex = [
                    jQuery.fn.validate.REGEX.date_short_pre,
                    jQuery.fn.validate.REGEX.date_short_suf
                ];
                
            else if ( !$.is_null( e.regex ) && e.regex instanceof Array )
                e.regex = e.regex ;
            
            else if ( !$.is_null( e.regex ) || !( e.regex instanceof RegExp ) ) {
                if ( e.regex.match( /^regex:(.*)$/ ) )
                    e.regex = RegExp.$1;
                e.regex = [ e.regex ];
            
            } else
                continue ALL_ELEMENTS;
                
            
            // create checkker funktion
            
            var self = this;
            
            var checker = function() {
                var e = this;
                var v = self._get_value( e.element );
                var ok = true;
                var options = $.merge_hashes( e, self._params, false );
                var check_modifier = options.check_modifier;
                
                if ( options.empty_ok && v == '' ) {
                } else {
                
                try {
                    if ( options.min_length && v.length < options.min_length ) {
                        ok = false; 
                        throw new '';
                    }
                    
                    if ( options.max_length && v.length > options.max_length ) {
                        ok = false;
                        throw new '';
                    }
                    
                    ok = e.regex.length == 0;
                    
                    // regex1 OR regex2 OR regex3 OR ..
                    for ( var i= 0; i< e.regex.length && !ok; i++ ) 
                        ok = v.match(e.regex[i]) != null;
                    
                    
                    
                    
                } catch (ee) {}
                }
                
                if ( check_modifier && check_modifier instanceof Function )
                    ok = check_modifier( ok, v, e.element ); 
                
                if (!ok && e.failure instanceof Function )
                    e.failure(e.element, self, e.message);
                
                else if (ok && e.success instanceof Function ) 
                    e.success(e.element, self);
                
                if ( ok )
                    self._params.call_on_success( $(e.element).get(0), self );
                else
                    self._params.call_on_failure( $(e.element).get(0), self, e.message );
                
                return ok;
            }.ref( e );
            
            res.push( [ e.element, checker ] );
        }
        
        return res;
    },
    
    
    _get_all_checker: function( params ) {
        var checkers = [];
        for ( type in params ) {
            checkers = checkers.concat( this._get_all_checkers_for_type( type, params[ type ] ) );
        }
        return checkers;
    },
    
    
    observe: function( params ) {
        if ( $.is_null( params ) )
            params = this._last_check;
        
        var element_checkers = this._get_all_checker( params );
        
        for ( var i = 0; i< element_checkers.length; i++ ) {
            var element = element_checkers[i][0];
            var checker = element_checkers[i][1];
            var node = $(element).get(0);
            
            if ( node.nodeName == 'TEXTAREA' || ( node.nodeName == 'INPUT' && node.type != 'checkbox' && node.type!= 'radio' ) ) {
                $(element).blur( checker );
            
            } else if ( node.nodeName == 'SELECT' || ( node.nodeName == 'INPUT' && ( node.type == 'checkbox' || element.type == 'radio' ) ) )
                $(element).change( checker );
        }
        this._last_check = params;
        
        return this;
    },
    
    
    check_fields: function( params, at_least_one_ok ) {
        if ( $.is_null( params ) )
            params = this._last_check;
            
        if ( $.is_null( at_least_one_ok ) ) at_least_one_ok = false;
        var all_ok = true, i = 0;
        var element_checkers = this._get_all_checker( params );
        for ( i= 0; i< element_checkers.length; i++ ) {
            if ( !element_checkers[i][1]() )
                all_ok = false;
        }
        this._last_check = params;
        return all_ok || ( i> 0 && i < element_checkers.length-1 && at_least_one_ok );
    }
    
    
    
};



/*
AUTHOR:
    uk@fortrabbit.com


DESCRIPTION:
    jquery-plugin to validate / check html-formulars


EXAMPLE:
    <style ..> .error { border-color: red } ..</style>
    
    <form id="formular_id" ..>
        ...
        <input type="text" id="field1" />
        ..
        <input type="text" id="field2" />
        ..
        <input type="text" id="field3" />
        ..
        <input type="text" id="field4" />
        ..
    </form>
    
    // ...
    
    // create checks ( we'll use them twice.. )
    var my_checks = {
        check: [
            {
                field: '#field1',
                type: 'alphanumeric',
                max_length: 200
            },{
                field: '#field2',
                regex: /^a\d+$/,            // begins with "a", trailing: numbers
                min_length: 2,
                max_length: 5,
                message: 'well.. not ok',
                success: function( element, validator ) { .. },
                failure: function( element, validator, message ) { .. }
            },{
                field: '#field3',
                type: 'email'
            },{
                field: '#field4',
                type: 'date_short'
            }
        ]
    };
    
    // define
    var validator = $('#formular_id').observing_validator({
        call_on_success: function( inp ) { $( inp ).removeClass('error') },
        call_on_failure: function( inp ) { $( inp ).addClass('error') },
    }, my_checks );
    
    
    
    
    // later:
    if ( validator.check_fields() ) { // uses last defined check..
        document.forms[0].submit();
    } else {
        alert("not everything is ok");
    }
    
    
    // [ here comes full description - anno 2099 ]


*/







