(function($) {
    $.fn.autocomplete = function(options) {
        return $(this).each(function() {
            var rand = Math.round(Math.random()*10000000);
            var input = $(this);
            var settings = $.extend({
                searchVar: 'search',
                url: null,
                minChars: 2,
                delay: 300,
                resultsHtml: '<div class="sub" style="display:none"><div class="sub-bg"><ul></ul></div><div class="sub-bl"><div class="sub-br"></div></div></div>',
                resultsWrapperId: 'autocomplete_' + rand,
                itemHoverClass: 'hover',
                useCache: true,
                hideOnBodyClick: true,
                hideOnBlur: true,
                extraParams: {},
                autoClearResults: true,
                itemsLimit: 10,
                formatItem: function(item, index, input, settings) {
                    return '<li>' + item;
                },
                onBeforeSend: function(input, settings) {},
                onFinish: function(input, settings) {},
                onItemSelect: function(item, input, settings) {},
                onItemOver: function(item, input, settings) {},
                onItemOut: function(item, input, settings) {},
                onEmptyField: function(input, settings) {},
                onHide: function(input, settings) {}
            }, options);
            var req = null;
            var timeout = null;
            var prevVal = null;
            var cache = {};
            var updateResults = function(data) {
                //if(!data || !data.length) return false;
                var html = '';
                var j = 0;
                for(i in data) {
                    html += settings.formatItem(data[i], i, input, settings);
                    if(settings.itemsLimit > 0 && ++j >= settings.itemsLimit) break;
                }
                settings.resultsContainer.html(html);
                if(settings.resultsContainer.children().length > 0) {
                    settings.resultsWrapper.show();
                    settings.resultsContainer.children()
                    .mouseover(function() {
                        settings.resultsContainer.children('.' + settings.itemHoverClass).removeClass(settings.itemHoverClass);
                        if(!$(this).hasClass(settings.itemHoverClass)) $(this).addClass(settings.itemHoverClass);
                        settings.onItemOver($(this), input, settings);
                    })
                    .mouseout(function() {
                        $(this).removeClass(settings.itemHoverClass);
                        settings.onItemOut($(this), input, settings);
                    })
                    .click(function() {
                        var cur = settings.resultsContainer.children('.' + settings.itemHoverClass);
                        var val = $.trim(cur.html());
                        var decoded = $("<div/>").html(val).text();
                        input.val(decoded);
                        settings.resultsWrapper.hide();
                        settings.onItemSelect(cur, input, settings);
                    });
                }
                else {
                    settings.resultsWrapper.hide();
                }
                input.trigger('autocomplete.finish');
                settings.onFinish(input, settings);
            };
            
            if (settings.url !== null) {
                var wrapper = $(settings.resultsHtml);
                $('body').append(wrapper);
                settings.resultsWrapper = wrapper;
                settings.resultsContainer = wrapper.find('ul');
            	
                input.attr('autocomplete', 'off')
                .focus(function() {
                    $(this).addClass('isfocused');
                })
                .blur(function() {
                    $(this).removeClass('isfocused');
                })
                .keyup(function(e) {
//                    e.preventDefault();
//                    if([13, 27, 35, 36, 37, 38, 39, 40].indexOf(e.which) > -1) return true;
                    clearTimeout(timeout);
                    timeout = setTimeout(function() {
                        clearTimeout(timeout);
                        var formVal = input.val();
                        if(formVal !== '' && formVal.length >= settings.minChars) {
                            if(req !== null && req.readyState < 4) req.abort();
                            if(!cache[formVal]) {
                                var params = {};
                                params[settings.searchVar] = formVal;
                                params = $.extend(true, settings.extraParams, params);
                                input.trigger('autocomplete.beforesend');
                                settings.onBeforeSend(input, settings);
                                req = $.get(settings.url, params, function(data) {
                                    if(settings.hideOnBlur && !input.hasClass('isfocused')) return true;
                                    if(settings.useCache) {
                                        cache[formVal] = data;
                                        prevVal = formVal;
                                    }
                                    if(input.val() === formVal) {
                                        updateResults(data);
                                    }
                                }, 'json');
                            }
                            else if(settings.useCache && (prevVal != formVal || prevVal == formVal && settings.resultsWrapper.is(':hidden'))) {
                                updateResults(cache[formVal]);
                            }
                        }
                        else {
                            if(settings.autoClearResults) {
                                settings.resultsContainer.html('');
                                settings.resultsWrapper.hide();
                            }
                            input.trigger('autocomplete.emptyfield');
                            settings.onEmptyField(input, settings);
                        }
                    }, settings.delay);
                })
                .keydown(function(e) {
                    if(settings.resultsWrapper.is(':hidden') || settings.resultsContainer.is(':hidden')) return true;
                    var ind = -1;
                    var items = settings.resultsContainer.children();
                    if(!items.length) return true;
                    var getIndex = function() {
                        items.each(function(i) {
                            if($(this).hasClass(settings.itemHoverClass)) {
                                ind = i;
                                $(this).removeClass(settings.itemHoverClass);
                                return false;
                            }
                        });
                    };
                    switch(e.which) {
                        case 13: // enter
                            if(settings.resultsWrapper.is(':visible') && settings.resultsContainer.is(':visible')) {
                                if(settings.resultsContainer.children('.' + settings.itemHoverClass).length > 0) {
                                    e.preventDefault();
                                    var item = settings.resultsContainer.children('.' + settings.itemHoverClass);
                                    var val = item.html();
                                    var decoded = $("<div/>").html(val).text();
                                    input.val(decoded);
                                    settings.onItemSelect(item, input, settings);
                                }
                                settings.resultsWrapper.hide();
                                settings.onHide(input, settings);
                            }
                            break;
                        case 38: // arrow up
                            e.preventDefault();
                            getIndex();
                            if(ind <= 0) ind = items.length;
                            var item = items.eq(ind-1).addClass(settings.itemHoverClass);
                            settings.onItemOver(item, input, settings);
                            break;
                        case 40: // arrow down
                            e.preventDefault();
                            getIndex();
                            if(items.length <= (ind + 1)) ind = -1;
                            var item = items.eq(ind+1).addClass(settings.itemHoverClass);
                            settings.onItemOver(item, input, settings);
                            break;
                        //case 34: // page down
                        //	e.preventDefault();
                        //	getIndex();
                        //	ind += 10;
                        //	if(items.length <= ind) ind = items.length - 1;
                        //	items.eq(ind).addClass(settings.itemHoverClass);
                        //	settings.onItemOver(items.eq(ind), input, settings);
                        //break;
                        //case 33: // page up
                        //	e.preventDefault();
                        //	getIndex();
                        //	ind -= 10;
                        //	if(ind <= 0) ind = 0;
                        //	items.eq(ind).addClass(settings.itemHoverClass);
                        //	settings.onItemOver(items.eq(ind), input, settings);
                        //break;
                        //case 36: // home
                        //	e.preventDefault();
                        //	getIndex();
                        //	ind = 0;
                        //	items.eq(ind).addClass(settings.itemHoverClass);
                        //	settings.onItemOver(items.eq(ind), input, settings);
                        //break;
                        //case 35: // end
                        //	e.preventDefault();
                        //	getIndex();
                        //	ind = items.length - 1;
                        //	items.eq(ind).addClass(settings.itemHoverClass);
                        //	settings.onItemOver(items.eq(ind), input, settings);
                        //break;
                        case 27: // esc
                            if(settings.resultsWrapper.is(':visible') && settings.resultsContainer.is(':visible')) {
                                e.preventDefault();
                                settings.resultsWrapper.hide();
                                settings.onHide(input, settings);
                            }
                            break;
                    }
                });
                
                if(settings.hideOnBodyClick) {
                    $('body').live('click',function(event) {
                        setTimeout(function() {
                            if(settings.resultsWrapper.is(':visible')) {
                                if($(event.target).get(0) == input.get(0))
                                    return true;
                                $(event.target).parents().each(function() {
                                    if($(this).get(0) != settings.resultsWrapper.get(0)) {
                                        settings.resultsWrapper.hide();
                                        settings.onHide(input, settings);
                                        return false;
                                    }
                                });
                            }
                        }, 200);
                    });
                }
                if(settings.hideOnBlur) {
                    input.blur(function(event) {
                        setTimeout(function() {
                            settings.resultsWrapper.hide();
                            settings.onHide(input, settings);
                        }, 300);
                    });
                }
            }
        });
    };
})(jQuery);
