/**
 * 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: '/dbcmd/applications/cbsuggest/1.2/php/cbsuggest.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' )
                .addClass('cb-suggest-input')
				.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') {
				var c, res = '';
				for (var i = 0; i < term.length; i++) {
					c = term.charAt(i);
					// Буквы (любые европейские [a-z] не подходит), цифры и пробелы
					if (/[\d\s]/.test(c) || c.toUpperCase() !== c.toLowerCase()) {
						res += c;
					}
				}
				return res.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 ));
