;(function($) {
    $.fn.inputHint = function( options ) {
        var opts = $.extend({}, $.fn.inputHint.defaults, options);
        
        if ( opts.hide ) {
            hideHint();
            return this;
        }
        
        initHint( opts );

        return this.each(function(){
                bindHint( this, opts );
            });
    };
    
    $.extend( $.fn.inputHint, {
        defaults: {
            bindTo:   null,
            fixOffset: null,
            cssClass: 'inputHint',
            position: ['top','begin'],
            offset  : [0,0],
            timeOut : null // sec
        }
    });
    
    var HINT = {
            $body   : null,
            $frame  : null,
            timer   : null,
            setOffet: null
        };
        
    function initHint( opts ) {
        if ( HINT.$body != null ) {
            return false;
        }

        HINT.$body = $('<div>')
            .css({  display : 'none',
                    position: 'absolute',
                    zIndex  : 9100
                })
            .prependTo( opts.bindTo || document.body );
            
        if ( $.browser.msie && $.browser.version < 7 ) {
            HINT.$frame = $('<iframe>')
                .attr({
                        frameborder : '0',
                        scrolling   : 'no',
                        src         : 'javascript:"<html></html>"'
                    })
                .css({  display : 'none',
                        position: 'absolute',
                        opacity : 0,
                        zIndex  : 9000
                    })
                .insertBefore( HINT.$body );
        }
    };
    
    function bindHint(el, opts) {
        var title = $(el).attr('title');
        
        var events = ['focus.hint', 'blur.hint'];
        
        if ( $(el).is(':checkbox,:radio,:button') ) {
            events = ['focus.hint mouseover.hint', 'blur.hint mouseout.hint'];
        }
        
        $( el )
            .filter(':input[@title]')
            .removeAttr('title')
            .bind(events[0], function(e){
                    HINT.$body
                        .html( title )
                        .addClass( opts.cssClass );

                    var offset = $(this).offset();

                    if ( $.isFunction( opts.fixOffset ) ) {
                        var fixedOffset = opts.fixOffset.call();
                        
                        offset.left -= fixedOffset.left;
                        offset.top -= fixedOffset.top;
                    }

                    offset.left += $(this).outerWidth() + opts.offset[0];
                    offset.top  -= HINT.$body.outerHeight() - opts.offset[1];

        			var size = {
                        w : HINT.$body.outerWidth() + $(this).outerWidth(),
                        h : HINT.$body.outerHeight() + $(this).outerHeight()
                    };
        			
        			switch ( opts.position[0] ) {
                        case 'center':  offset.top += size.h / 2;   break;
                        case 'bottom':  offset.top += size.h;       break;
                    }

        			switch ( opts.position[1] ) {
                        case 'center':  offset.left -= size.w / 2;                          break;
                        case 'left':    offset.left -= size.w;                              break;
                        case 'midleft': offset.left -= size.w - $(this).outerWidth() / 2;   break;
                        case 'midright':offset.left -= $(this).outerWidth() / 2;            break;
                        case 'begin':   offset.left -= $(this).outerWidth();                break;
                        case 'end':     offset.left -= size.w - $(this).outerWidth();       break;
                    }

                    offset.left = parseInt( offset.left );
                    offset.top = parseInt( offset.top );
                    
                    HINT.$body
                        .css({  left : offset.left +'px',
                                top  : offset.top +'px'
                            })
                        .show();

                    if ( $.browser.msie && $.browser.version < 7 ) {
                        HINT.$frame
                            .css({
                                    left    : offset.left +'px',
                                    top     : offset.top +'px',
                                    width   : HINT.$body.outerWidth() + 'px',
                                    height  : HINT.$body.outerHeight() + 'px'
                                })
                            .show();
                    }
                    
                    if ( HINT.timer ) {
                        window.clearTimeout( HINT.timer );
                    }

                    if ( parseInt( opts.timeOut ) > 0 ) {
                        HINT.timer = window.setTimeout( function(){
                                $(el).trigger('blur.hint');
                            }, parseInt( opts.timeOut ) * 1000 );
                    }
                })
            .bind(events[1], function(e){
                    if ( HINT.timer ) {
                        window.clearTimeout( HINT.timer );
                    }
                    
                    hideHint();
                });
    };
    
    function hideHint() {
        HINT.$body
            .hide()
            .removeClass()
            .empty();

        if ( $.browser.msie && $.browser.version < 7 ) {
            HINT.$frame.hide();
        }
    };
})(jQuery);
