/**
 * jQuery CB Suggest 1.2
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.autocomplete.js
 */
(function( $ ) {
    $.widget( 'cb.cbsuggest', {
        options: {
            height: 300,
            init: 1,
            menuClass: 'cb-suggest',
            mustMatch: false,
            renderItem: function( li, item ) {
                var $a = $('<a><b>' + item.label + '</b></a>');
                if (item.highlight) { $a.addClass('ui-state-highlight'); }
                else if (li.closest('ul').find('li').length % 2 > 0) { $a.addClass('ui-state-active'); }
                return $a;
            },
            result: {},
            searchParams: { url: '/emissions/emicomplete2.php', mode: 2, language: 'rus' },
            width: 0
        },
        _defaults: {
            width: 200
        },
        _create: function(settings) {

            var self = this,
                suppressKeyPress;

            self.cache = {};

            $.extend(true, self.options, settings);

            if (typeof self.options.width != 'number') self.options.width = 0;
            //
            self.busy = false;
            // Данные по-умолчанию
            self.default_item = { label: '' };
            // Здесь последние данные
            self.item = self.default_item;

            self.element
                .attr( 'autocomplete', 'off' )
                .autocomplete({
                    appendTo: self.options.appendTo,
                    source: function(request, response) {

                        if (self.busy) { return; }

                        var term = self._sanitizeTerm(request.term);

                        self._checkResult(0);

                        if (!term.length) { return; }

                        if (term in self.cache) {
                            self._response(response, self.cache[term], 0);
                            return;
                        }

                        var params = { term: request.term };

                        $.extend(params, self.options.searchParams);

                        self._request(params, response);
                    },
                    open: function( event, ui ) {
                        var $menu = self.element.data("autocomplete").menu.element;
                        if (self.options.width) $menu.width(self.options.width);
                        /* IE fix */
                        if ($.support.boxModel || !self.options.height) return;
                        if (!self.options.width) $menu.width(self._defaults.width);
                        $menu.height( 'auto' ).css({'overflow-y': 'visible'});
                        if ($menu[0].scrollHeight > self.options.height) {
                            $menu.height(self.options.height).css({'overflow-y': 'scroll'});
                        }
                    },
                    // закрытие списка
                    close: function(event) { self._checkResult(0); },
                    // потеря фокуса при закрытом списке
                    change: function(event, ui){
                        self._checkResult(self.options.mustMatch);
                        var previous = $.trim(self._sanitizeTerm(self.element.data("autocomplete").previous)),
                            term = $.trim(self._sanitizeTerm(self.element.val()));
                        if (previous !== term) {
                            self._trigger("change", event, self.item);
                        }
                    },
                    // при фокусе на элементе списка копирует данные в результат
                    focus: function(event, ui) {
                        // только клавиатурные события
                        if ( event.keyCode ) {
                            self._select(ui.item);
						}
                        return false;
                    },
                    // выбор элемента из выпадающего списка
                    select: function( event, ui ) {
                        self._select(ui.item);
                        self._trigger("select", event, ui.item);
                        return false;
                    }
                })
                .keydown(function(event){
                    var keyCode = $.ui.keyCode;
                    suppressKeyPress = false;
                    switch( event.keyCode ) {
                    case keyCode.ENTER:
                    case keyCode.NUMPAD_ENTER:
                        // предотвращение отправки формы, если активно меню или идет поиск
                        var ac = self.element.data("autocomplete");
                        if (ac.menu.element.is(':visible') || self.options.mustMatch && self._checkResult(0)) {
                            suppressKeyPress = true;
                            event.preventDefault();
                        }
                        self.element.autocomplete('close');
                        break;
                    }
                })
                .keypress(function(event){
                    if ( suppressKeyPress ) {
                        suppressKeyPress = false;
                        event.preventDefault();
                    }
                })
                ;

            var ac = self.element.data("autocomplete");
            ac._renderItem = function(ul, item) {
                var $li = $('<li></li>').data("item.autocomplete", item).appendTo(ul);
                if ($.isFunction(self.options.renderItem)) {
                    $li.append(self.options.renderItem($li, item));
                }
                return $li;
            };
            var $menu = ac.menu.element;
            $menu
                .addClass(self.options.menuClass)
                .removeClass('ui-corner-all')
                .bind('menufocus menublur', function(e, ui){
                    var li = $(this).data('menu').active;
                    var data = li.data('item.autocomplete') || {};
                    if (data.highlight) { li.children("a").toggleClass('ui-state-highlight'); }
                    else if (li.index() % 2 == 0) { li.children("a").toggleClass('ui-state-active'); }
                });
            if (self.options.height) {
                $menu.css({'max-height': self.options.height + 'px'});
            }

            if (self.options.init) {
                self.refresh();
            }
        },
        // Загрузка значений
        load: function() {
            var self = this,
                item = arguments[0] || {},
                need_request = false;

            self.item = self.default_item;

            if ($.isEmptyObject(item) && !$.isEmptyObject(self.options.result.data)) {
                // Сбор начальных значений
                $.each(self.options.result.data, function(k, v) {
                    if ($(v).length > 0) {
                        item[k] = $(v)[$(v).is(':input') ? 'val' : 'text']();
                    }
                });
            }

            $.each(item, function(k, v){
                need_request |= /^\d+$/.test(item[k]) ? item[k] * 1 > 0 : item[k].length > 0;
            });

            if (!need_request) {
                self.reset();
                return;
            }

            var params = { init: 1 };

            $.extend(params, self.options.searchParams, item);

            self._request(params, false);
        },
        refresh: function() { this.load(); },
        reset: function() {
            this._select(this.default_item);
            this.cache = {};
            this._trigger("change", null, this.item);
        },
        result: function() {
            this.element.autocomplete('close');
            this.item.lastTerm = this.element.val();
            return this.item;
        },
        // Проверка совпадения значения инпута и текущего элемента
        _checkResult: function(setLabel) {
            var term = $.trim(this._sanitizeTerm(this.element.val())),
                label = $.trim(this._sanitizeTerm(this.item.label)),
                changed = term !== label;
            this._select(changed ? this.default_item : this.item, setLabel);
            return changed;
        },
        _request: function(params, response) {
            var self = this, url = params.url;
            delete params.url;
            self.busy = true;
            $.ajax({
                url: url,
                dataType: "json",
                data: params,
                success: function(result) {
                    if (!params.init) {
                        self.cache[params.term] = result;
                        self._response(response, result, 0);
                    }
                    else {
                        self._response(null, result, 1);
                    }
                    self.busy = false;
                    if (params.init) self.element.autocomplete('option', 'change')(self.item);
                },
                error: function() { self.busy = false; }
            });
        },
        // Включение списка и выбор первого элемента
        _response: function(response, result, init) {
            if (this.options.result.total) {
                this._setValues({total: this.options.result.total}, {total: result.data.length + '/' + result.total});
            }
            if (!init && $.isFunction(response)) response(result.data);
            this._select((result.data.length ? result.data[0] : this.default_item), init);
        },
        // Если в конце есть пробел - не трогаем его
        _sanitizeTerm: function(term) {
            if (typeof term === 'string') {
                return term.toLowerCase().replace(/[^a-zа-я\d]+/ig, " ").replace(/\s+/g, " ").replace(/^\s+/, "");
            }
            return "";
        },
        // Установка this.item и значения элемента
        _select: function(item) {
            var setLabel = arguments.length == 1 || arguments[1];
            this.item = item;
            if (setLabel) {
                this.element.val(item.label);
                this._setCursorPosition(this.element.val().length);
            }
            this._setResult();
        },
        // Установка курсора в инпуте
        _setCursorPosition: function(pos) {
            var elem = this.element[0];
            if (this.element.is(':visible') && elem.setSelectionRange && pos>0) {
                elem.setSelectionRange(pos, pos);
            } else if (elem.createTextRange) {
                var range = elem.createTextRange();
                range.collapse(true);
                range.moveEnd('character', pos);
                range.moveStart('character', pos);
                range.select();
            }
        },
        // Запись результата в обозначенные при инициализации места
        _setResult: function() {
            if (this.options.result.data) {
                this._setValues(this.options.result.data, this.item);
            }
        },
        _setValues: function(target, data) {
            $.each(target, function(prop, el) {
                $.each($(el), function(){
                    $(this)[$(this).is(':input') ? 'val' : 'text'](data[prop]);
                });
            });
        }
    });
}( jQuery ));
