/*
* jQuery Autocomplete
* VERSION:  1.1  
* DATE: 2015.5.21
* REMARK: Maofangyun nocache版
*/

; (function($) {
    $.fn.extend({
        autocomplete: function(url, options) {
            options = $.extend({}, $.Autocompleter.defaults, {
                title: options && options.title ? options.title : "autocomplete",
                url: url,
                max: options && !options.scroll ? 10 : 150
            }, options);
            return this.each(function() {
                new $.Autocompleter($(this), options);
            });
        },
        result: function(handler) {//关联项的赋值
            return this.bind("result", handler);
        },
        removeRelation: function(handler) {//清除关联项的值
            return this.bind("removeRelation", handler);
        },
        unautocomplete: function() {
            return this.trigger("unautocomplete");
        }
    });

    $.Autocompleter = function(input, options) {
        var KEY = {
            UP: 38,
            DOWN: 40,
            DEL: 46,
            TAB: 9,
            RETURN: 13,
            ESC: 27,
            COMMA: 188,
            PAGEUP: 33,
            PAGEDOWN: 34,
            BACKSPACE: 8
        };

        var timeout;
        var hasFocus = 0;
        var select = $.Autocompleter.Select(options, input, selectCurrent);

        input.bind("keydown", function(event) {
            hasFocus = 1;
            switch (event.keyCode) {
                case KEY.DEL:
                case KEY.BACKSPACE:
                    input.trigger("removeRelation");
                    clearTimeout(timeout);
                    timeout = setTimeout(onChange, options.delay);
                    break;
                case KEY.UP:
                    event.preventDefault();
                    if (select.visible()) {
                        select.prev();
                    } else {
                        onChange();
                    }
                    break;
                case KEY.DOWN:
                    event.preventDefault();
                    if (select.visible()) {
                        select.next();
                    } else {
                        onChange();
                    }
                    break;
                case KEY.PAGEUP:
                    event.preventDefault();
                    if (select.visible()) {
                        select.pageUp();
                    } else {
                        onChange();
                    }
                    break;
                case KEY.PAGEDOWN:
                    event.preventDefault();
                    if (select.visible()) {
                        select.pageDown();
                    } else {
                        onChange();
                    }
                    break;
                case KEY.RETURN:
                    if (selectCurrent()) {
                        event.preventDefault();
                        return false;
                    }
                    break;
                case KEY.ESC:
                    select.hide();
                    break;
                default:
                    clearTimeout(timeout);
                    timeout = setTimeout(onChange, options.delay);
                    break;
            }
        }).focus(function() {
            hasFocus = 1;
            if (!select.visible()) {
                onChange();
            }
        }).click(function() {
            hasFocus = 1;
            if (!select.visible()) {
                onChange();
            }
        }).blur(function() {
            hasFocus = 0;
            clearTimeout(timeout);
            timeout = setTimeout(hideResultsNow, 200);
        }).bind("unautocomplete", function() {
            select.unbind();
            input.unbind();
        });

        function selectCurrent() {
            var selected = select.selected();
            if (!selected)
                return false;
            var v = selected.result;
            input.val(v);
            hideResultsNow();
            input.trigger("result", [selected.data]);
            return true;
        }

        function onChange() {
            if ((typeof options.url == "string") && (options.url.length > 0)) {
                $.ajax({
                    url: options.url,
                    data: { userInput: $.trim(input.val()) },
                    success: function(data) {
                        data = parse(data);
                        if (data && data.length && hasFocus) {
                            stopLoading();
                            select.display(data);
                            select.show();
                        } else {
                            hideResultsNow();
                        }
                    }
                });
            } else {
                select.emptyList();
                hideResultsNow();
            }
        }
        function parse(data) {
            var parsed = [];
            data = eval('(' + data + ')');
            for (var i = 0, k = data.length; i < k; i++) {
                var row = data[i];
                if (row) {
                    parsed[parsed.length] = {
                        data: row,
                        result: options.formatResult && options.formatResult(row, row[0]) || row[0]
                    };
                }
            }
            return parsed;
        };

        function hideResultsNow() {
            select.hide();
            clearTimeout(timeout);
            stopLoading();
        }

        function stopLoading() {
            input.removeClass(options.loadingClass);
        }
    };

    $.Autocompleter.defaults = {
        resultsClass: "ac_results",
        loadingClass: "ac_loading",
        delay: 400,
        max: 100,
        selectFirst: true,
        formatItem: function(row) { return row[0]; },
        width: 0,
        scroll: true,
        scrollHeight: 180,
        offsets: [0, 0]//下拉选项偏移量[left,top]
    };

    $.Autocompleter.Select = function(options, input, select) {
        var CLASSES = {
            ACTIVE: "ac_over"
        };

        var listItems,
		active = -1,
		data,
		element,
        table,
        tbody;

        // Create results
        function init() {
            var isExit = false;
            $.each($("body").find("." + options.resultsClass), function() {
                if ($(this).attr("title") == options.title) {
                    isExit = true;
                    element = $(this);
                    table = element.find("table");
                    tbody = table.find("tbody");
                    return false;
                }
            });

            if (isExit) {
                trbindEvent();
                element.hide();
                return;
            }
            element = $("<div/>").hide().addClass(options.resultsClass).attr("title", options.title).css("position", "absolute").appendTo(document.body);

            table = $("<table/>").appendTo(element);
            if (options.thead) {
                var thead = $("<thead/>").appendTo(table), theadTr = $("<tr/>").appendTo(thead);
                for (var i in options.thead) {
                    var th = $("<th/>").html(options.thead[i].text);
                    if (options.thead.width) {
                        th.css("width", options.thead[i].width);
                    }
                    th.appendTo(theadTr);
                }
            }
            tbody = $("<tbody/>").appendTo(table);
            trbindEvent();
            if (options.width > 0) {
                element.css("width", options.width);
            }
        }
        function trbindEvent() {
            tbody.mouseover(function(e) {
                var target = $(e.target);
                if (target.is("td")) {
                    active = target.parent().index();
                    $("tr", target.parent().parent()).removeClass(CLASSES.ACTIVE);
                    target.parent().addClass(CLASSES.ACTIVE);
                }
            }).click(function(e) {
                select();
            });
        }
        function hide() {
            element && element.hide();
            listItems && listItems.removeClass(CLASSES.ACTIVE);
            active = -1;
        }
        function moveSelect(step) {
            listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
            movePosition(step);
            var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
            if (options.scroll) {
                var offset = 0;
                listItems.slice(0, active).each(function() {
                    offset += this.offsetHeight;
                });
                if ((offset + activeItem[0].offsetHeight - table.scrollTop()) > table[0].clientHeight) {
                    table.scrollTop(offset + activeItem[0].offsetHeight - table.innerHeight());
                } else if (offset < table.scrollTop()) {
                    table.scrollTop(offset);
                }
            }
        };

        function movePosition(step) {
            active += step;
            if (active < 0) {
                active = listItems.size() - 1;
            } else if (active >= listItems.size()) {
                active = 0;
            }
        }

        function fillList() {
            tbody.empty();
            var max = options.max && options.max < data.length ? options.max : data.length;
            var fields = options.formatItem().split(',');
            for (var i = 0; i < max; i++) {
                if (!data[i]) continue;

                var tr = $("<tr/>").data("ac_data", data[i]).addClass(i % 2 == 0 ? "ac_even" : "ac_odd").appendTo(tbody);
                for (var j = 0; j < fields.length; j++) {
                    tr.append($("<td/>").html(data[i].data[fields[j]]));
                }
            }
            listItems = tbody.find("tr");
            if (options.selectFirst) {
                listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
                active = 0;
            }
        }

        return {
            display: function(d) {
                init();
                data = d;
                fillList();
            },
            next: function() {
                moveSelect(1);
            },
            prev: function() {
                moveSelect(-1);
            },
            pageUp: function() {
                if (active != 0 && active - 8 < 0) {
                    moveSelect(-active);
                } else {
                    moveSelect(-8);
                }
            },
            pageDown: function() {
                if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
                    moveSelect(listItems.size() - 1 - active);
                } else {
                    moveSelect(8);
                }
            },
            hide: function() {
                hide();
            },
            visible: function() {
                return element && element.is(":visible");
            },
            current: function() {
                return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
            },
            show: function() {
                var offset = input.offset();
                element.css({
                    width: typeof options.width == "string" || options.width > 0 ? options.width : input.width(),
                    top: offset.top + input[0].offsetHeight + options.offsets[1],
                    left: offset.left + options.offsets[0]
                }).show();
                if (options.scroll) {
                    table.scrollTop(0);
                    table.css({
                        maxHeight: options.scrollHeight,
                        overflow: 'auto'
                    });
                }
            },
            selected: function() {
                var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
                return selected && selected.length && $(selected[0]).data("ac_data");
            },
            emptyList: function() {
                table && table.empty();
            },
            unbind: function() {
                element && element.remove();
            }
        };
    };
})(jQuery);